You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by je...@apache.org on 2018/01/29 14:11:34 UTC

[geode] branch develop updated: GEODE-4139: Add javac processor to ensure correct RunWith options are enabled for parameterized tests (#1318)

This is an automated email from the ASF dual-hosted git repository.

jensdeppe pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new 819599b  GEODE-4139: Add javac processor to ensure correct RunWith options are enabled for parameterized tests (#1318)
819599b is described below

commit 819599ba26944353ed72ac312f3b11e72e2ce425
Author: Jens Deppe <jd...@pivotal.io>
AuthorDate: Mon Jan 29 06:11:31 2018 -0800

    GEODE-4139: Add javac processor to ensure correct RunWith options are enabled for parameterized tests (#1318)
    
    - This ensures that if @RunWith(Parameterized.class) is specified for a test class, then
      @Parameterized.UseParametersRunnerFactory(CategoryWithParameterizedRunnerFactory.class)
      is also specified for that class. Without that, the tests are not correctly run as the
      presence of @Category breaks the parameterization.
---
 buildSrc/build.gradle                              |   4 +-
 .../geode/gradle/TestPropertiesWriter.groovy       |   2 -
 .../javac/EnsureCorrectRunsWithProcessor.java      | 135 +++++++++++++++++++++
 .../services/javax.annotation.processing.Processor |   1 +
 .../org/apache/geode/javac/SimpleClassFile.java    |  42 +++++++
 .../org/apache/geode/javac/SimpleFileManager.java  |  46 +++++++
 .../org/apache/geode/javac/SimpleSourceFile.java   |  35 ++++++
 .../geode/javac/TestAnnotationProcessor.java       |  51 ++++++++
 .../java/org/apache/geode/javac/TestCompiler.java  |  51 ++++++++
 gradle/test.gradle                                 |  22 +++-
 10 files changed, 385 insertions(+), 4 deletions(-)

diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index d7ff581..21cf963 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -21,5 +21,7 @@ repositories {
 }
 
 dependencies {
-  compile group: 'org.apache.mina', name: 'mina-core', version: '2.0.14'
+  compile group: 'org.apache.geode', name: 'geode-junit', version: '1.3.0'
+  compile group: 'junit', name: 'junit', version: '4.12'
+  compile files("${System.getProperty('java.home')}/../lib/tools.jar")
 }
\ No newline at end of file
diff --git a/buildSrc/src/main/groovy/org/apache/geode/gradle/TestPropertiesWriter.groovy b/buildSrc/src/main/groovy/org/apache/geode/gradle/TestPropertiesWriter.groovy
index 58e61f7..8cd0d9b 100644
--- a/buildSrc/src/main/groovy/org/apache/geode/gradle/TestPropertiesWriter.groovy
+++ b/buildSrc/src/main/groovy/org/apache/geode/gradle/TestPropertiesWriter.groovy
@@ -18,8 +18,6 @@
 
 package org.apache.geode.gradle;
 
-import org.apache.mina.util.AvailablePortFinder;
-
 public class TestPropertiesWriter {
   public static void writeTestProperties(File parent, String name) {
     Properties props = new Properties();
diff --git a/buildSrc/src/main/java/org/apache/geode/javac/EnsureCorrectRunsWithProcessor.java b/buildSrc/src/main/java/org/apache/geode/javac/EnsureCorrectRunsWithProcessor.java
new file mode 100644
index 0000000..e4afa4c
--- /dev/null
+++ b/buildSrc/src/main/java/org/apache/geode/javac/EnsureCorrectRunsWithProcessor.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.javac;
+
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.MirroredTypeException;
+import javax.tools.Diagnostic;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactory;
+
+@SupportedAnnotationTypes("org.junit.runner.RunWith")
+@SupportedSourceVersion(SourceVersion.RELEASE_8)
+public class EnsureCorrectRunsWithProcessor extends AbstractProcessor {
+
+  private static final String RUNWITH = RunWith.class.getCanonicalName();
+
+  private Messager messager;
+
+  @Override
+  public synchronized void init(ProcessingEnvironment env) {
+    super.init(env);
+    messager = env.getMessager();
+  }
+
+  @Override
+  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+    boolean hasErrors = false;
+
+    for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(RunWith.class)) {
+      if (annotatedElement.getKind() != ElementKind.CLASS) {
+        continue;
+      }
+
+      TypeElement typeElement = (TypeElement) annotatedElement;
+
+      boolean hasUseParameterizedRunnerFactory = false;
+      boolean hasRunWithParameterized = false;
+
+      for (AnnotationMirror am : typeElement.getAnnotationMirrors()) {
+        String clazz = am.getAnnotationType().toString();
+        if (clazz.equals(RunWith.class.getCanonicalName())) {
+          hasRunWithParameterized = isRunWithParameterized(typeElement);
+        }
+
+        if (clazz.equals(Parameterized.UseParametersRunnerFactory.class.getCanonicalName())) {
+          hasUseParameterizedRunnerFactory = isUseParameterizedRunnerFactory(typeElement);
+        }
+      }
+
+      if (hasRunWithParameterized && !hasUseParameterizedRunnerFactory) {
+        error(typeElement, "class is annotated with @RunWith(Parameterized.class) but is missing the annotation @Parameterized.UseParametersRunnerFactory(CategoryWithParameterizedRunnerFactory.class)");
+        hasErrors = true;
+      }
+    }
+
+    return hasErrors;
+  }
+
+  private boolean isRunWithParameterized(TypeElement typeElement) {
+    RunWith runWith = typeElement.getAnnotation(RunWith.class);
+
+    String runWithValue;
+    try {
+      runWithValue = runWith.value().getSimpleName();
+    } catch (MirroredTypeException mex) {
+      DeclaredType classTypeMirror = (DeclaredType) mex.getTypeMirror();
+      TypeElement classTypeElement = (TypeElement) classTypeMirror.asElement();
+      runWithValue = classTypeElement.getSimpleName().toString();
+    }
+
+    if (runWithValue == null) {
+      return false;
+    }
+
+    return runWithValue.equals(Parameterized.class.getCanonicalName())
+        || runWithValue.equals(Parameterized.class.getSimpleName());
+  }
+
+  private boolean isUseParameterizedRunnerFactory(TypeElement typeElement) {
+    Parameterized.UseParametersRunnerFactory
+        runnerFactory = typeElement.getAnnotation(Parameterized.UseParametersRunnerFactory.class);
+
+    String runnerFactoryValue;
+    try {
+      runnerFactoryValue = runnerFactory.value().getSimpleName();
+    } catch (MirroredTypeException mex) {
+      DeclaredType classTypeMirror = (DeclaredType) mex.getTypeMirror();
+      TypeElement classTypeElement = (TypeElement) classTypeMirror.asElement();
+      runnerFactoryValue = classTypeElement.getSimpleName().toString();
+    }
+
+    if (runnerFactoryValue == null) {
+      return false;
+    }
+
+    return runnerFactoryValue.equals(CategoryWithParameterizedRunnerFactory.class.getCanonicalName())
+        || runnerFactoryValue.equals(CategoryWithParameterizedRunnerFactory.class.getSimpleName());
+  }
+
+  private void error(Element e, String msg, Object... args) {
+    messager.printMessage(
+        Diagnostic.Kind.ERROR,
+        String.format(msg, args),
+        e);
+  }
+}
diff --git a/buildSrc/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/buildSrc/src/main/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000..f1390a0
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+org.apache.geode.javac.EnsureCorrectRunsWithProcessor
\ No newline at end of file
diff --git a/buildSrc/src/test/java/org/apache/geode/javac/SimpleClassFile.java b/buildSrc/src/test/java/org/apache/geode/javac/SimpleClassFile.java
new file mode 100644
index 0000000..63ca323
--- /dev/null
+++ b/buildSrc/src/test/java/org/apache/geode/javac/SimpleClassFile.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.javac;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+
+import javax.tools.SimpleJavaFileObject;
+
+public class SimpleClassFile extends SimpleJavaFileObject {
+
+  private ByteArrayOutputStream out;
+
+  public SimpleClassFile(URI uri) {
+    super(uri, Kind.CLASS);
+  }
+
+  @Override
+  public OutputStream openOutputStream() throws IOException {
+    return out = new ByteArrayOutputStream();
+  }
+
+  public byte[] getCompiledBinaries() {
+    return out.toByteArray();
+  }
+
+}
diff --git a/buildSrc/src/test/java/org/apache/geode/javac/SimpleFileManager.java b/buildSrc/src/test/java/org/apache/geode/javac/SimpleFileManager.java
new file mode 100644
index 0000000..e65f287
--- /dev/null
+++ b/buildSrc/src/test/java/org/apache/geode/javac/SimpleFileManager.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.javac;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.tools.FileObject;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+
+public class SimpleFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
+
+  private List<SimpleClassFile> compiled = new ArrayList<>();
+
+  public SimpleFileManager(StandardJavaFileManager fileManager) {
+    super(fileManager);
+  }
+
+  @Override
+  public JavaFileObject getJavaFileForOutput(Location location, String className,
+      JavaFileObject.Kind kind, FileObject sibling) {
+    SimpleClassFile result = new SimpleClassFile(URI.create("string://" + className));
+    compiled.add(result);
+    return result;
+  }
+
+  public List<SimpleClassFile> getCompiled() {
+    return compiled;
+  }
+}
diff --git a/buildSrc/src/test/java/org/apache/geode/javac/SimpleSourceFile.java b/buildSrc/src/test/java/org/apache/geode/javac/SimpleSourceFile.java
new file mode 100644
index 0000000..5a4090f
--- /dev/null
+++ b/buildSrc/src/test/java/org/apache/geode/javac/SimpleSourceFile.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.javac;
+
+import java.net.URI;
+
+import javax.tools.SimpleJavaFileObject;
+
+public class SimpleSourceFile extends SimpleJavaFileObject {
+  private String content;
+
+  public SimpleSourceFile(String qualifiedClassName, String testSource) {
+    super(URI.create(String.format("file://%s%s", qualifiedClassName.replaceAll("\\.", "/"),
+        Kind.SOURCE.extension)), Kind.SOURCE);
+    content = testSource;
+  }
+
+  @Override
+  public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+    return content;
+  }
+}
diff --git a/buildSrc/src/test/java/org/apache/geode/javac/TestAnnotationProcessor.java b/buildSrc/src/test/java/org/apache/geode/javac/TestAnnotationProcessor.java
new file mode 100644
index 0000000..0b88d05
--- /dev/null
+++ b/buildSrc/src/test/java/org/apache/geode/javac/TestAnnotationProcessor.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.javac;
+
+import org.junit.Test;
+import sun.tools.java.CompilerError;
+
+public class TestAnnotationProcessor {
+  private static final String VALID_CLASS_TEMPLATE = "package org.apache.geode;\n"
+      + "import org.junit.runner.RunWith;\n" + "import org.junit.runners.Parameterized;\n"
+      + "import org.junit.experimental.categories.Category;\n"
+      + "import org.apache.geode.test.junit.categories.UnitTest;\n"
+      + "import org.apache.geode.test.junit.runners.CategoryWithParameterizedRunnerFactory;\n"
+      + "@Category(UnitTest.class)\n"
+      + "@Parameterized.UseParametersRunnerFactory(CategoryWithParameterizedRunnerFactory.class)\n"
+      + "@RunWith(Parameterized.class)\n" + "public class Test {\n" + "}\n";
+
+  private static final String INVALID_CLASS_TEMPLATE = "package org.apache.geode;\n"
+      + "import org.junit.runner.RunWith;\n" + "import org.junit.runners.Parameterized;\n"
+      + "import org.junit.experimental.categories.Category;\n"
+      + "import org.apache.geode.test.junit.categories.UnitTest;\n"
+      + "@Category(UnitTest.class)\n"
+      + "@RunWith(Parameterized.class)\n" + "public class Test {\n" + "}\n";
+
+  private TestCompiler compiler = new TestCompiler();
+
+  @Test
+  public void checkValidAnnotations() {
+    String qualifiedClassName = "org.apache.geode.Test";
+    compiler.compile(qualifiedClassName, VALID_CLASS_TEMPLATE);
+  }
+
+  @Test (expected = CompilerError.class)
+  public void checkInvalidAnnotations() {
+    String qualifiedClassName = "org.apache.geode.Test";
+    compiler.compile(qualifiedClassName, INVALID_CLASS_TEMPLATE);
+  }
+}
diff --git a/buildSrc/src/test/java/org/apache/geode/javac/TestCompiler.java b/buildSrc/src/test/java/org/apache/geode/javac/TestCompiler.java
new file mode 100644
index 0000000..2b8d78a
--- /dev/null
+++ b/buildSrc/src/test/java/org/apache/geode/javac/TestCompiler.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.javac;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.tools.JavaCompiler;
+import javax.tools.ToolProvider;
+
+import sun.tools.java.CompilerError;
+
+public class TestCompiler {
+  public byte[] compile(String qualifiedClassName, String testSource) {
+
+    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+    SimpleFileManager fileManager =
+        new SimpleFileManager(compiler.getStandardFileManager(null, null, null));
+
+    List<SimpleSourceFile> compilationUnits =
+        Collections.singletonList(new SimpleSourceFile(qualifiedClassName, testSource));
+
+    List<String> arguments = new ArrayList<>();
+    arguments.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path"),
+        "-processor", EnsureCorrectRunsWithProcessor.class.getName()));
+
+    JavaCompiler.CompilationTask task =
+        compiler.getTask(null, fileManager, null, arguments, null, compilationUnits);
+
+    if (!task.call()) {
+      throw new CompilerError("Compilation errors");
+    }
+
+    return fileManager.getCompiled().iterator().next().getCompiledBinaries();
+  }
+}
diff --git a/gradle/test.gradle b/gradle/test.gradle
index 762a2aa..95f3d89 100644
--- a/gradle/test.gradle
+++ b/gradle/test.gradle
@@ -56,7 +56,27 @@ subprojects {
 
     testRuntime 'cglib:cglib:' + project.'cglib.version'
   }
-  
+
+  configurations {
+    apt
+  }
+
+  dependencies {
+    apt files("${rootProject.projectDir}/buildSrc/build/libs/buildSrc.jar")
+    apt(group: 'junit', name: 'junit', version: project.'junit.version') {
+      transitive = false
+    }
+    // Because EnsureCorrectRunsWithProcessor needs access to
+    // CategoryWithParameterizedRunnerFactory. The specific version of geode-junit is not important.
+    apt(group: 'org.apache.geode', name: 'geode-junit', version: '1.3.0') {
+      transitive = false
+    }
+  }
+
+  compileTestJava {
+    options.annotationProcessorPath = files(configurations['apt'])
+  }
+
   //This target does not run any tests. Rather, it validates that there are no
   //tests that are missing a category annotation
   task checkMissedTests(type: Test) {

-- 
To stop receiving notification emails like this one, please contact
jensdeppe@apache.org.