/*
* Copyright 2017-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.facebook.buck.jvm.java.abi.source;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import com.facebook.buck.jvm.java.testutil.compiler.CompilerTreeApiParameterized;
import com.google.common.base.Joiner;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.SimpleAnnotationValueVisitor8;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(CompilerTreeApiParameterized.class)
public class TreeBackedAnnotationValueTest extends CompilerTreeApiParameterizedTest {
@Test
public void testBooleanValue() throws IOException {
compile(
Joiner.on('\n').join("public @interface Foo {", " boolean value() default true;", "}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitBoolean(boolean b, Void aVoid) {
assertTrue((boolean) defaultValue.getValue());
assertEquals(defaultValue.getValue(), b);
return null;
}
},
null);
assertEquals("true", defaultValue.toString());
}
@Test
public void testByteValue() throws IOException {
compile(Joiner.on('\n').join("public @interface Foo {", " byte value() default 42;", "}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitByte(byte b, Void aVoid) {
assertEquals((byte) 42, method.getDefaultValue().getValue());
assertEquals(defaultValue.getValue(), b);
return null;
}
},
null);
assertEquals("(byte)0x2a", defaultValue.toString());
}
@Test
public void testCharValue() throws IOException {
compile(Joiner.on('\n').join("public @interface Foo {", " char value() default 'x';", "}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitChar(char c, Void aVoid) {
assertEquals('x', method.getDefaultValue().getValue());
assertEquals(defaultValue.getValue(), c);
return null;
}
},
null);
assertEquals("'x'", defaultValue.toString());
}
@Test
public void testShortValue() throws IOException {
compile(Joiner.on('\n').join("public @interface Foo {", " short value() default 42;", "}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitShort(short s, Void aVoid) {
assertEquals((short) 42, method.getDefaultValue().getValue());
assertEquals(defaultValue.getValue(), s);
return null;
}
},
null);
assertEquals("42", defaultValue.toString());
}
@Test
public void testIntegerValue() throws IOException {
compile(Joiner.on('\n').join("public @interface Foo {", " int value() default 42;", "}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitInt(int i, Void aVoid) {
assertEquals(42, method.getDefaultValue().getValue());
assertEquals(defaultValue.getValue(), i);
return null;
}
},
null);
assertEquals("42", defaultValue.toString());
}
@Test
public void testLongValue() throws IOException {
compile(Joiner.on('\n').join("public @interface Foo {", " long value() default 42;", "}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitLong(long l, Void aVoid) {
assertEquals(42L, method.getDefaultValue().getValue());
assertEquals(defaultValue.getValue(), l);
return null;
}
},
null);
assertEquals("42L", defaultValue.toString());
}
@Test
public void testFloatValue() throws IOException {
compile(Joiner.on('\n').join("public @interface Foo {", " float value() default 42;", "}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitFloat(float f, Void aVoid) {
assertEquals(42f, method.getDefaultValue().getValue());
assertEquals(defaultValue.getValue(), f);
return null;
}
},
null);
assertEquals("42.0f", defaultValue.toString());
}
@Test
public void testDoubleValue() throws IOException {
compile(Joiner.on('\n').join("public @interface Foo {", " double value() default 42;", "}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitDouble(double d, Void aVoid) {
assertEquals(42.0, method.getDefaultValue().getValue());
assertEquals(defaultValue.getValue(), d);
return null;
}
},
null);
assertEquals("42.0", defaultValue.toString());
}
@Test
public void testStringValue() throws IOException {
compile(
Joiner.on('\n').join("public @interface Foo {", " String value() default \"42\";", "}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitString(String s, Void aVoid) {
assertEquals("42", method.getDefaultValue().getValue());
assertEquals(defaultValue.getValue(), s);
return null;
}
},
null);
assertEquals("\"42\"", defaultValue.toString());
}
@Test
public void testArrayValue() throws IOException {
compile(
Joiner.on('\n')
.join("public @interface Foo {", " char[] value() default {'4', '2'};", "}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitArray(List<? extends AnnotationValue> vals, Void aVoid) {
assertEquals(vals, defaultValue.getValue());
assertThat(
vals.stream().map(AnnotationValue::getValue).collect(Collectors.toList()),
Matchers.contains('4', '2'));
return null;
}
},
null);
assertEquals("{'4', '2'}", defaultValue.toString());
}
@Test
public void testClasspathEnumValue() throws IOException {
compile(
Joiner.on('\n')
.join(
"import java.lang.annotation.*",
"public @interface Foo {",
" RetentionPolicy value() default RetentionPolicy.SOURCE;",
"}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
VariableElement sourceField =
findField("SOURCE", elements.getTypeElement("java.lang.annotation.RetentionPolicy"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitEnumConstant(VariableElement constant, Void aVoid) {
assertSame(sourceField, constant);
assertSame(constant, defaultValue.getValue());
return null;
}
},
null);
assertEquals("java.lang.annotation.RetentionPolicy.SOURCE", defaultValue.toString());
}
@Test
public void testLocalEnumValue() throws IOException {
compile(
Joiner.on('\n')
.join(
"import java.lang.annotation.*",
"public @interface Foo {",
" FooEnum value() default FooEnum.Bar;",
"}",
"enum FooEnum { Foo, Bar, Baz };"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
VariableElement sourceField = findField("Bar", elements.getTypeElement("FooEnum"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitEnumConstant(VariableElement constant, Void aVoid) {
assertSame(sourceField, constant);
assertSame(constant, defaultValue.getValue());
return null;
}
},
null);
assertEquals("FooEnum.Bar", defaultValue.toString());
}
@Test
public void testPrimitiveClassValue() throws IOException {
compile(
Joiner.on('\n').join("public @interface Foo {", " Class value() default int.class;", "}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitType(TypeMirror t, Void aVoid) {
assertSameType(types.getPrimitiveType(TypeKind.INT), t);
assertSame(t, defaultValue.getValue());
return null;
}
},
null);
assertEquals("int.class", defaultValue.toString());
}
@Test
public void testClassValue() throws IOException {
compile(
Joiner.on('\n').join("public @interface Foo {", " Class value() default Foo.class;", "}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitType(TypeMirror t, Void aVoid) {
assertSameType(elements.getTypeElement("Foo").asType(), t);
assertSame(t, defaultValue.getValue());
return null;
}
},
null);
assertEquals("Foo.class", defaultValue.toString());
}
@Test
public void testClassValueBuiltinType() throws IOException {
compile(
Joiner.on('\n')
.join("public @interface Foo {", " Class value() default String.class;", "}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitType(TypeMirror t, Void aVoid) {
assertSameType(elements.getTypeElement("java.lang.String").asType(), t);
assertSame(t, defaultValue.getValue());
return null;
}
},
null);
assertEquals("java.lang.String.class", defaultValue.toString());
}
@Test
public void testClassArrayValue() throws IOException {
compile(
Joiner.on('\n')
.join(
"public @interface Foo {",
" Class[] value() default {int.class, Foo.class};",
"}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitArray(List<? extends AnnotationValue> vals, Void aVoid) {
assertEquals(vals, defaultValue.getValue());
assertSameType(
types.getPrimitiveType(TypeKind.INT), (TypeMirror) vals.get(0).getValue());
assertSameType(
elements.getTypeElement("Foo").asType(), (TypeMirror) vals.get(1).getValue());
return null;
}
},
null);
assertEquals("{int.class, Foo.class}", defaultValue.toString());
}
@Test
public void testAnnotationMirrorValue() throws IOException {
compile(
Joiner.on('\n')
.join(
"public @interface Foo {",
" FooHelper value() default @FooHelper;",
"}",
"@interface FooHelper {}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitAnnotation(AnnotationMirror a, Void aVoid) {
assertSameType(elements.getTypeElement("FooHelper").asType(), a.getAnnotationType());
assertSame(a, defaultValue.getValue());
return null;
}
},
null);
assertEquals("@FooHelper", defaultValue.toString());
}
@Test
public void testSingleElementAnnotationMirrorValue() throws IOException {
compile(
Joiner.on('\n')
.join(
"public @interface Foo {",
" FooHelper value() default @FooHelper(42);",
"}",
"@interface FooHelper {",
" int value();",
"}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitAnnotation(AnnotationMirror a, Void aVoid) {
AnnotationMirror annotationMirrorValue = (AnnotationMirror) defaultValue.getValue();
ExecutableElement keyElement =
findMethod("value", elements.getTypeElement("FooHelper"));
assertEquals(1, annotationMirrorValue.getElementValues().size());
assertEquals(42, annotationMirrorValue.getElementValues().get(keyElement).getValue());
return null;
}
},
null);
assertEquals("@FooHelper(42)", defaultValue.toString());
}
@Test
public void testMultiElementAnnotationMirrorValue() throws IOException {
compile(
Joiner.on('\n')
.join(
"public @interface Foo {",
" FooHelper value() default @FooHelper(number=42, string=\"42\");",
"}",
"@interface FooHelper {",
" int number();",
" String string();",
"}"));
ExecutableElement method = findMethod("value", elements.getTypeElement("Foo"));
AnnotationValue defaultValue = method.getDefaultValue();
defaultValue.accept(
new TestVisitor() {
@Override
public Void visitAnnotation(AnnotationMirror a, Void aVoid) {
AnnotationMirror annotationMirrorValue = (AnnotationMirror) defaultValue.getValue();
ExecutableElement numberKeyElement =
findMethod("number", elements.getTypeElement("FooHelper"));
ExecutableElement stringKeyElement =
findMethod("string", elements.getTypeElement("FooHelper"));
assertEquals(2, annotationMirrorValue.getElementValues().size());
assertEquals(
42, annotationMirrorValue.getElementValues().get(numberKeyElement).getValue());
assertEquals(
"42", annotationMirrorValue.getElementValues().get(stringKeyElement).getValue());
return null;
}
},
null);
assertEquals("@FooHelper(number=42, string=\"42\")", defaultValue.toString());
}
private static class TestVisitor extends SimpleAnnotationValueVisitor8<Void, Void> {
@Override
protected Void defaultAction(Object o, Void aVoid) {
throw new AssertionError(String.format("Called incorrect visit method for %s", o));
}
}
}