Skip to content

Commit

Permalink
Measure and output runtime for tests
Browse files Browse the repository at this point in the history
Issue: #86
  • Loading branch information
marcphilipp committed Jan 27, 2016
1 parent 02fb8b8 commit 5eeecaa
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 12 deletions.
Expand Up @@ -25,7 +25,10 @@
import java.io.StringWriter;
import java.io.Writer;
import java.nio.file.Files;
import java.text.NumberFormat;
import java.time.Clock;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
Expand All @@ -41,16 +44,27 @@

class XmlReportsWritingListener implements TestExecutionListener {

private static final int MILLIS_PER_SECOND = 1000;

private final File reportsDir;
private final PrintWriter out;
private final Clock clock;

private TestPlan testPlan;
private Map<TestIdentifier, TestExecutionResult> finishedTests = new ConcurrentHashMap<>();
private Map<TestIdentifier, String> skippedTests = new ConcurrentHashMap<>();
private Map<TestIdentifier, Long> startTimes = new ConcurrentHashMap<>();
private Map<TestIdentifier, Long> endTimes = new ConcurrentHashMap<>();

public XmlReportsWritingListener(String reportsDir, PrintWriter out) {
this(reportsDir, out, Clock.systemDefaultZone());
}

// For tests only
XmlReportsWritingListener(String reportsDir, PrintWriter out, Clock clock) {
this.reportsDir = new File(reportsDir);
this.out = out;
this.clock = clock;
}

@Override
Expand All @@ -73,12 +87,12 @@ public void executionSkipped(TestIdentifier testIdentifier, String reason) {

@Override
public void executionStarted(TestIdentifier testIdentifier) {
// TODO #86 start timer
startTimes.put(testIdentifier, clock.millis());
}

@Override
public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult result) {
// TODO #86 stop timer
endTimes.put(testIdentifier, clock.millis());
if (result.getStatus() == ABORTED) {
String reason = result.getThrowable().map(this::readStackTrace).orElse("");
skippedTests.put(testIdentifier, reason);
Expand Down Expand Up @@ -140,32 +154,43 @@ else if (finishedTests.containsKey(test)) {

writer.writeAttribute("name", testIdentifier.getDisplayName());
writer.writeAttribute("tests", String.valueOf(tests.size()));
// TODO #86 compute skipped/failure/error counts
// TODO #86 compute error count
writer.writeAttribute("skipped", String.valueOf(skipped));
writer.writeAttribute("failures", String.valueOf(failures));
writer.writeAttribute("errors", "0");
// TODO #86 measure time
writer.writeAttribute("time", "0.0");

NumberFormat numberFormat = NumberFormat.getInstance(Locale.US);
writer.writeAttribute("time", numberFormat.format(calculateDurationInSeconds(testIdentifier)));

writer.writeComment("Unique ID: " + testIdentifier.getUniqueId().toString());

for (TestIdentifier test : tests) {
writeTestcase(test, writer);
writeTestcase(test, numberFormat, writer);
}

writer.writeEndElement();
}

private void writeTestcase(TestIdentifier test, XMLStreamWriter writer) throws XMLStreamException {
private double calculateDurationInSeconds(TestIdentifier testIdentifier) {
return calculateDurationInMillis(testIdentifier) / (double) MILLIS_PER_SECOND;
}

private long calculateDurationInMillis(TestIdentifier testIdentifier) {
long start = startTimes.getOrDefault(testIdentifier, 0L);
long end = endTimes.getOrDefault(testIdentifier, start);
return end - start;
}

private void writeTestcase(TestIdentifier test, NumberFormat numberFormat, XMLStreamWriter writer)
throws XMLStreamException {
writer.writeStartElement("testcase");
writer.writeAttribute("name", test.getDisplayName());
Optional<TestIdentifier> parent = testPlan.getParent(test);
if (parent.isPresent()) {
writer.writeAttribute("classname", parent.get().getName());
}
// TODO #86 measure time
writer.writeAttribute("time", "0.0");
// TODO #86 write skipped/error/failure elements
writer.writeAttribute("time", numberFormat.format(calculateDurationInSeconds(test)));
// TODO #86 write error elements
writer.writeComment("Unique ID: " + test.getUniqueId().toString());
if (skippedTests.containsKey(test)) {
writeSkipped(skippedTests.get(test), writer);
Expand Down
@@ -0,0 +1,49 @@
/*
* Copyright 2015-2016 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/

package org.junit.gen5.console.tasks;

import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;

final class IncrementingClock extends Clock {

private final Duration duration;
private final ZoneId zone;

private int counter;

public IncrementingClock(int start, Duration duration) {
this(start, duration, ZoneId.systemDefault());
}

public IncrementingClock(int start, Duration duration, ZoneId zone) {
this.counter = start;
this.duration = duration;
this.zone = zone;
}

@Override
public Instant instant() {
return Instant.EPOCH.plus(duration.multipliedBy(counter++));
}

@Override
public Clock withZone(ZoneId zone) {
return new IncrementingClock(counter, duration, zone);
}

@Override
public ZoneId getZone() {
return zone;
}
}
Expand Up @@ -29,6 +29,10 @@
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;

import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
Expand All @@ -40,6 +44,7 @@
import org.junit.gen5.api.BeforeEach;
import org.junit.gen5.api.Test;
import org.junit.gen5.api.TestInfo;
import org.junit.gen5.engine.TestEngine;
import org.junit.gen5.engine.support.hierarchical.DummyTestDescriptor;
import org.junit.gen5.engine.support.hierarchical.DummyTestEngine;
import org.junit.gen5.launcher.Launcher;
Expand Down Expand Up @@ -179,9 +184,57 @@ void writesFileForSingleAbortedTest() throws Exception {
//@formatter:on
}

private void executeTests(DummyTestEngine engine) {
@Test
void measuresTimesInSeconds() throws Exception {
DummyTestEngine engine = new DummyTestEngine("dummy");
engine.addTest("firstTest", () -> {
});
engine.addTest("secondTest", () -> {
});

executeTests(engine, new IncrementingClock(0, Duration.ofMillis(333)));

String content = readValidXmlFile("TEST-dummy.xml");

//@formatter:off
// start end
// ----------- ---------- -----------
// engine 0 (1) 1,665 (6)
// firstTest 333 (2) 666 (3)
// secondTest 999 (4) 1,332 (5)
assertThat(content)
.containsSequence(
"<testsuite", "time=\"1.665\"",
"<testcase name=\"firstTest\" classname=\"dummy\" time=\"0.333\"",
"<testcase name=\"secondTest\" classname=\"dummy\" time=\"0.333\"");
//@formatter:on
}

@Test
void testWithImmeasurableTimeIsOutputCorrectly() throws Exception {
DummyTestEngine engine = new DummyTestEngine("dummy");
engine.addTest("test", () -> {
});

executeTests(engine, Clock.fixed(Instant.EPOCH, ZoneId.systemDefault()));

String content = readValidXmlFile("TEST-dummy.xml");

//@formatter:off
assertThat(content)
.containsSequence(
"<testsuite",
"<testcase name=\"test\" classname=\"dummy\" time=\"0\"");
//@formatter:on
}

private void executeTests(TestEngine engine) {
executeTests(engine, Clock.systemDefaultZone());
}

private void executeTests(TestEngine engine, Clock clock) {
PrintWriter out = new PrintWriter(new StringWriter());
XmlReportsWritingListener reportListener = new XmlReportsWritingListener(tempDirectory.toString(), out);
XmlReportsWritingListener reportListener = new XmlReportsWritingListener(tempDirectory.toString(), out, clock);
Launcher launcher = createLauncher(engine);
launcher.registerTestExecutionListeners(reportListener);
launcher.execute(request().select(forUniqueId(engine.getId())).build());
Expand Down

0 comments on commit 5eeecaa

Please sign in to comment.