aboutsummaryrefslogtreecommitdiff
path: root/javatests
diff options
context:
space:
mode:
Diffstat (limited to 'javatests')
-rw-r--r--javatests/com/google/turbine/binder/BinderErrorTest.java176
-rw-r--r--javatests/com/google/turbine/binder/BinderTest.java174
-rw-r--r--javatests/com/google/turbine/binder/ClassPathBinderTest.java17
-rw-r--r--javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java93
-rw-r--r--javatests/com/google/turbine/bytecode/ClassReaderTest.java13
-rw-r--r--javatests/com/google/turbine/bytecode/ClassWriterTest.java3
-rw-r--r--javatests/com/google/turbine/bytecode/sig/SigRegressionTest.java1
-rw-r--r--javatests/com/google/turbine/deps/AbstractTransitiveTest.java22
-rw-r--r--javatests/com/google/turbine/deps/DependenciesTest.java98
-rw-r--r--javatests/com/google/turbine/deps/TransitiveTest.java16
-rw-r--r--javatests/com/google/turbine/lower/IntegrationTestSupport.java138
-rw-r--r--javatests/com/google/turbine/lower/LowerIntegrationTest.java3
-rw-r--r--javatests/com/google/turbine/lower/LowerSignatureTest.java2
-rw-r--r--javatests/com/google/turbine/lower/LowerTest.java29
-rw-r--r--javatests/com/google/turbine/lower/testdata/local.test8
-rw-r--r--javatests/com/google/turbine/lower/testdata/tyanno_inner.test16
-rw-r--r--javatests/com/google/turbine/lower/testdata/tyanno_varargs.test12
-rw-r--r--javatests/com/google/turbine/main/MainTest.java315
-rw-r--r--javatests/com/google/turbine/main/ReducedClasspathTest.java252
-rw-r--r--javatests/com/google/turbine/model/ConstTest.java74
-rw-r--r--javatests/com/google/turbine/options/TurbineOptionsTest.java57
-rw-r--r--javatests/com/google/turbine/parse/CommentParserTest.java78
-rw-r--r--javatests/com/google/turbine/parse/JavacLexer.java3
-rw-r--r--javatests/com/google/turbine/parse/LexerTest.java6
-rw-r--r--javatests/com/google/turbine/parse/ParseErrorTest.java51
-rw-r--r--javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java21
-rw-r--r--javatests/com/google/turbine/processing/AbstractTurbineTypesBiPredicateTest.java46
-rw-r--r--javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java527
-rw-r--r--javatests/com/google/turbine/processing/ProcessingIntegrationTest.java346
-rw-r--r--javatests/com/google/turbine/processing/TurbineAnnotationMirrorTest.java249
-rw-r--r--javatests/com/google/turbine/processing/TurbineAnnotationProxyTest.java219
-rw-r--r--javatests/com/google/turbine/processing/TurbineElementTest.java234
-rw-r--r--javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java293
-rw-r--r--javatests/com/google/turbine/processing/TurbineElementsTest.java353
-rw-r--r--javatests/com/google/turbine/processing/TurbineFilerTest.java172
-rw-r--r--javatests/com/google/turbine/processing/TurbineMessagerTest.java248
-rw-r--r--javatests/com/google/turbine/processing/TurbineNameTest.java54
-rw-r--r--javatests/com/google/turbine/processing/TurbineTypeMirrorTest.java281
-rw-r--r--javatests/com/google/turbine/processing/TurbineTypesContainsTest.java53
-rw-r--r--javatests/com/google/turbine/processing/TurbineTypesFactoryTest.java181
-rw-r--r--javatests/com/google/turbine/processing/TurbineTypesIsAssignableTest.java57
-rw-r--r--javatests/com/google/turbine/processing/TurbineTypesIsSameTypeTest.java42
-rw-r--r--javatests/com/google/turbine/processing/TurbineTypesIsSubsignatureTest.java50
-rw-r--r--javatests/com/google/turbine/processing/TurbineTypesIsSubtypeTest.java57
-rw-r--r--javatests/com/google/turbine/processing/TurbineTypesUnaryTest.java147
-rw-r--r--javatests/com/google/turbine/testing/TestClassPaths.java2
-rw-r--r--javatests/com/google/turbine/type/TypeTest.java69
-rw-r--r--javatests/com/google/turbine/zip/ZipTest.java2
48 files changed, 5082 insertions, 278 deletions
diff --git a/javatests/com/google/turbine/binder/BinderErrorTest.java b/javatests/com/google/turbine/binder/BinderErrorTest.java
index 5a4d97e..15b54eb 100644
--- a/javatests/com/google/turbine/binder/BinderErrorTest.java
+++ b/javatests/com/google/turbine/binder/BinderErrorTest.java
@@ -22,11 +22,19 @@ import static org.junit.Assert.fail;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.turbine.binder.Processing.ProcessorInfo;
import com.google.turbine.diag.TurbineError;
import com.google.turbine.parse.Parser;
import com.google.turbine.tree.Tree.CompUnit;
import java.util.Arrays;
import java.util.Optional;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -259,7 +267,7 @@ public class BinderErrorTest {
{
"<>:2: error: java.lang.Object is not an annotation", //
" @Object int x;",
- " ^",
+ " ^",
},
},
{
@@ -271,7 +279,7 @@ public class BinderErrorTest {
{
"<>:2: error: java.lang.Deprecated is not @Repeatable", //
" @Deprecated @Deprecated int x;",
- " ^",
+ " ^",
},
},
{
@@ -283,7 +291,7 @@ public class BinderErrorTest {
{
"<>:2: error: could not resolve NoSuch.NoSuch", //
" @NoSuch.NoSuch int x;",
- " ^",
+ " ^",
},
},
{
@@ -417,6 +425,9 @@ public class BinderErrorTest {
"}",
},
{
+ "<>:1: error: cycle in class hierarchy: Cycle",
+ "class Cycle extends Cycle {",
+ " ^",
"<>:2: error: could not resolve NoSuch", //
" NoSuch f;",
" ^",
@@ -501,7 +512,7 @@ public class BinderErrorTest {
" ^",
"<>:3: error: could not resolve NoSuchAnno",
"@NoSuchAnno",
- " ^",
+ "^",
},
},
{
@@ -568,7 +579,127 @@ public class BinderErrorTest {
"@One.A(b = {@One.NoSuch})",
" ^",
},
- }
+ },
+ {
+ {
+ "public class Test {", //
+ " @interface Anno {",
+ " Class<?> value() default Object.class;",
+ " }",
+ " @Anno(NoSuch.class) int x;",
+ " @Anno(NoSuch.class) int y;",
+ "}",
+ },
+ {
+ "<>:5: error: could not resolve NoSuch",
+ " @Anno(NoSuch.class) int x;",
+ " ^",
+ "<>:6: error: could not resolve NoSuch",
+ " @Anno(NoSuch.class) int y;",
+ " ^",
+ },
+ },
+ {
+ {
+ "public class Test {", //
+ " @A @B void f() {}",
+ "}",
+ },
+ {
+ "<>:2: error: could not resolve A",
+ " @A @B void f() {}",
+ " ^",
+ "<>:2: error: could not resolve B",
+ " @A @B void f() {}",
+ " ^",
+ },
+ },
+ {
+ {
+ "public class Test {", //
+ " @A(\"bar\") void f() {}",
+ "}",
+ },
+ {
+ "<>:2: error: could not resolve A", //
+ " @A(\"bar\") void f() {}",
+ " ^",
+ },
+ },
+ {
+ {
+ "@NoSuch",
+ "@interface A {", //
+ "}",
+ },
+ {
+ "<>:1: error: could not resolve NoSuch", //
+ "@NoSuch",
+ "^",
+ },
+ },
+ {
+ {
+ "public class Test {", //
+ " @String @String int x;",
+ "}",
+ },
+ {
+ "<>:2: error: java.lang.String is not an annotation",
+ " @String @String int x;",
+ " ^",
+ "<>:2: error: java.lang.String is not an annotation",
+ " @String @String int x;",
+ " ^",
+ },
+ },
+ {
+ {
+ "@interface Anno {",
+ " int value();",
+ "}",
+ "enum E {",
+ " ONE",
+ "}",
+ "@Anno(value = E.ONE)",
+ "interface Test {}",
+ },
+ {
+ "<>:7: error: could not evaluate constant expression", //
+ "@Anno(value = E.ONE)",
+ " ^",
+ },
+ },
+ {
+ {
+ "class T extends T {}",
+ },
+ {
+ "<>:1: error: cycle in class hierarchy: T", "class T extends T {}", " ^",
+ },
+ },
+ {
+ {
+ "class T implements T {}",
+ },
+ {
+ "<>:1: error: cycle in class hierarchy: T",
+ "class T implements T {}",
+ " ^",
+ },
+ },
+ {
+ {
+ "class T {", //
+ " static final String s = \"a\" + + \"b\";",
+ "}",
+ },
+ {
+ "<>:2: error: bad operand type String",
+ " static final String s = \"a\" + + \"b\";",
+ " ^",
+ },
+ },
};
return Arrays.asList((Object[][]) testCases);
}
@@ -596,6 +727,41 @@ public class BinderErrorTest {
}
}
+ @SupportedAnnotationTypes("*")
+ static class HelloWorldProcessor extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ return false;
+ }
+ }
+
+ // exercise error reporting with annotation enabled, which should be identical
+ @Test
+ public void testWithProcessors() throws Exception {
+ try {
+ Binder.bind(
+ ImmutableList.of(parseLines(source)),
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ ProcessorInfo.create(
+ ImmutableList.of(new HelloWorldProcessor()),
+ /* loader= */ getClass().getClassLoader(),
+ /* options= */ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TURBINE_BOOTCLASSPATH,
+ /* moduleVersion=*/ Optional.empty())
+ .units();
+ fail(Joiner.on('\n').join(source));
+ } catch (TurbineError e) {
+ assertThat(e).hasMessageThat().isEqualTo(lines(expected));
+ }
+ }
+
private static CompUnit parseLines(String... lines) {
return Parser.parse(lines(lines));
}
diff --git a/javatests/com/google/turbine/binder/BinderTest.java b/javatests/com/google/turbine/binder/BinderTest.java
index 4b1e890..e238ee0 100644
--- a/javatests/com/google/turbine/binder/BinderTest.java
+++ b/javatests/com/google/turbine/binder/BinderTest.java
@@ -36,8 +36,6 @@ import com.google.turbine.tree.Tree;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.jar.JarEntry;
@@ -55,22 +53,21 @@ public class BinderTest {
@Test
public void hello() throws Exception {
- List<Tree.CompUnit> units = new ArrayList<>();
- units.add(
- parseLines(
- "package a;", //
- "public class A {",
- " public class Inner1 extends b.B {",
- " }",
- " public class Inner2 extends A.Inner1 {",
- " }",
- "}"));
- units.add(
- parseLines(
- "package b;", //
- "import a.A;",
- "public class B extends A {",
- "}"));
+ ImmutableList<Tree.CompUnit> units =
+ ImmutableList.of(
+ parseLines(
+ "package a;", //
+ "public class A {",
+ " public class Inner1 extends b.B {",
+ " }",
+ " public class Inner2 extends A.Inner1 {",
+ " }",
+ "}"),
+ parseLines(
+ "package b;", //
+ "import a.A;",
+ "public class B extends A {",
+ "}"));
ImmutableMap<ClassSymbol, SourceTypeBoundClass> bound =
Binder.bind(
@@ -103,20 +100,19 @@ public class BinderTest {
@Test
public void interfaces() throws Exception {
- List<Tree.CompUnit> units = new ArrayList<>();
- units.add(
- parseLines(
- "package com.i;", //
- "public interface I {",
- " public class IInner {",
- " }",
- "}"));
- units.add(
- parseLines(
- "package b;", //
- "class B implements com.i.I {",
- " class BInner extends IInner {}",
- "}"));
+ ImmutableList<Tree.CompUnit> units =
+ ImmutableList.of(
+ parseLines(
+ "package com.i;", //
+ "public interface I {",
+ " public class IInner {",
+ " }",
+ "}"),
+ parseLines(
+ "package b;", //
+ "class B implements com.i.I {",
+ " class BInner extends IInner {}",
+ "}"));
ImmutableMap<ClassSymbol, SourceTypeBoundClass> bound =
Binder.bind(
@@ -143,20 +139,19 @@ public class BinderTest {
@Test
public void imports() throws Exception {
- List<Tree.CompUnit> units = new ArrayList<>();
- units.add(
- parseLines(
- "package com.test;", //
- "public class Test {",
- " public static class Inner {}",
- "}"));
- units.add(
- parseLines(
- "package other;", //
- "import com.test.Test.Inner;",
- "import no.such.Class;", // imports are resolved lazily on-demand
- "public class Foo extends Inner {",
- "}"));
+ ImmutableList<Tree.CompUnit> units =
+ ImmutableList.of(
+ parseLines(
+ "package com.test;", //
+ "public class Test {",
+ " public static class Inner {}",
+ "}"),
+ parseLines(
+ "package other;", //
+ "import com.test.Test.Inner;",
+ "import no.such.Class;", // imports are resolved lazily on-demand
+ "public class Foo extends Inner {",
+ "}"));
ImmutableMap<ClassSymbol, SourceTypeBoundClass> bound =
Binder.bind(
@@ -172,21 +167,20 @@ public class BinderTest {
@Test
public void cycle() throws Exception {
- List<Tree.CompUnit> units = new ArrayList<>();
- units.add(
- parseLines(
- "package a;", //
- "import b.B;",
- "public class A extends B.Inner {",
- " class Inner {}",
- "}"));
- units.add(
- parseLines(
- "package b;", //
- "import a.A;",
- "public class B extends A.Inner {",
- " class Inner {}",
- "}"));
+ ImmutableList<Tree.CompUnit> units =
+ ImmutableList.of(
+ parseLines(
+ "package a;", //
+ "import b.B;",
+ "public class A extends B.Inner {",
+ " class Inner {}",
+ "}"),
+ parseLines(
+ "package b;", //
+ "import a.A;",
+ "public class B extends A.Inner {",
+ " class Inner {}",
+ "}"));
try {
Binder.bind(
@@ -202,12 +196,12 @@ public class BinderTest {
@Test
public void annotationDeclaration() throws Exception {
- List<Tree.CompUnit> units = new ArrayList<>();
- units.add(
- parseLines(
- "package com.test;", //
- "public @interface Annotation {",
- "}"));
+ ImmutableList<Tree.CompUnit> units =
+ ImmutableList.of(
+ parseLines(
+ "package com.test;", //
+ "public @interface Annotation {",
+ "}"));
ImmutableMap<ClassSymbol, SourceTypeBoundClass> bound =
Binder.bind(
@@ -230,13 +224,13 @@ public class BinderTest {
@Test
public void helloBytecode() throws Exception {
- List<Tree.CompUnit> units = new ArrayList<>();
- units.add(
- parseLines(
- "package a;", //
- "import java.util.Map.Entry;",
- "public class A implements Entry {",
- "}"));
+ ImmutableList<Tree.CompUnit> units =
+ ImmutableList.of(
+ parseLines(
+ "package a;", //
+ "import java.util.Map.Entry;",
+ "public class A implements Entry {",
+ "}"));
ImmutableMap<ClassSymbol, SourceTypeBoundClass> bound =
Binder.bind(
@@ -268,15 +262,15 @@ public class BinderTest {
jos.write(lib.get("B"));
}
- List<Tree.CompUnit> units = new ArrayList<>();
- units.add(
- parseLines(
- "import java.lang.annotation.Target;",
- "import java.lang.annotation.ElementType;",
- "public class C implements B {",
- " @Target(ElementType.TYPE_USE)",
- " @interface A {};",
- "}"));
+ ImmutableList<Tree.CompUnit> units =
+ ImmutableList.of(
+ parseLines(
+ "import java.lang.annotation.Target;",
+ "import java.lang.annotation.ElementType;",
+ "public class C implements B {",
+ " @Target(ElementType.TYPE_USE)",
+ " @interface A {};",
+ "}"));
ImmutableMap<ClassSymbol, SourceTypeBoundClass> bound =
Binder.bind(
@@ -294,13 +288,13 @@ public class BinderTest {
// (Error reporting is deferred to javac.)
@Test
public void invalidConst() throws Exception {
- List<Tree.CompUnit> units = new ArrayList<>();
- units.add(
- parseLines(
- "package a;", //
- "public class A {",
- " public static final boolean b = true == 42;",
- "}"));
+ ImmutableList<Tree.CompUnit> units =
+ ImmutableList.of(
+ parseLines(
+ "package a;", //
+ "public class A {",
+ " public static final boolean b = true == 42;",
+ "}"));
ImmutableMap<ClassSymbol, SourceTypeBoundClass> bound =
Binder.bind(
diff --git a/javatests/com/google/turbine/binder/ClassPathBinderTest.java b/javatests/com/google/turbine/binder/ClassPathBinderTest.java
index 3f41706..c11d814 100644
--- a/javatests/com/google/turbine/binder/ClassPathBinderTest.java
+++ b/javatests/com/google/turbine/binder/ClassPathBinderTest.java
@@ -44,7 +44,10 @@ import com.google.turbine.type.AnnoInfo;
import com.google.turbine.type.Type.ClassTy;
import java.io.IOError;
import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -161,4 +164,18 @@ public class ClassPathBinderTest {
assertThat(e).hasMessageThat().contains("NOT_A_JAR");
}
}
+
+ @Test
+ public void resources() throws Exception {
+ Path path = temporaryFolder.newFile("tmp.jar").toPath();
+ try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(path))) {
+ jos.putNextEntry(new JarEntry("foo/bar/hello.txt"));
+ jos.write("hello".getBytes(UTF_8));
+ jos.putNextEntry(new JarEntry("foo/bar/Baz.class"));
+ jos.write("goodbye".getBytes(UTF_8));
+ }
+ ClassPath classPath = ClassPathBinder.bindClasspath(ImmutableList.of(path));
+ assertThat(new String(classPath.resource("foo/bar/hello.txt").get(), UTF_8)).isEqualTo("hello");
+ assertThat(classPath.resource("foo/bar/Baz.class")).isNull();
+ }
}
diff --git a/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java b/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java
index 2a0de48..3e841a5 100644
--- a/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java
+++ b/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java
@@ -22,14 +22,14 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH;
import static java.util.Objects.requireNonNull;
-import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
import com.google.common.io.ByteStreams;
import com.google.turbine.binder.bound.TurbineClassValue;
import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo;
import com.google.turbine.binder.env.CompoundEnv;
import com.google.turbine.binder.env.Env;
-import com.google.turbine.binder.env.SimpleEnv;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.type.Type;
import com.google.turbine.type.Type.ClassTy;
@@ -37,7 +37,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.io.UncheckedIOException;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -84,6 +86,10 @@ public class BytecodeBoundClassTest {
<X, Y extends X, Z extends Throwable> X foo(@Deprecated X bar, Y baz) throws IOException, Z {
return null;
}
+
+ void baz() throws IOException {
+ throw new IOException();
+ }
}
@Test
@@ -97,6 +103,12 @@ public class BytecodeBoundClassTest {
assertThat(m.parameters().get(0).annotations()).hasSize(1);
assertThat(m.parameters().get(0).name()).isEqualTo("bar");
assertThat(m.exceptions()).hasSize(2);
+
+ MethodInfo b =
+ getBytecodeBoundClass(HasMethod.class).methods().stream()
+ .filter(x -> x.name().equals("baz"))
+ .collect(onlyElement());
+ assertThat(b.exceptions()).hasSize(1);
}
@interface VoidAnno {
@@ -116,6 +128,53 @@ public class BytecodeBoundClassTest {
.isEqualTo(Type.TyKind.ARRAY_TY);
}
+ static class HasField {
+ @Deprecated List<String> foo;
+ }
+
+ @Test
+ public void fieldTypes() {
+ FieldInfo f =
+ getBytecodeBoundClass(HasField.class).fields().stream()
+ .filter(x -> x.name().equals("foo"))
+ .collect(onlyElement());
+
+ assertThat(Iterables.getLast(((ClassTy) f.type()).classes()).targs()).hasSize(1);
+ assertThat(f.annotations()).hasSize(1);
+ }
+
+ interface Y {
+ Object f();
+ }
+
+ interface X extends Y {
+ String f();
+ }
+
+ @Test
+ public void covariantBridges() {
+ assertThat(getBytecodeBoundClass(X.class, Y.class).methods()).hasSize(1);
+ }
+
+ interface A<T> {
+ void f(T t);
+ }
+
+ interface B<T extends Number> extends A<T> {
+ @Override
+ void f(T t);
+ }
+
+ interface C<T extends Integer> extends B<T> {
+ @Override
+ void f(T t);
+ }
+
+ @Test
+ public void genericBridges() {
+ assertThat(getBytecodeBoundClass(C.class, B.class, A.class).methods()).hasSize(1);
+ }
+
private static byte[] toByteArrayOrDie(InputStream is) {
try {
return ByteStreams.toByteArray(is);
@@ -135,15 +194,29 @@ public class BytecodeBoundClassTest {
"test.jar");
}
- private BytecodeBoundClass getBytecodeBoundClass(Class<?> clazz) {
- Env<ClassSymbol, BytecodeBoundClass> env = TURBINE_BOOTCLASSPATH.env();
- env =
- CompoundEnv.of(env)
+ private BytecodeBoundClass getBytecodeBoundClass(Class<?> clazz, Class<?>... classpath) {
+ Map<ClassSymbol, BytecodeBoundClass> map = new HashMap<>();
+ Env<ClassSymbol, BytecodeBoundClass> env =
+ CompoundEnv.of(TURBINE_BOOTCLASSPATH.env())
.append(
- new SimpleEnv<>(
- ImmutableMap.of(
- new ClassSymbol(BytecodeBoundClass.class.getName().replace('.', '/')),
- getBytecodeBoundClass(env, BytecodeBoundClassTest.class))));
+ new Env<ClassSymbol, BytecodeBoundClass>() {
+ @Override
+ public BytecodeBoundClass get(ClassSymbol sym) {
+ return map.get(sym);
+ }
+ });
+ addClass(clazz, map, env);
+ addClass(BytecodeBoundClassTest.class, map, env);
+ for (Class<?> c : classpath) {
+ addClass(c, map, env);
+ }
return getBytecodeBoundClass(env, clazz);
}
+
+ private void addClass(
+ Class<?> clazz,
+ Map<ClassSymbol, BytecodeBoundClass> map,
+ Env<ClassSymbol, BytecodeBoundClass> env) {
+ map.put(new ClassSymbol(clazz.getName().replace('.', '/')), getBytecodeBoundClass(env, clazz));
+ }
}
diff --git a/javatests/com/google/turbine/bytecode/ClassReaderTest.java b/javatests/com/google/turbine/bytecode/ClassReaderTest.java
index dda29ac..fb64541 100644
--- a/javatests/com/google/turbine/bytecode/ClassReaderTest.java
+++ b/javatests/com/google/turbine/bytecode/ClassReaderTest.java
@@ -35,6 +35,7 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.ModuleVisitor;
import org.objectweb.asm.Opcodes;
@@ -152,7 +153,8 @@ public class ClassReaderTest {
"<X:Ljava/lang/Object;>Ljava/lang/Object;",
"java/lang/Object",
null);
- cw.visitField(Opcodes.ACC_PUBLIC, "x", "I", null, null);
+ FieldVisitor fv = cw.visitField(Opcodes.ACC_PUBLIC, "x", "I", null, null);
+ fv.visitAnnotation("Ljava/lang/Deprecated;", true);
cw.visitField(
Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,
"y",
@@ -173,7 +175,9 @@ public class ClassReaderTest {
assertThat(x.descriptor()).isEqualTo("I");
assertThat(x.signature()).isNull();
assertThat(x.value()).isNull();
- assertThat(x.annotations()).isEmpty();
+ assertThat(x.annotations()).hasSize(1);
+ ClassFile.AnnotationInfo annotation = Iterables.getOnlyElement(x.annotations());
+ assertThat(annotation.typeName()).isEqualTo("Ljava/lang/Deprecated;");
ClassFile.FieldInfo y = classFile.fields().get(1);
assertThat(y.access())
@@ -182,12 +186,13 @@ public class ClassReaderTest {
assertThat(y.descriptor()).isEqualTo("I");
assertThat(y.value().constantTypeKind()).isEqualTo(TurbineConstantTypeKind.INT);
assertThat(((Const.IntValue) y.value()).value()).isEqualTo(42);
+ assertThat(y.annotations()).isEmpty();
ClassFile.FieldInfo z = classFile.fields().get(2);
assertThat(z.name()).isEqualTo("z");
assertThat(z.descriptor()).isEqualTo("Ljava/util/List;");
- // don't bother reading signatures for fields; we only care about constants
- assertThat(z.signature()).isNull();
+ assertThat(z.signature()).isEqualTo("Ljava/util/List<TX;>;");
+ assertThat(z.annotations()).isEmpty();
}
@Test
diff --git a/javatests/com/google/turbine/bytecode/ClassWriterTest.java b/javatests/com/google/turbine/bytecode/ClassWriterTest.java
index e544c15..71cf356 100644
--- a/javatests/com/google/turbine/bytecode/ClassWriterTest.java
+++ b/javatests/com/google/turbine/bytecode/ClassWriterTest.java
@@ -17,6 +17,7 @@
package com.google.turbine.bytecode;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
@@ -84,7 +85,7 @@ public class ClassWriterTest {
/* classes= */ null,
fileManager.getJavaFileObjects(path));
- assertThat(task.call()).named(collector.getDiagnostics().toString()).isTrue();
+ assertWithMessage(collector.getDiagnostics().toString()).that(task.call()).isTrue();
byte[] original = Files.readAllBytes(out.resolve("test/Test.class"));
byte[] actual = ClassWriter.writeClass(ClassReader.read(null, original));
diff --git a/javatests/com/google/turbine/bytecode/sig/SigRegressionTest.java b/javatests/com/google/turbine/bytecode/sig/SigRegressionTest.java
index a0f0774..042bb80 100644
--- a/javatests/com/google/turbine/bytecode/sig/SigRegressionTest.java
+++ b/javatests/com/google/turbine/bytecode/sig/SigRegressionTest.java
@@ -102,5 +102,6 @@ public class SigRegressionTest {
input = "LA<[-[Z>.I;";
sig = new SigParser(input).parseClassSig();
+ assertThat(SigWriter.classSig(sig)).isEqualTo(input);
}
}
diff --git a/javatests/com/google/turbine/deps/AbstractTransitiveTest.java b/javatests/com/google/turbine/deps/AbstractTransitiveTest.java
index 4cb8adf..c5b68ff 100644
--- a/javatests/com/google/turbine/deps/AbstractTransitiveTest.java
+++ b/javatests/com/google/turbine/deps/AbstractTransitiveTest.java
@@ -36,7 +36,6 @@ import java.nio.file.Path;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@@ -143,7 +142,7 @@ public abstract class AbstractTransitiveTest {
// Explicitly use turbine; javac-turbine doesn't support direct-classpath compilations.
Path libc = temporaryFolder.newFolder().toPath().resolve("out.jar");
- List<String> sources =
+ ImmutableList<String> sources =
new SourceBuilder()
.addSourceLines(
"c/C.java",
@@ -152,18 +151,17 @@ public abstract class AbstractTransitiveTest {
" @Anno(x = 2) static final Inner i; // a.A$Inner ",
" static final int X = CONST; // a.A#CONST",
"}")
- .build().stream()
+ .build()
+ .stream()
.map(Path::toString)
.collect(toImmutableList());
- boolean ok =
- Main.compile(
- optionsWithBootclasspath()
- .addSources(sources)
- .addClassPathEntries(
- ImmutableList.of(libb).stream().map(Path::toString).collect(toImmutableList()))
- .setOutput(libc.toString())
- .build());
- assertThat(ok).isTrue();
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSources(sources)
+ .setClassPath(
+ ImmutableList.of(libb).stream().map(Path::toString).collect(toImmutableList()))
+ .setOutput(libc.toString())
+ .build());
assertThat(readJar(libc).keySet())
.containsExactly(
diff --git a/javatests/com/google/turbine/deps/DependenciesTest.java b/javatests/com/google/turbine/deps/DependenciesTest.java
index b1da209..bc663cd 100644
--- a/javatests/com/google/turbine/deps/DependenciesTest.java
+++ b/javatests/com/google/turbine/deps/DependenciesTest.java
@@ -22,7 +22,6 @@ import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Streams;
import com.google.turbine.binder.Binder;
import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.binder.ClassPathBinder;
@@ -40,7 +39,6 @@ import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -88,7 +86,7 @@ public class DependenciesTest {
static class DepsBuilder {
List<Path> classpath;
- List<CompUnit> units = new ArrayList<>();
+ ImmutableList.Builder<CompUnit> units = ImmutableList.builder();
DepsBuilder setClasspath(Path... classpath) {
this.classpath = ImmutableList.copyOf(classpath);
@@ -103,7 +101,7 @@ public class DependenciesTest {
DepsProto.Dependencies run() throws IOException {
BindingResult bound =
Binder.bind(
- units,
+ units.build(),
ClassPathBinder.bindClasspath(classpath),
TestClassPaths.TURBINE_BOOTCLASSPATH,
/* moduleVersion=*/ Optional.empty());
@@ -116,7 +114,7 @@ public class DependenciesTest {
}
private Map<Path, DepsProto.Dependency.Kind> depsMap(DepsProto.Dependencies deps) {
- return Streams.stream(deps.getDependencyList())
+ return deps.getDependencyList().stream()
.collect(Collectors.toMap(d -> Paths.get(d.getPath()), DepsProto.Dependency::getKind));
}
@@ -333,6 +331,96 @@ public class DependenciesTest {
}
}
+ @Test
+ public void annotations_recursive() throws Exception {
+ Path libA = libA();
+ Path libB = libB();
+
+ DepsProto.Dependencies deps =
+ new DepsBuilder()
+ .setClasspath(libA, libB)
+ .addSourceLines(
+ "Test.java", //
+ "import a.A;",
+ "import b.B;",
+ "@A(B.class)",
+ "class Test {",
+ "}")
+ .run();
+ assertThat(depsMap(deps))
+ .containsExactly(
+ libA, DepsProto.Dependency.Kind.EXPLICIT, libB, DepsProto.Dependency.Kind.EXPLICIT);
+ }
+
+ @Test
+ public void annotations_field() throws Exception {
+ Path libA = libA();
+ Path libB = libB();
+ DepsProto.Dependencies deps =
+ new DepsBuilder()
+ .setClasspath(libA, libB)
+ .addSourceLines(
+ "Test.java", //
+ "import a.A;",
+ "import b.B;",
+ "class Test {",
+ " @A(B.class)",
+ " int x;",
+ "}")
+ .run();
+ assertThat(depsMap(deps))
+ .containsExactly(
+ libA, DepsProto.Dependency.Kind.EXPLICIT, libB, DepsProto.Dependency.Kind.EXPLICIT);
+ }
+
+ @Test
+ public void annotations_method() throws Exception {
+ Path libA = libA();
+ Path libB = libB();
+ DepsProto.Dependencies deps =
+ new DepsBuilder()
+ .setClasspath(libA, libB)
+ .addSourceLines(
+ "Test.java", //
+ "import a.A;",
+ "import b.B;",
+ "class Test {",
+ " @A(B.class)",
+ " void f() {}",
+ "}")
+ .run();
+ assertThat(depsMap(deps))
+ .containsExactly(
+ libA, DepsProto.Dependency.Kind.EXPLICIT, libB, DepsProto.Dependency.Kind.EXPLICIT);
+ }
+
+ private Path libB() throws Exception {
+ return new LibraryBuilder()
+ .addSourceLines(
+ "b/B.java",
+ "package b;",
+ "import java.lang.annotation.Retention;",
+ "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
+ "@Retention(RUNTIME)",
+ "public @interface B {",
+ "}")
+ .compileToJar("libb.jar");
+ }
+
+ private Path libA() throws Exception {
+ return new LibraryBuilder()
+ .addSourceLines(
+ "a/A.java",
+ "package a;",
+ "import java.lang.annotation.Retention;",
+ "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
+ "@Retention(RUNTIME)",
+ "public @interface A {",
+ " Class<?> value() default Object.class;",
+ "}")
+ .compileToJar("liba.jar");
+ }
+
void writeDeps(Path path, ImmutableMap<String, DepsProto.Dependency.Kind> deps)
throws IOException {
DepsProto.Dependencies.Builder builder =
diff --git a/javatests/com/google/turbine/deps/TransitiveTest.java b/javatests/com/google/turbine/deps/TransitiveTest.java
index f8c5b50..2c9f807 100644
--- a/javatests/com/google/turbine/deps/TransitiveTest.java
+++ b/javatests/com/google/turbine/deps/TransitiveTest.java
@@ -17,7 +17,6 @@
package com.google.turbine.deps;
import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.truth.Truth.assertThat;
import static com.google.turbine.testing.TestClassPaths.optionsWithBootclasspath;
import com.google.common.collect.ImmutableList;
@@ -34,15 +33,12 @@ public class TransitiveTest extends AbstractTransitiveTest {
protected Path runTurbine(ImmutableList<Path> sources, ImmutableList<Path> classpath)
throws IOException {
Path out = temporaryFolder.newFolder().toPath().resolve("out.jar");
- boolean ok =
- Main.compile(
- optionsWithBootclasspath()
- .addSources(sources.stream().map(Path::toString).collect(toImmutableList()))
- .addClassPathEntries(
- classpath.stream().map(Path::toString).collect(toImmutableList()))
- .setOutput(out.toString())
- .build());
- assertThat(ok).isTrue();
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSources(sources.stream().map(Path::toString).collect(toImmutableList()))
+ .setClassPath(classpath.stream().map(Path::toString).collect(toImmutableList()))
+ .setOutput(out.toString())
+ .build());
return out;
}
}
diff --git a/javatests/com/google/turbine/lower/IntegrationTestSupport.java b/javatests/com/google/turbine/lower/IntegrationTestSupport.java
index 680b073..a03473d 100644
--- a/javatests/com/google/turbine/lower/IntegrationTestSupport.java
+++ b/javatests/com/google/turbine/lower/IntegrationTestSupport.java
@@ -16,11 +16,13 @@
package com.google.turbine.lower;
-import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
+import static org.junit.Assert.fail;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
@@ -29,12 +31,13 @@ import com.google.common.io.MoreFiles;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import com.google.turbine.binder.Binder;
+import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.binder.ClassPath;
import com.google.turbine.binder.ClassPathBinder;
import com.google.turbine.diag.SourceFile;
import com.google.turbine.parse.Parser;
import com.google.turbine.testing.AsmUtils;
-import com.google.turbine.tree.Tree;
+import com.google.turbine.tree.Tree.CompUnit;
import com.sun.source.util.JavacTask;
import com.sun.tools.javac.api.JavacTool;
import com.sun.tools.javac.file.JavacFileManager;
@@ -100,8 +103,11 @@ public class IntegrationTestSupport {
public static Map<String, byte[]> canonicalize(Map<String, byte[]> in) {
List<ClassNode> classes = toClassNodes(in);
- // drop anonymous classes
- classes = classes.stream().filter(n -> !isAnonymous(n)).collect(toCollection(ArrayList::new));
+ // drop local and anonymous classes
+ classes =
+ classes.stream()
+ .filter(n -> !isAnonymous(n) && !isLocal(n))
+ .collect(toCollection(ArrayList::new));
// collect all inner classes attributes
Map<String, InnerClassNode> infos = new HashMap<>();
@@ -123,6 +129,10 @@ public class IntegrationTestSupport {
return toByteCode(classes);
}
+ private static boolean isLocal(ClassNode n) {
+ return n.outerMethod != null;
+ }
+
private static boolean isAnonymous(ClassNode n) {
// JVMS 4.7.6: if C is anonymous, the value of the inner_name_index item must be zero
return n.innerClasses.stream().anyMatch(i -> i.name.equals(n.name) && i.innerName == null);
@@ -436,15 +446,41 @@ public class IntegrationTestSupport {
ClassPath bootClassPath,
Optional<String> moduleVersion)
throws IOException {
- List<Tree.CompUnit> units =
+ BindingResult bound = turbineAnalysis(input, classpath, bootClassPath, moduleVersion);
+ return Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes();
+ }
+
+ public static BindingResult turbineAnalysis(
+ Map<String, String> input,
+ ImmutableList<Path> classpath,
+ ClassPath bootClassPath,
+ Optional<String> moduleVersion)
+ throws IOException {
+ ImmutableList<CompUnit> units =
input.entrySet().stream()
.map(e -> new SourceFile(e.getKey(), e.getValue()))
.map(Parser::parse)
- .collect(toList());
+ .collect(toImmutableList());
- Binder.BindingResult bound =
- Binder.bind(units, ClassPathBinder.bindClasspath(classpath), bootClassPath, moduleVersion);
- return Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes();
+ return Binder.bind(
+ units, ClassPathBinder.bindClasspath(classpath), bootClassPath, moduleVersion);
+ }
+
+ public static JavacTask runJavacAnalysis(
+ Map<String, String> sources, Collection<Path> classpath, ImmutableList<String> options)
+ throws Exception {
+ return runJavacAnalysis(sources, classpath, options, new DiagnosticCollector<>());
+ }
+
+ public static JavacTask runJavacAnalysis(
+ Map<String, String> sources,
+ Collection<Path> classpath,
+ ImmutableList<String> options,
+ DiagnosticCollector<JavaFileObject> collector)
+ throws Exception {
+ FileSystem fs = Jimfs.newFileSystem(Configuration.unix());
+ Path out = fs.getPath("out");
+ return setupJavac(sources, classpath, options, collector, fs, out);
}
public static Map<String, byte[]> runJavac(
@@ -457,10 +493,46 @@ public class IntegrationTestSupport {
Map<String, String> sources, Collection<Path> classpath, ImmutableList<String> options)
throws Exception {
+ DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
FileSystem fs = Jimfs.newFileSystem(Configuration.unix());
+ Path out = fs.getPath("out");
+ JavacTask task = setupJavac(sources, classpath, options, collector, fs, out);
+
+ if (!task.call()) {
+ fail(collector.getDiagnostics().stream().map(d -> d.toString()).collect(joining("\n")));
+ }
+
+ List<Path> classes = new ArrayList<>();
+ Files.walkFileTree(
+ out,
+ new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(Path path, BasicFileAttributes attrs)
+ throws IOException {
+ if (path.getFileName().toString().endsWith(".class")) {
+ classes.add(path);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ Map<String, byte[]> result = new LinkedHashMap<>();
+ for (Path path : classes) {
+ String r = out.relativize(path).toString();
+ result.put(r.substring(0, r.length() - ".class".length()), Files.readAllBytes(path));
+ }
+ return result;
+ }
+
+ private static JavacTask setupJavac(
+ Map<String, String> sources,
+ Collection<Path> classpath,
+ ImmutableList<String> options,
+ DiagnosticCollector<JavaFileObject> collector,
+ FileSystem fs,
+ Path out)
+ throws IOException {
Path srcs = fs.getPath("srcs");
- Path out = fs.getPath("out");
Files.createDirectories(out);
@@ -475,7 +547,6 @@ public class IntegrationTestSupport {
}
JavacTool compiler = JavacTool.create();
- DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
JavacFileManager fileManager = new JavacFileManager(new Context(), true, UTF_8);
fileManager.setLocationFromPaths(StandardLocation.CLASS_OUTPUT, ImmutableList.of(out));
fileManager.setLocationFromPaths(StandardLocation.CLASS_PATH, classpath);
@@ -487,36 +558,13 @@ public class IntegrationTestSupport {
StandardLocation.locationFor("MODULE_SOURCE_PATH"), ImmutableList.of(srcs));
}
- JavacTask task =
- compiler.getTask(
- new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.err, UTF_8)), true),
- fileManager,
- collector,
- options,
- ImmutableList.of(),
- fileManager.getJavaFileObjectsFromPaths(inputs));
-
- assertThat(task.call()).named(collector.getDiagnostics().toString()).isTrue();
-
- List<Path> classes = new ArrayList<>();
- Files.walkFileTree(
- out,
- new SimpleFileVisitor<Path>() {
- @Override
- public FileVisitResult visitFile(Path path, BasicFileAttributes attrs)
- throws IOException {
- if (path.getFileName().toString().endsWith(".class")) {
- classes.add(path);
- }
- return FileVisitResult.CONTINUE;
- }
- });
- Map<String, byte[]> result = new LinkedHashMap<>();
- for (Path path : classes) {
- String r = out.relativize(path).toString();
- result.put(r.substring(0, r.length() - ".class".length()), Files.readAllBytes(path));
- }
- return result;
+ return compiler.getTask(
+ new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.err, UTF_8)), true),
+ fileManager,
+ collector,
+ options,
+ ImmutableList.of(),
+ fileManager.getJavaFileObjectsFromPaths(inputs));
}
/** Normalizes and stringifies a collection of class files. */
@@ -535,17 +583,17 @@ public class IntegrationTestSupport {
return sb.toString();
}
- static class TestInput {
+ public static class TestInput {
- final Map<String, String> sources;
- final Map<String, String> classes;
+ public final Map<String, String> sources;
+ public final Map<String, String> classes;
public TestInput(Map<String, String> sources, Map<String, String> classes) {
this.sources = sources;
this.classes = classes;
}
- static TestInput parse(String text) {
+ public static TestInput parse(String text) {
Map<String, String> sources = new LinkedHashMap<>();
Map<String, String> classes = new LinkedHashMap<>();
String className = null;
diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
index f7e9c18..85c3450 100644
--- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java
+++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
@@ -308,6 +308,9 @@ public class LowerIntegrationTest {
"shadow_inherited.test",
"static_final_boxed.test",
"anno_void.test",
+ "tyanno_varargs.test",
+ "tyanno_inner.test",
+ "local.test",
};
List<Object[]> tests =
ImmutableList.copyOf(testCases).stream().map(x -> new Object[] {x}).collect(toList());
diff --git a/javatests/com/google/turbine/lower/LowerSignatureTest.java b/javatests/com/google/turbine/lower/LowerSignatureTest.java
index 5ccbf01..08bc46d 100644
--- a/javatests/com/google/turbine/lower/LowerSignatureTest.java
+++ b/javatests/com/google/turbine/lower/LowerSignatureTest.java
@@ -83,7 +83,7 @@ public class LowerSignatureTest {
assertThat(SigWriter.type(new LowerSignature().signature(type)))
.isEqualTo("Ltest/Outer<Ljava/lang/Object;>.Inner<Ljava/lang/Object;>;");
// Type#toString is only for debugging
- assertThat(type.toString()).isEqualTo("test/Outer<java/lang/Object>.Inner<java/lang/Object>");
+ assertThat(type.toString()).isEqualTo("test.Outer<java.lang.Object>.Inner<java.lang.Object>");
}
@Test
diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java
index 0de55c3..8151e81 100644
--- a/javatests/com/google/turbine/lower/LowerTest.java
+++ b/javatests/com/google/turbine/lower/LowerTest.java
@@ -33,6 +33,7 @@ import com.google.turbine.binder.env.SimpleEnv;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.FieldSymbol;
import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.ParamSymbol;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.bytecode.ByteReader;
import com.google.turbine.bytecode.ConstantPoolReader;
@@ -104,12 +105,13 @@ public class LowerTest {
new ClassSymbol("test/Test$Inner"),
ImmutableList.of(),
ImmutableList.of()))))),
+ /* lowerBound= */ null,
ImmutableList.of()));
int access = TurbineFlag.ACC_SUPER | TurbineFlag.ACC_PUBLIC;
ImmutableList<SourceTypeBoundClass.MethodInfo> methods =
ImmutableList.of(
new SourceTypeBoundClass.MethodInfo(
- new MethodSymbol(new ClassSymbol("test/Test"), "f"),
+ new MethodSymbol(-1, new ClassSymbol("test/Test"), "f"),
ImmutableMap.of(),
PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()),
ImmutableList.of(),
@@ -120,9 +122,9 @@ public class LowerTest {
ImmutableList.of(),
null),
new SourceTypeBoundClass.MethodInfo(
- new MethodSymbol(new ClassSymbol("test/Test"), "g"),
+ new MethodSymbol(-1, new ClassSymbol("test/Test"), "g"),
ImmutableMap.of(
- new TyVarSymbol(new MethodSymbol(new ClassSymbol("test/Test"), "g"), "V"),
+ new TyVarSymbol(new MethodSymbol(-1, new ClassSymbol("test/Test"), "g"), "V"),
new SourceTypeBoundClass.TyVarInfo(
IntersectionTy.create(
ImmutableList.of(
@@ -132,8 +134,9 @@ public class LowerTest {
new ClassSymbol("java/lang/Runnable"),
ImmutableList.of(),
ImmutableList.of()))))),
+ /* lowerBound= */ null,
ImmutableList.of()),
- new TyVarSymbol(new MethodSymbol(new ClassSymbol("test/Test"), "g"), "E"),
+ new TyVarSymbol(new MethodSymbol(-1, new ClassSymbol("test/Test"), "g"), "E"),
new SourceTypeBoundClass.TyVarInfo(
IntersectionTy.create(
ImmutableList.of(
@@ -143,17 +146,20 @@ public class LowerTest {
new ClassSymbol("java/lang/Error"),
ImmutableList.of(),
ImmutableList.of()))))),
+ /* lowerBound= */ null,
ImmutableList.of())),
Type.VOID,
ImmutableList.of(
new SourceTypeBoundClass.ParamInfo(
+ new ParamSymbol(
+ new MethodSymbol(-1, new ClassSymbol("test/Test"), "g"), "foo"),
PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()),
- "foo",
ImmutableList.of(),
0)),
ImmutableList.of(
TyVar.create(
- new TyVarSymbol(new MethodSymbol(new ClassSymbol("test/Test"), "g"), "E"),
+ new TyVarSymbol(
+ new MethodSymbol(-1, new ClassSymbol("test/Test"), "g"), "E"),
ImmutableList.of())),
TurbineFlag.ACC_PUBLIC,
null,
@@ -350,7 +356,7 @@ public class LowerTest {
int typeRef, TypePath typePath, String desc, boolean visible) {
path[0] = typePath;
return null;
- };
+ }
};
}
},
@@ -604,9 +610,10 @@ public class LowerTest {
assertThat(error)
.hasMessageThat()
.contains(
- "Test.java:3: error: could not locate class file for A\n"
- + " I i;\n"
- + " ^");
+ lines(
+ "Test.java:3: error: could not locate class file for A",
+ " I i;",
+ " ^"));
}
}
@@ -640,6 +647,6 @@ public class LowerTest {
}
static String lines(String... lines) {
- return Joiner.on("\n").join(lines);
+ return Joiner.on(System.lineSeparator()).join(lines);
}
}
diff --git a/javatests/com/google/turbine/lower/testdata/local.test b/javatests/com/google/turbine/lower/testdata/local.test
new file mode 100644
index 0000000..fd6d4c5
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/local.test
@@ -0,0 +1,8 @@
+=== T.java ===
+class T {
+ Object f() {
+ class Local {
+ }
+ return new Local();
+ }
+} \ No newline at end of file
diff --git a/javatests/com/google/turbine/lower/testdata/tyanno_inner.test b/javatests/com/google/turbine/lower/testdata/tyanno_inner.test
new file mode 100644
index 0000000..d42b3a3
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/tyanno_inner.test
@@ -0,0 +1,16 @@
+%%% A.java %%%
+import static java.lang.annotation.ElementType.TYPE_PARAMETER;
+
+import java.lang.annotation.Target;
+
+@interface A {
+
+ @Target(TYPE_PARAMETER)
+ @interface I {
+ }
+}
+
+=== T.java ===
+abstract class T<@A.I X> {
+
+}
diff --git a/javatests/com/google/turbine/lower/testdata/tyanno_varargs.test b/javatests/com/google/turbine/lower/testdata/tyanno_varargs.test
new file mode 100644
index 0000000..1fbc1e1
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/tyanno_varargs.test
@@ -0,0 +1,12 @@
+=== T.java ===
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE_USE;
+
+import java.lang.annotation.Target;
+
+abstract class T {
+ @Target({PARAMETER, TYPE_USE})
+ @interface A {}
+
+ void f(boolean a, @A String b, Object @A... xs) {}
+}
diff --git a/javatests/com/google/turbine/main/MainTest.java b/javatests/com/google/turbine/main/MainTest.java
index 65f3167..5d47632 100644
--- a/javatests/com/google/turbine/main/MainTest.java
+++ b/javatests/com/google/turbine/main/MainTest.java
@@ -17,17 +17,29 @@
package com.google.turbine.main;
import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_VERSION;
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
+import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static com.google.turbine.testing.TestClassPaths.optionsWithBootclasspath;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.fail;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteStreams;
import com.google.common.io.MoreFiles;
+import com.google.protobuf.ExtensionRegistry;
import com.google.turbine.diag.TurbineError;
import com.google.turbine.options.TurbineOptions;
+import com.google.turbine.proto.ManifestProto;
+import java.io.BufferedInputStream;
+import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
@@ -35,16 +47,27 @@ import java.time.ZoneId;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
+import java.util.stream.Stream;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
@RunWith(JUnit4.class)
public class MainTest {
@@ -84,13 +107,11 @@ public class MainTest {
Path output = temporaryFolder.newFile("output.jar").toPath();
- boolean ok =
- Main.compile(
- optionsWithBootclasspath()
- .addSources(ImmutableList.of(src.toString()))
- .setOutput(output.toString())
- .build());
- assertThat(ok).isTrue();
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSources(ImmutableList.of(src.toString()))
+ .setOutput(output.toString())
+ .build());
Map<String, byte[]> data = readJar(output);
assertThat(data.keySet()).containsExactly("test/package-info.class");
@@ -106,13 +127,11 @@ public class MainTest {
Path output = temporaryFolder.newFile("output.jar").toPath();
- boolean ok =
- Main.compile(
- optionsWithBootclasspath()
- .setSourceJars(ImmutableList.of(srcjar.toString()))
- .setOutput(output.toString())
- .build());
- assertThat(ok).isTrue();
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSourceJars(ImmutableList.of(srcjar.toString()))
+ .setOutput(output.toString())
+ .build());
Map<String, byte[]> data = readJar(output);
assertThat(data.keySet()).containsExactly("test/package-info.class");
@@ -150,15 +169,13 @@ public class MainTest {
Path output = temporaryFolder.newFile("output.jar").toPath();
- boolean ok =
- Main.compile(
- TurbineOptions.builder()
- .setRelease("9")
- .addSources(ImmutableList.of(src.toString()))
- .setSourceJars(ImmutableList.of(srcjar.toString()))
- .setOutput(output.toString())
- .build());
- assertThat(ok).isTrue();
+ Main.compile(
+ TurbineOptions.builder()
+ .setRelease("9")
+ .setSources(ImmutableList.of(src.toString()))
+ .setSourceJars(ImmutableList.of(srcjar.toString()))
+ .setOutput(output.toString())
+ .build());
Map<String, byte[]> data = readJar(output);
assertThat(data.keySet())
@@ -171,26 +188,48 @@ public class MainTest {
MoreFiles.asCharSink(src, UTF_8).write("class Foo {}");
Path output = temporaryFolder.newFile("output.jar").toPath();
+ Path gensrcOutput = temporaryFolder.newFile("gensrcOutput.jar").toPath();
- boolean ok =
- Main.compile(
- optionsWithBootclasspath()
- .addSources(ImmutableList.of(src.toString()))
- .setTargetLabel("//foo:foo")
- .setInjectingRuleKind("foo_library")
- .setOutput(output.toString())
- .build());
- assertThat(ok).isTrue();
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSources(ImmutableList.of(src.toString()))
+ .setTargetLabel("//foo:foo")
+ .setInjectingRuleKind("foo_library")
+ .setOutput(output.toString())
+ .setGensrcOutput(gensrcOutput.toString())
+ .build());
try (JarFile jarFile = new JarFile(output.toFile())) {
+ try (Stream<JarEntry> entries = jarFile.stream()) {
+ assertThat(entries.map(JarEntry::getName))
+ .containsAtLeast("META-INF/", "META-INF/MANIFEST.MF");
+ }
Manifest manifest = jarFile.getManifest();
Attributes attributes = manifest.getMainAttributes();
- assertThat(attributes.getValue("Target-Label")).isEqualTo("//foo:foo");
- assertThat(attributes.getValue("Injecting-Rule-Kind")).isEqualTo("foo_library");
+ ImmutableMap<String, ?> entries =
+ attributes.entrySet().stream()
+ .collect(toImmutableMap(e -> e.getKey().toString(), Map.Entry::getValue));
+ assertThat(entries)
+ .containsExactly(
+ "Created-By", "bazel",
+ "Manifest-Version", "1.0",
+ "Target-Label", "//foo:foo",
+ "Injecting-Rule-Kind", "foo_library");
assertThat(jarFile.getEntry(JarFile.MANIFEST_NAME).getLastModifiedTime().toInstant())
.isEqualTo(
LocalDateTime.of(2010, 1, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant());
}
+ try (JarFile jarFile = new JarFile(gensrcOutput.toFile())) {
+ Manifest manifest = jarFile.getManifest();
+ Attributes attributes = manifest.getMainAttributes();
+ ImmutableMap<String, ?> entries =
+ attributes.entrySet().stream()
+ .collect(toImmutableMap(e -> e.getKey().toString(), Map.Entry::getValue));
+ assertThat(entries)
+ .containsExactly(
+ "Created-By", "bazel",
+ "Manifest-Version", "1.0");
+ }
}
@Test
@@ -201,13 +240,11 @@ public class MainTest {
Path output = temporaryFolder.newFile("output.jar").toPath();
- boolean ok =
- Main.compile(
- TurbineOptions.builder()
- .addSources(ImmutableList.of(src.toString()))
- .setOutput(output.toString())
- .build());
- assertThat(ok).isTrue();
+ Main.compile(
+ TurbineOptions.builder()
+ .setSources(ImmutableList.of(src.toString()))
+ .setOutput(output.toString())
+ .build());
Map<String, byte[]> data = readJar(output);
assertThat(data.keySet()).containsExactly("java/lang/Object.class");
@@ -223,7 +260,7 @@ public class MainTest {
try {
Main.compile(
TurbineOptions.builder()
- .addSources(ImmutableList.of(src.toString()))
+ .setSources(ImmutableList.of(src.toString()))
.setOutput(output.toString())
.build());
fail();
@@ -238,10 +275,200 @@ public class MainTest {
MoreFiles.asCharSink(src, UTF_8).write("public class Test {}");
try {
- Main.compile(optionsWithBootclasspath().addSources(ImmutableList.of(src.toString())).build());
+ Main.compile(optionsWithBootclasspath().setSources(ImmutableList.of(src.toString())).build());
fail();
} catch (UsageException expected) {
- assertThat(expected).hasMessageThat().contains("--output is required");
+ assertThat(expected)
+ .hasMessageThat()
+ .contains("at least one of --output, --gensrc_output, or --resource_output is required");
+ }
+ }
+
+ @Test
+ public void noSources() throws IOException {
+ // Compilations with no sources (or source jars) are accepted, and create empty for requested
+ // outputs. This is helpful for the Bazel integration, which allows java_library rules to be
+ // declared without sources.
+ File gensrc = temporaryFolder.newFile("gensrc.jar");
+ Main.compile(optionsWithBootclasspath().setGensrcOutput(gensrc.toString()).build());
+ try (JarFile jarFile = new JarFile(gensrc);
+ Stream<JarEntry> entries = jarFile.stream()) {
+ assertThat(entries.map(JarEntry::getName))
+ .containsExactly("META-INF/", "META-INF/MANIFEST.MF");
+ }
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class SourceGeneratingProcessor extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ private boolean first = true;
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (first) {
+ try (Writer writer = processingEnv.getFiler().createSourceFile("g.Gen").openWriter()) {
+ writer.write("package g; class Gen {}");
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ first = false;
+ }
+ return false;
+ }
+ }
+
+ @Test
+ public void testManifestProto() throws IOException {
+ Path src = temporaryFolder.newFile("Foo.java").toPath();
+ MoreFiles.asCharSink(src, UTF_8).write("package f; @Deprecated class Foo {}");
+
+ Path output = temporaryFolder.newFile("output.jar").toPath();
+ Path gensrcOutput = temporaryFolder.newFile("gensrcOutput.jar").toPath();
+ Path manifestProtoOutput = temporaryFolder.newFile("manifest.proto").toPath();
+
+ Main.compile(
+ optionsWithBootclasspath()
+ .setSources(ImmutableList.of(src.toString()))
+ .setTargetLabel("//foo:foo")
+ .setInjectingRuleKind("foo_library")
+ .setOutput(output.toString())
+ .setGensrcOutput(gensrcOutput.toString())
+ .setOutputManifest(manifestProtoOutput.toString())
+ .setProcessors(ImmutableList.of(SourceGeneratingProcessor.class.getName()))
+ .build());
+
+ assertThat(readManifestProto(manifestProtoOutput))
+ .isEqualTo(
+ ManifestProto.Manifest.newBuilder()
+ .addCompilationUnit(
+ ManifestProto.CompilationUnit.newBuilder()
+ .setPkg("f")
+ .addTopLevel("Foo")
+ .setPath(src.toString())
+ .setGeneratedByAnnotationProcessor(false)
+ .build())
+ .addCompilationUnit(
+ ManifestProto.CompilationUnit.newBuilder()
+ .setPkg("g")
+ .addTopLevel("Gen")
+ .setPath("g/Gen.java")
+ .setGeneratedByAnnotationProcessor(true)
+ .build())
+ .build());
+ }
+
+ private static ManifestProto.Manifest readManifestProto(Path manifestProtoOutput)
+ throws IOException {
+ ManifestProto.Manifest.Builder manifest = ManifestProto.Manifest.newBuilder();
+ try (InputStream is = new BufferedInputStream(Files.newInputStream(manifestProtoOutput))) {
+ manifest.mergeFrom(is, ExtensionRegistry.getEmptyRegistry());
+ }
+ return manifest.build();
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class CrashyProcessor extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public synchronized void init(ProcessingEnvironment processingEnv) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ return false;
+ }
+ }
+
+ @Test
+ public void noSourcesProcessing() throws IOException {
+ // Compilations with no sources shouldn't initialize annotation processors.
+ File gensrc = temporaryFolder.newFile("gensrc.jar");
+ Main.compile(
+ optionsWithBootclasspath()
+ .setProcessors(ImmutableList.of(CrashyProcessor.class.getName()))
+ .setGensrcOutput(gensrc.toString())
+ .build());
+ try (JarFile jarFile = new JarFile(gensrc);
+ Stream<JarEntry> entries = jarFile.stream()) {
+ assertThat(entries.map(JarEntry::getName))
+ .containsExactly("META-INF/", "META-INF/MANIFEST.MF");
+ }
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class ClassGeneratingProcessor extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ private boolean first = true;
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (first) {
+ try (OutputStream outputStream =
+ processingEnv.getFiler().createClassFile("g.Gen").openOutputStream()) {
+ outputStream.write(dump());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ first = false;
+ }
+ return false;
+ }
+
+ public static byte[] dump() {
+ ClassWriter classWriter = new ClassWriter(0);
+ classWriter.visit(
+ Opcodes.V1_8,
+ Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
+ "g/Gen",
+ null,
+ "java/lang/Object",
+ null);
+ {
+ MethodVisitor methodVisitor =
+ classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
+ methodVisitor.visitCode();
+ methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
+ methodVisitor.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ methodVisitor.visitInsn(Opcodes.RETURN);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+ }
+ classWriter.visitEnd();
+ return classWriter.toByteArray();
+ }
+ }
+
+ @Test
+ public void classGeneration() throws IOException {
+ Path src = temporaryFolder.newFile("package-info.jar").toPath();
+ MoreFiles.asCharSink(src, UTF_8).write("@Deprecated package test;");
+ File resources = temporaryFolder.newFile("resources.jar");
+ Main.compile(
+ optionsWithBootclasspath()
+ .setProcessors(ImmutableList.of(ClassGeneratingProcessor.class.getName()))
+ .setSources(ImmutableList.of(src.toString()))
+ .setResourceOutput(resources.toString())
+ .build());
+ try (JarFile jarFile = new JarFile(resources);
+ Stream<JarEntry> entries = jarFile.stream()) {
+ assertThat(entries.map(JarEntry::getName)).containsExactly("g/Gen.class");
}
}
}
diff --git a/javatests/com/google/turbine/main/ReducedClasspathTest.java b/javatests/com/google/turbine/main/ReducedClasspathTest.java
new file mode 100644
index 0000000..d74c640
--- /dev/null
+++ b/javatests/com/google/turbine/main/ReducedClasspathTest.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.main;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
+import static com.google.turbine.testing.TestClassPaths.optionsWithBootclasspath;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.protobuf.ExtensionRegistry;
+import com.google.turbine.diag.TurbineError;
+import com.google.turbine.lower.IntegrationTestSupport;
+import com.google.turbine.lower.IntegrationTestSupport.TestInput;
+import com.google.turbine.main.Main.Result;
+import com.google.turbine.options.TurbineOptions.ReducedClasspathMode;
+import com.google.turbine.proto.DepsProto;
+import com.google.turbine.proto.DepsProto.Dependency.Kind;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ReducedClasspathTest {
+
+ @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private Path liba;
+ private Path libb;
+ private Path libc;
+ private Path libcJdeps;
+
+ @Before
+ public void setup() throws Exception {
+ Map<String, byte[]> compiled =
+ IntegrationTestSupport.runJavac(
+ TestInput.parse(
+ String.join(
+ "\n",
+ ImmutableList.of(
+ "=== a/A.java ===",
+ "package a;",
+ "public class A {",
+ " public static class I {}",
+ "}",
+ "=== b/B.java ===",
+ "package b;",
+ "import a.A;",
+ "public class B extends A {}",
+ "=== c/C.java ===",
+ "package c;",
+ "import b.B;",
+ "public class C extends B {}")))
+ .sources,
+ /* classpath= */ ImmutableList.of());
+
+ liba = createLibrary(compiled, "liba.jar", "a/A", "a/A$I");
+ libb = createLibrary(compiled, "libb.jar", "b/B");
+ libc = createLibrary(compiled, "libc.jar", "c/C");
+
+ libcJdeps = temporaryFolder.newFile("libc.jdeps").toPath();
+ try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(libcJdeps))) {
+ DepsProto.Dependencies.newBuilder()
+ .addDependency(
+ DepsProto.Dependency.newBuilder()
+ .setKind(Kind.EXPLICIT)
+ .setPath(libb.toString())
+ .build())
+ .build()
+ .writeTo(os);
+ }
+ }
+
+ private Path createLibrary(Map<String, byte[]> compiled, String jarPath, String... classNames)
+ throws IOException {
+ Path lib = temporaryFolder.newFile(jarPath).toPath();
+ try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(lib))) {
+ for (String className : classNames) {
+ jos.putNextEntry(new JarEntry(className + ".class"));
+ jos.write(compiled.get(className));
+ }
+ }
+ return lib;
+ }
+
+ @Test
+ public void succeedsWithoutFallingBack() throws Exception {
+ Path src = temporaryFolder.newFile("Test.java").toPath();
+ Files.write(
+ src,
+ ImmutableList.of(
+ "import c.C;", //
+ "class Test extends C {",
+ "}"),
+ UTF_8);
+
+ Path output = temporaryFolder.newFile("output.jar").toPath();
+
+ Result result =
+ Main.compile(
+ optionsWithBootclasspath()
+ .setOutput(output.toString())
+ .setSources(ImmutableList.of(src.toString()))
+ .setReducedClasspathMode(ReducedClasspathMode.JAVABUILDER_REDUCED)
+ .setClassPath(
+ ImmutableList.of(
+ // ensure that the compilation succeeds without falling back by adding
+ // a jar to the transitive classpath that doesn't exist, which would cause
+ // the compilation to fail if it fell back
+ temporaryFolder.newFile("no.such.jar").toString(),
+ liba.toString(),
+ libb.toString(),
+ libc.toString()))
+ .setDirectJars(ImmutableList.of(libc.toString()))
+ .setDepsArtifacts(ImmutableList.of(libcJdeps.toString()))
+ .build());
+ assertThat(result.transitiveClasspathFallback()).isFalse();
+ }
+
+ @Test
+ public void succeedsAfterFallingBack() throws Exception {
+ Path src = temporaryFolder.newFile("Test.java").toPath();
+ Files.write(
+ src,
+ ImmutableList.of(
+ "import c.C;", //
+ "class Test extends C {",
+ " I i;",
+ "}"),
+ UTF_8);
+
+ Path output = temporaryFolder.newFile("output.jar").toPath();
+
+ Result result =
+ Main.compile(
+ optionsWithBootclasspath()
+ .setOutput(output.toString())
+ .setSources(ImmutableList.of(src.toString()))
+ .setReducedClasspathMode(ReducedClasspathMode.JAVABUILDER_REDUCED)
+ .setClassPath(ImmutableList.of(liba.toString(), libb.toString(), libc.toString()))
+ .setDirectJars(ImmutableList.of(libc.toString()))
+ .setDepsArtifacts(ImmutableList.of(libcJdeps.toString()))
+ .build());
+ assertThat(result.transitiveClasspathFallback()).isTrue();
+ assertThat(result.reducedClasspathLength()).isEqualTo(2);
+ assertThat(result.transitiveClasspathLength()).isEqualTo(3);
+ }
+
+ @Test
+ public void bazelFallback() throws Exception {
+ Path src = temporaryFolder.newFile("Test.java").toPath();
+ Files.write(
+ src,
+ ImmutableList.of(
+ "import c.C;", //
+ "class Test extends C {",
+ " I i;",
+ "}"),
+ UTF_8);
+
+ Path output = temporaryFolder.newFile("output.jar").toPath();
+ Path jdeps = temporaryFolder.newFile("output.jdeps").toPath();
+
+ Result result =
+ Main.compile(
+ optionsWithBootclasspath()
+ .setOutput(output.toString())
+ .setTargetLabel("//java/com/google/foo")
+ .setOutputDeps(jdeps.toString())
+ .setSources(ImmutableList.of(src.toString()))
+ .setReducedClasspathMode(ReducedClasspathMode.BAZEL_REDUCED)
+ .setClassPath(ImmutableList.of(libc.toString()))
+ .setReducedClasspathLength(1)
+ .setFullClasspathLength(3)
+ .build());
+ assertThat(result.transitiveClasspathFallback()).isTrue();
+ assertThat(result.reducedClasspathLength()).isEqualTo(1);
+ assertThat(result.transitiveClasspathLength()).isEqualTo(3);
+ DepsProto.Dependencies.Builder deps = DepsProto.Dependencies.newBuilder();
+ try (InputStream is = new BufferedInputStream(Files.newInputStream(jdeps))) {
+ deps.mergeFrom(is, ExtensionRegistry.getEmptyRegistry());
+ }
+ assertThat(deps.build())
+ .isEqualTo(
+ DepsProto.Dependencies.newBuilder()
+ .setRequiresReducedClasspathFallback(true)
+ .setRuleLabel("//java/com/google/foo")
+ .build());
+ }
+
+ @Test
+ public void noFallbackWithoutDirectJarsAndJdeps() throws Exception {
+ Path src = temporaryFolder.newFile("Test.java").toPath();
+ Files.write(
+ src,
+ ImmutableList.of(
+ "import c.C;", //
+ "class Test extends C {",
+ " I i;",
+ "}"),
+ UTF_8);
+
+ Path output = temporaryFolder.newFile("output.jar").toPath();
+
+ try {
+ Main.compile(
+ optionsWithBootclasspath()
+ .setOutput(output.toString())
+ .setSources(ImmutableList.of(src.toString()))
+ .setReducedClasspathMode(ReducedClasspathMode.JAVABUILDER_REDUCED)
+ .setClassPath(ImmutableList.of(libc.toString()))
+ .setDepsArtifacts(ImmutableList.of(libcJdeps.toString()))
+ .build());
+ fail();
+ } catch (TurbineError e) {
+ assertThat(e).hasMessageThat().contains("could not resolve I");
+ }
+ }
+
+ static String lines(String... lines) {
+ return Joiner.on(System.lineSeparator()).join(lines);
+ }
+}
diff --git a/javatests/com/google/turbine/model/ConstTest.java b/javatests/com/google/turbine/model/ConstTest.java
index a64d0bf..984fd5a 100644
--- a/javatests/com/google/turbine/model/ConstTest.java
+++ b/javatests/com/google/turbine/model/ConstTest.java
@@ -16,12 +16,19 @@
package com.google.turbine.model;
+import static com.google.common.truth.Truth.assertThat;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.testing.EqualsTester;
-import com.google.turbine.binder.bound.AnnotationValue;
+import com.google.turbine.binder.bound.EnumConstantValue;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
import com.google.turbine.binder.bound.TurbineClassValue;
import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.model.Const.ArrayInitValue;
+import com.google.turbine.model.Const.IntValue;
+import com.google.turbine.type.AnnoInfo;
import com.google.turbine.type.Type.ClassTy;
import com.google.turbine.type.Type.PrimTy;
import org.junit.Test;
@@ -62,15 +69,31 @@ public class ConstTest {
new Const.ArrayInitValue(
ImmutableList.of(new Const.IntValue(3), new Const.IntValue(4))))
.addEqualityGroup(
- new AnnotationValue(
- new ClassSymbol("test/Anno"), ImmutableMap.of("value", new Const.IntValue(3))),
- new AnnotationValue(
- new ClassSymbol("test/Anno"), ImmutableMap.of("value", new Const.IntValue(3))))
+ new TurbineAnnotationValue(
+ new AnnoInfo(
+ null,
+ new ClassSymbol("test/Anno"),
+ null,
+ ImmutableMap.of("value", new Const.IntValue(3)))),
+ new TurbineAnnotationValue(
+ new AnnoInfo(
+ null,
+ new ClassSymbol("test/Anno"),
+ null,
+ ImmutableMap.of("value", new Const.IntValue(3)))))
.addEqualityGroup(
- new AnnotationValue(
- new ClassSymbol("test/Anno"), ImmutableMap.of("value", new Const.IntValue(4))),
- new AnnotationValue(
- new ClassSymbol("test/Anno"), ImmutableMap.of("value", new Const.IntValue(4))))
+ new TurbineAnnotationValue(
+ new AnnoInfo(
+ null,
+ new ClassSymbol("test/Anno"),
+ null,
+ ImmutableMap.of("value", new Const.IntValue(4)))),
+ new TurbineAnnotationValue(
+ new AnnoInfo(
+ null,
+ new ClassSymbol("test/Anno"),
+ null,
+ ImmutableMap.of("value", new Const.IntValue(4)))))
.addEqualityGroup(
new TurbineClassValue(ClassTy.asNonParametricClassTy(new ClassSymbol("test/Clazz"))),
new TurbineClassValue(ClassTy.asNonParametricClassTy(new ClassSymbol("test/Clazz"))))
@@ -82,4 +105,37 @@ public class ConstTest {
new TurbineClassValue(PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of())))
.testEquals();
}
+
+ @Test
+ public void toStringTest() {
+ assertThat(new Const.CharValue('\t').toString()).isEqualTo("\'\\t\'");
+ assertThat(new EnumConstantValue(new FieldSymbol(new ClassSymbol("Foo"), "CONST")).toString())
+ .isEqualTo("CONST");
+ assertThat(makeAnno(ImmutableMap.of())).isEqualTo("@p.Anno");
+ assertThat(makeAnno(ImmutableMap.of("value", new IntValue(1)))).isEqualTo("@p.Anno(1)");
+ assertThat(makeAnno(ImmutableMap.of("x", new IntValue(1)))).isEqualTo("@p.Anno(x=1)");
+ assertThat(
+ makeAnno(
+ ImmutableMap.of("value", new ArrayInitValue(ImmutableList.of(new IntValue(1))))))
+ .isEqualTo("@p.Anno({1})");
+ assertThat(
+ makeAnno(
+ ImmutableMap.of(
+ "value",
+ new ArrayInitValue(ImmutableList.of(new IntValue(1), new IntValue(2))))))
+ .isEqualTo("@p.Anno({1, 2})");
+ assertThat(
+ makeAnno(ImmutableMap.of("xs", new ArrayInitValue(ImmutableList.of(new IntValue(1))))))
+ .isEqualTo("@p.Anno(xs={1})");
+ assertThat(makeAnno(ImmutableMap.of("x", new IntValue(1), "y", new IntValue(2))))
+ .isEqualTo("@p.Anno(x=1, y=2)");
+ assertThat(new Const.StringValue("\"").toString()).isEqualTo("\"\\\"\"");
+ assertThat(new Const.ByteValue((byte) 42).toString()).isEqualTo("(byte)0x2a");
+ assertThat(new Const.ShortValue((short) 42).toString()).isEqualTo("42");
+ }
+
+ private static String makeAnno(ImmutableMap<String, Const> value) {
+ return new TurbineAnnotationValue(new AnnoInfo(null, new ClassSymbol("p/Anno"), null, value))
+ .toString();
+ }
}
diff --git a/javatests/com/google/turbine/options/TurbineOptionsTest.java b/javatests/com/google/turbine/options/TurbineOptionsTest.java
index a5872d9..d4b468b 100644
--- a/javatests/com/google/turbine/options/TurbineOptionsTest.java
+++ b/javatests/com/google/turbine/options/TurbineOptionsTest.java
@@ -22,6 +22,7 @@ import static org.junit.Assert.fail;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
+import com.google.turbine.options.TurbineOptions.ReducedClasspathMode;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -95,7 +96,7 @@ public class TurbineOptionsTest {
assertThat(options.outputDeps()).hasValue("out.jdeps");
assertThat(options.targetLabel()).hasValue("//java/com/google/test");
assertThat(options.injectingRuleKind()).hasValue("foo_library");
- assertThat(options.shouldReduceClassPath()).isTrue();
+ assertThat(options.reducedClasspathMode()).isEqualTo(ReducedClasspathMode.NONE);
}
@Test
@@ -261,13 +262,22 @@ public class TurbineOptionsTest {
@Test
public void javacopts() throws Exception {
String[] lines = {
- "--javacopts", "--release", "9", "--", "--sources", "Test.java",
+ "--javacopts",
+ "--release",
+ "8",
+ "--",
+ "--sources",
+ "Test.java",
+ "--javacopts",
+ "--release",
+ "9",
+ "--",
};
TurbineOptions options =
TurbineOptionsParser.parse(Iterables.concat(BASE_ARGS, Arrays.asList(lines)));
- assertThat(options.javacOpts()).containsExactly("--release", "9").inOrder();
+ assertThat(options.javacOpts()).containsExactly("--release", "8", "--release", "9").inOrder();
assertThat(options.sources()).containsExactly("Test.java");
}
@@ -315,20 +325,14 @@ public class TurbineOptionsTest {
}
@Test
- public void shouldReduceClasspath() throws Exception {
- {
- TurbineOptions options =
- TurbineOptionsParser.parse(
- Iterables.concat(BASE_ARGS, ImmutableList.of("--reduce_classpath")));
- assertThat(options.shouldReduceClassPath()).isTrue();
- }
-
- {
- TurbineOptions options =
- TurbineOptionsParser.parse(
- Iterables.concat(BASE_ARGS, ImmutableList.of("--noreduce_classpath")));
- assertThat(options.shouldReduceClassPath()).isFalse();
- }
+ public void miscOutputs() throws Exception {
+ TurbineOptions options =
+ TurbineOptionsParser.parse(
+ Iterables.concat(
+ BASE_ARGS,
+ ImmutableList.of("--gensrc_output", "gensrc.jar", "--profile", "turbine.prof")));
+ assertThat(options.gensrcOutput()).hasValue("gensrc.jar");
+ assertThat(options.profile()).hasValue("turbine.prof");
}
@Test
@@ -350,4 +354,23 @@ public class TurbineOptionsTest {
} catch (IllegalArgumentException expected) {
}
}
+
+ @Test
+ public void builtinProcessors() throws Exception {
+ String[] lines = {"--builtin_processors", "BuiltinProcessor"};
+ TurbineOptions options =
+ TurbineOptionsParser.parse(Iterables.concat(BASE_ARGS, Arrays.asList(lines)));
+ assertThat(options.builtinProcessors()).containsExactly("BuiltinProcessor");
+ }
+
+ @Test
+ public void reducedClasspathMode() throws Exception {
+ for (ReducedClasspathMode mode : ReducedClasspathMode.values()) {
+ TurbineOptions options =
+ TurbineOptionsParser.parse(
+ Iterables.concat(
+ BASE_ARGS, ImmutableList.of("--reduce_classpath_mode", mode.name())));
+ assertThat(options.reducedClasspathMode()).isEqualTo(mode);
+ }
+ }
}
diff --git a/javatests/com/google/turbine/parse/CommentParserTest.java b/javatests/com/google/turbine/parse/CommentParserTest.java
new file mode 100644
index 0000000..a2f84d5
--- /dev/null
+++ b/javatests/com/google/turbine/parse/CommentParserTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.parse;
+
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Joiner;
+import com.google.turbine.tree.Tree;
+import com.google.turbine.tree.Tree.MethDecl;
+import com.google.turbine.tree.Tree.TyDecl;
+import com.google.turbine.tree.Tree.VarDecl;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class CommentParserTest {
+
+ @Test
+ public void comments() {
+ Tree.CompUnit unit =
+ Parser.parse(
+ Joiner.on('\n')
+ .join(
+ "package p;",
+ "/** hello world */",
+ "class Test {",
+ " /**",
+ " * This is",
+ " * class A",
+ " */",
+ " class A {",
+ " /** This is a method */",
+ " void f() {}",
+ " /** This is a field */",
+ " int g;",
+ " }",
+ " /* This is not javadoc */",
+ " class B {}",
+ " /**",
+ " * This is",
+ " * class C",
+ " */",
+ " class C {}",
+ "}\n"));
+ TyDecl decl = getOnlyElement(unit.decls());
+ assertThat(decl.javadoc()).isEqualTo(" hello world ");
+ assertThat(
+ decl.members().stream()
+ .map(Tree.TyDecl.class::cast)
+ .filter(c -> c.javadoc() != null)
+ .collect(toImmutableMap(c -> c.name().value(), c -> c.javadoc())))
+ .containsExactly(
+ "A", "\n * This is\n * class A\n ",
+ "C", "\n * This is\n * class C\n ");
+ TyDecl a = (TyDecl) decl.members().get(0);
+ MethDecl f = (MethDecl) a.members().get(0);
+ assertThat(f.javadoc()).isEqualTo(" This is a method ");
+ VarDecl g = (VarDecl) a.members().get(1);
+ assertThat(g.javadoc()).isEqualTo(" This is a field ");
+ }
+}
diff --git a/javatests/com/google/turbine/parse/JavacLexer.java b/javatests/com/google/turbine/parse/JavacLexer.java
index af82dbe..d8939f1 100644
--- a/javatests/com/google/turbine/parse/JavacLexer.java
+++ b/javatests/com/google/turbine/parse/JavacLexer.java
@@ -280,8 +280,7 @@ public class JavacLexer {
case CHARLITERAL:
return String.format(
"CHAR_LITERAL(%s)", SourceCodeEscapers.javaCharEscaper().escape(token.stringVal()));
- default:
- return token.kind.toString();
}
+ return token.kind.toString();
}
}
diff --git a/javatests/com/google/turbine/parse/LexerTest.java b/javatests/com/google/turbine/parse/LexerTest.java
index 4867a22..8530d52 100644
--- a/javatests/com/google/turbine/parse/LexerTest.java
+++ b/javatests/com/google/turbine/parse/LexerTest.java
@@ -40,12 +40,6 @@ public class LexerTest {
}
@Test
- public void unterminated() {
- assertThat(lex("/* foo")).containsExactly("EOF");
- assertThat(lex("\" foo")).containsExactly("EOF");
- }
-
- @Test
public void boolLiteral() {
lexerComparisonTest("0b0101__01010");
assertThat(lex("1 + 0b1000100101"))
diff --git a/javatests/com/google/turbine/parse/ParseErrorTest.java b/javatests/com/google/turbine/parse/ParseErrorTest.java
index 49fb273..6a9ad11 100644
--- a/javatests/com/google/turbine/parse/ParseErrorTest.java
+++ b/javatests/com/google/turbine/parse/ParseErrorTest.java
@@ -228,6 +228,57 @@ public class ParseErrorTest {
}
}
+ @Test
+ public void abruptMultivariableDeclaration() {
+ String input = "class T { int x,; }";
+ try {
+ Parser.parse(input);
+ fail("expected parsing to fail");
+ } catch (TurbineError e) {
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: expected token <identifier>", //
+ "class T { int x,; }",
+ " ^"));
+ }
+ }
+
+ @Test
+ public void invalidAnnotation() {
+ String input = "@Foo(x = @E [] x) class T {}";
+ try {
+ Parser.parse(input);
+ fail("expected parsing to fail");
+ } catch (TurbineError e) {
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: invalid annotation argument", //
+ "@Foo(x = @E [] x) class T {}",
+ " ^"));
+ }
+ }
+
+ @Test
+ public void unclosedComment() {
+ String input = "/** *\u001a/ class Test {}";
+ try {
+ Parser.parse(input);
+ fail("expected parsing to fail");
+ } catch (TurbineError e) {
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ lines(
+ "<>:1: error: unclosed comment", //
+ "/** *\u001a/ class Test {}",
+ "^"));
+ }
+ }
+
private static String lines(String... lines) {
return Joiner.on(System.lineSeparator()).join(lines);
}
diff --git a/javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java b/javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java
index 2637091..e3f7b63 100644
--- a/javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java
+++ b/javatests/com/google/turbine/parse/UnicodeEscapePreprocessorTest.java
@@ -16,10 +16,13 @@
package com.google.turbine.parse;
+import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import com.google.turbine.diag.SourceFile;
+import com.google.turbine.diag.TurbineError;
+import com.google.turbine.diag.TurbineError.ErrorKind;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
@@ -57,15 +60,15 @@ public class UnicodeEscapePreprocessorTest {
try {
readAll("\\u00");
fail();
- } catch (AssertionError e) {
- assertThat(e).hasMessage("unexpected end of input");
+ } catch (TurbineError e) {
+ assertThat(getOnlyElement(e.diagnostics()).kind()).isEqualTo(ErrorKind.UNEXPECTED_EOF);
}
try {
readAll("\\u");
fail();
- } catch (AssertionError e) {
- assertThat(e).hasMessage("unexpected end of input");
+ } catch (TurbineError e) {
+ assertThat(getOnlyElement(e.diagnostics()).kind()).isEqualTo(ErrorKind.UNEXPECTED_EOF);
}
}
@@ -74,6 +77,16 @@ public class UnicodeEscapePreprocessorTest {
assertThat(readAll("\\u005C\\\\u005C")).containsExactly('\\', '\\', '\\');
}
+ @Test
+ public void invalidEscape() {
+ try {
+ readAll("\\uUUUU");
+ fail();
+ } catch (TurbineError e) {
+ assertThat(getOnlyElement(e.diagnostics()).kind()).isEqualTo(ErrorKind.INVALID_UNICODE);
+ }
+ }
+
private List<Character> readAll(String input) {
UnicodeEscapePreprocessor reader = new UnicodeEscapePreprocessor(new SourceFile(null, input));
List<Character> result = new ArrayList<>();
diff --git a/javatests/com/google/turbine/processing/AbstractTurbineTypesBiPredicateTest.java b/javatests/com/google/turbine/processing/AbstractTurbineTypesBiPredicateTest.java
new file mode 100644
index 0000000..6ea6e72
--- /dev/null
+++ b/javatests/com/google/turbine/processing/AbstractTurbineTypesBiPredicateTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Types;
+
+/**
+ * A combo test for {@link TurbineTypes} that compares the behaviour of bipredicates like {@link
+ * Types#isSubtype(TypeMirror, TypeMirror)} with javac's implementation.
+ */
+abstract class AbstractTurbineTypesBiPredicateTest extends AbstractTurbineTypesTest {
+
+ final String testDescription;
+ final TypesBiFunctionInput javacInput;
+ final TypesBiFunctionInput turbineInput;
+
+ public AbstractTurbineTypesBiPredicateTest(
+ String testDescription, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) {
+ this.testDescription = testDescription;
+ this.javacInput = javacInput;
+ this.turbineInput = turbineInput;
+ }
+
+ protected void test(String symbol, TypeBiPredicate predicate) {
+ assertWithMessage("%s = %s", javacInput.format(symbol), turbineInput.format(symbol))
+ .that(turbineInput.apply(predicate))
+ .isEqualTo(javacInput.apply(predicate));
+ }
+}
diff --git a/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java b/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java
new file mode 100644
index 0000000..e6a59bf
--- /dev/null
+++ b/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.stream.Collectors.joining;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.Streams;
+import com.google.turbine.binder.Binder;
+import com.google.turbine.binder.Binder.BindingResult;
+import com.google.turbine.binder.ClassPathBinder;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.env.SimpleEnv;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.parse.Parser;
+import com.google.turbine.testing.TestClassPaths;
+import com.google.turbine.tree.Tree.CompUnit;
+import com.sun.source.util.JavacTask;
+import com.sun.tools.javac.api.JavacTool;
+import com.sun.tools.javac.file.JavacFileManager;
+import com.sun.tools.javac.util.Context;
+import java.net.URI;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.WildcardType;
+import javax.lang.model.util.ElementScanner8;
+import javax.lang.model.util.SimpleTypeVisitor8;
+import javax.lang.model.util.Types;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.SimpleJavaFileObject;
+
+class AbstractTurbineTypesTest {
+
+ protected static class TypeParameters {
+ protected final Types javacTypes;
+ protected final List<List<TypeMirror>> aGroups;
+ protected final Types turbineTypes;
+ protected final List<List<TypeMirror>> bGroups;
+
+ private TypeParameters(
+ Types javacTypes,
+ List<List<TypeMirror>> aGroups,
+ Types turbineTypes,
+ List<List<TypeMirror>> bGroups) {
+ this.javacTypes = javacTypes;
+ this.aGroups = aGroups;
+ this.turbineTypes = turbineTypes;
+ this.bGroups = bGroups;
+ }
+ }
+
+ protected interface TypeBiPredicate {
+ boolean apply(Types types, TypeMirror a, TypeMirror b);
+ }
+
+ static class TypesBiFunctionInput {
+ final Types types;
+ final TypeMirror lhs;
+ final TypeMirror rhs;
+
+ TypesBiFunctionInput(Types types, TypeMirror lhs, TypeMirror rhs) {
+ this.types = types;
+ this.lhs = lhs;
+ this.rhs = rhs;
+ }
+
+ boolean apply(TypeBiPredicate predicate) {
+ return predicate.apply(types, lhs, rhs);
+ }
+
+ String format(String symbol) {
+ return String.format("`%s` %s `%s`", lhs, symbol, rhs);
+ }
+ }
+
+ protected static Iterable<Object[]> binaryParameters() throws Exception {
+ TypeParameters typeParameters = typeParameters();
+ List<Object[]> params = new ArrayList<>();
+ for (int i = 0; i < typeParameters.aGroups.size(); i++) {
+ List<TypeMirror> ax = typeParameters.aGroups.get(i);
+ List<TypeMirror> bx = typeParameters.bGroups.get(i);
+ Streams.zip(
+ Lists.cartesianProduct(ax, ax).stream(),
+ Lists.cartesianProduct(bx, bx).stream(),
+ (a, b) ->
+ new Object[] {
+ a.get(0) + " " + a.get(1),
+ new TypesBiFunctionInput(typeParameters.javacTypes, a.get(0), a.get(1)),
+ new TypesBiFunctionInput(typeParameters.turbineTypes, b.get(0), b.get(1)),
+ })
+ .forEachOrdered(params::add);
+ }
+ return params;
+ }
+
+ protected static Iterable<Object[]> unaryParameters() throws Exception {
+ TypeParameters typeParameters = typeParameters();
+ List<Object[]> params = new ArrayList<>();
+ for (int i = 0; i < typeParameters.aGroups.size(); i++) {
+ Streams.zip(
+ typeParameters.aGroups.get(i).stream(),
+ typeParameters.bGroups.get(i).stream(),
+ (a, b) ->
+ new Object[] {
+ a.toString(), typeParameters.javacTypes, a, typeParameters.turbineTypes, b,
+ })
+ .forEachOrdered(params::add);
+ }
+ return params;
+ }
+
+ protected static TypeParameters typeParameters() throws Exception {
+ String[][] types = {
+ // generics
+ {
+ "Object",
+ "String",
+ "Cloneable",
+ "Serializable",
+ "List",
+ "Set",
+ "ArrayList",
+ "Collection",
+ "List<Object>",
+ "List<Number>",
+ "List<Integer>",
+ "ArrayList<Object>",
+ "ArrayList<Number>",
+ "ArrayList<Integer>",
+ },
+ // wildcards
+ {
+ "Object",
+ "String",
+ "Cloneable",
+ "Serializable",
+ "List",
+ "List<?>",
+ "List<Object>",
+ "List<Number>",
+ "List<Integer>",
+ "List<? extends Object>",
+ "List<? extends Number>",
+ "List<? extends Integer>",
+ "List<? super Object>",
+ "List<? super Number>",
+ "List<? super Integer>",
+ },
+ // arrays
+ {
+ "Object",
+ "String",
+ "Cloneable",
+ "Serializable",
+ "List",
+ "Object[]",
+ "Number[]",
+ "List<Integer>[]",
+ "List<? extends Integer>[]",
+ "long[]",
+ "int[]",
+ "int[][]",
+ "Long[]",
+ "Integer[]",
+ "Integer[][]",
+ },
+ // primitives
+ {
+ "Object",
+ "String",
+ "Cloneable",
+ "Serializable",
+ "List",
+ "int",
+ "char",
+ "byte",
+ "short",
+ "boolean",
+ "long",
+ "float",
+ "double",
+ "Integer",
+ "Character",
+ "Byte",
+ "Short",
+ "Boolean",
+ "Long",
+ "Float",
+ "Double",
+ },
+ };
+ List<String> files = new ArrayList<>();
+ AtomicInteger idx = new AtomicInteger();
+ for (String[] group : types) {
+ StringBuilder sb = new StringBuilder();
+ Joiner.on('\n')
+ .appendTo(
+ sb,
+ "package p;",
+ "import java.util.*;",
+ "import java.io.*;",
+ String.format("abstract class Test%s {", idx.getAndIncrement()),
+ Streams.mapWithIndex(
+ Arrays.stream(group), (x, i) -> String.format(" %s f%d;\n", x, i))
+ .collect(joining("\n")),
+ " abstract <T extends Serializable & List<T>> T f();",
+ " abstract <V extends List<V>> V g();",
+ " abstract <W extends ArrayList> W h();",
+ " abstract <X extends Serializable> X i();",
+ "}");
+ String content = sb.toString();
+ files.add(content);
+ }
+ // type hierarchies
+ files.add(
+ Joiner.on('\n')
+ .join(
+ "import java.util.*;",
+ "class Hierarchy {", //
+ " static class A<T> {",
+ " class I {}",
+ " }",
+ " static class D<T> extends A<T[]> {}",
+ " static class E<T> extends A<T> {",
+ " class J extends I {}",
+ " }",
+ " static class F<T> extends A {}",
+ " static class G<T> extends A<List<T>> {}",
+ " A rawA;",
+ " A<Object[]> a1;",
+ " A<Number[]> a2;",
+ " A<Integer[]> a3;",
+ " A<? super Object> a4;",
+ " A<? super Number> a5;",
+ " A<? super Integer> a6;",
+ " A<? extends Object> a7;",
+ " A<? extends Number> a8;",
+ " A<? extends Integer> a9;",
+ " A<List<Integer>> a10;",
+ " D<Object> d1;",
+ " D<Number> d2;",
+ " D<Integer> d3;",
+ " A<Object>.I i1;",
+ " A<Number>.I i2;",
+ " A<Integer>.I i3;",
+ " E<Object>.J j1;",
+ " E<Number>.J j2;",
+ " E<Integer>.J j3;",
+ " F<Integer> f1;",
+ " F<Number> f2;",
+ " F<Object> f3;",
+ " G<Integer> g1;",
+ " G<Number> g2;",
+ "}"));
+ // methods
+ files.add(
+ Joiner.on('\n')
+ .join(
+ "import java.io.*;",
+ "class Methods {",
+ " void f() {}",
+ " void g() {}",
+ " void f(int x) {}",
+ " void f(int x, int y) {}",
+ " abstract static class I {",
+ " abstract int f();",
+ " abstract void g() throws IOException;",
+ " abstract <T> void h();",
+ " abstract <T extends String> T i(T s);",
+ " }",
+ " abstract static class J {",
+ " abstract long f();",
+ " abstract void g();",
+ " abstract <T> void h();",
+ " abstract <T extends Number> T i(T s);",
+ " }",
+ " class K {",
+ " void f(K this, int x) {}",
+ " void g(K this) {}",
+ " <T extends Enum<T> & Serializable> void h(T t) {}",
+ " }",
+ " class L {",
+ " void f(int x) {}",
+ " void g() {}",
+ " <E extends Enum<E> & Serializable> void h(E t) {}",
+ " }",
+ "}",
+ "class M<T extends Enum<T> & Serializable> {",
+ " void h(T t) {}",
+ "}"));
+
+ Context context = new Context();
+ JavaFileManager fileManager = new JavacFileManager(context, true, UTF_8);
+ idx.set(0);
+ ImmutableList<SimpleJavaFileObject> compilationUnits =
+ files.stream()
+ .map(
+ x ->
+ new SimpleJavaFileObject(
+ URI.create("file://test" + idx.getAndIncrement() + ".java"), Kind.SOURCE) {
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return x;
+ }
+ })
+ .collect(toImmutableList());
+ JavacTask task =
+ JavacTool.create()
+ .getTask(
+ /* out= */ null,
+ fileManager,
+ /* diagnosticListener= */ null,
+ /* options= */ ImmutableList.of(),
+ /* classes= */ ImmutableList.of(),
+ compilationUnits);
+
+ Types javacTypes = task.getTypes();
+ ImmutableMap<String, Element> javacElements =
+ Streams.stream(task.analyze())
+ .collect(toImmutableMap(e -> e.getSimpleName().toString(), x -> x));
+
+ ImmutableList<CompUnit> units = files.stream().map(Parser::parse).collect(toImmutableList());
+ BindingResult bound =
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+ Env<ClassSymbol, TypeBoundClass> env =
+ CompoundEnv.<ClassSymbol, TypeBoundClass>of(bound.classPathEnv())
+ .append(new SimpleEnv<>(bound.units()));
+ ModelFactory factory = new ModelFactory(env, ClassLoader.getSystemClassLoader(), bound.tli());
+ Types turbineTypes = new TurbineTypes(factory);
+ ImmutableMap<String, Element> turbineElements =
+ bound.units().keySet().stream()
+ .filter(x -> !x.binaryName().contains("$")) // only top level classes
+ .collect(toImmutableMap(x -> x.simpleName(), factory::element));
+
+ assertThat(javacElements.keySet()).containsExactlyElementsIn(turbineElements.keySet());
+
+ List<List<TypeMirror>> aGroups = new ArrayList<>();
+ List<List<TypeMirror>> bGroups = new ArrayList<>();
+ for (String name : javacElements.keySet()) {
+
+ List<TypeMirror> aGroup = new ArrayList<>();
+ List<TypeMirror> bGroup = new ArrayList<>();
+
+ ListMultimap<String, TypeMirror> javacInputs =
+ MultimapBuilder.linkedHashKeys().arrayListValues().build();
+ javacElements
+ .get(name)
+ .getEnclosedElements()
+ .forEach(e -> getTypes(javacTypes, e, javacInputs));
+
+ ListMultimap<String, TypeMirror> turbineInputs =
+ MultimapBuilder.linkedHashKeys().arrayListValues().build();
+ turbineElements
+ .get(name)
+ .getEnclosedElements()
+ .forEach(e -> getTypes(turbineTypes, e, turbineInputs));
+
+ assertThat(turbineInputs.keySet()).containsExactlyElementsIn(javacInputs.keySet());
+
+ for (String key : javacInputs.keySet()) {
+ List<TypeMirror> a = javacInputs.get(key);
+ List<TypeMirror> b = turbineInputs.get(key);
+ assertWithMessage(key)
+ .that(b.stream().map(x -> x.getKind() + " " + x).collect(toImmutableList()))
+ .containsExactlyElementsIn(
+ a.stream().map(x -> x.getKind() + " " + x).collect(toImmutableList()))
+ .inOrder();
+ aGroup.addAll(a);
+ bGroup.addAll(b);
+ }
+ aGroups.add(aGroup);
+ bGroups.add(bGroup);
+ }
+ return new TypeParameters(javacTypes, aGroups, turbineTypes, bGroups);
+ }
+
+ /**
+ * Discover all types contained in the given element, keyed by their immediate enclosing element.
+ */
+ private static void getTypes(
+ Types typeUtils, Element element, Multimap<String, TypeMirror> types) {
+ element.accept(
+ new ElementScanner8<Void, Void>() {
+
+ /**
+ * Returns an element name qualified by all enclosing elements, to allow comparison
+ * between javac and turbine's implementation to group related types.
+ */
+ String key(Element e) {
+ Deque<String> flat = new ArrayDeque<>();
+ while (e != null) {
+ flat.addFirst(e.getSimpleName().toString());
+ if (e.getKind() == ElementKind.PACKAGE) {
+ break;
+ }
+ e = e.getEnclosingElement();
+ }
+ return Joiner.on('.').join(flat);
+ }
+
+ void addType(Element e, TypeMirror t) {
+ if (t != null) {
+ types.put(key(e), t);
+ t.accept(
+ new SimpleTypeVisitor8<Void, Void>() {
+ @Override
+ public Void visitDeclared(DeclaredType t, Void aVoid) {
+ for (TypeMirror a : t.getTypeArguments()) {
+ a.accept(this, null);
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitWildcard(WildcardType t, Void aVoid) {
+ types.put(key(e), t);
+ return null;
+ }
+
+ @Override
+ public Void visitTypeVariable(TypeVariable t, Void aVoid) {
+ if (t.getUpperBound() != null) {
+ types.put(key(e), t.getUpperBound());
+ }
+ return null;
+ }
+ },
+ null);
+ }
+ }
+
+ void addType(Element e, List<? extends TypeMirror> types) {
+ for (TypeMirror type : types) {
+ addType(e, type);
+ }
+ }
+
+ @Override
+ public Void visitVariable(VariableElement e, Void unused) {
+ if (e.getSimpleName().toString().contains("this$")) {
+ // enclosing instance parameters
+ return null;
+ }
+ addType(e, e.asType());
+ return super.visitVariable(e, null);
+ }
+
+ @Override
+ public Void visitType(TypeElement e, Void unused) {
+ addType(e, e.asType());
+ return super.visitType(e, null);
+ }
+
+ @Override
+ public Void visitExecutable(ExecutableElement e, Void unused) {
+ scan(e.getTypeParameters(), null);
+ scan(e.getParameters(), null);
+ addType(e, e.asType());
+ addType(e, e.getReturnType());
+ TypeMirror receiverType = e.getReceiverType();
+ if (receiverType == null) {
+ // work around a javac bug in JDK < 14, see:
+ // https://bugs.openjdk.java.net/browse/JDK-8222369
+ receiverType = typeUtils.getNoType(TypeKind.NONE);
+ }
+ addType(e, receiverType);
+ addType(e, e.getThrownTypes());
+ return null;
+ }
+
+ @Override
+ public Void visitTypeParameter(TypeParameterElement e, Void unused) {
+ addType(e, e.asType());
+ addType(e, e.getBounds());
+ return null;
+ }
+ },
+ null);
+ }
+}
diff --git a/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java b/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java
new file mode 100644
index 0000000..ed5af6a
--- /dev/null
+++ b/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.MoreCollectors.onlyElement;
+import static com.google.common.truth.Truth.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.stream.Collectors.joining;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.turbine.binder.Binder;
+import com.google.turbine.binder.Binder.BindingResult;
+import com.google.turbine.binder.ClassPathBinder;
+import com.google.turbine.binder.Processing;
+import com.google.turbine.binder.Processing.ProcessorInfo;
+import com.google.turbine.diag.SourceFile;
+import com.google.turbine.diag.TurbineError;
+import com.google.turbine.lower.IntegrationTestSupport;
+import com.google.turbine.parse.Parser;
+import com.google.turbine.testing.TestClassPaths;
+import com.google.turbine.tree.Tree;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardLocation;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ProcessingIntegrationTest {
+
+ @SupportedAnnotationTypes("*")
+ public static class CrashingProcessor extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ throw new RuntimeException("crash!");
+ }
+ }
+
+ private static final IntegrationTestSupport.TestInput SOURCES =
+ IntegrationTestSupport.TestInput.parse(
+ Joiner.on('\n')
+ .join(
+ "=== Test.java ===", //
+ "@Deprecated",
+ "class Test extends NoSuch {",
+ "}"));
+
+ @Test
+ public void crash() throws IOException {
+ ImmutableList<Tree.CompUnit> units =
+ SOURCES.sources.entrySet().stream()
+ .map(e -> new SourceFile(e.getKey(), e.getValue()))
+ .map(Parser::parse)
+ .collect(toImmutableList());
+ try {
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ Processing.ProcessorInfo.create(
+ ImmutableList.of(new CrashingProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+ fail();
+ } catch (TurbineError e) {
+ assertThat(e.diagnostics()).hasSize(2);
+ assertThat(e.diagnostics().get(0).message()).contains("could not resolve NoSuch");
+ assertThat(e.diagnostics().get(1).message()).contains("crash!");
+ }
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class WarningProcessor extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ private boolean first = true;
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (first) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "proc warning");
+ try {
+ JavaFileObject file = processingEnv.getFiler().createSourceFile("Gen.java");
+ try (Writer writer = file.openWriter()) {
+ writer.write("class Gen {}");
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ first = false;
+ }
+ if (roundEnv.processingOver()) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "proc error");
+ }
+ return false;
+ }
+ }
+
+ @Test
+ public void warnings() throws IOException {
+ ImmutableList<Tree.CompUnit> units =
+ IntegrationTestSupport.TestInput.parse(
+ Joiner.on('\n')
+ .join(
+ "=== Test.java ===", //
+ "@Deprecated",
+ "class Test {",
+ "}"))
+ .sources
+ .entrySet()
+ .stream()
+ .map(e -> new SourceFile(e.getKey(), e.getValue()))
+ .map(Parser::parse)
+ .collect(toImmutableList());
+ try {
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ Processing.ProcessorInfo.create(
+ ImmutableList.of(new WarningProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+ fail();
+ } catch (TurbineError e) {
+ ImmutableList<String> diags =
+ e.diagnostics().stream().map(d -> d.message()).collect(toImmutableList());
+ assertThat(diags).hasSize(2);
+ assertThat(diags.get(0)).contains("proc warning");
+ assertThat(diags.get(1)).contains("proc error");
+ }
+ }
+
+ @SupportedAnnotationTypes("*")
+ public static class ResourceProcessor extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ private boolean first = true;
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (first) {
+ try {
+ try (Writer writer = processingEnv.getFiler().createSourceFile("Gen").openWriter()) {
+ writer.write("class Gen {}");
+ }
+ try (Writer writer =
+ processingEnv
+ .getFiler()
+ .createResource(StandardLocation.SOURCE_OUTPUT, "", "source.txt")
+ .openWriter()) {
+ writer.write("hello source output");
+ }
+ try (Writer writer =
+ processingEnv
+ .getFiler()
+ .createResource(StandardLocation.CLASS_OUTPUT, "", "class.txt")
+ .openWriter()) {
+ writer.write("hello class output");
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ first = false;
+ }
+ return false;
+ }
+ }
+
+ @Test
+ public void resources() throws IOException {
+ ImmutableList<Tree.CompUnit> units =
+ IntegrationTestSupport.TestInput.parse(
+ Joiner.on('\n')
+ .join(
+ "=== Test.java ===", //
+ "@Deprecated",
+ "class Test {",
+ "}"))
+ .sources
+ .entrySet()
+ .stream()
+ .map(e -> new SourceFile(e.getKey(), e.getValue()))
+ .map(Parser::parse)
+ .collect(toImmutableList());
+ BindingResult bound =
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ ProcessorInfo.create(
+ ImmutableList.of(new ResourceProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+
+ assertThat(bound.generatedSources().keySet()).containsExactly("Gen.java", "source.txt");
+ assertThat(bound.generatedClasses().keySet()).containsExactly("class.txt");
+
+ assertThat(bound.generatedSources().get("source.txt").source())
+ .isEqualTo("hello source output");
+ assertThat(new String(bound.generatedClasses().get("class.txt"), UTF_8))
+ .isEqualTo("hello class output");
+ }
+
+ @Test
+ public void getAllAnnotations() throws IOException {
+ ImmutableList<Tree.CompUnit> units =
+ IntegrationTestSupport.TestInput.parse(
+ Joiner.on('\n')
+ .join(
+ "=== A.java ===", //
+ "import java.lang.annotation.Inherited;",
+ "@Inherited",
+ "@interface A {}",
+ "=== B.java ===", //
+ "@interface B {}",
+ "=== One.java ===", //
+ "@A @B class One {}",
+ "=== Two.java ===", //
+ "class Two extends One {}"))
+ .sources
+ .entrySet()
+ .stream()
+ .map(e -> new SourceFile(e.getKey(), e.getValue()))
+ .map(Parser::parse)
+ .collect(toImmutableList());
+ BindingResult bound =
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ ProcessorInfo.create(
+ ImmutableList.of(new ElementsAnnotatedWithProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+
+ assertThat(
+ Splitter.on(System.lineSeparator())
+ .omitEmptyStrings()
+ .split(
+ new String(
+ bound.generatedClasses().entrySet().stream()
+ .filter(s -> s.getKey().equals("output.txt"))
+ .collect(onlyElement())
+ .getValue(),
+ UTF_8)))
+ .containsExactly("A: One, Two", "B: One");
+ }
+
+ @SupportedAnnotationTypes("*")
+ private static class ElementsAnnotatedWithProcessor extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ private boolean first = true;
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (first) {
+ try (PrintWriter writer =
+ new PrintWriter(
+ processingEnv
+ .getFiler()
+ .createResource(StandardLocation.CLASS_OUTPUT, "", "output.txt")
+ .openWriter(),
+ /* autoFlush= */ true)) {
+ printAnnotatedElements(roundEnv, writer, "A");
+ printAnnotatedElements(roundEnv, writer, "B");
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ first = false;
+ }
+ return false;
+ }
+
+ private void printAnnotatedElements(
+ RoundEnvironment roundEnv, PrintWriter writer, String annotation) {
+ writer.println(
+ annotation
+ + ": "
+ + roundEnv
+ .getElementsAnnotatedWith(
+ processingEnv.getElementUtils().getTypeElement(annotation))
+ .stream()
+ .map(e -> e.getSimpleName().toString())
+ .collect(joining(", ")));
+ }
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineAnnotationMirrorTest.java b/javatests/com/google/turbine/processing/TurbineAnnotationMirrorTest.java
new file mode 100644
index 0000000..a049860
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineAnnotationMirrorTest.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.Multimaps;
+import com.google.turbine.binder.Binder;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.env.SimpleEnv;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.lower.IntegrationTestSupport;
+import com.google.turbine.lower.IntegrationTestSupport.TestInput;
+import com.google.turbine.processing.TurbineElement.TurbineTypeElement;
+import com.google.turbine.testing.TestClassPaths;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.AbstractAnnotationValueVisitor8;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineAnnotationMirrorTest {
+
+ private AnnotationMirror getAnnotation(
+ List<? extends AnnotationMirror> annotationMirrors, String name) {
+ return annotationMirrors.stream()
+ .filter(x -> x.getAnnotationType().asElement().getSimpleName().contentEquals(name))
+ .findFirst()
+ .get();
+ }
+
+ private ImmutableMap<String, Object> values(AnnotationMirror a) {
+ return values(a.getElementValues());
+ }
+
+ /**
+ * Returns a map from the name of annotation elements to their values, see also {@link
+ * #getValue(AnnotationValue)}.
+ */
+ private ImmutableMap<String, Object> values(
+ Map<? extends ExecutableElement, ? extends AnnotationValue> values) {
+ return values.entrySet().stream()
+ .collect(
+ toImmutableMap(
+ e -> e.getKey().getSimpleName().toString(), e -> getValue(e.getValue())));
+ }
+
+ /**
+ * Returns the given annotation value as an Object (for primitives), or a list (for arrays), or
+ * strings (for compound annotations, enums, and class literals).
+ */
+ static Object getValue(AnnotationValue value) {
+ return value.accept(
+ new AbstractAnnotationValueVisitor8<Object, Void>() {
+ @Override
+ public Object visitBoolean(boolean b, Void unused) {
+ return b;
+ }
+
+ @Override
+ public Object visitByte(byte b, Void unused) {
+ return b;
+ }
+
+ @Override
+ public Object visitChar(char c, Void unused) {
+ return c;
+ }
+
+ @Override
+ public Object visitDouble(double d, Void unused) {
+ return d;
+ }
+
+ @Override
+ public Object visitFloat(float f, Void unused) {
+ return f;
+ }
+
+ @Override
+ public Object visitInt(int i, Void unused) {
+ return i;
+ }
+
+ @Override
+ public Object visitLong(long i, Void unused) {
+ return i;
+ }
+
+ @Override
+ public Object visitShort(short s, Void unused) {
+ return s;
+ }
+
+ @Override
+ public Object visitString(String s, Void unused) {
+ return s;
+ }
+
+ @Override
+ public Object visitType(TypeMirror t, Void unused) {
+ return value.toString();
+ }
+
+ @Override
+ public Object visitEnumConstant(VariableElement c, Void unused) {
+ return value.toString();
+ }
+
+ @Override
+ public Object visitAnnotation(AnnotationMirror a, Void unused) {
+ return value.toString();
+ }
+
+ @Override
+ public Object visitArray(List<? extends AnnotationValue> vals, Void unused) {
+ return vals.stream().map(v -> v.accept(this, null)).collect(toImmutableList());
+ }
+ },
+ null);
+ }
+
+ private static Stream<String> typeAnnotationNames(Element e) {
+ return e.asType().getAnnotationMirrors().stream()
+ .map(anno -> anno.getAnnotationType().asElement().getSimpleName().toString());
+ }
+
+ @Test
+ public void test() throws Exception {
+ TestInput input =
+ TestInput.parse(
+ Joiner.on('\n')
+ .join(
+ "=== Test.java ===",
+ "import java.lang.annotation.ElementType;",
+ "import java.lang.annotation.Retention;",
+ "import java.lang.annotation.RetentionPolicy;",
+ "import java.lang.annotation.Target;",
+ "import java.util.Map;",
+ "import java.util.Map.Entry;",
+ "@Retention(RetentionPolicy.RUNTIME)",
+ "@interface A {",
+ " int x() default 0;",
+ " int y() default 1;",
+ " int[] z() default {};",
+ "}",
+ "@interface B {",
+ " Class<?> c() default String.class;",
+ " ElementType e() default ElementType.TYPE_USE;",
+ " A f() default @A;",
+ "}",
+ "@Retention(RetentionPolicy.RUNTIME)",
+ "@Target(ElementType.TYPE_USE)",
+ "@interface T {}",
+ "@Target(ElementType.TYPE_USE)",
+ "@interface V {}",
+ "",
+ "@A(y = 42, z = {43})",
+ "@B",
+ "class Test {",
+ " class I {}",
+ " @T Test. @V I f;",
+ " Map. @T Entry g;",
+ " @T Entry h;",
+ "}",
+ ""));
+
+ Binder.BindingResult bound =
+ IntegrationTestSupport.turbineAnalysis(
+ input.sources,
+ ImmutableList.of(),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+
+ Env<ClassSymbol, TypeBoundClass> env =
+ CompoundEnv.<ClassSymbol, TypeBoundClass>of(bound.classPathEnv())
+ .append(new SimpleEnv<>(bound.units()));
+ ModelFactory factory = new ModelFactory(env, ClassLoader.getSystemClassLoader(), bound.tli());
+ TurbineTypes turbineTypes = new TurbineTypes(factory);
+ TurbineElements turbineElements = new TurbineElements(factory, turbineTypes);
+
+ TurbineTypeElement te = factory.typeElement(new ClassSymbol("Test"));
+
+ AnnotationMirror a = getAnnotation(te.getAnnotationMirrors(), "A");
+ ((TypeElement) a.getAnnotationType().asElement()).getQualifiedName().contentEquals("A");
+ assertThat(values(a)).containsExactly("y", 42, "z", ImmutableList.of(43));
+ assertThat(values(turbineElements.getElementValuesWithDefaults(a)))
+ .containsExactly(
+ "x", 0,
+ "y", 42,
+ "z", ImmutableList.of(43));
+
+ AnnotationMirror b = getAnnotation(te.getAnnotationMirrors(), "B");
+ assertThat(values(turbineElements.getElementValuesWithDefaults(b)))
+ .containsExactly(
+ "c", "java.lang.String.class",
+ "e", "java.lang.annotation.ElementType.TYPE_USE",
+ "f", "@A");
+
+ ListMultimap<String, String> fieldTypeAnnotations =
+ te.getEnclosedElements().stream()
+ .filter(e -> e.getKind().equals(ElementKind.FIELD))
+ .collect(
+ Multimaps.flatteningToMultimap(
+ e -> e.getSimpleName().toString(),
+ e -> typeAnnotationNames(e),
+ MultimapBuilder.linkedHashKeys().arrayListValues()::build));
+ assertThat(fieldTypeAnnotations)
+ .containsExactly(
+ "f", "V",
+ "g", "T",
+ "h", "T");
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineAnnotationProxyTest.java b/javatests/com/google/turbine/processing/TurbineAnnotationProxyTest.java
new file mode 100644
index 0000000..d339700
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineAnnotationProxyTest.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
+import com.google.common.primitives.Ints;
+import com.google.common.testing.EqualsTester;
+import com.google.turbine.binder.Binder;
+import com.google.turbine.binder.ClassPathBinder;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.env.SimpleEnv;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.diag.SourceFile;
+import com.google.turbine.lower.IntegrationTestSupport.TestInput;
+import com.google.turbine.parse.Parser;
+import com.google.turbine.processing.TurbineElement.TurbineTypeElement;
+import com.google.turbine.testing.TestClassPaths;
+import com.google.turbine.tree.Tree.CompUnit;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.MirroredTypeException;
+import javax.lang.model.type.MirroredTypesException;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineAnnotationProxyTest {
+
+ @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface A {
+ B b() default @B(-1);
+
+ ElementType e() default ElementType.PACKAGE;
+
+ int[] xs() default {};
+
+ Class<?> c() default String.class;
+
+ Class<?>[] cx() default {};
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Inherited
+ public @interface B {
+ int value();
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface C {}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface RS {
+ R[] value() default {};
+ }
+
+ @Repeatable(RS.class)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface R {
+ int value() default 1;
+ }
+
+ @A
+ static class I {}
+
+ @Test
+ public void test() throws IOException {
+
+ Path lib = temporaryFolder.newFile("lib.jar").toPath();
+ try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(lib))) {
+ addClass(jos, TurbineAnnotationProxyTest.class);
+ addClass(jos, A.class);
+ addClass(jos, B.class);
+ addClass(jos, C.class);
+ addClass(jos, R.class);
+ }
+
+ TestInput input =
+ TestInput.parse(
+ Joiner.on('\n')
+ .join(
+ "=== Super.java ===",
+ "import " + B.class.getCanonicalName() + ";",
+ "import " + C.class.getCanonicalName() + ";",
+ "@B(42)",
+ "@C",
+ "class Super {}",
+ "=== Test.java ===",
+ "import " + A.class.getCanonicalName() + ";",
+ "import " + R.class.getCanonicalName() + ";",
+ "@A(xs = {1,2,3}, cx = {Integer.class, Long.class})",
+ "@R(1)",
+ "@R(2)",
+ "@R(3)",
+ "class Test extends Super {}",
+ ""));
+
+ ImmutableList<CompUnit> units =
+ input.sources.entrySet().stream()
+ .map(e -> new SourceFile(e.getKey(), e.getValue()))
+ .map(Parser::parse)
+ .collect(toImmutableList());
+
+ Binder.BindingResult bound =
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of(lib)),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+
+ Env<ClassSymbol, TypeBoundClass> env =
+ CompoundEnv.<ClassSymbol, TypeBoundClass>of(bound.classPathEnv())
+ .append(new SimpleEnv<>(bound.units()));
+ ModelFactory factory = new ModelFactory(env, ClassLoader.getSystemClassLoader(), bound.tli());
+ TurbineTypeElement te = factory.typeElement(new ClassSymbol("Test"));
+
+ A a = te.getAnnotation(A.class);
+ B b = te.getAnnotation(B.class);
+ assertThat(te.getAnnotation(C.class)).isNull();
+
+ assertThat(a.b().value()).isEqualTo(-1);
+ assertThat(a.e()).isEqualTo(ElementType.PACKAGE);
+ try {
+ a.c();
+ fail();
+ } catch (MirroredTypeException e) {
+ assertThat(e.getTypeMirror().getKind()).isEqualTo(TypeKind.DECLARED);
+ assertThat(getQualifiedName(e.getTypeMirror())).contains("java.lang.String");
+ }
+ try {
+ a.cx();
+ fail();
+ } catch (MirroredTypesException e) {
+ assertThat(
+ e.getTypeMirrors().stream().map(m -> getQualifiedName(m)).collect(toImmutableList()))
+ .containsExactly("java.lang.Integer", "java.lang.Long");
+ }
+ assertThat(Ints.asList(a.xs())).containsExactly(1, 2, 3).inOrder();
+ assertThat(a.annotationType()).isEqualTo(A.class);
+
+ assertThat(b.value()).isEqualTo(42);
+
+ RS container = te.getAnnotation(RS.class);
+ assertThat(container.value()).hasLength(3);
+ R[] rs = te.getAnnotationsByType(R.class);
+ assertThat(rs).hasLength(3);
+ assertThat(Arrays.toString(rs))
+ .isEqualTo(
+ String.format(
+ "[@%s(1), @%s(2), @%s(3)]",
+ R.class.getCanonicalName(),
+ R.class.getCanonicalName(),
+ R.class.getCanonicalName()));
+
+ new EqualsTester()
+ .addEqualityGroup(a, te.getAnnotation(A.class))
+ .addEqualityGroup(b, te.getAnnotation(B.class))
+ .addEqualityGroup(rs[0])
+ .addEqualityGroup(rs[1])
+ .addEqualityGroup(rs[2])
+ .addEqualityGroup(container)
+ .addEqualityGroup(I.class.getAnnotation(A.class))
+ .addEqualityGroup("unrelated")
+ .testEquals();
+ }
+
+ private static void addClass(JarOutputStream jos, Class<?> clazz) throws IOException {
+ String entryPath = clazz.getName().replace('.', '/') + ".class";
+ jos.putNextEntry(new JarEntry(entryPath));
+ try (InputStream is = clazz.getClassLoader().getResourceAsStream(entryPath)) {
+ ByteStreams.copy(is, jos);
+ }
+ }
+
+ private static String getQualifiedName(TypeMirror typeMirror) {
+ return ((TypeElement) ((DeclaredType) typeMirror).asElement()).getQualifiedName().toString();
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineElementTest.java b/javatests/com/google/turbine/processing/TurbineElementTest.java
new file mode 100644
index 0000000..0b3448f
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineElementTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.MoreCollectors;
+import com.google.common.testing.EqualsTester;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.binder.sym.PackageSymbol;
+import com.google.turbine.testing.TestClassPaths;
+import com.google.turbine.type.Type.ClassTy;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineElementTest {
+
+ private final ModelFactory factory =
+ new ModelFactory(
+ TestClassPaths.TURBINE_BOOTCLASSPATH.env(),
+ ClassLoader.getSystemClassLoader(),
+ TestClassPaths.TURBINE_BOOTCLASSPATH.index());
+
+ @Test
+ public void typeElement() {
+ TypeElement e = factory.typeElement(new ClassSymbol("java/util/Map$Entry"));
+ TypeElement m = (TypeElement) e.getEnclosingElement();
+ TypeMirror t = e.asType();
+
+ assertThat(e.getSimpleName().toString()).isEqualTo("Entry");
+ assertThat(e.getQualifiedName().toString()).isEqualTo("java.util.Map.Entry");
+ assertThat(e.toString()).isEqualTo("java.util.Map.Entry");
+ assertThat(e.asType().toString()).isEqualTo("java.util.Map.Entry<K,V>");
+ assertThat(e.getKind()).isEqualTo(ElementKind.INTERFACE);
+ assertThat(e.getNestingKind()).isEqualTo(NestingKind.MEMBER);
+ assertThat(e.getModifiers())
+ .containsExactly(Modifier.PUBLIC, Modifier.ABSTRACT, Modifier.STATIC);
+
+ assertThat(m.getSimpleName().toString()).isEqualTo("Map");
+ assertThat(m.getSuperclass().getKind()).isEqualTo(TypeKind.NONE);
+ assertThat(m.getQualifiedName().toString()).isEqualTo("java.util.Map");
+ assertThat(m.toString()).isEqualTo("java.util.Map");
+ assertThat(m.asType().toString()).isEqualTo("java.util.Map<K,V>");
+ assertThat(m.getNestingKind()).isEqualTo(NestingKind.TOP_LEVEL);
+ assertThat(m.getSuperclass().getKind()).isEqualTo(TypeKind.NONE);
+ assertThat(m.getEnclosingElement().getKind()).isEqualTo(ElementKind.PACKAGE);
+
+ assertThat(t.getKind()).isEqualTo(TypeKind.DECLARED);
+ }
+
+ @Test
+ public void superClass() {
+ TypeElement e = factory.typeElement(new ClassSymbol("java/util/HashMap"));
+ assertThat(
+ ((TypeElement) ((DeclaredType) e.getSuperclass()).asElement())
+ .getQualifiedName()
+ .toString())
+ .isEqualTo("java.util.AbstractMap");
+
+ e = factory.typeElement(new ClassSymbol("java/lang/annotation/ElementType"));
+ assertThat(
+ ((TypeElement) ((DeclaredType) e.getSuperclass()).asElement())
+ .getQualifiedName()
+ .toString())
+ .isEqualTo("java.lang.Enum");
+ }
+
+ @Test
+ public void interfaces() {
+ TypeElement e = factory.typeElement(new ClassSymbol("java/util/HashMap"));
+ assertThat(
+ e.getInterfaces().stream()
+ .map(
+ i ->
+ ((TypeElement) ((DeclaredType) i).asElement())
+ .getQualifiedName()
+ .toString())
+ .collect(toImmutableList()))
+ .contains("java.util.Map");
+ }
+
+ @Test
+ public void typeParameters() {
+ TypeElement e = factory.typeElement(new ClassSymbol("java/util/HashMap"));
+ assertThat(e.getTypeParameters().stream().map(Object::toString).collect(toImmutableList()))
+ .containsExactly("K", "V");
+ for (TypeParameterElement t : e.getTypeParameters()) {
+ assertThat(t.getGenericElement()).isEqualTo(e);
+ assertThat(t.getEnclosingElement()).isEqualTo(e);
+ assertThat(t.getBounds()).containsExactly(factory.asTypeMirror(ClassTy.OBJECT));
+ }
+ }
+
+ @Test
+ public void enclosed() {
+ assertThat(
+ factory.typeElement(new ClassSymbol("java/lang/Integer")).getEnclosedElements().stream()
+ .map(e -> e.getKind() + " " + e)
+ .collect(toImmutableList()))
+ .containsAtLeast("METHOD parseInt(java.lang.String)", "FIELD MAX_VALUE");
+ }
+
+ @Test
+ public void equals() {
+ new EqualsTester()
+ .addEqualityGroup(
+ factory.typeElement(new ClassSymbol("java/util/List")),
+ factory.typeElement(new ClassSymbol("java/util/List")))
+ .addEqualityGroup(factory.typeElement(new ClassSymbol("java/util/ArrayList")))
+ .addEqualityGroup(
+ factory.typeElement(new ClassSymbol("java/util/Map")).getTypeParameters().get(0),
+ factory.typeElement(new ClassSymbol("java/util/Map")).getTypeParameters().get(0))
+ .addEqualityGroup(
+ factory.typeElement(new ClassSymbol("java/util/ArrayList")).getTypeParameters().get(0))
+ .addEqualityGroup(
+ factory.fieldElement(
+ new FieldSymbol(new ClassSymbol("java/util/ArrayList"), "elementData")),
+ factory.fieldElement(
+ new FieldSymbol(new ClassSymbol("java/util/ArrayList"), "elementData")))
+ .addEqualityGroup(
+ factory.fieldElement(
+ new FieldSymbol(new ClassSymbol("java/util/ArrayList"), "serialVersionUID")))
+ .addEqualityGroup(
+ ((ExecutableElement)
+ factory
+ .typeElement(new ClassSymbol("java/util/ArrayList"))
+ .getEnclosedElements()
+ .stream()
+ .filter(
+ e ->
+ e.getKind().equals(ElementKind.METHOD)
+ && e.getSimpleName().contentEquals("add"))
+ .skip(1)
+ .findFirst()
+ .get())
+ .getParameters()
+ .get(0))
+ .addEqualityGroup(
+ factory
+ .typeElement(new ClassSymbol("java/util/ArrayList"))
+ .getEnclosedElements()
+ .stream()
+ .filter(e -> e.getKind().equals(ElementKind.METHOD))
+ .skip(1)
+ .findFirst()
+ .get())
+ .addEqualityGroup(
+ factory
+ .typeElement(new ClassSymbol("java/util/ArrayList"))
+ .getEnclosedElements()
+ .stream()
+ .filter(e -> e.getKind().equals(ElementKind.METHOD))
+ .findFirst()
+ .get(),
+ factory
+ .typeElement(new ClassSymbol("java/util/ArrayList"))
+ .getEnclosedElements()
+ .stream()
+ .filter(e -> e.getKind().equals(ElementKind.METHOD))
+ .findFirst()
+ .get())
+ .addEqualityGroup(
+ factory.packageElement(new PackageSymbol("java/util")),
+ factory.typeElement(new ClassSymbol("java/util/ArrayList")).getEnclosingElement())
+ .addEqualityGroup(factory.packageElement(new PackageSymbol("java/lang")))
+ .testEquals();
+ }
+
+ @Test
+ public void noElement() {
+ PackageElement p = factory.packageElement(new PackageSymbol("java/lang"));
+ assertThat(p.getEnclosingElement()).isNull();
+ }
+
+ @Test
+ public void objectSuper() {
+ assertThat(factory.typeElement(new ClassSymbol("java/lang/Object")).getSuperclass().getKind())
+ .isEqualTo(TypeKind.NONE);
+ }
+
+ @Test
+ public void typeKind() {
+ assertThat(factory.typeElement(new ClassSymbol("java/lang/annotation/Target")).getKind())
+ .isEqualTo(ElementKind.ANNOTATION_TYPE);
+ assertThat(factory.typeElement(new ClassSymbol("java/lang/annotation/ElementType")).getKind())
+ .isEqualTo(ElementKind.ENUM);
+ }
+
+ @Test
+ public void parameter() {
+ ExecutableElement equals =
+ (ExecutableElement)
+ factory.typeElement(new ClassSymbol("java/lang/Object")).getEnclosedElements().stream()
+ .filter(e -> e.getSimpleName().contentEquals("equals"))
+ .collect(MoreCollectors.onlyElement());
+ VariableElement parameter = getOnlyElement(equals.getParameters());
+ assertThat(parameter.getKind()).isEqualTo(ElementKind.PARAMETER);
+ assertThat(parameter.asType().toString()).isEqualTo("java.lang.Object");
+ assertThat(parameter.getModifiers()).isEmpty();
+ assertThat(parameter.getEnclosedElements()).isEmpty();
+ assertThat(parameter.getSimpleName().toString()).isNotEmpty();
+ assertThat(parameter.getConstantValue()).isNull();
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java b/javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java
new file mode 100644
index 0000000..11dedbf
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineElementsGetAllMembersTest.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.truth.Truth.assertThat;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.turbine.binder.Binder;
+import com.google.turbine.binder.ClassPathBinder;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.env.SimpleEnv;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.diag.SourceFile;
+import com.google.turbine.lower.IntegrationTestSupport;
+import com.google.turbine.lower.IntegrationTestSupport.TestInput;
+import com.google.turbine.parse.Parser;
+import com.google.turbine.testing.TestClassPaths;
+import com.google.turbine.tree.Tree.CompUnit;
+import com.sun.source.util.JavacTask;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import javax.lang.model.element.Element;
+import javax.lang.model.util.Elements;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TurbineElementsGetAllMembersTest {
+
+ @Parameters
+ public static Iterable<Object[]> parameters() {
+ // An array of test inputs. Each element is an array of lines of sources to compile.
+ String[][] inputs = {
+ {
+ "=== Test.java ===", //
+ "class Test {",
+ "}",
+ },
+ {
+ "=== A.java ===",
+ "interface A {",
+ " Integer f();",
+ "}",
+ "=== B.java ===",
+ "interface B {",
+ " Integer f();",
+ "}",
+ "=== Test.java ===", //
+ "class Test implements A, B {",
+ " Integer f() {",
+ " return 42;",
+ " }",
+ "}",
+ },
+ {
+ "=== I.java ===",
+ "abstract class I {",
+ " abstract Integer f();",
+ "}",
+ "=== J.java ===",
+ "interface J extends I {",
+ " default Integer f() {",
+ " return 42;",
+ " }",
+ "}",
+ "=== Test.java ===", //
+ "class Test extends I implements J {",
+ "}",
+ },
+ {
+ "=== I.java ===",
+ "interface I {",
+ " Integer f();",
+ "}",
+ "=== J.java ===",
+ "interface J extends I {",
+ " default Integer f() {",
+ " return 42;",
+ " }",
+ "}",
+ "=== Test.java ===", //
+ "class Test implements J, I {",
+ "}",
+ },
+ {
+ "=== p/A.java ===",
+ "package p;",
+ "public class A {",
+ " public boolean f() {",
+ " return true;",
+ " }",
+ "}",
+ "=== p/B.java ===",
+ "package p;",
+ "public interface B {",
+ " public boolean f();",
+ "}",
+ "=== Test.java ===", //
+ "import p.*;",
+ "class Test extends A implements B {",
+ "}",
+ },
+ {
+ "=== p/A.java ===",
+ "package p;",
+ "public class A {",
+ " public boolean f() {",
+ " return true;",
+ " }",
+ "}",
+ "=== p/B.java ===",
+ "package p;",
+ "public interface B {",
+ " public boolean f();",
+ "}",
+ "=== Middle.java ===", //
+ "import p.*;",
+ "public abstract class Middle extends A implements B {",
+ "}",
+ "=== Test.java ===", //
+ "class Test extends Middle {",
+ "}",
+ },
+ {
+ "=== A.java ===",
+ "interface A {",
+ " Integer f();",
+ "}",
+ "=== B.java ===",
+ "interface B {",
+ " Number f();",
+ "}",
+ "=== Test.java ===", //
+ "abstract class Test implements A, B {",
+ "}",
+ },
+ {
+ "=== A.java ===",
+ "interface A {",
+ " Integer f();",
+ "}",
+ "=== B.java ===",
+ "interface B {",
+ " Integer f();",
+ "}",
+ "=== Test.java ===", //
+ "abstract class Test implements A, B {",
+ "}",
+ },
+ {
+ "=== I.java ===",
+ "interface I {",
+ " int x;",
+ "}",
+ "=== J.java ===",
+ "interface J {",
+ " int x;",
+ "}",
+ "=== B.java ===",
+ "class B {",
+ " int x;",
+ "}",
+ "=== C.java ===",
+ "class C extends B {",
+ " static int x;",
+ "}",
+ "=== Test.java ===",
+ "class Test extends C implements I, J {",
+ " int x;",
+ "}",
+ },
+ {
+ "=== one/A.java ===",
+ "public class A {",
+ " int a;",
+ "}",
+ "=== two/B.java ===",
+ "public class B extends A {",
+ " int b;",
+ " private int c;",
+ " protected int d;",
+ "}",
+ "=== Test.java ===",
+ "public class Test extends B {",
+ " int x;",
+ "}",
+ },
+ {
+ "=== A.java ===",
+ "interface A {",
+ " class I {}",
+ "}",
+ "=== B.java ===",
+ "interface B {",
+ " class J {}",
+ "}",
+ "=== Test.java ===", //
+ "abstract class Test implements A, B {",
+ "}",
+ },
+ {
+ "=== A.java ===",
+ "import java.util.List;",
+ "interface A<T> {",
+ " List<? extends T> f();",
+ "}",
+ "=== Test.java ===",
+ "import java.util.List;",
+ "class Test<T extends Number> implements A<T> {",
+ " public List<? extends T> f() {",
+ " return null;",
+ " }",
+ "}",
+ },
+ };
+ return Arrays.stream(inputs)
+ .map(input -> TestInput.parse(Joiner.on('\n').join(input)))
+ .map(x -> new Object[] {x})
+ .collect(toImmutableList());
+ }
+
+ private final TestInput input;
+
+ public TurbineElementsGetAllMembersTest(TestInput input) {
+ this.input = input;
+ }
+
+ // Compile the test inputs with javac and turbine, and assert that getAllMembers returns the
+ // same elements under each implementation.
+ @Test
+ public void test() throws Exception {
+ JavacTask javacTask =
+ IntegrationTestSupport.runJavacAnalysis(
+ input.sources, ImmutableList.of(), ImmutableList.of());
+ Elements javacElements = javacTask.getElements();
+ List<? extends Element> javacMembers =
+ javacElements.getAllMembers(requireNonNull(javacElements.getTypeElement("Test")));
+
+ ImmutableList<CompUnit> units =
+ input.sources.entrySet().stream()
+ .map(e -> new SourceFile(e.getKey(), e.getValue()))
+ .map(Parser::parse)
+ .collect(toImmutableList());
+
+ Binder.BindingResult bound =
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+
+ Env<ClassSymbol, TypeBoundClass> env =
+ CompoundEnv.<ClassSymbol, TypeBoundClass>of(bound.classPathEnv())
+ .append(new SimpleEnv<>(bound.units()));
+ ModelFactory factory = new ModelFactory(env, ClassLoader.getSystemClassLoader(), bound.tli());
+ TurbineTypes turbineTypes = new TurbineTypes(factory);
+ TurbineElements turbineElements = new TurbineElements(factory, turbineTypes);
+ List<? extends Element> turbineMembers =
+ turbineElements.getAllMembers(factory.typeElement(new ClassSymbol("Test")));
+
+ assertThat(formatElements(turbineMembers))
+ .containsExactlyElementsIn(formatElements(javacMembers));
+ }
+
+ private static ImmutableList<String> formatElements(Collection<? extends Element> elements) {
+ return elements.stream()
+ .map(e -> String.format("%s %s.%s %s", e.getKind(), e.getEnclosingElement(), e, e.asType()))
+ .collect(toImmutableList());
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineElementsTest.java b/javatests/com/google/turbine/processing/TurbineElementsTest.java
new file mode 100644
index 0000000..770e6f6
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineElementsTest.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.collect.MoreCollectors.onlyElement;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.testing.EqualsTester;
+import com.google.turbine.binder.Binder.BindingResult;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.env.SimpleEnv;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.lower.IntegrationTestSupport;
+import com.google.turbine.testing.TestClassPaths;
+import com.sun.source.util.JavacTask;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.util.Elements;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineElementsTest {
+
+ private static final IntegrationTestSupport.TestInput SOURCES =
+ IntegrationTestSupport.TestInput.parse(
+ Joiner.on('\n')
+ .join(
+ "=== Test.java ===",
+ "@Deprecated",
+ "@A class Test extends One {}",
+ "=== One.java ===",
+ "/** javadoc",
+ " * for",
+ " * one",
+ " */",
+ "@B class One extends Two {",
+ " /** method javadoc */",
+ " void f() {}",
+ " /** field javadoc */",
+ " int x;",
+ "}",
+ "=== Two.java ===",
+ "/** javadoc",
+ " for",
+ " two with extra *",
+ " */",
+ "@C(1) class Two extends Three {}",
+ "=== Three.java ===",
+ "@C(2) class Three extends Four {}",
+ "=== Four.java ===",
+ "@D class Four {}",
+ "=== Annotations.java ===",
+ "import java.lang.annotation.Inherited;",
+ "@interface A {}",
+ "@interface B {}",
+ "@Inherited",
+ "@interface C {",
+ " int value() default 42;",
+ "}",
+ "@Inherited",
+ "@interface D {}",
+ "=== com/pkg/P.java ===",
+ "package com.pkg;",
+ "@interface P {}",
+ "=== com/pkg/package-info.java ===",
+ "@P",
+ "package com.pkg;",
+ "=== Const.java ===",
+ "class Const {",
+ " static final int X = 1867;",
+ "}",
+ "=== com/pkg/empty/package-info.java ===",
+ "@P",
+ "package com.pkg.empty;",
+ "import com.pkg.P;",
+ "=== com/pkg/A.java ===",
+ "package com.pkg;",
+ "class A {",
+ " class I {}",
+ "}",
+ "=== com/pkg/B.java ===",
+ "package com.pkg;",
+ "class B {}"));
+
+ Elements javacElements;
+ ModelFactory factory;
+ TurbineElements turbineElements;
+
+ @Before
+ public void setup() throws Exception {
+ JavacTask task =
+ IntegrationTestSupport.runJavacAnalysis(
+ SOURCES.sources, ImmutableList.of(), ImmutableList.of());
+ task.analyze();
+ javacElements = task.getElements();
+
+ BindingResult bound =
+ IntegrationTestSupport.turbineAnalysis(
+ SOURCES.sources,
+ ImmutableList.of(),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+ Env<ClassSymbol, TypeBoundClass> env =
+ CompoundEnv.<ClassSymbol, TypeBoundClass>of(bound.classPathEnv())
+ .append(new SimpleEnv<>(bound.units()));
+ factory = new ModelFactory(env, TurbineElementsTest.class.getClassLoader(), bound.tli());
+ TurbineTypes turbineTypes = new TurbineTypes(factory);
+ turbineElements = new TurbineElements(factory, turbineTypes);
+ }
+
+ @Test
+ public void constants() {
+ for (Object value :
+ Arrays.asList(
+ Short.valueOf((short) 1),
+ Short.MIN_VALUE,
+ Short.MAX_VALUE,
+ Byte.valueOf((byte) 1),
+ Byte.MIN_VALUE,
+ Byte.MAX_VALUE,
+ Integer.valueOf(1),
+ Integer.MIN_VALUE,
+ Integer.MAX_VALUE,
+ Long.valueOf(1),
+ Long.MIN_VALUE,
+ Long.MAX_VALUE,
+ Float.valueOf(1),
+ Float.NaN,
+ Double.NEGATIVE_INFINITY,
+ Double.POSITIVE_INFINITY,
+ Float.MAX_VALUE,
+ Float.MIN_VALUE,
+ Double.valueOf(1),
+ Double.NaN,
+ Double.NEGATIVE_INFINITY,
+ Double.POSITIVE_INFINITY,
+ Double.MAX_VALUE,
+ Double.MIN_VALUE,
+ 'a',
+ '\n',
+ "hello",
+ "\"hello\n\"")) {
+ assertThat(turbineElements.getConstantExpression(value))
+ .isEqualTo(javacElements.getConstantExpression(value));
+ }
+ }
+
+ @Test
+ public void getName() {
+ Name n = turbineElements.getName("hello");
+ assertThat(n.contentEquals("hello")).isTrue();
+ assertThat(n.contentEquals("goodbye")).isFalse();
+
+ assertThat(n.toString()).isEqualTo("hello");
+ assertThat(n.toString())
+ .isEqualTo(new String(new char[] {'h', 'e', 'l', 'l', 'o'})); // defeat interning
+
+ assertThat(n.length()).isEqualTo(5);
+
+ new EqualsTester()
+ .addEqualityGroup(turbineElements.getName("hello"), turbineElements.getName("hello"))
+ .addEqualityGroup(turbineElements.getName("goodbye"))
+ .testEquals();
+ }
+
+ @Test
+ public void getAllAnnotationMirrors() {
+ assertThat(
+ toStrings(
+ turbineElements.getAllAnnotationMirrors(
+ factory.typeElement(new ClassSymbol("Test")))))
+ .containsExactly("@java.lang.Deprecated", "@A", "@C(1)", "@D");
+ }
+
+ @Test
+ public void getTypeElement() {
+ for (String name : Arrays.asList("java.util.Map", "java.util.Map.Entry")) {
+ assertThat(turbineElements.getTypeElement(name).getQualifiedName().toString())
+ .isEqualTo(name);
+ }
+ assertThat(turbineElements.getTypeElement("NoSuch")).isNull();
+ assertThat(turbineElements.getTypeElement("java.lang.Object.NoSuch")).isNull();
+ assertThat(turbineElements.getTypeElement("java.lang.NoSuch")).isNull();
+ assertThat(turbineElements.getTypeElement("java.lang.Integer.MAX_VALUE")).isNull();
+ }
+
+ private static ImmutableList<String> toStrings(List<?> inputs) {
+ return inputs.stream().map(String::valueOf).collect(toImmutableList());
+ }
+
+ @Test
+ public void isDeprecated() {
+ assertThat(turbineElements.isDeprecated(turbineElements.getTypeElement("java.lang.Object")))
+ .isFalse();
+ assertThat(turbineElements.isDeprecated(turbineElements.getTypeElement("One"))).isFalse();
+ assertThat(turbineElements.isDeprecated(turbineElements.getTypeElement("Test"))).isTrue();
+ }
+
+ @Test
+ public void getBinaryName() {
+ assertThat(
+ turbineElements
+ .getBinaryName(turbineElements.getTypeElement("java.util.Map.Entry"))
+ .toString())
+ .isEqualTo("java.util.Map$Entry");
+ }
+
+ @Test
+ public void methodDefaultTest() {
+ assertThat(
+ ((ExecutableElement)
+ getOnlyElement(turbineElements.getTypeElement("C").getEnclosedElements()))
+ .getDefaultValue()
+ .getValue())
+ .isEqualTo(42);
+ }
+
+ @Test
+ public void constantFieldTest() {
+ assertThat(
+ ((VariableElement)
+ turbineElements.getTypeElement("Const").getEnclosedElements().stream()
+ .filter(x -> x.getKind().equals(ElementKind.FIELD))
+ .collect(onlyElement()))
+ .getConstantValue())
+ .isEqualTo(1867);
+ }
+
+ @Test
+ public void packageElement() {
+ assertThat(
+ toStrings(
+ turbineElements.getAllAnnotationMirrors(
+ turbineElements.getPackageElement("com.pkg"))))
+ .containsExactly("@com.pkg.P");
+ assertThat(
+ turbineElements.getAllAnnotationMirrors(turbineElements.getPackageElement("java.lang")))
+ .isEmpty();
+ assertThat(turbineElements.getPackageElement("com.google.no.such.pkg")).isNull();
+ }
+
+ @Test
+ public void packageMembers() {
+ assertThat(
+ turbineElements.getPackageElement("com.pkg").getEnclosedElements().stream()
+ .map(e -> ((TypeElement) e).getQualifiedName().toString())
+ .collect(toImmutableList()))
+ .containsExactly("com.pkg.P", "com.pkg.A", "com.pkg.B");
+ assertThat(turbineElements.getPackageElement("com.pkg.empty").getEnclosedElements()).isEmpty();
+ }
+
+ @Test
+ public void noElement() {
+ Element e = factory.noElement("com.google.Foo");
+ assertThat(e.getKind()).isEqualTo(ElementKind.CLASS);
+ assertThat(e.getSimpleName().toString()).isEqualTo("Foo");
+ assertThat(e.getEnclosingElement().toString()).isEqualTo("com.google");
+ assertThat(e.getEnclosingElement().getKind()).isEqualTo(ElementKind.PACKAGE);
+
+ e = factory.noElement("Foo");
+ assertThat(e.getSimpleName().toString()).isEqualTo("Foo");
+ assertThat(e.getEnclosingElement().toString()).isEmpty();
+ assertThat(e.getEnclosingElement().getKind()).isEqualTo(ElementKind.PACKAGE);
+ }
+
+ @Test
+ public void javadoc() {
+ TypeElement e = turbineElements.getTypeElement("One");
+ assertThat(turbineElements.getDocComment(e))
+ .isEqualTo(
+ " javadoc\n" //
+ + " for\n"
+ + " one\n"
+ + "");
+
+ assertThat(
+ turbineElements.getDocComment(
+ e.getEnclosedElements().stream()
+ .filter(x -> x.getKind().equals(ElementKind.FIELD))
+ .collect(onlyElement())))
+ .isEqualTo(" field javadoc ");
+
+ assertThat(
+ turbineElements.getDocComment(
+ e.getEnclosedElements().stream()
+ .filter(x -> x.getKind().equals(ElementKind.METHOD))
+ .collect(onlyElement())))
+ .isEqualTo(" method javadoc ");
+
+ e = turbineElements.getTypeElement("Two");
+ assertThat(turbineElements.getDocComment(e))
+ .isEqualTo(
+ " javadoc\n" //
+ + "for\n"
+ + "two with extra *\n"
+ + "");
+ }
+
+ @Test
+ public void syntheticParameters() {
+ assertThat(
+ ((ExecutableElement)
+ getOnlyElement(
+ turbineElements.getTypeElement("com.pkg.A.I").getEnclosedElements()))
+ .getParameters())
+ .isEmpty();
+ }
+
+ @Test
+ public void printElements() {
+ StringWriter w = new StringWriter();
+ turbineElements.printElements(
+ w,
+ turbineElements.getTypeElement("com.pkg.A"),
+ turbineElements.getTypeElement("com.pkg.A.I"));
+ assertThat(w.toString()).isEqualTo(lines("com.pkg.A", "com.pkg.A.I"));
+ }
+
+ private String lines(String... lines) {
+ return Joiner.on(System.lineSeparator()).join(lines) + System.lineSeparator();
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineFilerTest.java b/javatests/com/google/turbine/processing/TurbineFilerTest.java
new file mode 100644
index 0000000..40b78ea
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineFilerTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.truth.Truth.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.CharStreams;
+import com.google.turbine.diag.SourceFile;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.processing.FilerException;
+import javax.lang.model.element.Element;
+import javax.tools.FileObject;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardLocation;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineFilerTest {
+
+ private final Set<String> seen = new HashSet<>();
+ private TurbineFiler filer;
+
+ @Before
+ public void setup() {
+ Function<String, Supplier<byte[]>> classpath =
+ new Function<String, Supplier<byte[]>>() {
+ @Nullable
+ @Override
+ public Supplier<byte[]> apply(String input) {
+ return null;
+ }
+ };
+ this.filer = new TurbineFiler(seen, classpath, TurbineFilerTest.class.getClassLoader());
+ }
+
+ @Test
+ public void hello() throws IOException {
+ JavaFileObject sourceFile = filer.createSourceFile("com.foo.Bar", (Element[]) null);
+ try (OutputStream os = sourceFile.openOutputStream()) {
+ os.write("hello".getBytes(UTF_8));
+ }
+ assertThat(sourceFile.getLastModified()).isEqualTo(0);
+
+ JavaFileObject classFile = filer.createClassFile("com.foo.Baz", (Element[]) null);
+ try (OutputStream os = classFile.openOutputStream()) {
+ os.write("goodbye".getBytes(UTF_8));
+ }
+ assertThat(classFile.getLastModified()).isEqualTo(0);
+
+ Collection<SourceFile> roundSources = filer.finishRound();
+ assertThat(roundSources).hasSize(1);
+ assertThat(filer.generatedSources()).hasSize(1);
+ assertThat(filer.generatedClasses()).hasSize(1);
+
+ SourceFile source = getOnlyElement(roundSources);
+ assertThat(source.path()).isEqualTo("com/foo/Bar.java");
+ assertThat(source.source()).isEqualTo("hello");
+
+ Map.Entry<String, byte[]> clazz = getOnlyElement(filer.generatedClasses().entrySet());
+ assertThat(clazz.getKey()).isEqualTo("com/foo/Baz.class");
+ assertThat(new String(clazz.getValue(), UTF_8)).isEqualTo("goodbye");
+ }
+
+ @Test
+ public void existing() throws IOException {
+ seen.add("com/foo/Bar.java");
+ seen.add("com/foo/Baz.class");
+
+ try {
+ filer.createSourceFile("com.foo.Bar", (Element[]) null);
+ fail();
+ } catch (FilerException expected) {
+ }
+ filer.createSourceFile("com.foo.Baz", (Element[]) null);
+
+ filer.createClassFile("com.foo.Bar", (Element[]) null);
+ try {
+ filer.createClassFile("com.foo.Baz", (Element[]) null);
+ fail();
+ } catch (FilerException expected) {
+ }
+ }
+
+ @Test
+ public void get() throws IOException {
+ for (StandardLocation location :
+ Arrays.asList(
+ StandardLocation.CLASS_OUTPUT,
+ StandardLocation.SOURCE_OUTPUT,
+ StandardLocation.ANNOTATION_PROCESSOR_PATH,
+ StandardLocation.CLASS_PATH)) {
+ try {
+ filer.getResource(location, "", "NoSuch");
+ fail();
+ } catch (FileNotFoundException expected) {
+ }
+ }
+ }
+
+ @Test
+ public void sourceOutput() throws IOException {
+ JavaFileObject classFile = filer.createSourceFile("com.foo.Bar", (Element[]) null);
+ try (Writer writer = classFile.openWriter()) {
+ writer.write("hello");
+ }
+ filer.finishRound();
+
+ FileObject output = filer.getResource(StandardLocation.SOURCE_OUTPUT, "com.foo", "Bar.java");
+ assertThat(new String(ByteStreams.toByteArray(output.openInputStream()), UTF_8))
+ .isEqualTo("hello");
+ assertThat(output.getCharContent(false).toString()).isEqualTo("hello");
+ assertThat(CharStreams.toString(output.openReader(true))).isEqualTo("hello");
+ }
+
+ @Test
+ public void classOutput() throws IOException {
+ JavaFileObject classFile = filer.createClassFile("com.foo.Baz", (Element[]) null);
+ try (OutputStream os = classFile.openOutputStream()) {
+ os.write("goodbye".getBytes(UTF_8));
+ }
+ filer.finishRound();
+
+ FileObject output = filer.getResource(StandardLocation.CLASS_OUTPUT, "com.foo", "Baz.class");
+ assertThat(new String(ByteStreams.toByteArray(output.openInputStream()), UTF_8))
+ .isEqualTo("goodbye");
+ assertThat(output.getCharContent(false).toString()).isEqualTo("goodbye");
+ assertThat(CharStreams.toString(output.openReader(true))).isEqualTo("goodbye");
+ }
+
+ @Test
+ public void classpathResources() throws IOException {
+ FileObject resource =
+ filer.getResource(StandardLocation.ANNOTATION_PROCESSOR_PATH, "META-INF", "MANIFEST.MF");
+
+ assertThat(new String(ByteStreams.toByteArray(resource.openInputStream()), UTF_8))
+ .contains("Manifest-Version:");
+ assertThat(CharStreams.toString(resource.openReader(true))).contains("Manifest-Version:");
+ assertThat(resource.getCharContent(false).toString()).contains("Manifest-Version:");
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineMessagerTest.java b/javatests/com/google/turbine/processing/TurbineMessagerTest.java
new file mode 100644
index 0000000..c1e6401
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineMessagerTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.truth.Truth.assertThat;
+import static java.util.Comparator.comparing;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.turbine.binder.Binder;
+import com.google.turbine.binder.ClassPathBinder;
+import com.google.turbine.binder.Processing;
+import com.google.turbine.diag.SourceFile;
+import com.google.turbine.diag.TurbineDiagnostic;
+import com.google.turbine.diag.TurbineError;
+import com.google.turbine.lower.IntegrationTestSupport;
+import com.google.turbine.parse.Parser;
+import com.google.turbine.testing.TestClassPaths;
+import com.google.turbine.tree.Tree;
+import com.sun.source.util.JavacTask;
+import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.ElementScanner8;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticCollector;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineMessagerTest {
+
+ private static final IntegrationTestSupport.TestInput SOURCES =
+ IntegrationTestSupport.TestInput.parse(
+ Joiner.on('\n')
+ .join(
+ "=== Test.java ===",
+ "@A class Test {",
+ " @A @B void f() {}",
+ " @B int x;",
+ "}",
+ "=== One.java ===",
+ "class One<U, V> {",
+ " @A void f(@B int x, @C int y) {}",
+ " <X, Y> void g(X x) {}",
+ "}",
+ "=== Two.java ===",
+ "class Two {",
+ " @D(value = 1) int x1;",
+ " @D(1) int x2;",
+ " @E(1) int x3;",
+ " @E({1, 2}) int x4;",
+ " @E(value = {1, 2}, y = 3) int x5;",
+ " @E(y = 0, value = {1, 2}) int x6;",
+ "}",
+ "=== Annotations.java ===",
+ "@interface A {}",
+ "@interface B {}",
+ "@interface C {}",
+ "@interface D {",
+ " int value() default 0;",
+ "}",
+ "@interface E {",
+ " int[] value() default {};",
+ " int y() default 0;",
+ "}"));
+
+ /**
+ * Tests {@link TurbineMessager} by logging a message at each {@link Element}, {@link
+ * AnnotationMirror}, and {@link AnnotationValue}.
+ */
+ @SupportedAnnotationTypes("*")
+ public static class DiagnosticTesterProcessor extends AbstractProcessor {
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ for (Element e : roundEnv.getRootElements()) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, String.valueOf(e));
+ e.accept(
+ new ElementScanner8<Void, Void>() {
+ @Override
+ public Void scan(Element e, Void unused) {
+ processingEnv
+ .getMessager()
+ .printMessage(Diagnostic.Kind.ERROR, String.valueOf(e), e);
+ for (AnnotationMirror a : e.getAnnotationMirrors()) {
+ processingEnv
+ .getMessager()
+ .printMessage(Diagnostic.Kind.ERROR, String.format("%s %s", e, a), e, a);
+ for (AnnotationValue av : a.getElementValues().values()) {
+ processAnnotation(e, a, av);
+ }
+ }
+ return null;
+ }
+
+ private void processAnnotation(Element e, AnnotationMirror a, AnnotationValue av) {
+ processingEnv
+ .getMessager()
+ .printMessage(
+ Diagnostic.Kind.ERROR, String.format("%s %s %s", e, a, av), e, a, av);
+ av.accept(
+ new SimpleAnnotationValueVisitor8<Void, Void>() {
+ @Override
+ public Void visitAnnotation(AnnotationMirror a, Void unused) {
+ visitAnnotationValues(a.getElementValues().values());
+ return null;
+ }
+
+ @Override
+ public Void visitArray(List<? extends AnnotationValue> vals, Void unused) {
+ visitAnnotationValues(vals);
+ return null;
+ }
+
+ private void visitAnnotationValues(
+ Collection<? extends AnnotationValue> values) {
+ for (AnnotationValue av : values) {
+ processAnnotation(e, a, av);
+ }
+ }
+ },
+ null);
+ }
+
+ @Override
+ public Void visitExecutable(ExecutableElement e, Void unused) {
+ scan(e.getTypeParameters(), null);
+ return super.visitExecutable(e, unused);
+ }
+
+ @Override
+ public Void visitType(TypeElement e, Void unused) {
+ scan(e.getTypeParameters(), null);
+ return super.visitType(e, unused);
+ }
+ },
+ null);
+ }
+ return false;
+ }
+ }
+
+ @Test
+ public void test() throws Exception {
+
+ // Processes the test sources with the DiagnosticTesterProcessor under both javac and turbine,
+ // and asserts that the diagnostics have the same source path, line, and column under each.
+
+ DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
+ JavacTask task =
+ IntegrationTestSupport.runJavacAnalysis(
+ SOURCES.sources, ImmutableList.of(), ImmutableList.of(), collector);
+ task.setProcessors(ImmutableList.of(new DiagnosticTesterProcessor()));
+ task.call();
+ ImmutableList<String> javacDiagnostics =
+ collector.getDiagnostics().stream()
+ // sort the diagnostics for nicer test failure messages
+ .sorted(
+ comparing(TurbineMessagerTest::shortPath)
+ .thenComparing(Diagnostic::getLineNumber)
+ .thenComparing(Diagnostic::getColumnNumber))
+ .map(TurbineMessagerTest::formatDiagnostic)
+ .collect(toImmutableList());
+
+ ImmutableList<String> turbineDiagnostics;
+ ImmutableList<Tree.CompUnit> units =
+ SOURCES.sources.entrySet().stream()
+ .map(e -> new SourceFile(e.getKey(), e.getValue()))
+ .map(Parser::parse)
+ .collect(toImmutableList());
+ try {
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ Processing.ProcessorInfo.create(
+ ImmutableList.of(new DiagnosticTesterProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+ throw new AssertionError();
+ } catch (TurbineError e) {
+ turbineDiagnostics =
+ e.diagnostics().stream()
+ .sorted(
+ comparing(TurbineDiagnostic::path)
+ .thenComparing(TurbineDiagnostic::line)
+ .thenComparing(TurbineDiagnostic::column))
+ .map(TurbineMessagerTest::formatDiagnostic)
+ .collect(toImmutableList());
+ }
+
+ assertThat(turbineDiagnostics).containsExactlyElementsIn(javacDiagnostics).inOrder();
+ }
+
+ private static String formatDiagnostic(TurbineDiagnostic d) {
+ return String.format("%s:%s:%s %s", d.path(), d.line(), d.column(), d.message());
+ }
+
+ private static String formatDiagnostic(Diagnostic<? extends JavaFileObject> d) {
+ return String.format(
+ "%s:%s:%s %s",
+ shortPath(d), d.getLineNumber(), d.getColumnNumber(), d.getMessage(Locale.ENGLISH));
+ }
+
+ private static String shortPath(Diagnostic<? extends JavaFileObject> d) {
+ return d.getSource() != null
+ ? Paths.get(d.getSource().getName()).getFileName().toString()
+ : "<>";
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineNameTest.java b/javatests/com/google/turbine/processing/TurbineNameTest.java
new file mode 100644
index 0000000..f67fc82
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineNameTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineNameTest {
+
+ @Test
+ public void equals() {
+ new EqualsTester()
+ .addEqualityGroup(
+ new TurbineName("hello"), new TurbineName("hello"), new TurbineName("hello"))
+ .addEqualityGroup(new TurbineName("is"))
+ .addEqualityGroup(new TurbineName("there"))
+ .addEqualityGroup(new TurbineName("anybody"))
+ .addEqualityGroup(new TurbineName("in"))
+ .testEquals();
+ }
+
+ @Test
+ public void asd() {
+ assertThat(new TurbineName("hello").contentEquals("hello")).isTrue();
+ assertThat(new TurbineName("hello").contentEquals("goodbye")).isFalse();
+
+ assertThat(new TurbineName("hello").length()).isEqualTo(5);
+
+ assertThat(new TurbineName("hello").charAt(0)).isEqualTo('h');
+
+ assertThat(new TurbineName("hello").subSequence(1, 4).toString()).isEqualTo("ell");
+
+ assertThat(new TurbineName("hello").toString()).isEqualTo("hello");
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineTypeMirrorTest.java b/javatests/com/google/turbine/processing/TurbineTypeMirrorTest.java
new file mode 100644
index 0000000..bf08f89
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypeMirrorTest.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.collect.MoreCollectors.onlyElement;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.testing.EqualsTester;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.PackageSymbol;
+import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.model.TurbineConstantTypeKind;
+import com.google.turbine.testing.TestClassPaths;
+import com.google.turbine.type.Type;
+import com.google.turbine.type.Type.PrimTy;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.IntersectionType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.WildcardType;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineTypeMirrorTest {
+
+ private final ModelFactory factory =
+ new ModelFactory(
+ TestClassPaths.TURBINE_BOOTCLASSPATH.env(),
+ ClassLoader.getSystemClassLoader(),
+ TestClassPaths.TURBINE_BOOTCLASSPATH.index());
+
+ @Test
+ public void primitiveTypes() {
+ for (TypeKind kind : TypeKind.values()) {
+ if (!kind.isPrimitive()) {
+ continue;
+ }
+ TurbineConstantTypeKind turbineKind = TurbineConstantTypeKind.valueOf(kind.name());
+ TypeMirror type = factory.asTypeMirror(PrimTy.create(turbineKind, ImmutableList.of()));
+ assertThat(type.getKind()).isEqualTo(kind);
+ }
+ }
+
+ @Test
+ public void equals() {
+ new EqualsTester()
+ .addEqualityGroup(
+ factory.asTypeMirror(
+ Type.ClassTy.create(
+ ImmutableList.of(
+ Type.ClassTy.SimpleClassTy.create(
+ new ClassSymbol("java/util/Map"),
+ ImmutableList.of(),
+ ImmutableList.of()),
+ Type.ClassTy.SimpleClassTy.create(
+ new ClassSymbol("java/util/Map$Entry"),
+ ImmutableList.of(Type.ClassTy.STRING, Type.ClassTy.STRING),
+ ImmutableList.of())))))
+ .addEqualityGroup(
+ factory.asTypeMirror(
+ Type.ClassTy.create(
+ ImmutableList.of(
+ Type.ClassTy.SimpleClassTy.create(
+ new ClassSymbol("java/util/Map$Entry"),
+ ImmutableList.of(Type.ClassTy.STRING, Type.ClassTy.OBJECT),
+ ImmutableList.of())))))
+ .addEqualityGroup(
+ factory.asTypeMirror(
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/util/Map$Entry"))))
+ .addEqualityGroup(
+ factory.asTypeMirror(PrimTy.create(TurbineConstantTypeKind.LONG, ImmutableList.of())),
+ factory.asTypeMirror(PrimTy.create(TurbineConstantTypeKind.LONG, ImmutableList.of())))
+ .addEqualityGroup(
+ factory.asTypeMirror(PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of())))
+ .addEqualityGroup(
+ factory.asTypeMirror(
+ Type.WildLowerBoundedTy.create(
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/lang/Integer")),
+ ImmutableList.of())))
+ .addEqualityGroup(
+ factory.asTypeMirror(
+ Type.WildUpperBoundedTy.create(
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/lang/Integer")),
+ ImmutableList.of())))
+ .addEqualityGroup(factory.asTypeMirror(Type.WildUnboundedTy.create(ImmutableList.of())))
+ .addEqualityGroup(
+ factory.asTypeMirror(
+ Type.ArrayTy.create(
+ PrimTy.create(TurbineConstantTypeKind.LONG, ImmutableList.of()),
+ ImmutableList.of())))
+ .addEqualityGroup(factory.packageType(new PackageSymbol("java/lang")))
+ .addEqualityGroup(
+ factory.asTypeMirror(
+ Type.TyVar.create(
+ new TyVarSymbol(new ClassSymbol("java/util/List"), "V"), ImmutableList.of())))
+ .addEqualityGroup(
+ factory.asTypeMirror(
+ Type.IntersectionTy.create(
+ ImmutableList.of(
+ Type.ClassTy.asNonParametricClassTy(
+ new ClassSymbol("java/io/Serializable")),
+ Type.ClassTy.asNonParametricClassTy(
+ new ClassSymbol("java/lang/Cloneable"))))))
+ .addEqualityGroup(factory.noType())
+ .testEquals();
+ }
+
+ @Test
+ public void roundTrip() {
+ DeclaredType te =
+ (DeclaredType)
+ factory.asTypeMirror(
+ Type.ClassTy.create(
+ ImmutableList.of(
+ Type.ClassTy.SimpleClassTy.create(
+ new ClassSymbol("java/util/List"),
+ ImmutableList.of(
+ Type.ClassTy.asNonParametricClassTy(
+ new ClassSymbol("java/lang/String"))),
+ ImmutableList.of()))));
+ assertThat(te.asElement().asType()).isNotEqualTo(te);
+ assertThat(te.asElement().asType())
+ .isEqualTo(
+ factory.asTypeMirror(
+ Type.ClassTy.create(
+ ImmutableList.of(
+ Type.ClassTy.SimpleClassTy.create(
+ new ClassSymbol("java/util/List"),
+ ImmutableList.of(
+ Type.TyVar.create(
+ new TyVarSymbol(new ClassSymbol("java/util/List"), "E"),
+ ImmutableList.of())),
+ ImmutableList.of())))));
+ }
+
+ @Test
+ public void wildTy() {
+ WildcardType lower =
+ (WildcardType)
+ factory.asTypeMirror(
+ Type.WildLowerBoundedTy.create(
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/lang/Integer")),
+ ImmutableList.of()));
+ WildcardType upper =
+ (WildcardType)
+ factory.asTypeMirror(
+ Type.WildUpperBoundedTy.create(
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/lang/Long")),
+ ImmutableList.of()));
+ WildcardType unbound =
+ (WildcardType) factory.asTypeMirror(Type.WildUnboundedTy.create(ImmutableList.of()));
+
+ assertThat(lower.getKind()).isEqualTo(TypeKind.WILDCARD);
+ assertThat(lower.getExtendsBound()).isNull();
+ assertThat(lower.getSuperBound().getKind()).isEqualTo(TypeKind.DECLARED);
+
+ assertThat(upper.getKind()).isEqualTo(TypeKind.WILDCARD);
+ assertThat(upper.getExtendsBound().getKind()).isEqualTo(TypeKind.DECLARED);
+ assertThat(upper.getSuperBound()).isNull();
+
+ assertThat(unbound.getKind()).isEqualTo(TypeKind.WILDCARD);
+ assertThat(unbound.getExtendsBound()).isNull();
+ assertThat(unbound.getSuperBound()).isNull();
+ }
+
+ @Test
+ public void intersection() {
+ IntersectionType t =
+ (IntersectionType)
+ factory.asTypeMirror(
+ Type.IntersectionTy.create(
+ ImmutableList.of(
+ Type.ClassTy.asNonParametricClassTy(
+ new ClassSymbol("java/io/Serializable")),
+ Type.ClassTy.asNonParametricClassTy(
+ new ClassSymbol("java/lang/Cloneable")))));
+
+ assertThat(t.getKind()).isEqualTo(TypeKind.INTERSECTION);
+ assertThat(t.getBounds())
+ .containsExactlyElementsIn(
+ factory.asTypeMirrors(
+ ImmutableList.of(
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/lang/Object")),
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/io/Serializable")),
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/lang/Cloneable")))));
+ }
+
+ @Test
+ public void tyVar() {
+ TypeVariable t =
+ (TypeVariable)
+ Iterables.getOnlyElement(
+ factory
+ .typeElement(new ClassSymbol("java/util/Collections"))
+ .getEnclosedElements()
+ .stream()
+ .filter(e -> e.getSimpleName().contentEquals("sort"))
+ .filter(ExecutableElement.class::isInstance)
+ .map(ExecutableElement.class::cast)
+ .filter(e -> e.getParameters().size() == 1)
+ .findFirst()
+ .get()
+ .getTypeParameters())
+ .asType();
+ assertThat(t.getKind()).isEqualTo(TypeKind.TYPEVAR);
+ assertThat(t.getLowerBound().getKind()).isEqualTo(TypeKind.NONE);
+ assertThat(t.getUpperBound().toString()).isEqualTo("java.lang.Comparable<? super T>");
+ }
+
+ @Test
+ public void arrayType() {
+ ArrayType t =
+ (ArrayType)
+ factory.asTypeMirror(
+ Type.ArrayTy.create(
+ PrimTy.create(TurbineConstantTypeKind.LONG, ImmutableList.of()),
+ ImmutableList.of()));
+ assertThat(t.getKind()).isEqualTo(TypeKind.ARRAY);
+ assertThat(t.getComponentType().getKind()).isEqualTo(TypeKind.LONG);
+ }
+
+ @Test
+ public void declared() {
+ DeclaredType a =
+ (DeclaredType)
+ factory.asTypeMirror(
+ Type.ClassTy.create(
+ ImmutableList.of(
+ Type.ClassTy.SimpleClassTy.create(
+ new ClassSymbol("java/util/Map"),
+ ImmutableList.of(),
+ ImmutableList.of()),
+ Type.ClassTy.SimpleClassTy.create(
+ new ClassSymbol("java/util/Map$Entry"),
+ ImmutableList.of(Type.ClassTy.STRING, Type.ClassTy.STRING),
+ ImmutableList.of()))));
+ DeclaredType b =
+ (DeclaredType)
+ factory.asTypeMirror(
+ Type.ClassTy.asNonParametricClassTy(new ClassSymbol("java/util/Map$Entry")));
+
+ assertThat(a.getEnclosingType().getKind()).isEqualTo(TypeKind.NONE);
+ assertThat(b.getEnclosingType().getKind()).isEqualTo(TypeKind.NONE);
+ }
+
+ @Test
+ public void method() {
+ ExecutableType type =
+ (ExecutableType)
+ ((TypeElement) factory.typeElement(new ClassSymbol("java/util/Collections")))
+ .getEnclosedElements().stream()
+ .filter(e -> e.getSimpleName().contentEquals("replaceAll"))
+ .collect(onlyElement())
+ .asType();
+ assertThat(type.getTypeVariables()).hasSize(1);
+ assertThat(type.toString()).isEqualTo("<T>(java.util.List<T>,T,T)boolean");
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineTypesContainsTest.java b/javatests/com/google/turbine/processing/TurbineTypesContainsTest.java
new file mode 100644
index 0000000..e9e411f
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypesContainsTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.truth.TruthJUnit.assume;
+
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.Types;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TurbineTypesContainsTest extends AbstractTurbineTypesBiPredicateTest {
+
+ @Parameters(name = "{index}: {0}")
+ public static Iterable<Object[]> parameters() throws Exception {
+ return binaryParameters();
+ }
+
+ public TurbineTypesContainsTest(
+ String name, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) {
+ super(name, javacInput, turbineInput);
+ }
+
+ @Test
+ public void contains() {
+ // crashes javac
+ assume().that(javacInput.lhs.getKind()).isNotEqualTo(TypeKind.NONE);
+ assume().that(javacInput.rhs.getKind()).isNotEqualTo(TypeKind.NONE);
+
+ // crashes javac
+ assume().that(javacInput.lhs.getKind()).isNotEqualTo(TypeKind.EXECUTABLE);
+ assume().that(javacInput.rhs.getKind()).isNotEqualTo(TypeKind.EXECUTABLE);
+
+ test("<=", Types::contains);
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineTypesFactoryTest.java b/javatests/com/google/turbine/processing/TurbineTypesFactoryTest.java
new file mode 100644
index 0000000..0f9e6a6
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypesFactoryTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.turbine.binder.Binder.BindingResult;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.env.SimpleEnv;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.lower.IntegrationTestSupport;
+import com.google.turbine.testing.TestClassPaths;
+import com.google.turbine.type.Type.ArrayTy;
+import com.google.turbine.type.Type.ClassTy;
+import com.google.turbine.type.Type.ClassTy.SimpleClassTy;
+import com.google.turbine.type.Type.WildLowerBoundedTy;
+import com.google.turbine.type.Type.WildUnboundedTy;
+import com.google.turbine.type.Type.WildUpperBoundedTy;
+import java.util.Optional;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TurbineTypesFactoryTest {
+
+ private static final IntegrationTestSupport.TestInput SOURCES =
+ IntegrationTestSupport.TestInput.parse(
+ Joiner.on('\n')
+ .join(
+ "=== Test.java ===", //
+ "class Test {",
+ " class I {}",
+ "}"));
+
+ ModelFactory factory;
+ TurbineElements turbineElements;
+ TurbineTypes turbineTypes;
+
+ @Before
+ public void setup() throws Exception {
+
+ BindingResult bound =
+ IntegrationTestSupport.turbineAnalysis(
+ SOURCES.sources,
+ ImmutableList.of(),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+ Env<ClassSymbol, TypeBoundClass> env =
+ CompoundEnv.<ClassSymbol, TypeBoundClass>of(bound.classPathEnv())
+ .append(new SimpleEnv<>(bound.units()));
+ factory = new ModelFactory(env, getClass().getClassLoader(), bound.tli());
+ turbineTypes = new TurbineTypes(factory);
+ turbineElements = new TurbineElements(factory, turbineTypes);
+ }
+
+ @Test
+ public void primitiveTypes() {
+ for (TypeKind kind : TypeKind.values()) {
+ if (kind.isPrimitive()) {
+ PrimitiveType type = turbineTypes.getPrimitiveType(kind);
+ assertThat(type.getKind()).isEqualTo(kind);
+ } else {
+ try {
+ turbineTypes.getPrimitiveType(kind);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+ }
+ }
+
+ @Test
+ public void arrayType() {
+ assertThat(
+ turbineTypes.isSameType(
+ turbineTypes.getArrayType(
+ turbineTypes.erasure(turbineElements.getTypeElement("java.util.Map").asType())),
+ factory.asTypeMirror(
+ ArrayTy.create(
+ ClassTy.asNonParametricClassTy(new ClassSymbol("java/util/Map")),
+ ImmutableList.of()))))
+ .isTrue();
+ }
+
+ @Test
+ public void wildcardType() {
+ // wildcard types don't compare equal with isSameType, so compare their string representations
+ assertThat(turbineTypes.getWildcardType(null, null).toString())
+ .isEqualTo(factory.asTypeMirror(WildUnboundedTy.create(ImmutableList.of())).toString());
+ assertThat(
+ turbineTypes
+ .getWildcardType(turbineElements.getTypeElement("java.lang.String").asType(), null)
+ .toString())
+ .isEqualTo(
+ factory
+ .asTypeMirror(WildUpperBoundedTy.create(ClassTy.STRING, ImmutableList.of()))
+ .toString());
+ assertThat(
+ turbineTypes
+ .getWildcardType(null, turbineElements.getTypeElement("java.lang.String").asType())
+ .toString())
+ .isEqualTo(
+ factory
+ .asTypeMirror(WildLowerBoundedTy.create(ClassTy.STRING, ImmutableList.of()))
+ .toString());
+ }
+
+ @Test
+ public void declaredType() {
+ assertThat(
+ turbineTypes.isSameType(
+ turbineTypes.getDeclaredType(
+ turbineElements.getTypeElement("java.util.Map"),
+ turbineElements.getTypeElement("java.lang.String").asType(),
+ turbineElements.getTypeElement("java.lang.Integer").asType()),
+ factory.asTypeMirror(
+ ClassTy.create(
+ ImmutableList.of(
+ SimpleClassTy.create(
+ new ClassSymbol("java/util/Map"),
+ ImmutableList.of(
+ ClassTy.STRING,
+ ClassTy.asNonParametricClassTy(ClassSymbol.INTEGER)),
+ ImmutableList.of()))))))
+ .isTrue();
+ assertThat(
+ turbineTypes.isSameType(
+ turbineTypes.getDeclaredType(
+ turbineTypes.getDeclaredType(turbineElements.getTypeElement("Test")),
+ turbineElements.getTypeElement("Test.I")),
+ factory.asTypeMirror(
+ ClassTy.create(
+ ImmutableList.of(
+ SimpleClassTy.create(
+ new ClassSymbol("Test"), ImmutableList.of(), ImmutableList.of()),
+ SimpleClassTy.create(
+ new ClassSymbol("Test$I"),
+ ImmutableList.of(),
+ ImmutableList.of()))))))
+ .isTrue();
+ }
+
+ @Test
+ public void noType() {
+ assertThat(turbineTypes.getNoType(TypeKind.VOID).getKind()).isEqualTo(TypeKind.VOID);
+ assertThat(turbineTypes.getNoType(TypeKind.NONE).getKind()).isEqualTo(TypeKind.NONE);
+ try {
+ turbineTypes.getNoType(TypeKind.DECLARED);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void nullType() {
+ assertThat(turbineTypes.getNullType().getKind()).isEqualTo(TypeKind.NULL);
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineTypesIsAssignableTest.java b/javatests/com/google/turbine/processing/TurbineTypesIsAssignableTest.java
new file mode 100644
index 0000000..75b93b0
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypesIsAssignableTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.truth.TruthJUnit.assume;
+
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.Types;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TurbineTypesIsAssignableTest extends AbstractTurbineTypesBiPredicateTest {
+
+ @Parameters(name = "{index}: {0}")
+ public static Iterable<Object[]> parameters() throws Exception {
+ return binaryParameters();
+ }
+
+ public TurbineTypesIsAssignableTest(
+ String testDescription, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) {
+ super(testDescription, javacInput, turbineInput);
+ }
+
+ @Test
+ public void isAssignable() {
+ // see JDK-8039198
+ assume().that(javacInput.lhs.getKind()).isNotEqualTo(TypeKind.WILDCARD);
+ assume().that(javacInput.rhs.getKind()).isNotEqualTo(TypeKind.WILDCARD);
+
+ // crashes javac
+ assume().that(javacInput.lhs.getKind()).isNotEqualTo(TypeKind.NONE);
+ assume().that(javacInput.rhs.getKind()).isNotEqualTo(TypeKind.NONE);
+
+ // crashes javac
+ assume().that(javacInput.lhs.getKind()).isNotEqualTo(TypeKind.EXECUTABLE);
+ assume().that(javacInput.rhs.getKind()).isNotEqualTo(TypeKind.EXECUTABLE);
+
+ test("isAssignable", Types::isAssignable);
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineTypesIsSameTypeTest.java b/javatests/com/google/turbine/processing/TurbineTypesIsSameTypeTest.java
new file mode 100644
index 0000000..ef45991
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypesIsSameTypeTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import javax.lang.model.util.Types;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TurbineTypesIsSameTypeTest extends AbstractTurbineTypesBiPredicateTest {
+
+ @Parameters(name = "{index}: {0}")
+ public static Iterable<Object[]> parameters() throws Exception {
+ return binaryParameters();
+ }
+
+ public TurbineTypesIsSameTypeTest(
+ String testDescription, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) {
+ super(testDescription, javacInput, turbineInput);
+ }
+
+ @Test
+ public void isSameType() {
+ test("isSameType", Types::isSameType);
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineTypesIsSubsignatureTest.java b/javatests/com/google/turbine/processing/TurbineTypesIsSubsignatureTest.java
new file mode 100644
index 0000000..5f315da
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypesIsSubsignatureTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.truth.TruthJUnit.assume;
+
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeKind;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TurbineTypesIsSubsignatureTest extends AbstractTurbineTypesBiPredicateTest {
+
+ @Parameters(name = "{index}: {0}")
+ public static Iterable<Object[]> parameters() throws Exception {
+ return binaryParameters();
+ }
+
+ public TurbineTypesIsSubsignatureTest(
+ String testDescription, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) {
+ super(testDescription, javacInput, turbineInput);
+ }
+
+ @Test
+ public void isSubsignature() {
+ assume().that(javacInput.lhs.getKind()).isEqualTo(TypeKind.EXECUTABLE);
+ assume().that(javacInput.rhs.getKind()).isEqualTo(TypeKind.EXECUTABLE);
+
+ test(
+ "isSubsignature",
+ (types, x, y) -> types.isSubsignature((ExecutableType) x, (ExecutableType) y));
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineTypesIsSubtypeTest.java b/javatests/com/google/turbine/processing/TurbineTypesIsSubtypeTest.java
new file mode 100644
index 0000000..8370126
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypesIsSubtypeTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.truth.TruthJUnit.assume;
+
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.Types;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TurbineTypesIsSubtypeTest extends AbstractTurbineTypesBiPredicateTest {
+
+ @Parameters(name = "{index}: {0}")
+ public static Iterable<Object[]> parameters() throws Exception {
+ return binaryParameters();
+ }
+
+ public TurbineTypesIsSubtypeTest(
+ String testDescription, TypesBiFunctionInput javacInput, TypesBiFunctionInput turbineInput) {
+ super(testDescription, javacInput, turbineInput);
+ }
+
+ @Test
+ public void isSubtype() {
+ // see JDK-8039198
+ assume().that(javacInput.lhs.getKind()).isNotEqualTo(TypeKind.WILDCARD);
+ assume().that(javacInput.rhs.getKind()).isNotEqualTo(TypeKind.WILDCARD);
+
+ // crashes javac
+ assume().that(javacInput.lhs.getKind()).isNotEqualTo(TypeKind.NONE);
+ assume().that(javacInput.rhs.getKind()).isNotEqualTo(TypeKind.NONE);
+
+ // crashes javac
+ assume().that(javacInput.lhs.getKind()).isNotEqualTo(TypeKind.EXECUTABLE);
+ assume().that(javacInput.rhs.getKind()).isNotEqualTo(TypeKind.EXECUTABLE);
+
+ test("<:", Types::isSubtype);
+ }
+}
diff --git a/javatests/com/google/turbine/processing/TurbineTypesUnaryTest.java b/javatests/com/google/turbine/processing/TurbineTypesUnaryTest.java
new file mode 100644
index 0000000..eb5ee6c
--- /dev/null
+++ b/javatests/com/google/turbine/processing/TurbineTypesUnaryTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.processing;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.common.truth.TruthJUnit.assume;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Types;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TurbineTypesUnaryTest extends AbstractTurbineTypesTest {
+
+ @Parameters(name = "{index}: {0}")
+ public static Iterable<Object[]> parameters() throws Exception {
+ return unaryParameters();
+ }
+
+ final String testDescription;
+ final Types javacTypes;
+ final TypeMirror javacA;
+ final Types turbineTypes;
+ final TypeMirror turbineA;
+
+ public TurbineTypesUnaryTest(
+ String testDescription,
+ Types javacTypes,
+ TypeMirror javacA,
+ Types turbineTypes,
+ TypeMirror turbineA) {
+ this.testDescription = testDescription;
+ this.javacTypes = javacTypes;
+ this.javacA = javacA;
+ this.turbineTypes = turbineTypes;
+ this.turbineA = turbineA;
+ }
+
+ @Test
+ public void unboxedType() {
+ IllegalArgumentException thrown = null;
+ String expectedType = null;
+ try {
+ expectedType = javacTypes.unboxedType(javacA).toString();
+ } catch (IllegalArgumentException e) {
+ thrown = e;
+ }
+ if (thrown != null) {
+ try {
+ turbineTypes.unboxedType(turbineA).toString();
+ fail(String.format("expected unboxedType(`%s`) to throw", turbineA));
+ } catch (IllegalArgumentException expected) {
+ // expected
+ }
+ } else {
+ String actual = turbineTypes.unboxedType(turbineA).toString();
+ assertWithMessage("unboxedClass(`%s`) = unboxedClass(`%s`)", javacA, turbineA)
+ .that(actual)
+ .isEqualTo(expectedType);
+ }
+ }
+
+ @Test
+ public void boxedClass() {
+ assume().that(javacA).isInstanceOf(PrimitiveType.class);
+ assume().that(turbineA).isInstanceOf(PrimitiveType.class);
+
+ String expected = javacTypes.boxedClass((PrimitiveType) javacA).toString();
+ String actual = turbineTypes.boxedClass((PrimitiveType) turbineA).toString();
+ assertWithMessage("boxedClass(`%s`) = boxedClass(`%s`)", javacA, turbineA)
+ .that(actual)
+ .isEqualTo(expected);
+ }
+
+ @Test
+ public void erasure() {
+ String expected = javacTypes.erasure(javacA).toString();
+ String actual = turbineTypes.erasure(turbineA).toString();
+ assertWithMessage("erasure(`%s`) = erasure(`%s`)", javacA, turbineA)
+ .that(actual)
+ .isEqualTo(expected);
+ }
+
+ private static final ImmutableSet<TypeKind> UNSUPPORTED_BY_DIRECT_SUPERTYPES =
+ ImmutableSet.of(TypeKind.EXECUTABLE, TypeKind.PACKAGE);
+
+ @Test
+ public void directSupertypes() {
+ assume().that(UNSUPPORTED_BY_DIRECT_SUPERTYPES).doesNotContain(javacA.getKind());
+
+ String expected = Joiner.on(", ").join(javacTypes.directSupertypes(javacA));
+ String actual = Joiner.on(", ").join(turbineTypes.directSupertypes(turbineA));
+ assertWithMessage("directSupertypes(`%s`) = directSupertypes(`%s`)", javacA, turbineA)
+ .that(actual)
+ .isEqualTo(expected);
+ }
+
+ @Test
+ public void directSupertypesThrows() {
+ assume().that(UNSUPPORTED_BY_DIRECT_SUPERTYPES).contains(javacA.getKind());
+
+ try {
+ javacTypes.directSupertypes(turbineA);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ turbineTypes.directSupertypes(turbineA);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void asElement() {
+ // TODO(cushon): this looks like a javac bug
+ assume().that(javacA.getKind()).isNotEqualTo(TypeKind.INTERSECTION);
+
+ String expected = String.valueOf(javacTypes.asElement(javacA));
+ String actual = String.valueOf(turbineTypes.asElement(turbineA));
+ assertWithMessage("asElement(`%s`) = asElement(`%s`)", javacA, turbineA)
+ .that(actual)
+ .isEqualTo(expected);
+ }
+}
diff --git a/javatests/com/google/turbine/testing/TestClassPaths.java b/javatests/com/google/turbine/testing/TestClassPaths.java
index bf38913..93be916 100644
--- a/javatests/com/google/turbine/testing/TestClassPaths.java
+++ b/javatests/com/google/turbine/testing/TestClassPaths.java
@@ -67,7 +67,7 @@ public class TestClassPaths {
public static TurbineOptions.Builder optionsWithBootclasspath() {
TurbineOptions.Builder options = TurbineOptions.builder();
if (!BOOTCLASSPATH.isEmpty()) {
- options.addBootClassPathEntries(
+ options.setBootClassPath(
BOOTCLASSPATH.stream().map(Path::toString).collect(toImmutableList()));
} else {
options.setRelease("8");
diff --git a/javatests/com/google/turbine/type/TypeTest.java b/javatests/com/google/turbine/type/TypeTest.java
new file mode 100644
index 0000000..be3eb9c
--- /dev/null
+++ b/javatests/com/google/turbine/type/TypeTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019 Google Inc. All Rights Reserved.
+ *
+ * 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.google.turbine.type;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.testing.EqualsTester;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.tree.Tree.Ident;
+import com.google.turbine.type.Type.ClassTy;
+import com.google.turbine.type.Type.ClassTy.SimpleClassTy;
+import com.google.turbine.type.Type.ErrorTy;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TypeTest {
+
+ @Test
+ public void equals() {
+ new EqualsTester()
+ .addEqualityGroup(
+ ClassTy.create(
+ ImmutableList.of(
+ SimpleClassTy.create(
+ new ClassSymbol("java/util/Map"), ImmutableList.of(), ImmutableList.of()),
+ SimpleClassTy.create(
+ new ClassSymbol("java/util/Map$Entry"),
+ ImmutableList.of(ClassTy.STRING, ClassTy.STRING),
+ ImmutableList.of()))))
+ .addEqualityGroup(
+ SimpleClassTy.create(
+ new ClassSymbol("java/util/Map$Entry"),
+ ImmutableList.of(ClassTy.STRING, ClassTy.OBJECT),
+ ImmutableList.of()))
+ .addEqualityGroup(ClassTy.asNonParametricClassTy(new ClassSymbol("java/util/Map$Entry")))
+ .testEquals();
+ }
+
+ private static final int NO_POSITION = -1;
+
+ @Test
+ public void error() {
+ assertThat(
+ ErrorTy.create(
+ ImmutableList.of(
+ new Ident(NO_POSITION, "com"),
+ new Ident(NO_POSITION, "foo"),
+ new Ident(NO_POSITION, "Bar")))
+ .name())
+ .isEqualTo("com.foo.Bar");
+ }
+}
diff --git a/javatests/com/google/turbine/zip/ZipTest.java b/javatests/com/google/turbine/zip/ZipTest.java
index 67dcfe7..bfc9cdf 100644
--- a/javatests/com/google/turbine/zip/ZipTest.java
+++ b/javatests/com/google/turbine/zip/ZipTest.java
@@ -165,7 +165,7 @@ public class ZipTest {
actual(path);
fail();
} catch (ZipException e) {
- assertThat(e).hasMessage("zip file comment length was 33, expected 17");
+ assertThat(e).hasMessageThat().isEqualTo("zip file comment length was 33, expected 17");
}
}
}