/*
* Copyright 2017-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.facebook.buck.jvm.java.abi.source;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.facebook.buck.jvm.java.testutil.compiler.CompilerTreeApiTestRunner;
import com.facebook.buck.jvm.java.testutil.compiler.TestCompiler;
import com.google.common.base.Joiner;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import javax.annotation.processing.Completion;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;
import org.hamcrest.Matchers;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(CompilerTreeApiTestRunner.class)
public class TreeBackedProcessorWrapperTest {
@Rule public TestCompiler testCompiler = new TestCompiler();
private ProcessingEnvironment processingEnv;
private Elements elements;
private Messager messager;
@Test
public void testElementUtilsIsWrapped() throws IOException {
runTestProcessor(
(annotations, roundEnv) -> {
assertTrue(processingEnv.getElementUtils() instanceof TreeBackedElements);
return false;
});
}
@Test
public void testTypeUtilsIsWrapped() throws IOException {
runTestProcessor(
(annotations, roundEnv) -> {
assertTrue(processingEnv.getTypeUtils() instanceof TreeBackedTypes);
return false;
});
}
@Test
public void testAnnotationsAreWrapped() throws IOException {
runTestProcessor(
(annotations, roundEnv) -> {
if (!roundEnv.processingOver()) {
TreeBackedTypeElement annotationType =
(TreeBackedTypeElement) elements.getTypeElement("com.example.buck.FooAnno");
assertThat(annotations, Matchers.contains(annotationType));
} else {
assertThat(annotations, Matchers.empty());
}
return false;
});
}
@Test
public void testRootElementsAreWrapped() throws IOException {
runTestProcessor(
(annotations, roundEnv) -> {
if (!roundEnv.processingOver()) {
TreeBackedTypeElement annotationType =
(TreeBackedTypeElement) elements.getTypeElement("com.example.buck.FooAnno");
TreeBackedTypeElement fooType =
(TreeBackedTypeElement) elements.getTypeElement("com.example.buck.Foo");
assertThat(
roundEnv.getRootElements(), Matchers.containsInAnyOrder(annotationType, fooType));
} else {
assertThat(roundEnv.getRootElements(), Matchers.empty());
}
return false;
});
}
@Test
public void testGetElementsAnnotatedWith() throws IOException {
runTestProcessor(
(annotations, roundEnv) -> {
TreeBackedTypeElement annotationType =
(TreeBackedTypeElement) elements.getTypeElement("com.example.buck.FooAnno");
TreeBackedTypeElement fooType =
(TreeBackedTypeElement) elements.getTypeElement("com.example.buck.Foo");
Set<? extends Element> annotatedElements =
roundEnv.getElementsAnnotatedWith(annotationType);
if (!roundEnv.processingOver()) {
assertThat(annotatedElements, Matchers.contains(fooType));
} else {
assertThat(annotatedElements, Matchers.empty());
}
return false;
});
}
@Test
public void testElementMessager() throws IOException {
runTestProcessor(
(annotations, roundEnv) -> {
messager.printMessage(
Diagnostic.Kind.ERROR, "Foo", elements.getTypeElement("com.example.buck.Foo"));
return false;
});
assertThat(
testCompiler
.getDiagnosticMessages()
.stream()
.map(message -> message.substring(message.indexOf("Foo.java")))
.collect(Collectors.toList()),
Matchers.contains(
Joiner.on('\n').join("Foo.java:3: error: Foo", "public class Foo {}", " ^")));
}
private void runTestProcessor(
BiFunction<Set<? extends TypeElement>, RoundEnvironment, Boolean> processMethod)
throws IOException {
testCompiler.useFrontendOnlyJavacTask();
testCompiler.addSourceFileLines(
"Foo.java",
"package com.example.buck;",
"@FooAnno",
"public class Foo {}",
"@interface FooAnno {}");
testCompiler.setProcessors(
Collections.singletonList(
new Processor() {
@Override
public Set<String> getSupportedOptions() {
return Collections.emptySet();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return Collections.singleton("com.example.buck.FooAnno");
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_8;
}
@Override
public void init(ProcessingEnvironment processingEnv) {
TreeBackedProcessorWrapperTest.this.processingEnv = processingEnv;
elements = processingEnv.getElementUtils();
messager = processingEnv.getMessager();
}
@Override
public Iterable<? extends Completion> getCompletions(
Element element,
AnnotationMirror annotation,
ExecutableElement member,
String userText) {
fail("Should never be called");
return null;
}
@Override
public boolean process(
Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return processMethod.apply(annotations, roundEnv);
}
}));
testCompiler.enter();
}
}