/* * Copyright 2015-2017 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.platform.console.tasks; import static org.junit.platform.commons.util.ExceptionUtils.readStackTrace; import static org.junit.platform.console.tasks.Color.NONE; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.Deque; import org.junit.platform.console.options.Theme; import org.junit.platform.engine.TestExecutionResult; import org.junit.platform.engine.reporting.ReportEntry; import org.junit.platform.launcher.TestExecutionListener; import org.junit.platform.launcher.TestIdentifier; import org.junit.platform.launcher.TestPlan; /** * @since 1.0 */ class VerboseTreePrintingListener implements TestExecutionListener { private final PrintWriter out; private final boolean disableAnsiColors; private final Theme theme; private final Deque<Long> frames; private final String[] verticals; private long executionStartedMillis; VerboseTreePrintingListener(PrintWriter out, boolean disableAnsiColors, int maxContainerNestingLevel, Theme theme) { this.out = out; this.disableAnsiColors = disableAnsiColors; this.theme = theme; // create frame stack and push initial root frame this.frames = new ArrayDeque<>(); this.frames.push(0L); // create and populate vertical indentation lookup table this.verticals = new String[Math.max(10, maxContainerNestingLevel) + 1]; this.verticals[0] = ""; // no frame this.verticals[1] = ""; // synthetic root "/" level this.verticals[2] = ""; // "engine" level for (int i = 3; i < verticals.length; i++) { verticals[i] = verticals[i - 1] + theme.vertical(); } } @Override public void testPlanExecutionStarted(TestPlan testPlan) { frames.push(System.currentTimeMillis()); long tests = testPlan.countTestIdentifiers(TestIdentifier::isTest); printf(NONE, "Test plan execution started. Number of static tests: "); printf(Color.TEST, "%d%n", tests); printf(Color.CONTAINER, "%s%n", theme.root()); } @Override public void testPlanExecutionFinished(TestPlan testPlan) { frames.pop(); long tests = testPlan.countTestIdentifiers(TestIdentifier::isTest); printf(NONE, "Test plan execution finished. Number of all tests: "); printf(Color.TEST, "%d%n", tests); } @Override public void executionStarted(TestIdentifier testIdentifier) { this.executionStartedMillis = System.currentTimeMillis(); if (testIdentifier.isContainer()) { printVerticals(theme.entry()); printf(Color.CONTAINER, " %s", testIdentifier.getDisplayName()); printf(NONE, "%n"); frames.push(System.currentTimeMillis()); } if (testIdentifier.isContainer()) { return; } printVerticals(theme.entry()); printf(Color.valueOf(testIdentifier), " %s%n", testIdentifier.getDisplayName()); printDetails(testIdentifier); } @Override public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) { if (testIdentifier.isContainer()) { Long creationMillis = frames.pop(); printVerticals(theme.end()); printf(Color.CONTAINER, " %s", testIdentifier.getDisplayName()); printf(NONE, " finished after %d ms.%n", System.currentTimeMillis() - creationMillis); return; } testExecutionResult.getThrowable().ifPresent(t -> printDetail(Color.FAILED, "caught", readStackTrace(t))); printDetail(NONE, "duration", "%d ms%n", System.currentTimeMillis() - executionStartedMillis); String status = theme.status(testExecutionResult) + " " + testExecutionResult.getStatus(); printDetail(Color.valueOf(testExecutionResult), "status", "%s%n", status); } @Override public void executionSkipped(TestIdentifier testIdentifier, String reason) { printVerticals(theme.entry()); printf(Color.valueOf(testIdentifier), " %s%n", testIdentifier.getDisplayName()); printDetails(testIdentifier); printDetail(Color.SKIPPED, "reason", reason); printDetail(Color.SKIPPED, "status", theme.skipped() + " SKIPPED"); } @Override public void dynamicTestRegistered(TestIdentifier testIdentifier) { printVerticals(theme.entry()); printf(Color.DYNAMIC, " %s", testIdentifier.getDisplayName()); printf(NONE, " dynamically registered%n"); } @Override public void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) { printDetail(Color.REPORTED, "reports", entry.toString()); } /** * Print static information about the test identifier. */ private void printDetails(TestIdentifier testIdentifier) { printDetail(NONE, "tags", "%s%n", testIdentifier.getTags()); printDetail(NONE, "uniqueId", "%s%n", testIdentifier.getUniqueId()); printDetail(NONE, "parent", "%s%n", testIdentifier.getParentId().orElse("[]")); testIdentifier.getSource().ifPresent(source -> printDetail(NONE, "source", "%s%n", source)); } private String verticals() { return verticals(frames.size()); } private String verticals(int index) { return verticals[Math.min(index, verticals.length)]; } private void printVerticals(String tile) { printf(NONE, verticals()); printf(NONE, tile); } private void printf(Color color, String message, Object... args) { if (disableAnsiColors || color == NONE) { out.printf(message, args); } else { out.printf(color + message + NONE, args); } out.flush(); } /** * Print single detail with a potential multi-line message. */ private void printDetail(Color color, String detail, String format, Object... args) { // print initial verticals - expecting to be at start of the line String verticals = verticals(frames.size() + 1); printf(NONE, verticals); String detailFormat = "%9s"; // omit detail string if it's empty if (!detail.isEmpty()) { printf(NONE, String.format(detailFormat + ": ", detail)); } // trivial case: at least one arg is given? Let printf do the entire work if (args.length > 0) { printf(color, format, args); return; } // still here? Split format into separate lines and indent them from the second line on String[] lines = format.split("\\R"); printf(color, lines[0]); if (lines.length > 1) { String delimiter = System.lineSeparator() + verticals + String.format(detailFormat + " ", ""); for (int i = 1; i < lines.length; i++) { printf(NONE, delimiter); printf(color, lines[i]); } } printf(NONE, "%n"); } }