diff --git a/.travis.yml b/.travis.yml index 439fada..f8d0631 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,8 @@ matrix: dist: bionic before_install: -- git clone https://github.com/spideruci/primitive-hamcrest.git +- git clone https://github.com/spideruci/projects4testing.git +- git clone https://github.com/spideruci/primitive-hamcrest.git - cd primitive-hamcrest - mvn install - cd .. diff --git a/pom.xml b/pom.xml index e0c7714..2bb1fd5 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,8 @@ UTF-8 UTF-8 + 5.6.0 + 4.12 tacoco the per testcase junit runner @@ -147,9 +149,37 @@ org.apache.maven.shared maven-invoker - 3.0.1 + 3.0.1 - + + org.junit.platform + junit-platform-launcher + 1.6.0 + + + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + runtime + + + org.junit.vintage + junit-vintage-engine + ${junit.jupiter.version} + runtime + + + com.github.testng-team + testng-junit5 + 0.0.1 + runtime + + @@ -200,6 +230,22 @@ + + org.codehaus.mojo + exec-maven-plugin + 1.4.0 + + + integration-test + + exec + + + ${basedir}/tacoco-diagnoses + + + + diff --git a/src/main/java/org/spideruci/tacoco/testlisteners/UnifiedTestListenerAdapter.java b/src/main/java/org/spideruci/tacoco/testlisteners/UnifiedTestListenerAdapter.java new file mode 100644 index 0000000..8f9aa8d --- /dev/null +++ b/src/main/java/org/spideruci/tacoco/testlisteners/UnifiedTestListenerAdapter.java @@ -0,0 +1,58 @@ +package org.spideruci.tacoco.testlisteners; + +import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.engine.TestExecutionResult.Status; +import org.junit.platform.launcher.TestExecutionListener; +import org.junit.platform.launcher.TestIdentifier; +import org.junit.platform.launcher.TestPlan; + +public class UnifiedTestListenerAdapter implements TestExecutionListener { + + private final ITacocoTestListener listener; + public UnifiedTestListenerAdapter(final ITacocoTestListener listener) { + this.listener = listener; + } + + private String getUniqueTestName(TestIdentifier testIdentifier) { + final String testUid = testIdentifier.getUniqueId(); + final String testName = testIdentifier.getDisplayName(); + final String testUniqueName = String.format("%s.%s", testName, testUid); + return testUniqueName; + } + + @Override + public void executionStarted(final TestIdentifier testIdentifier) { + final String testUniqueName = getUniqueTestName(testIdentifier); + listener.onTestStart(testUniqueName); + } + + @Override + public void executionSkipped(final TestIdentifier testIdentifier, final String reason) { + listener.onTestSkipped(); + } + + @Override + public void executionFinished(final TestIdentifier testIdentifier, final TestExecutionResult testExecutionResult) { + final Status status = testExecutionResult.getStatus(); + + switch (status) { + case SUCCESSFUL: + listener.onTestPassed(); + break; + case FAILED: + case ABORTED: + listener.onTestFailed(); + break; + } + + listener.onTestEnd(); + } + + public void testPlanExecutionStarted(final TestPlan testPlan) { + listener.onStart(); + } + + public void testPlanExecutionFinished(final TestPlan testPlan) { + listener.onEnd(); + } +} \ No newline at end of file diff --git a/src/main/java/org/spideruci/tacoco/testrunners/AbstractTestRunner.java b/src/main/java/org/spideruci/tacoco/testrunners/AbstractTestRunner.java index d4814d9..d3f850e 100644 --- a/src/main/java/org/spideruci/tacoco/testrunners/AbstractTestRunner.java +++ b/src/main/java/org/spideruci/tacoco/testrunners/AbstractTestRunner.java @@ -2,11 +2,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.Callable; import org.spideruci.tacoco.analysis.AnalysisResults; @@ -17,7 +12,7 @@ public abstract class AbstractTestRunner { - public static enum TestType {JUNIT, TESTNG, UNKNOWN}; + public static enum TestType {JUNIT, TESTNG, UNIFIED, UNKNOWN}; public static boolean LOGGING = false; @@ -31,39 +26,44 @@ public static enum TestType {JUNIT, TESTNG, UNKNOWN}; public abstract Callable getExecutableTest(Class test); public abstract void printTestRunSummary(AnalysisResults results); - public static AbstractTestRunner getInstance(AbstractBuildProbe probe) { - for(String test : probe.getTestClasses()){ + public static AbstractTestRunner getInstance(final AbstractBuildProbe probe) { + for(final String test : probe.getTestClasses()){ try { switch(getTestType(Class.forName(test))){ case JUNIT: return new JUnitRunner(); case TESTNG: return new TestNGRunner(); + case UNIFIED: + return new UnifiedTestRunner(); case UNKNOWN: continue; } - } catch (ClassNotFoundException e) { + } catch (final ClassNotFoundException e) { e.printStackTrace(); } } return null; } - private static TestType getTestType(Class test){ + private static TestType getTestType(final Class test){ - if(test == null) { + if(test == null || Modifier.isAbstract(test.getModifiers())) { return TestType.UNKNOWN; } - - if(Modifier.isAbstract(test.getModifiers())) { - return TestType.UNKNOWN; + + if (UnifiedTestRunner.containsExecutableTest(test)) { + // We are going to give the UnifiedTestRunner first dibs. + // If it is able to find any executable test, then we run with it + // else, fall back on the individual test types + return TestType.UNIFIED; // UnifiedTestRunner } if(junit.framework.TestCase.class.isAssignableFrom(test)) { return TestType.JUNIT; //JUnit3 } - for(Method testMethod : test.getMethods()) { + for(final Method testMethod : test.getMethods()) { if(testMethod.getAnnotation(org.junit.Test.class) != null){ return TestType.JUNIT; //JUnit4 } diff --git a/src/main/java/org/spideruci/tacoco/testrunners/UnifiedTestRunner.java b/src/main/java/org/spideruci/tacoco/testrunners/UnifiedTestRunner.java new file mode 100644 index 0000000..0e2b59e --- /dev/null +++ b/src/main/java/org/spideruci/tacoco/testrunners/UnifiedTestRunner.java @@ -0,0 +1,153 @@ +package org.spideruci.tacoco.testrunners; + +import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; + +import java.util.concurrent.Callable; + +import org.junit.platform.launcher.Launcher; +import org.junit.platform.launcher.LauncherDiscoveryRequest; +import org.junit.platform.launcher.TestPlan; +import org.junit.platform.launcher.core.LauncherFactory; +import org.junit.platform.launcher.listeners.SummaryGeneratingListener; +import org.junit.platform.launcher.listeners.TestExecutionSummary; +import org.junit.platform.launcher.listeners.TestExecutionSummary.Failure; +import org.spideruci.tacoco.analysis.AnalysisResults; +import org.spideruci.tacoco.testlisteners.ITacocoTestListener; +import org.spideruci.tacoco.testlisteners.UnifiedTestListenerAdapter; + +public class UnifiedTestRunner extends AbstractTestRunner { + + private final static String TEST_CLASS_NAME = "test-class-name"; + private final static String TEST_SUMMARY = "test-summary"; + + final Launcher launcher = LauncherFactory.create(); + + + public static boolean containsExecutableTest(final Class test, final boolean doSanityDryRun) { + try { + final LauncherDiscoveryRequest discoveryRequest = request().selectors(selectClass(test)).build(); + final Launcher launcher = LauncherFactory.create(); + final TestPlan testplan = launcher.discover(discoveryRequest); + final boolean containsTests = testplan.containsTests(); + + if (containsTests && doSanityDryRun) { + // the idea here is this: if this *silent* execute call + // throws any exception then we will return false in the catch + // block below. This is a silent execute call because it does + // not register any listeners at any point. + launcher.execute(testplan); + } + + return containsTests; + } catch (final Exception e) { + return false; + } + } + + public static boolean containsExecutableTest(final Class test) { + return containsExecutableTest(test, false); + } + + private LauncherDiscoveryRequest discoveryRequest(final Class test) { + try { + final LauncherDiscoveryRequest discoveryRequest = request().selectors(selectClass(test)).build(); + return discoveryRequest; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + public boolean shouldRun(final Class test) { + return UnifiedTestRunner.containsExecutableTest(test); + } + + @Override + public void listenThrough(final ITacocoTestListener listener) { + final UnifiedTestListenerAdapter testListenerAdapter = new UnifiedTestListenerAdapter(listener); + launcher.registerTestExecutionListeners(testListenerAdapter); + } + + @Override + public Callable getExecutableTest(final Class test) { + final LauncherDiscoveryRequest discoveryRequest = this.discoveryRequest(test); + final Launcher launcher = this.launcher; + final String testClassName = test.getName(); + + final Callable execTest = new Callable() { + + @Override + public AnalysisResults call() throws Exception { + try { + if (discoveryRequest == null) { + return null; + } + + final SummaryGeneratingListener sGeneratingListener = new SummaryGeneratingListener(); + + launcher.execute(discoveryRequest, sGeneratingListener); + final TestExecutionSummary summary = sGeneratingListener.getSummary(); + + final AnalysisResults results = new AnalysisResults(); + results.put(TEST_CLASS_NAME, testClassName); + results.put(TEST_SUMMARY, summary); + return results; + } catch(Exception e) { + e.printStackTrace(); + return null; + } + } + }; + + return execTest; + } + + @Override + public void printTestRunSummary(final AnalysisResults results) { + if (results == null || results.iterator() == null || !results.iterator().hasNext()) { + return; + } + + final TestExecutionSummary summary = results.get(TEST_SUMMARY); + if (summary == null) { + return; + } + + final String testName = results.get(TEST_CLASS_NAME); + if (testName == null) { + return; + } + + try { + this.testRunTime = (summary.getTimeFinished() - summary.getTimeStarted()) / 1000.0; + this.executedTestCount = (int) summary.getTestsStartedCount(); + this.failedTestCount = (int) (summary.getTestsFailedCount() + summary.getTestsAbortedCount()); + this.ignoredTestCount = (int) summary.getTestsSkippedCount(); + + System.out.println("Finishing " + testName + " Tests run: " + executedTestCount + " Failures: " + + failedTestCount + " Errors: " + summary.getTestsAbortedCount() + " Skipped: " + ignoredTestCount + + " Time elapsed: " + testRunTime + "sec"); + + if (this.failedTestCount != 0) { + System.out.println("---------------------Failures--------------------"); + for (final Failure f : summary.getFailures()) { + System.out.println("Test Name: " + f.getTestIdentifier().getDisplayName()); + System.out.println("Test Identifier: " + f.getTestIdentifier().getUniqueId()); + + System.out.println("Message: " + f.getException().getMessage()); + System.out.println("Description: " + f.getException().getCause()); + System.out.println("Trace: "); + f.getException().printStackTrace(); + } + } + } catch (Exception e) { + System.err.println("---------------------Tacoco Error--------------------"); + System.err.printf("Failed to parse Analysis Results for testName: %s\n", testName); + e.printStackTrace(); + } + + } + +} \ No newline at end of file diff --git a/tacoco-diagnoses b/tacoco-diagnoses new file mode 100755 index 0000000..df7ad12 --- /dev/null +++ b/tacoco-diagnoses @@ -0,0 +1,47 @@ +#!/bin/bash + +tacoco_home=$(pwd) +projects4testing=$tacoco_home/projects4testing + +ls -alh . +ls -alh $projects4testing/ + +cd $projects4testing/spiderMath_Gradle && gradle build +cd $projects4testing/spiderMath_JUnit4 && mvn clean compile test-compile -Dmaven.compiler.source=1.8 -Dmaven.compiler.target=1.8 +cd $projects4testing/spiderMath_TestNG && mvn clean compile test-compile -Dmaven.compiler.source=1.8 -Dmaven.compiler.target=1.8 + +cd $tacoco_home + +mvn exec:java -Plauncher -Dtacoco.sut=$projects4testing/spiderMath_Gradle -Dtacoco.home=$tacoco_home -Dtacoco.project=spiderMath_Gradle -Danalyzer.opts="configs/tacoco-analyzer.config" + +if [ $? -ne 0 ]; +then + echo "Tacoco failed with spiderMath_Gradle..." + exit 1 +fi + +mvn exec:java -Plauncher -Dtacoco.sut=$projects4testing/spiderMath_TestNG -Dtacoco.home=$tacoco_home -Dtacoco.project=spiderMath_TestNG -Danalyzer.opts="configs/tacoco-analyzer.config" + +if [ $? -ne 0 ]; +then + echo "Tacoco failed with spiderMath_TestNG..." + exit 1 +fi + +mvn exec:java -Plauncher -Dtacoco.sut=$projects4testing/spiderMath_JUnit4 -Dtacoco.home=$tacoco_home -Dtacoco.project=spiderMath_JUnit4 -Danalyzer.opts="configs/tacoco-analyzer.config" + +if [ $? -ne 0 ]; +then + echo "Tacoco failed with spiderMath_JUnit4..." + exit 1 +fi + +mvn exec:java -Plauncher -Dtacoco.sut=$projects4testing/spiderMath_JUnit4_single_module -Dtacoco.home=$tacoco_home -Dtacoco.project=spiderMath_JUnit4_single_module -Danalyzer.opts="configs/tacoco-analyzer.config" + +if [ $? -ne 0 ]; +then + echo "Tacoco failed with spiderMath_JUnit4_single_module..." + exit 1 +fi + +exit 0 \ No newline at end of file