/* * Copyright 2016-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.testutil.compiler; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import com.facebook.buck.jvm.java.plugin.adapter.BuckJavacTask; import com.google.common.collect.ImmutableMap; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.util.TaskListener; import com.sun.source.util.Trees; import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.Map; import java.util.stream.Collectors; 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.TypeParameterElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.Matchers; import org.junit.Rule; /** Base class for tests that want to use the Compiler Tree API exposed by javac. */ public abstract class CompilerTreeApiTest { public interface TaskListenerFactory { TaskListener newTaskListener(BuckJavacTask task); } @Rule public TestCompiler testCompiler = new TestCompiler(); protected Elements elements; protected Trees trees; protected Types types; protected boolean useFrontendOnlyJavacTask() { return false; } protected final void initCompiler() throws IOException { initCompiler(Collections.emptyMap()); } protected void initCompiler(Map<String, String> fileNamesToContents) throws IOException { if (useFrontendOnlyJavacTask()) { testCompiler.useFrontendOnlyJavacTask(); } for (Map.Entry<String, String> fileNameToContents : fileNamesToContents.entrySet()) { testCompiler.addSourceFileContents( fileNameToContents.getKey(), fileNameToContents.getValue()); } trees = testCompiler.getTrees(); elements = testCompiler.getElements(); types = testCompiler.getTypes(); // Suppress processor auto-discovery; it was picking up the immutables processor unnecessarily testCompiler.setProcessors(Collections.emptyList()); } protected final Iterable<? extends CompilationUnitTree> compile(String source) throws IOException { return compile(ImmutableMap.of("Foo.java", source)); } protected final Iterable<? extends CompilationUnitTree> compile(Map<String, String> sources) throws IOException { return compile(sources, null); } protected final Iterable<? extends CompilationUnitTree> compile( Map<String, String> fileNamesToContents, TaskListenerFactory taskListenerFactory) throws IOException { initCompiler(fileNamesToContents); if (taskListenerFactory != null) { testCompiler.setTaskListener( taskListenerFactory.newTaskListener(testCompiler.getJavacTask())); } final Iterable<? extends CompilationUnitTree> compilationUnits = testCompiler.parse(); // Make sure we've got elements for things. testCompiler.enter(); return compilationUnits; } protected void withClasspath(Map<String, String> fileNamesToContents) throws IOException { for (Map.Entry<String, String> fileNameToContents : fileNamesToContents.entrySet()) { testCompiler.addClasspathFileContents( fileNameToContents.getKey(), fileNameToContents.getValue()); } } protected TypeMirror getTypeParameterUpperBound(String typeName, int typeParameterIndex) { TypeParameterElement typeParameter = elements.getTypeElement(typeName).getTypeParameters().get(typeParameterIndex); TypeVariable typeVariable = (TypeVariable) typeParameter.asType(); return typeVariable.getUpperBound(); } protected ExecutableElement findDefaultConstructor(TypeElement typeElement) { return ElementFilter.constructorsIn(typeElement.getEnclosedElements()) .stream() .filter(element -> element.getParameters().isEmpty()) .findFirst() .get(); } protected ExecutableElement findMethod(String name, TypeElement typeElement) { for (Element element : typeElement.getEnclosedElements()) { if (element.getKind() == ElementKind.METHOD && element.getSimpleName().contentEquals(name)) { return (ExecutableElement) element; } } throw new IllegalArgumentException( String.format("No such method in %s: %s", typeElement.getQualifiedName(), name)); } protected VariableElement findField(String name, TypeElement typeElement) { for (Element element : typeElement.getEnclosedElements()) { if (element.getKind().isField() && element.getSimpleName().contentEquals(name)) { return (VariableElement) element; } } throw new IllegalArgumentException( String.format("No such field in %s: %s", typeElement.getQualifiedName(), name)); } protected VariableElement findParameter(String name, ExecutableElement method) { for (VariableElement parameter : method.getParameters()) { if (parameter.getSimpleName().contentEquals(name)) { return parameter; } } throw new IllegalArgumentException( String.format("No such parameter on %s: %s", method.getSimpleName(), name)); } protected void assertNameEquals(String expected, Name actual) { assertEquals(elements.getName(expected), actual); } protected void assertSameType(TypeMirror expected, TypeMirror actual) { if (!types.isSameType(expected, actual)) { fail(String.format("Types are not the same.\nExpected: %s\nActual: %s", expected, actual)); } } protected Matcher<TypeMirror> sameType(TypeMirror expected) { return new BaseMatcher<TypeMirror>() { @Override public void describeTo(Description description) { description.appendText(expected.toString()); } @Override public boolean matches(Object o) { if (o instanceof TypeMirror) { return types.isSameType(expected, (TypeMirror) o); } return false; } }; } protected void assertNotSameType(TypeMirror expected, TypeMirror actual) { if (types.isSameType(expected, actual)) { fail(String.format("Expected different types, but both were: %s", expected)); } } protected void assertNoErrors() { assertThat(testCompiler.getDiagnosticMessages(), Matchers.empty()); } protected void assertError(String message) { assertErrors(message); } protected void assertErrors(String... messages) { assertThat( testCompiler .getDiagnosticMessages() .stream() .map( diagnosticMessage -> diagnosticMessage.substring( diagnosticMessage.lastIndexOf(File.separatorChar) + 1)) .collect(Collectors.toSet()), Matchers.containsInAnyOrder(messages)); } }