Skip to content

Commit

Permalink
Move data structures to separate class
Browse files Browse the repository at this point in the history
Issue: #86
  • Loading branch information
marcphilipp committed Jan 27, 2016
1 parent af972ac commit c193362
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 49 deletions.
@@ -0,0 +1,96 @@
/*
* 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 static org.junit.gen5.engine.TestExecutionResult.Status.ABORTED;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Clock;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.junit.gen5.engine.TestExecutionResult;
import org.junit.gen5.launcher.TestIdentifier;
import org.junit.gen5.launcher.TestPlan;

class XmlReportData {

private static final int MILLIS_PER_SECOND = 1000;

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

private final TestPlan testPlan;
private final Clock clock;

XmlReportData(TestPlan testPlan, Clock clock) {
this.testPlan = testPlan;
this.clock = clock;
}

TestPlan getTestPlan() {
return testPlan;
}

void markSkipped(TestIdentifier testIdentifier, String reason) {
skippedTests.put(testIdentifier, reason == null ? "" : reason);
}

void markStarted(TestIdentifier testIdentifier) {
startTimes.put(testIdentifier, clock.millis());
}

void markFinished(TestIdentifier testIdentifier, TestExecutionResult result) {
endTimes.put(testIdentifier, clock.millis());
if (result.getStatus() == ABORTED) {
String reason = result.getThrowable().map(XmlReportData::readStackTrace).orElse("");
skippedTests.put(testIdentifier, reason);
}
else {
finishedTests.put(testIdentifier, result);
}
}

boolean wasSkipped(TestIdentifier testIdentifier) {
return skippedTests.containsKey(testIdentifier);
}

boolean wasFinished(TestIdentifier testIdentifier) {
return finishedTests.containsKey(testIdentifier);
}

double getDurationInSeconds(TestIdentifier testIdentifier) {
long startMillis = startTimes.getOrDefault(testIdentifier, 0L);
long endMillis = endTimes.getOrDefault(testIdentifier, startMillis);
return (endMillis - startMillis) / (double) MILLIS_PER_SECOND;
}

String getSkipReason(TestIdentifier test) {
return skippedTests.get(test);
}

TestExecutionResult getResult(TestIdentifier testIdentifier) {
return finishedTests.get(testIdentifier);
}

// TODO #86 Move to ExceptionUtils
static String readStackTrace(Throwable throwable) {
StringWriter stringWriter = new StringWriter();
try (PrintWriter printWriter = new PrintWriter(stringWriter)) {
throwable.printStackTrace(printWriter);
}
return stringWriter.toString();
}

}
Expand Up @@ -13,7 +13,6 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toList;
import static org.junit.gen5.commons.util.StringUtils.isNotBlank;
import static org.junit.gen5.engine.TestExecutionResult.Status.ABORTED;
import static org.junit.gen5.engine.TestExecutionResult.Status.FAILED;

import java.io.BufferedWriter;
Expand All @@ -29,9 +28,7 @@
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;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
Expand All @@ -44,17 +41,11 @@

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<>();
private XmlReportData reportData;

public XmlReportsWritingListener(String reportsDir, PrintWriter out) {
this(reportsDir, out, Clock.systemDefaultZone());
Expand All @@ -69,7 +60,7 @@ public XmlReportsWritingListener(String reportsDir, PrintWriter out) {

@Override
public void testPlanExecutionStarted(TestPlan testPlan) {
this.testPlan = testPlan;
this.reportData = new XmlReportData(testPlan, clock);
try {
Files.createDirectories(reportsDir.toPath());
}
Expand All @@ -81,43 +72,40 @@ public void testPlanExecutionStarted(TestPlan testPlan) {

@Override
public void executionSkipped(TestIdentifier testIdentifier, String reason) {
skippedTests.put(testIdentifier, reason == null ? "" : reason);
reportData.markSkipped(testIdentifier, reason);
// TODO #86 write file for roots
}

@Override
public void executionStarted(TestIdentifier testIdentifier) {
startTimes.put(testIdentifier, clock.millis());
reportData.markStarted(testIdentifier);
}

@Override
public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult result) {
endTimes.put(testIdentifier, clock.millis());
if (result.getStatus() == ABORTED) {
String reason = result.getThrowable().map(this::readStackTrace).orElse("");
skippedTests.put(testIdentifier, reason);
}
else {
finishedTests.put(testIdentifier, result);
}
reportData.markFinished(testIdentifier, result);
if (isARoot(testIdentifier)) {
// TODO #86 consider skipped/failed/aborted containers
// @formatter:off
List<TestIdentifier> tests = testPlan.getDescendants(testIdentifier).stream()
.filter(TestIdentifier::isTest)
.collect(toList());
// @formatter:on
if (!tests.isEmpty()) {
File xmlFile = new File(reportsDir, "TEST-" + testIdentifier.getUniqueId() + ".xml");
writeXmlReport(testIdentifier, tests, xmlFile);
}
File xmlFile = new File(reportsDir, "TEST-" + testIdentifier.getUniqueId() + ".xml");
writeXmlReport(testIdentifier, xmlFile);
}
}

private boolean isARoot(TestIdentifier testIdentifier) {
return !testIdentifier.getParentId().isPresent();
}

private void writeXmlReport(TestIdentifier testIdentifier, File xmlFile) {
List<TestIdentifier> tests = reportData.getTestPlan().getDescendants(testIdentifier).stream()
.filter(TestIdentifier::isTest)
.collect(toList());
// @formatter:on
if (!tests.isEmpty()) {
writeXmlReport(testIdentifier, tests, xmlFile);
}
}

private void writeXmlReport(TestIdentifier testIdentifier, List<TestIdentifier> tests, File xmlFile) {
try (Writer fileWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(xmlFile), UTF_8))) {
XMLOutputFactory factory = XMLOutputFactory.newInstance();
Expand All @@ -141,11 +129,11 @@ private void writeTestsuite(TestIdentifier testIdentifier, List<TestIdentifier>
long skipped = 0;
long failures = 0;
for (TestIdentifier test : tests) {
if (skippedTests.containsKey(test)) {
if (reportData.wasSkipped(test)) {
skipped++;
}
else if (finishedTests.containsKey(test)) {
TestExecutionResult result = finishedTests.get(test);
else if (reportData.wasFinished(test)) {
TestExecutionResult result = reportData.getResult(test);
if (result.getStatus() == FAILED) {
failures++;
}
Expand All @@ -160,7 +148,7 @@ else if (finishedTests.containsKey(test)) {
writer.writeAttribute("errors", "0");

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

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

Expand All @@ -171,32 +159,22 @@ else if (finishedTests.containsKey(test)) {
writer.writeEndElement();
}

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);
Optional<TestIdentifier> parent = reportData.getTestPlan().getParent(test);
if (parent.isPresent()) {
writer.writeAttribute("classname", parent.get().getName());
}
writer.writeAttribute("time", numberFormat.format(calculateDurationInSeconds(test)));
writer.writeAttribute("time", numberFormat.format(reportData.getDurationInSeconds(test)));
// TODO #86 write error elements
writer.writeComment("Unique ID: " + test.getUniqueId().toString());
if (skippedTests.containsKey(test)) {
writeSkipped(skippedTests.get(test), writer);
if (reportData.wasSkipped(test)) {
writeSkipped(reportData.getSkipReason(test), writer);
}
else if (finishedTests.containsKey(test)) {
TestExecutionResult result = finishedTests.get(test);
else if (reportData.wasFinished(test)) {
TestExecutionResult result = reportData.getResult(test);
if (result.getStatus() == FAILED) {
writeFailure(result.getThrowable(), writer);
}
Expand Down

0 comments on commit c193362

Please sign in to comment.