| /* |
| * 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 |
| * |
| * https://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.tools.ant.taskdefs.optional.junitlauncher; |
| |
| import org.junit.platform.engine.TestExecutionResult; |
| import org.junit.platform.engine.reporting.ReportEntry; |
| import org.junit.platform.engine.support.descriptor.ClassSource; |
| import org.junit.platform.launcher.TestIdentifier; |
| import org.junit.platform.launcher.TestPlan; |
| |
| import java.io.BufferedWriter; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.nio.charset.StandardCharsets; |
| import java.util.Map; |
| import java.util.Optional; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.atomic.AtomicLong; |
| |
| |
| /** |
| * A {@link TestResultFormatter} which prints a short statistic for each of the tests |
| */ |
| class LegacyPlainResultFormatter extends AbstractJUnitResultFormatter implements TestResultFormatter { |
| |
| private OutputStream outputStream; |
| private final Map<TestIdentifier, Stats> testIds = new ConcurrentHashMap<>(); |
| private TestPlan testPlan; |
| private BufferedWriter writer; |
| private boolean useLegacyReportingName = true; |
| |
| @Override |
| public void testPlanExecutionStarted(final TestPlan testPlan) { |
| this.testPlan = testPlan; |
| } |
| |
| @Override |
| public void testPlanExecutionFinished(final TestPlan testPlan) { |
| for (final Map.Entry<TestIdentifier, Stats> entry : this.testIds.entrySet()) { |
| final TestIdentifier testIdentifier = entry.getKey(); |
| if (!isTestClass(testIdentifier).isPresent()) { |
| // we are not interested in anything other than a test "class" in this section |
| continue; |
| } |
| final Stats stats = entry.getValue(); |
| final StringBuilder sb = new StringBuilder("Tests run: ").append(stats.numTestsRun.get()); |
| sb.append(", Failures: ").append(stats.numTestsFailed.get()); |
| sb.append(", Skipped: ").append(stats.numTestsSkipped.get()); |
| sb.append(", Aborted: ").append(stats.numTestsAborted.get()); |
| sb.append(", Time elapsed: "); |
| stats.appendElapsed(sb); |
| try { |
| this.writer.write(sb.toString()); |
| this.writer.newLine(); |
| } catch (IOException ioe) { |
| handleException(ioe); |
| return; |
| } |
| } |
| // write out sysout and syserr content if any |
| try { |
| if (this.hasSysOut()) { |
| this.writer.write("------------- Standard Output ---------------"); |
| this.writer.newLine(); |
| writeSysOut(writer); |
| this.writer.write("------------- ---------------- ---------------"); |
| this.writer.newLine(); |
| } |
| if (this.hasSysErr()) { |
| this.writer.write("------------- Standard Error ---------------"); |
| this.writer.newLine(); |
| writeSysErr(writer); |
| this.writer.write("------------- ---------------- ---------------"); |
| this.writer.newLine(); |
| } |
| } catch (IOException ioe) { |
| handleException(ioe); |
| } |
| } |
| |
| @Override |
| public void dynamicTestRegistered(final TestIdentifier testIdentifier) { |
| // nothing to do |
| } |
| |
| @Override |
| public void executionSkipped(final TestIdentifier testIdentifier, final String reason) { |
| final long currentTime = System.currentTimeMillis(); |
| this.testIds.putIfAbsent(testIdentifier, new Stats(testIdentifier, currentTime)); |
| final Stats stats = this.testIds.get(testIdentifier); |
| stats.setEndedAt(currentTime); |
| if (testIdentifier.isTest()) { |
| final StringBuilder sb = new StringBuilder(); |
| sb.append("Test: "); |
| sb.append(this.useLegacyReportingName ? testIdentifier.getLegacyReportingName() : testIdentifier.getDisplayName()); |
| sb.append(" took "); |
| stats.appendElapsed(sb); |
| sb.append(" SKIPPED"); |
| if (reason != null && !reason.isEmpty()) { |
| sb.append(": ").append(reason); |
| } |
| try { |
| this.writer.write(sb.toString()); |
| this.writer.newLine(); |
| } catch (IOException ioe) { |
| handleException(ioe); |
| return; |
| } |
| } |
| // get the parent test class to which this skipped test belongs to |
| final Optional<TestIdentifier> parentTestClass = traverseAndFindTestClass(this.testPlan, testIdentifier); |
| if (!parentTestClass.isPresent()) { |
| return; |
| } |
| final Stats parentClassStats = this.testIds.get(parentTestClass.get()); |
| parentClassStats.numTestsSkipped.incrementAndGet(); |
| } |
| |
| @Override |
| public void executionStarted(final TestIdentifier testIdentifier) { |
| final long currentTime = System.currentTimeMillis(); |
| // record this testidentifier's start |
| this.testIds.putIfAbsent(testIdentifier, new Stats(testIdentifier, currentTime)); |
| final Optional<ClassSource> testClass = isTestClass(testIdentifier); |
| if (testClass.isPresent()) { |
| // if this is a test class, then print it out |
| try { |
| this.writer.write("Testcase: " + testClass.get().getClassName()); |
| this.writer.newLine(); |
| } catch (IOException ioe) { |
| handleException(ioe); |
| return; |
| } |
| } |
| // if this is a test (method) then increment the tests run for the test class to which |
| // this test belongs to |
| if (testIdentifier.isTest()) { |
| final Optional<TestIdentifier> parentTestClass = traverseAndFindTestClass(this.testPlan, testIdentifier); |
| if (parentTestClass.isPresent()) { |
| final Stats parentClassStats = this.testIds.get(parentTestClass.get()); |
| if (parentClassStats != null) { |
| parentClassStats.numTestsRun.incrementAndGet(); |
| } |
| } |
| } |
| } |
| |
| @SuppressWarnings("incomplete-switch") |
| @Override |
| public void executionFinished(final TestIdentifier testIdentifier, final TestExecutionResult testExecutionResult) { |
| final long currentTime = System.currentTimeMillis(); |
| final Stats stats = this.testIds.get(testIdentifier); |
| if (stats != null) { |
| stats.setEndedAt(currentTime); |
| } |
| if (testIdentifier.isTest() && shouldReportExecutionFinished(testIdentifier, testExecutionResult)) { |
| final StringBuilder sb = new StringBuilder(); |
| sb.append("Test: "); |
| sb.append(this.useLegacyReportingName ? testIdentifier.getLegacyReportingName() : testIdentifier.getDisplayName()); |
| if (stats != null) { |
| sb.append(" took "); |
| stats.appendElapsed(sb); |
| } |
| switch (testExecutionResult.getStatus()) { |
| case ABORTED: { |
| sb.append(" ABORTED"); |
| appendThrowable(sb, testExecutionResult); |
| break; |
| } |
| case FAILED: { |
| sb.append(" FAILED"); |
| appendThrowable(sb, testExecutionResult); |
| break; |
| } |
| } |
| try { |
| this.writer.write(sb.toString()); |
| this.writer.newLine(); |
| } catch (IOException ioe) { |
| handleException(ioe); |
| return; |
| } |
| } |
| // get the parent test class in which this test completed |
| final Optional<TestIdentifier> parentTestClass = traverseAndFindTestClass(this.testPlan, testIdentifier); |
| if (!parentTestClass.isPresent()) { |
| return; |
| } |
| // update the stats of the parent test class |
| final Stats parentClassStats = this.testIds.get(parentTestClass.get()); |
| switch (testExecutionResult.getStatus()) { |
| case ABORTED: { |
| parentClassStats.numTestsAborted.incrementAndGet(); |
| break; |
| } |
| case FAILED: { |
| parentClassStats.numTestsFailed.incrementAndGet(); |
| break; |
| } |
| } |
| } |
| |
| @Override |
| public void reportingEntryPublished(final TestIdentifier testIdentifier, final ReportEntry entry) { |
| // nothing to do |
| } |
| |
| @Override |
| public void setDestination(final OutputStream os) { |
| this.outputStream = os; |
| this.writer = new BufferedWriter(new OutputStreamWriter(this.outputStream, StandardCharsets.UTF_8)); |
| } |
| |
| @Override |
| public void setUseLegacyReportingName(final boolean useLegacyReportingName) { |
| this.useLegacyReportingName = useLegacyReportingName; |
| } |
| |
| protected boolean shouldReportExecutionFinished(final TestIdentifier testIdentifier, final TestExecutionResult testExecutionResult) { |
| return true; |
| } |
| |
| private static void appendThrowable(final StringBuilder sb, TestExecutionResult result) { |
| if (!result.getThrowable().isPresent()) { |
| return; |
| } |
| final Throwable throwable = result.getThrowable().get(); |
| sb.append(String.format(": %s%n", throwable.getMessage())); |
| final StringWriter stacktrace = new StringWriter(); |
| throwable.printStackTrace(new PrintWriter(stacktrace)); |
| sb.append(stacktrace.toString()); |
| } |
| |
| @Override |
| public void close() throws IOException { |
| if (this.writer != null) { |
| this.writer.close(); |
| } |
| super.close(); |
| } |
| |
| private final class Stats { |
| @SuppressWarnings("unused") |
| private final TestIdentifier testIdentifier; |
| private final AtomicLong numTestsRun = new AtomicLong(0); |
| private final AtomicLong numTestsFailed = new AtomicLong(0); |
| private final AtomicLong numTestsSkipped = new AtomicLong(0); |
| private final AtomicLong numTestsAborted = new AtomicLong(0); |
| private final long startedAt; |
| private long endedAt; |
| |
| private Stats(final TestIdentifier testIdentifier, final long startedAt) { |
| this.testIdentifier = testIdentifier; |
| this.startedAt = startedAt; |
| } |
| |
| private void setEndedAt(final long endedAt) { |
| this.endedAt = endedAt; |
| } |
| |
| private void appendElapsed(StringBuilder sb) { |
| final long timeElapsed = endedAt - startedAt; |
| if (timeElapsed < 1000) { |
| sb.append(timeElapsed).append(" milli sec(s)"); |
| } else { |
| sb.append(TimeUnit.SECONDS.convert(timeElapsed, TimeUnit.MILLISECONDS)).append(" sec(s)"); |
| } |
| } |
| } |
| } |