Skip to content

Commit

Permalink
Concurrency fixes for junit-interface (#401)
Browse files Browse the repository at this point in the history
  • Loading branch information
jenshalm authored Aug 20, 2021
1 parent 61da011 commit 9cfd8b1
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 202 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.io.IOException;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.stream.Collectors;
import java.util.concurrent.atomic.AtomicBoolean;

import org.junit.runner.Description;
import org.junit.runner.Result;
Expand All @@ -23,16 +21,13 @@ final class EventDispatcher extends RunListener
{
private final RichLogger logger;
private final Set<Description> reported = Collections.newSetFromMap(new ConcurrentHashMap<Description, Boolean>());
private final Set<String> reportedSuites = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
private final AtomicBoolean suiteStartReported = new AtomicBoolean(false);
private final ConcurrentHashMap<String, Long> startTimes = new ConcurrentHashMap<String, Long>();
private final EventHandler handler;
private final RunSettings settings;
private final Fingerprint fingerprint;
private final String taskInfo;
private final RunStatistics runStatistics;
private OutputCapture capture;

private final static Description TEST_RUN = Description.createTestDescription("Test", "run");

EventDispatcher(RichLogger logger, EventHandler handler, RunSettings settings, Fingerprint fingerprint,
Description taskDescription, RunStatistics runStatistics)
Expand Down Expand Up @@ -71,11 +66,10 @@ private abstract class InfoEvent extends Event {
@Override
public void testAssumptionFailure(final Failure failure)
{
uncapture(true);
postIfFirst(failure.getDescription(), new ErrorEvent(failure, Status.Skipped) {
void logTo(RichLogger logger) {
if (settings.verbose) {
logger.info(failure.getDescription(), Ansi.c("==> i " + failure.getDescription().getMethodName(), WARNMSG));
logger.info(Ansi.c("==> i " + failure.getDescription().getMethodName(), WARNMSG));
}
}
});
Expand All @@ -96,10 +90,9 @@ public void testFailure(final Failure failure)
// Ignore error.
}
}
uncapture(true);
postIfFirst(failure.getDescription(), new ErrorEvent(failure, Status.Failure) {
void logTo(RichLogger logger) {
logger.error( failure.getDescription(), settings.buildTestResult(Status.Failure) +ansiName+" "+ durationSuffix() + " " + ansiMsg, error);
logger.error(settings.buildTestResult(Status.Failure) +ansiName+" "+ durationSuffix() + " " + ansiMsg, error);
}
});
}
Expand All @@ -108,21 +101,19 @@ void logTo(RichLogger logger) {
@Override
public void testFinished(Description desc)
{
uncapture(false);
postIfFirst(desc, new InfoEvent(desc, Status.Success) {
void logTo(RichLogger logger) {
logger.info(desc, settings.buildTestResult(Status.Success) + Ansi.c(desc.getMethodName(), SUCCESS1) + durationSuffix());
logger.info(settings.buildTestResult(Status.Success) + Ansi.c(desc.getMethodName(), SUCCESS1) + durationSuffix());
}
});
logger.popCurrentTestClassName();
}

@Override
public void testIgnored(Description desc)
{
postIfFirst(desc, new InfoEvent(desc, Status.Skipped) {
void logTo(RichLogger logger) {
logger.warn(desc, settings.buildTestResult(Status.Ignored) + ansiName+" ignored" + durationSuffix());
logger.warn(settings.buildTestResult(Status.Ignored) + ansiName+" ignored" + durationSuffix());
}
});
}
Expand All @@ -132,31 +123,26 @@ void logTo(RichLogger logger) {
public void testSuiteStarted(Description desc)
{
if (desc == null || desc.getClassName() == null || desc.getClassName().equals("null")) return;
reportedSuites.add(desc.getClassName());
logger.info(desc, c(desc.getClassName() + ":", SUCCESS1));
if (suiteStartReported.compareAndSet(false, true)) logger.info(c(desc.getClassName() + ":", SUCCESS1));
}


@Override
public void testStarted(Description desc)
{
recordStartTime(desc);
if (reportedSuites.add(desc.getClassName())) {
testSuiteStarted(desc);
}
logger.pushCurrentTestClassName(desc.getClassName());
testSuiteStarted(desc);
if (settings.verbose) {
logger.info(desc, settings.buildPlainName(desc) + " started");
logger.info(settings.buildPlainName(desc) + " started");
}
capture();
}

private void recordStartTime(Description description) {
startTimes.putIfAbsent(settings.buildPlainName(description), System.currentTimeMillis());
startTimes.putIfAbsent(description.getMethodName(), System.currentTimeMillis());
}

private Long elapsedTime(Description description) {
Long startTime = startTimes.get(settings.buildPlainName(description));
Long startTime = startTimes.get(description.getMethodName());
if( startTime == null ) {
return 0l;
} else {
Expand All @@ -168,34 +154,36 @@ private Long elapsedTime(Description description) {
public void testRunFinished(Result result)
{
if (settings.verbose) {
logger.info(TEST_RUN, "Test run " +taskInfo+" finished: "+
logger.info("Test run " +taskInfo+" finished: "+
result.getFailureCount()+" failed" +
", " +
result.getIgnoreCount()+" ignored" +
", "+result.getRunCount()+" total, "+(result.getRunTime()/1000.0)+"s") ;
logger.flush();
}
runStatistics.addTime(result.getRunTime());
logger.flush(TEST_RUN);
}

@Override
public void testSuiteFinished(Description description) throws Exception {
logger.flush(description);
public void testSuiteFinished(Description desc) throws Exception {
logger.flush();
}

@Override
public void testRunStarted(Description desc)
{
if (settings.verbose) {
logger.info(desc, taskInfo + " started");
logger.info(taskInfo + " started");
logger.flush();
}
}

void testExecutionFailed(String testName, Throwable err)
{
post(new Event(Ansi.c(testName, Ansi.ERRMSG), settings.buildErrorMessage(err), Status.Error, 0L, err) {
void logTo(RichLogger logger) {
logger.error(TEST_RUN, ansiName+" failed: "+ansiMsg, error);
logger.error(ansiName+" failed: "+ansiMsg, error);
logger.flush();
}
});
}
Expand All @@ -217,26 +205,6 @@ void post(AbstractEvent e)
handler.handle(e);
}

private void capture()
{
if(settings.quiet && capture == null)
capture = OutputCapture.start();
}

void uncapture(boolean replay)
{
if(settings.quiet && capture != null)
{
capture.stop();
if(replay)
{
try { capture.replay(); }
catch(IOException ex) { logger.error(TEST_RUN, "Error replaying captured stdio", ex); }
}
capture = null;
}
}


// Removes stack trace elements that reference the reflective invocation in TestLauncher.
private static void trimStackTrace(Throwable ex, String fromClassName, String toClassName, Settings settings) {
Expand All @@ -248,7 +216,7 @@ private static void trimStackTrace(Throwable ex, String fromClassName, String to
int end = stackTrace.length - 1;
StackTraceElement last = stackTrace[end];
if (last.getClassName() != null && last.getClassName().equals(fromClassName)) {
for (int i = 0; end >= 0; end--) {
for (; end >= 0; end--) {
StackTraceElement e = stackTrace[end];
if (e.getClassName().equals(toClassName)) {
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,9 @@ final class JUnitRunner implements Runner {
this.customRunners = customRunners;
Settings defaults = Settings.defaults();

boolean quiet = false, nocolor = false, decodeScalaNames = false,
boolean nocolor = false, decodeScalaNames = false,
logAssert = true, logExceptionClass = true, useSbtLoggers = false, useBufferedLoggers = true;
boolean verbose = false;
boolean suppressSystemError = false;
boolean trimStackTraces = defaults.trimStackTraces();
RunSettings.Summary summary = RunSettings.Summary.SBT;
HashMap<String, String> sysprops = new HashMap<String, String>();
Expand All @@ -51,8 +50,7 @@ final class JUnitRunner implements Runner {
String ignoreRunners = "org.junit.runners.Suite";
String runListener = null;
for(String s : args) {
if("-q".equals(s)) quiet = true;
else if("-v".equals(s) || "--verbose".equals(s)) verbose = true;
if("-v".equals(s) || "--verbose".equals(s)) verbose = true;
else if(s.startsWith("--summary=")) summary = RunSettings.Summary.values()[Integer.parseInt(s.substring(10))];
else if("-n".equals(s)) nocolor = true;
else if("-s".equals(s)) decodeScalaNames = true;
Expand Down Expand Up @@ -80,18 +78,15 @@ else if(s.startsWith("-D") && s.contains("=")) {
else if(!s.startsWith("-") && !s.startsWith("+")) globPatterns.add(s);
}
for(String s : args) {
if("+q".equals(s)) quiet = false;
else if("+n".equals(s)) nocolor = false;
if("+n".equals(s)) nocolor = false;
else if("+s".equals(s)) decodeScalaNames = false;
else if("+a".equals(s)) logAssert = false;
else if("+c".equals(s)) logExceptionClass = true;
else if("+l".equals(s)) useSbtLoggers = true;
else if("--no-stderr".equals(s)) suppressSystemError = true;
else if("--stderr".equals(s)) suppressSystemError = false;
}
this.settings =
new RunSettings(!nocolor, decodeScalaNames, quiet, verbose, useSbtLoggers, useBufferedLoggers, trimStackTraces, summary, logAssert, ignoreRunners, logExceptionClass,
suppressSystemError, sysprops, globPatterns, includeCategories, excludeCategories, includeTags, excludeTags,
new RunSettings(!nocolor, decodeScalaNames, verbose, useSbtLoggers, useBufferedLoggers, trimStackTraces, summary, logAssert, ignoreRunners, logExceptionClass,
sysprops, globPatterns, includeCategories, excludeCategories, includeTags, excludeTags,
testFilter);
this.runListener = createRunListener(runListener);
this.runStatistics = new RunStatistics(settings);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package munit.internal.junitinterface;

import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Map;
Expand Down Expand Up @@ -55,9 +53,7 @@ public Task[] execute(EventHandler eventHandler, Logger[] loggers) {
if (runner.runListener != null) ju.addListener(runner.runListener);

Map<String, Object> oldprops = settings.overrideSystemProperties();
PrintStream oldSystemError = System.err;
try {
suppressSystemError();
try {
Class<?> cl = runner.testClassLoader.loadClass(testClassName);
boolean isRun = shouldRun(fingerprint, cl, settings);
Expand All @@ -84,23 +80,11 @@ public Task[] execute(EventHandler eventHandler, Logger[] loggers) {
}
} finally {
settings.restoreSystemProperties(oldprops);
System.setErr(oldSystemError);
}
return new Task[0]; // junit tests do not nest
}


private static final PrintStream EMPTY_PRINTSTREAM = new PrintStream(new OutputStream() {
@Override
public void write(int b) {}
});
private void suppressSystemError() {
if (settings.suppressSystemError) {
System.setErr(EMPTY_PRINTSTREAM);
}
}


private boolean shouldRun(Fingerprint fingerprint, Class<?> clazz, RunSettings settings) {
if(JUNIT_FP.equals(fingerprint)) {
// Ignore classes which are matched by the other fingerprints
Expand Down

This file was deleted.

Loading

0 comments on commit 9cfd8b1

Please sign in to comment.