/* * Copyright 2017-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.jvm.java.abi; import static org.junit.Assert.assertThat; import com.facebook.buck.jvm.java.testutil.compiler.CompilerTreeApiTestRunner; import com.facebook.buck.jvm.java.testutil.compiler.TestCompiler; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ErrorType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.IntersectionType; import javax.lang.model.type.NoType; import javax.lang.model.type.NullType; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeVariable; import javax.lang.model.type.UnionType; import javax.lang.model.type.WildcardType; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import org.hamcrest.Matchers; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(CompilerTreeApiTestRunner.class) public class TypeScanner8Test { @Rule public TestCompiler compiler = new TestCompiler(); @Test public void testScansPrimitives() throws IOException { new Tester().scanType("int").assertScans("int"); } @Test public void testScansPrimitiveArrays() throws IOException { new Tester().scanType("int[][]").assertScans("int[][]", "int[]", "int"); } @Test public void testScansDeclaredTypes() throws IOException { new Tester().scanType("Object").assertScans("java.lang.Object", "none"); } @Test public void testScansNestedTypes() throws IOException { new Tester() .addExtraCode("private static class Inner { }") .scanType("Inner") .assertScans("Foo.Inner", "none"); } @Test public void testScansInnerTypes() throws IOException { new Tester() .addExtraCode("private class Inner { }") .scanType("Inner") .assertScans("Foo.Inner", "Foo", "none"); } @Test public void testScansTypeVars() throws IOException { new Tester() .defineTypeParameter("T extends Runnable") .scanType("T") .assertScans("T", "<nulltype>", "java.lang.Runnable", "none"); } @Test public void testScansIntersectionTypes() throws IOException { new Tester() .defineTypeParameter("T extends CharSequence & Runnable") .scanType("T") .assertScans( "T", "<nulltype>", "java.lang.Object&java.lang.CharSequence&java.lang.Runnable", "java.lang.CharSequence", "none", "java.lang.Runnable", "none"); } @Test public void testScansRecursiveTypeVars() throws IOException { new Tester() .defineTypeParameter("T extends List<T>") .scanType("T") .assertScans("T", "<nulltype>", "java.util.List<T>", "none", "T"); } @Test public void testScansWildcards() throws IOException { new Tester().scanType("List<?>").assertScans("java.util.List<?>", "none", "?"); } @Test public void testScansExtendsWildcards() throws IOException { new Tester() .scanType("List<? extends Runnable>") .assertScans( "java.util.List<? extends java.lang.Runnable>", "none", "? extends java.lang.Runnable", "java.lang.Runnable", "none"); } @Test public void testScansSuperWildcards() throws IOException { new Tester() .scanType("List<? super String>") .assertScans( "java.util.List<? super java.lang.String>", "none", "? super java.lang.String", "java.lang.String", "none"); } private class Tester { private final List<String> typeParameters = new ArrayList<>(); private List<String> scanResults; private String extraCode = ""; public Tester defineTypeParameter(String typeParam) { typeParameters.add(typeParam); return this; } public Tester addExtraCode(String... lines) { extraCode = extraCode + Arrays.stream(lines).collect(Collectors.joining("\n")); return this; } public Tester scanType(String type) throws IOException { compiler.addSourceFileLines( "Foo.java", "import java.util.*;", String.format("public class Foo%s {", formatTypeParameters()), extraCode, String.format(" public %s toScan;", type), "}"); compiler.setProcessors(Collections.emptyList()); compiler.enter(); Elements elements = compiler.getElements(); TypeElement fooType = elements.getTypeElement("Foo"); VariableElement toScan = ElementFilter.fieldsIn(fooType.getEnclosedElements()).get(0); TestTypeScanner scanner = new TestTypeScanner(); scanner.scan(toScan.asType()); scanResults = scanner.scans; return this; } public void assertScans(String... scans) { assertThat(scanResults, Matchers.contains(scans)); } private String formatTypeParameters() { if (typeParameters.isEmpty()) { return ""; } return String.format("<%s>", typeParameters.stream().collect(Collectors.joining(","))); } } private static class TestTypeScanner extends TypeScanner8<Void, Void> { private final List<String> scans = new ArrayList<>(); @Override public Void visitArray(ArrayType t, Void aVoid) { scans.add(t.toString()); return super.visitArray(t, aVoid); } @Override public Void visitDeclared(DeclaredType t, Void aVoid) { scans.add(t.toString()); return super.visitDeclared(t, aVoid); } @Override public Void visitTypeVariable(TypeVariable t, Void aVoid) { scans.add(t.toString()); return super.visitTypeVariable(t, aVoid); } @Override public Void visitWildcard(WildcardType t, Void aVoid) { scans.add(t.toString()); return super.visitWildcard(t, aVoid); } @Override public Void visitExecutable(ExecutableType t, Void aVoid) { scans.add(t.toString()); return super.visitExecutable(t, aVoid); } @Override public Void visitIntersection(IntersectionType t, Void aVoid) { scans.add(t.toString()); return super.visitIntersection(t, aVoid); } @Override public Void visitUnion(UnionType t, Void aVoid) { scans.add(t.toString()); return super.visitUnion(t, aVoid); } @Override public Void visitPrimitive(PrimitiveType t, Void aVoid) { scans.add(t.toString()); return super.visitPrimitive(t, aVoid); } @Override public Void visitNull(NullType t, Void aVoid) { scans.add(t.toString()); return super.visitNull(t, aVoid); } @Override public Void visitError(ErrorType t, Void aVoid) { scans.add(t.toString()); return super.visitError(t, aVoid); } @Override public Void visitNoType(NoType t, Void aVoid) { scans.add(t.toString()); return super.visitNoType(t, aVoid); } } }