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

onichols pushed a commit to branch support/1.12
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/support/1.12 by this push:
     new 9b2aea9  GEODE-8603: Potentially expand classes identified for CI 
stressing to include subclasses (#5601) (#5674)
9b2aea9 is described below

commit 9b2aea942d162f6ee43e3a7bcf8e654d5fbb9d3d
Author: Jens Deppe <jde...@pivotal.io>
AuthorDate: Tue Oct 27 06:04:00 2020 -0700

    GEODE-8603: Potentially expand classes identified for CI stressing to 
include subclasses (#5601) (#5674)
    
    - Make StressNewTestHelper create the complete gradle test task commands
    - Since some tests may have subclasses in different source sets, (which
      would require a different repeat task name), it's easier for the
      command generation to all happen in the java helper rather than a
      combination of bash and java.
    - Include candidate test class if it is not abstract
    - Output a fake Gradle param so that scripts can determine the number of
      tests included.
    - Change the CI stress job timeout from 6 to 10 hours.
    - Increase the test count threshold from 25 to 35 changed tests. This
      number also includes any tests inferred by this new code.
    
    (cherry picked from commit 4039a363a4b057bca322f29dcf33aa0664f1a912)
---
 build.gradle                                       |  29 +++
 ci/pipelines/shared/jinja.variables.yml            |   2 +-
 ci/scripts/repeat-new-tests.sh                     |  54 +++---
 geode-junit/build.gradle                           |   1 +
 .../geode/test/util/StressNewTestHelper.java       | 197 +++++++++++++++++++++
 .../geode/test/util/WhatExtendsJUnitTest.java      |  99 +++++++++++
 geode-junit/src/test/resources/expected-pom.xml    |   5 +
 7 files changed, 358 insertions(+), 29 deletions(-)

diff --git a/build.gradle b/build.gradle
index efbc2c9..67fdec4 100755
--- a/build.gradle
+++ b/build.gradle
@@ -140,6 +140,35 @@ tasks.register('generate') {
   // `afterEvaluate.rootProject.generate.dependsOn(generateProto)`
 }
 
+tasks.register('printTestClasspath') {
+  group 'Build'
+  description "Print the classpath used in all tests for all subprojects"
+
+  doLast {
+    Set result = new LinkedHashSet()
+    // Prefer sources at the start of the classpath
+    subprojects.each { sub ->
+      if (sub.hasProperty("sourceSets")) {
+        sub.sourceSets.each { ss ->
+          ss.each { x ->
+            x.output.classesDirs.each { y -> result.add(y) }
+          }
+        }
+      }
+    }
+
+    subprojects.each { sub ->
+      sub.configurations.each { c ->
+        if (c.name.toLowerCase().endsWith("runtimeclasspath")) {
+          c.each { f -> result.add(f) }
+        }
+      }
+    }
+
+    println result.join(File.pathSeparator)
+  }
+}
+
 // Prompt the user for a publication passsword to sign archives or upload 
artifacts, if requested
 if (project.hasProperty('askpass')) {
   gradle.taskGraph.whenReady { taskGraph ->
diff --git a/ci/pipelines/shared/jinja.variables.yml 
b/ci/pipelines/shared/jinja.variables.yml
index 77ce929..32585b2 100644
--- a/ci/pipelines/shared/jinja.variables.yml
+++ b/ci/pipelines/shared/jinja.variables.yml
@@ -143,7 +143,7 @@ tests:
   CALL_STACK_TIMEOUT: '20700'
   CPUS: '96'
   DUNIT_PARALLEL_FORKS: '24'
-  EXECUTE_TEST_TIMEOUT: 6h
+  EXECUTE_TEST_TIMEOUT: 10h
   GRADLE_TASK: repeatTest
   PARALLEL_DUNIT: 'true'
   PARALLEL_GRADLE: 'false'
diff --git a/ci/scripts/repeat-new-tests.sh b/ci/scripts/repeat-new-tests.sh
index 4a54062..f744523 100755
--- a/ci/scripts/repeat-new-tests.sh
+++ b/ci/scripts/repeat-new-tests.sh
@@ -42,6 +42,19 @@ function changes_for_path() {
   popd >> /dev/null
 }
 
+function save_classpath() {
+  echo "Building and saving classpath"
+  pushd geode >> /dev/null
+    # Do this twice since devBuild still dumps a warning string to stdout.
+    ./gradlew --console=plain -q devBuild 2>/dev/null
+    ./gradlew --console=plain -q printTestClasspath 2>/dev/null 
>/tmp/classpath.txt
+  popd >> /dev/null
+}
+
+function create_gradle_test_targets() {
+  echo $(${JAVA_HOME}/bin/java -cp $(cat /tmp/classpath.txt) 
org.apache.geode.test.util.StressNewTestHelper $@)
+}
+
 UNIT_TEST_CHANGES=$(changes_for_path '*/src/test/java') || exit $?
 INTEGRATION_TEST_CHANGES=$(changes_for_path '*/src/integrationTest/java') || 
exit $?
 DISTRIBUTED_TEST_CHANGES=$(changes_for_path '*/src/distributedTest/java') || 
exit $?
@@ -51,7 +64,7 @@ UPGRADE_TEST_CHANGES=$(changes_for_path 
'*/src/upgradeTest/java') || exit $?
 CHANGED_FILES_ARRAY=( $UNIT_TEST_CHANGES $INTEGRATION_TEST_CHANGES 
$DISTRIBUTED_TEST_CHANGES $ACCEPTANCE_TEST_CHANGES $UPGRADE_TEST_CHANGES )
 NUM_CHANGED_FILES=${#CHANGED_FILES_ARRAY[@]}
 
-echo "${NUM_CHANGED_FILES} changed tests"
+echo "${NUM_CHANGED_FILES} changed test files"
 
 if [[  "${NUM_CHANGED_FILES}" -eq 0 ]]
 then
@@ -59,36 +72,21 @@ then
   exit 0
 fi
 
-if [[ "${NUM_CHANGED_FILES}" -gt 25 ]]
-then
-  echo "${NUM_CHANGED_FILES} is too many changed tests to stress test. 
Allowing this job to pass without stress testing."
-  exit 0
-fi
+save_classpath
 
-TEST_TARGETS=""
-
-function append_to_test_targets() {
-  local target="$1"
-  local files="$2"
-  if [[ -n "$files" ]]
-  then
-    TEST_TARGETS="$TEST_TARGETS $target"
-    for FILENAME in $files
-    do
-      SHORT_NAME=$(basename $FILENAME)
-      SHORT_NAME="${SHORT_NAME%.java}"
-      TEST_TARGETS="$TEST_TARGETS --tests $SHORT_NAME"
-    done
-  fi
-}
+TEST_TARGETS=$(create_gradle_test_targets ${CHANGED_FILES_ARRAY[@]})
+TEST_COUNT=$(echo ${TEST_TARGETS} | sed -e 's/.*testCount=\([0-9]*\).*/\1/g')
 
-append_to_test_targets "repeatUnitTest" "$UNIT_TEST_CHANGES"
-append_to_test_targets "repeatIntegrationTest" "$INTEGRATION_TEST_CHANGES"
-append_to_test_targets "repeatDistributedTest" "$DISTRIBUTED_TEST_CHANGES"
-append_to_test_targets "repeatUpgradeTest" "$UPGRADE_TEST_CHANGES"
+if [[ "${NUM_CHANGED_FILES}" -ne "${TEST_COUNT}" ]]
+then
+  echo "Changed test files increased to ${TEST_COUNT} after including 
subclasses"
+fi
 
-# Acceptance tests cannot currently run in parallel, so do not stress these 
tests
-#append_to_test_targets "repeatAcceptanceTest" "$ACCEPTANCE_TEST_CHANGES"
+if [[ "${TEST_COUNT}" -gt 35 ]]
+then
+  echo "${TEST_COUNT} is too many changed tests to stress test. Allowing this 
job to pass without stress testing."
+  exit 0
+fi
 
 export GRADLE_TASK="compileTestJava compileIntegrationTestJava 
compileDistributedTestJava $TEST_TARGETS"
 export GRADLE_TASK_OPTIONS="-Prepeat=50 -PfailOnNoMatchingTests=false"
diff --git a/geode-junit/build.gradle b/geode-junit/build.gradle
index 5c83d2d..8f55211 100755
--- a/geode-junit/build.gradle
+++ b/geode-junit/build.gradle
@@ -29,6 +29,7 @@ dependencies {
   compileOnly(project(':geode-unsafe'))
   compileOnly(project(':geode-gfsh'))
 
+  api('io.github.classgraph:classgraph')
   compile('com.fasterxml.jackson.core:jackson-annotations')
   compile('com.fasterxml.jackson.core:jackson-databind')
   compile('com.github.stefanbirkner:system-rules') {
diff --git 
a/geode-junit/src/main/java/org/apache/geode/test/util/StressNewTestHelper.java 
b/geode-junit/src/main/java/org/apache/geode/test/util/StressNewTestHelper.java
new file mode 100644
index 0000000..6806f01
--- /dev/null
+++ 
b/geode-junit/src/main/java/org/apache/geode/test/util/StressNewTestHelper.java
@@ -0,0 +1,197 @@
+/*
+ * 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.test.util;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import io.github.classgraph.ClassGraph;
+import io.github.classgraph.ClassInfo;
+import io.github.classgraph.ClassInfoList;
+import io.github.classgraph.ScanResult;
+
+/**
+ * This class is intended as a helper to the CI StressNewTest job. Given a 
list of changed test java
+ * files, it expands the list to include any subclasses and outputs a partial 
Gradle command line to
+ * execute those tests depending on the 'category' of test (unit, distributed, 
etc.).
+ */
+public class StressNewTestHelper {
+
+  private ScanResult scanResult;
+  private String packageToScan;
+
+  // Mapping of source set to list of tests
+  private Map<String, Set<String>> sourceToTestMapping = new HashMap<>();
+
+  private static final Pattern categoryPattern = 
Pattern.compile(".*/src/(.*?)/java/.*");
+  private static final Pattern intellijCategoryPattern =
+      Pattern.compile(".*/out/test/.*\\.(.*?)/.*");
+  private static final Pattern gradleCategoryPattern =
+      Pattern.compile(".*/build/classes/java/(.*?)/.*");
+
+  private static final Map<String, String> sourceToGradleMapping = new 
HashMap<>();
+
+  static {
+    sourceToGradleMapping.put("test", "repeatUnitTest");
+    sourceToGradleMapping.put("integrationTest", "repeatIntegrationTest");
+    sourceToGradleMapping.put("distributedTest", "repeatDistributedTest");
+    sourceToGradleMapping.put("upgradeTest", "repeatUpgradeTest");
+    // Cannot currently be run repeatedly because of docker issues
+    // sourceToGradleMapping.put("acceptanceTest", "repeatAcceptanceTest");
+  }
+
+  private static class TestClassInfo {
+    final String originalFilename;
+    final String category;
+    final String className;
+    final String simpleClassName;
+
+    TestClassInfo(String originalFilename, String category, String className,
+        String simpleClassName) {
+      this.originalFilename = originalFilename;
+      this.category = category;
+      this.className = className;
+      this.simpleClassName = simpleClassName;
+    }
+  }
+
+  public StressNewTestHelper(String packageToScan) {
+    this.packageToScan = packageToScan;
+    scanResult = new ClassGraph().whitelistPackages(packageToScan)
+        .enableClassInfo()
+        .enableAnnotationInfo().scan();
+  }
+
+  public String buildGradleCommand() {
+    StringBuilder command = new StringBuilder();
+
+    int testCount = 0;
+    for (Map.Entry<String, Set<String>> entry : 
sourceToTestMapping.entrySet()) {
+      String sourceSet = entry.getKey();
+      if (sourceToGradleMapping.get(sourceSet) == null) {
+        System.err.println("Skipping repeat test for " + sourceSet);
+        continue;
+      }
+
+      command.append(sourceToGradleMapping.get(sourceSet));
+      command.append(" --tests ");
+      command.append(String.join(",", entry.getValue()));
+      command.append(" ");
+      testCount += entry.getValue().size();
+    }
+
+    // This exists so that scripts processing this output can extract the 
number of tests
+    // included here. Yes, it's pretty hacky...
+    command.append("-PtestCount=" + testCount);
+
+    return command.toString();
+  }
+
+  public void add(String javaFile) {
+    TestClassInfo testClassInfo = createTestClassInfo(javaFile);
+    List<TestClassInfo> extenders = whatExtends(testClassInfo);
+
+    if (!scanResult.getClassInfo(testClassInfo.className).isAbstract()) {
+      extenders.add(testClassInfo);
+    }
+
+    if (extenders.isEmpty()) {
+      addTestToCategory(testClassInfo.category, testClassInfo.simpleClassName);
+      return;
+    }
+
+    extenders.forEach(e -> addTestToCategory(e.category, e.simpleClassName));
+  }
+
+  private void addTestToCategory(String category, String testClass) {
+    Set<String> listOfTests = sourceToTestMapping.computeIfAbsent(category, k 
-> new TreeSet<>());
+    listOfTests.add(testClass);
+  }
+
+  private List<TestClassInfo> whatExtends(TestClassInfo testClass) {
+    List<TestClassInfo> results = new ArrayList<>();
+    ClassInfoList subClasses = scanResult.getSubclasses(testClass.className);
+
+    for (ClassInfo classInfo : subClasses) {
+      String classFilename = classInfo.getClasspathElementURL().getFile();
+      results.add(
+          new TestClassInfo(classFilename, getCategory(classFilename), 
classInfo.getName(),
+              classInfo.getSimpleName()));
+    }
+
+    return results;
+  }
+
+  private TestClassInfo createTestClassInfo(String javaFile) {
+    String category = getCategory(javaFile);
+    String sanitized = javaFile.replace("/", ".");
+
+    int packageStart = sanitized.indexOf(packageToScan);
+    if (packageStart >= 0) {
+      sanitized = sanitized.substring(packageStart);
+    }
+
+    if (sanitized.endsWith(".java")) {
+      int javaIdx = sanitized.indexOf(".java");
+      sanitized = sanitized.substring(0, javaIdx);
+    }
+
+    int classIndex = sanitized.lastIndexOf(".");
+
+    return new TestClassInfo(javaFile, category, sanitized, 
sanitized.substring(classIndex + 1));
+  }
+
+  private String getCategory(String javaFile) {
+    Matcher matcher = categoryPattern.matcher(javaFile);
+
+    // Maybe we're running tests in Intellij
+    if (!matcher.matches()) {
+      matcher = intellijCategoryPattern.matcher(javaFile);
+    }
+
+    // Maybe we're running tests in Gradle
+    if (!matcher.matches()) {
+      matcher = gradleCategoryPattern.matcher(javaFile);
+    }
+
+    if (!matcher.matches()) {
+      throw new IllegalArgumentException("Unable to determine category for " + 
javaFile);
+    }
+
+    return matcher.group(1);
+  }
+
+  public static void main(String[] args) {
+    StressNewTestHelper helper = new StressNewTestHelper("org.apache.geode");
+
+    for (String arg : args) {
+      try {
+        helper.add(arg);
+      } catch (Exception e) {
+        System.err.println("ERROR: Unable to process " + arg + " : " + 
e.getMessage());
+      }
+    }
+
+    System.out.println(helper.buildGradleCommand());
+  }
+
+}
diff --git 
a/geode-junit/src/test/java/org/apache/geode/test/util/WhatExtendsJUnitTest.java
 
b/geode-junit/src/test/java/org/apache/geode/test/util/WhatExtendsJUnitTest.java
new file mode 100644
index 0000000..ea59a03
--- /dev/null
+++ 
b/geode-junit/src/test/java/org/apache/geode/test/util/WhatExtendsJUnitTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.test.util;
+
+import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class WhatExtendsJUnitTest {
+
+  private StressNewTestHelper scanner;
+
+  public abstract static class A {
+  }
+
+  public static class B extends A {
+  }
+
+  public static class C extends B {
+  }
+
+  @Before
+  public void setup() {
+    scanner = new StressNewTestHelper("org.apache.geode");
+  }
+
+  @Test
+  public void nothingExtendsC() {
+    scanner.add(getClassLocation(C.class));
+    assertThat(scanner.buildGradleCommand())
+        .isEqualTo("repeatUnitTest --tests WhatExtendsJUnitTest$C 
-PtestCount=1");
+  }
+
+  @Test
+  public void classAisExtendedByBandC() {
+    scanner.add(getClassLocation(A.class));
+    assertThat(scanner.buildGradleCommand()).isEqualTo(
+        "repeatUnitTest --tests WhatExtendsJUnitTest$B,WhatExtendsJUnitTest$C 
-PtestCount=2");
+  }
+
+  @Test
+  public void classAisExtendedByBandC_withDuplicatesRemoved() {
+    scanner.add(getClassLocation(A.class));
+    scanner.add(getClassLocation(A.class));
+    assertThat(scanner.buildGradleCommand())
+        .isEqualTo(
+            "repeatUnitTest --tests 
WhatExtendsJUnitTest$B,WhatExtendsJUnitTest$C -PtestCount=2");
+  }
+
+  @Test
+  public void usingJavaFileWithSameCategoryAsSubClasses() {
+    scanner.add(getClassLocation(A.class, "foo/src/test/java/"));
+    assertThat(scanner.buildGradleCommand())
+        .isEqualTo(
+            "repeatUnitTest --tests 
WhatExtendsJUnitTest$B,WhatExtendsJUnitTest$C -PtestCount=2");
+  }
+
+  @Test
+  public void usingJavaFileWithDifferentCategoryAsSubClasses() {
+    scanner.add(getClassLocation(A.class, "foo/src/integrationTest/java/"));
+    scanner.add(getClassLocation(this.getClass(), 
"foo/src/integrationTest/java/"));
+    assertThat(scanner.buildGradleCommand())
+        .isEqualTo(
+            "repeatUnitTest --tests 
WhatExtendsJUnitTest$B,WhatExtendsJUnitTest$C repeatIntegrationTest --tests 
WhatExtendsJUnitTest -PtestCount=3");
+  }
+
+  @Test
+  public void ignoreAcceptanceTestSourcesForNow() {
+    scanner.add(getClassLocation(this.getClass(), 
"foo/src/acceptanceTest/java/"));
+    assertThat(scanner.buildGradleCommand()).isEqualTo("-PtestCount=0");
+  }
+
+  private String getClassLocation(Class<?> clazz) {
+    String codeSource = 
clazz.getProtectionDomain().getCodeSource().getLocation().getFile();
+    String classFile = clazz.getName().replace(".", "/");
+
+    return codeSource + classFile;
+  }
+
+  private String getClassLocation(Class<?> clazz, String fakePrefix) {
+    String classFile = clazz.getName().replace(".", "/");
+
+    return fakePrefix + classFile;
+  }
+}
diff --git a/geode-junit/src/test/resources/expected-pom.xml 
b/geode-junit/src/test/resources/expected-pom.xml
index ca38f34..b90a0de 100644
--- a/geode-junit/src/test/resources/expected-pom.xml
+++ b/geode-junit/src/test/resources/expected-pom.xml
@@ -47,6 +47,11 @@
   </dependencyManagement>
   <dependencies>
     <dependency>
+      <groupId>io.github.classgraph</groupId>
+      <artifactId>classgraph</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-annotations</artifactId>
       <scope>compile</scope>

Reply via email to