diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/DefaultSummaryPrinter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/DefaultSummaryPrinter.java index 9c0c754be7..bbc12787c7 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/DefaultSummaryPrinter.java +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/DefaultSummaryPrinter.java @@ -1,42 +1,69 @@ package io.cucumber.core.plugin; +import io.cucumber.messages.types.Envelope; +import io.cucumber.messages.types.Exception; +import io.cucumber.messages.types.Location; +import io.cucumber.messages.types.Pickle; +import io.cucumber.messages.types.Snippet; +import io.cucumber.messages.types.Suggestion; +import io.cucumber.messages.types.TestCaseFinished; +import io.cucumber.messages.types.TestStepFinished; +import io.cucumber.messages.types.TestStepResult; +import io.cucumber.messages.types.TestStepResultStatus; import io.cucumber.plugin.ColorAware; import io.cucumber.plugin.ConcurrentEventListener; import io.cucumber.plugin.event.EventPublisher; -import io.cucumber.plugin.event.SnippetsSuggestedEvent; -import io.cucumber.plugin.event.TestRunFinished; +import io.cucumber.query.Query; +import io.cucumber.query.Repository; import java.io.OutputStream; import java.io.PrintStream; +import java.time.Duration; +import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; -import java.util.Locale; +import java.util.Map; +import java.util.Optional; import java.util.Set; +import java.util.StringJoiner; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +import static io.cucumber.core.plugin.Formats.ansi; +import static io.cucumber.core.plugin.Formats.monochrome; +import static io.cucumber.query.Repository.RepositoryFeature.INCLUDE_GHERKIN_DOCUMENTS; +import static io.cucumber.query.Repository.RepositoryFeature.INCLUDE_SUGGESTIONS; +import static java.util.Collections.emptyList; +import static java.util.Locale.ROOT; +import static java.util.stream.Collectors.counting; +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.toList; public final class DefaultSummaryPrinter implements ColorAware, ConcurrentEventListener { - private final Set snippets = new LinkedHashSet<>(); - private final Stats stats; + private final Repository repository = Repository.builder() + .feature(INCLUDE_GHERKIN_DOCUMENTS, true) + .feature(INCLUDE_SUGGESTIONS, true) + .build(); + private final Query query = new Query(repository); private final PrintStream out; + private Formats formats = ansi(); public DefaultSummaryPrinter() { - this(System.out, Locale.getDefault()); + this(System.out); } - DefaultSummaryPrinter(OutputStream out, Locale locale) { + DefaultSummaryPrinter(OutputStream out) { this.out = new PrintStream(out); - this.stats = new Stats(locale); } @Override public void setEventPublisher(EventPublisher publisher) { - stats.setEventPublisher(publisher); - publisher.registerHandlerFor(SnippetsSuggestedEvent.class, this::handleSnippetsSuggestedEvent); - publisher.registerHandlerFor(TestRunFinished.class, event -> print()); - } - - private void handleSnippetsSuggestedEvent(SnippetsSuggestedEvent event) { - this.snippets.addAll(event.getSuggestion().getSnippets()); + publisher.registerHandlerFor(Envelope.class, envelope -> { + repository.update(envelope); + envelope.getTestRunFinished().ifPresent(testRunFinished -> print()); + }); } private void print() { @@ -44,27 +71,124 @@ private void print() { printStats(); printErrors(); printSnippets(); - out.println(); } private void printStats() { - stats.printStats(out); - out.println(); + printNonPassingScenarios(); + printScenarioCounts(); + printStepCounts(); + printDuration(); + } + + private void printNonPassingScenarios() { + Map> testCaseFinishedByStatus = query + .findAllTestCaseFinished() + .stream() + .collect(groupingBy(this::getTestStepResultStatusBy)); + + printScenarios(testCaseFinishedByStatus, TestStepResultStatus.FAILED); + printScenarios(testCaseFinishedByStatus, TestStepResultStatus.AMBIGUOUS); + printScenarios(testCaseFinishedByStatus, TestStepResultStatus.PENDING); + printScenarios(testCaseFinishedByStatus, TestStepResultStatus.UNDEFINED); + } + + private void printScenarios( + Map> testCaseFinishedByStatus, + TestStepResultStatus type + ) { + List scenarios = testCaseFinishedByStatus.getOrDefault(type, emptyList()); + Format format = formats.get(type.name().toLowerCase(ROOT)); + if (!scenarios.isEmpty()) { + out.println(format.text(firstLetterCapitalizedName(type) + " scenarios:")); + } + for (TestCaseFinished testCaseFinished : scenarios) { + query.findPickleBy(testCaseFinished).ifPresent(pickle -> { + out.println(formatLocation(pickle) + " # " + pickle.getName()); + }); + } + if (!scenarios.isEmpty()) { + out.println(); + } + } + + private String formatLocation(Pickle pickle) { + return pickle.getUri() + query.findLocationOf(pickle).map(Location::getLine).map(line -> ":" + line).orElse(""); + } + + private void printScenarioCounts() { + out.println(formatSubCounts( + "Scenarios", + query.findAllTestCaseFinished(), + countTestStepResultStatusByTestCaseFinished())); + + } + + private void printStepCounts() { + out.println(formatSubCounts( + "Steps", + query.findAllTestStepFinished(), + countTestStepResultStatusByTestStepFinished())); + } + + private String formatSubCounts(String itemName, List finishedItems, Collector> countTestStepResultStatusByItem) { + String countAndName = finishedItems.size() + " " + itemName; + StringJoiner joiner = new StringJoiner(",", countAndName + " (",")"); + joiner.setEmptyValue(countAndName); + Map subCounts = finishedItems.stream() + .collect(countTestStepResultStatusByItem); + for (TestStepResultStatus value : TestStepResultStatus.values()) { + long count = subCounts.getOrDefault(value, 0L); + if (count != 0) { + Format format = formats.get(value.name().toLowerCase(ROOT)); + joiner.add(format.text(count + " " + value.name().toLowerCase(ROOT))); + } + } + return joiner.toString(); + } + + private void printDuration() { + query.findTestRunDuration() + .map(DefaultSummaryPrinter::formatDuration) + .ifPresent(out::println); + } + + private static String formatDuration(Duration duration) { + long minutes = duration.toMinutes(); + long seconds = duration.minusMinutes(minutes).getSeconds(); + long milliseconds = TimeUnit.NANOSECONDS.toMillis(duration.getNano()); + return String.format("%sm%s.%ss", minutes, seconds, milliseconds); } private void printErrors() { - List errors = stats.getErrors(); + List errors = query.findAllTestStepFinished() + .stream() + .map(TestStepFinished::getTestStepResult) + .map(TestStepResult::getException) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(toList()); + if (errors.isEmpty()) { return; } out.println(); - for (Throwable error : errors) { - error.printStackTrace(out); + for (Exception error : errors) { + out.println(error.getStackTrace()); out.println(); } } private void printSnippets() { + Set snippets = query.findAllTestCaseFinished().stream() + .map(query::findPickleBy) + .filter(Optional::isPresent) + .map(Optional::get) + .map(query::findSuggestionsBy) + .flatMap(Collection::stream) + .map(Suggestion::getSnippets) + .flatMap(Collection::stream) + .collect(Collectors.toCollection(LinkedHashSet::new)); + if (snippets.isEmpty()) { return; } @@ -72,15 +196,39 @@ private void printSnippets() { out.println(); out.println("You can implement missing steps with the snippets below:"); out.println(); - for (String snippet : snippets) { - out.println(snippet); + for (Snippet snippet : snippets) { + out.println(snippet.getCode()); out.println(); } } + private Collector> countTestStepResultStatusByTestCaseFinished() { + return groupingBy(this::getTestStepResultStatusBy, counting()); + } + + private TestStepResultStatus getTestStepResultStatusBy(TestCaseFinished testCaseFinished) { + return query.findMostSevereTestStepResultBy(testCaseFinished) + .map(TestStepResult::getStatus) + // By definition + .orElse(TestStepResultStatus.PASSED); + } + + private static Collector> countTestStepResultStatusByTestStepFinished() { + return groupingBy(DefaultSummaryPrinter::getTestStepResultStatusBy, counting()); + } + + private static TestStepResultStatus getTestStepResultStatusBy(TestStepFinished testStepFinished) { + return testStepFinished.getTestStepResult().getStatus(); + } + + private String firstLetterCapitalizedName(TestStepResultStatus status) { + String name = status.name(); + return name.charAt(0) + name.substring(1).toLowerCase(ROOT); + } + @Override public void setMonochrome(boolean monochrome) { - stats.setMonochrome(monochrome); + formats = monochrome ? monochrome() : ansi(); } } diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/Stats.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/Stats.java deleted file mode 100755 index 6cecc731b3..0000000000 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/Stats.java +++ /dev/null @@ -1,242 +0,0 @@ -package io.cucumber.core.plugin; - -import io.cucumber.plugin.ColorAware; -import io.cucumber.plugin.ConcurrentEventListener; -import io.cucumber.plugin.event.EventPublisher; -import io.cucumber.plugin.event.PickleStepTestStep; -import io.cucumber.plugin.event.Result; -import io.cucumber.plugin.event.Status; -import io.cucumber.plugin.event.TestCase; -import io.cucumber.plugin.event.TestCaseFinished; -import io.cucumber.plugin.event.TestRunFinished; -import io.cucumber.plugin.event.TestRunStarted; -import io.cucumber.plugin.event.TestStepFinished; - -import java.io.PrintStream; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.time.Duration; -import java.time.Instant; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -import static io.cucumber.core.plugin.Formats.ansi; -import static io.cucumber.core.plugin.Formats.monochrome; -import static java.util.Locale.ROOT; -import static java.util.concurrent.TimeUnit.SECONDS; - -class Stats implements ConcurrentEventListener, ColorAware { - - private static final long ONE_SECOND = SECONDS.toNanos(1); - private static final long ONE_MINUTE = 60 * ONE_SECOND; - private final SubCounts scenarioSubCounts = new SubCounts(); - private final SubCounts stepSubCounts = new SubCounts(); - private final Locale locale; - private final List failedScenarios = new ArrayList<>(); - private final List ambiguousScenarios = new ArrayList<>(); - private final List pendingScenarios = new ArrayList<>(); - private final List undefinedScenarios = new ArrayList<>(); - private final List errors = new ArrayList<>(); - private Instant startTime = Instant.EPOCH; - private Duration totalDuration = Duration.ZERO; - private Formats formats = ansi(); - - Stats(Locale locale) { - this.locale = locale; - } - - @Override - public void setMonochrome(boolean monochrome) { - formats = monochrome ? monochrome() : ansi(); - } - - @Override - public void setEventPublisher(EventPublisher publisher) { - publisher.registerHandlerFor(TestRunStarted.class, this::setStartTime); - publisher.registerHandlerFor(TestStepFinished.class, this::addStepResult); - publisher.registerHandlerFor(TestCaseFinished.class, this::addScenario); - publisher.registerHandlerFor(TestRunFinished.class, this::setFinishTime); - } - - private void setStartTime(TestRunStarted event) { - setStartTime(event.getInstant()); - } - - private void addStepResult(TestStepFinished event) { - Result result = event.getResult(); - if (result.getError() != null) { - addError(result.getError()); - } - if (event.getTestStep() instanceof PickleStepTestStep) { - addStep(result.getStatus()); - } - } - - private void addScenario(TestCaseFinished event) { - TestCase testCase = event.getTestCase(); - addScenario(event.getResult().getStatus(), testCase); - } - - private void setFinishTime(TestRunFinished event) { - setFinishTime(event.getInstant()); - } - - void setStartTime(Instant startTime) { - this.startTime = startTime; - } - - private void addError(Throwable error) { - errors.add(error); - } - - void addStep(Status resultStatus) { - addResultToSubCount(stepSubCounts, resultStatus); - } - - void addScenario(Status resultStatus, TestCase testCase) { - addResultToSubCount(scenarioSubCounts, resultStatus); - switch (resultStatus) { - case FAILED: - failedScenarios.add(testCase); - break; - case AMBIGUOUS: - ambiguousScenarios.add(testCase); - break; - case PENDING: - pendingScenarios.add(testCase); - break; - case UNDEFINED: - undefinedScenarios.add(testCase); - break; - default: - // intentionally left blank - } - } - - void setFinishTime(Instant finishTime) { - this.totalDuration = Duration.between(startTime, finishTime); - } - - private void addResultToSubCount(SubCounts subCounts, Status resultStatus) { - switch (resultStatus) { - case FAILED: - subCounts.failed++; - break; - case AMBIGUOUS: - subCounts.ambiguous++; - break; - case PENDING: - subCounts.pending++; - break; - case UNDEFINED: - subCounts.undefined++; - break; - case SKIPPED: - subCounts.skipped++; - break; - default: - subCounts.passed++; - } - } - - public List getErrors() { - return errors; - } - - void printStats(PrintStream out) { - printNonZeroResultScenarios(out); - if (stepSubCounts.getTotal() == 0) { - out.println("0 Scenarios"); - out.println("0 Steps"); - } else { - printScenarioCounts(out); - printStepCounts(out); - } - printDuration(out); - } - - private void printStepCounts(PrintStream out) { - out.print(stepSubCounts.getTotal()); - out.print(" Steps ("); - printSubCounts(out, stepSubCounts); - out.println(")"); - } - - private void printScenarioCounts(PrintStream out) { - out.print(scenarioSubCounts.getTotal()); - out.print(" Scenarios ("); - printSubCounts(out, scenarioSubCounts); - out.println(")"); - } - - private void printSubCounts(PrintStream out, SubCounts subCounts) { - boolean addComma = false; - addComma = printSubCount(out, subCounts.failed, Status.FAILED, addComma); - addComma = printSubCount(out, subCounts.ambiguous, Status.AMBIGUOUS, addComma); - addComma = printSubCount(out, subCounts.skipped, Status.SKIPPED, addComma); - addComma = printSubCount(out, subCounts.pending, Status.PENDING, addComma); - addComma = printSubCount(out, subCounts.undefined, Status.UNDEFINED, addComma); - addComma = printSubCount(out, subCounts.passed, Status.PASSED, addComma); - } - - private boolean printSubCount(PrintStream out, int count, Status type, boolean addComma) { - if (count != 0) { - if (addComma) { - out.print(", "); - } - Format format = formats.get(type.name().toLowerCase(ROOT)); - out.print(format.text(count + " " + type.name().toLowerCase(ROOT))); - addComma = true; - } - return addComma; - } - - private void printDuration(PrintStream out) { - out.print(String.format("%dm", (totalDuration.toNanos() / ONE_MINUTE))); - DecimalFormat format = new DecimalFormat("0.000", new DecimalFormatSymbols(locale)); - out.println(format.format(((double) (totalDuration.toNanos() % ONE_MINUTE) / ONE_SECOND)) + "s"); - } - - private void printNonZeroResultScenarios(PrintStream out) { - printScenarios(out, failedScenarios, Status.FAILED); - printScenarios(out, ambiguousScenarios, Status.AMBIGUOUS); - printScenarios(out, pendingScenarios, Status.PENDING); - printScenarios(out, undefinedScenarios, Status.UNDEFINED); - } - - private void printScenarios(PrintStream out, List scenarios, Status type) { - Format format = formats.get(type.name().toLowerCase(ROOT)); - if (!scenarios.isEmpty()) { - out.println(format.text(firstLetterCapitalizedName(type) + " scenarios:")); - } - for (TestCase scenario : scenarios) { - String location = scenario.getUri() + ":" + scenario.getLocation().getLine(); - out.println(location + " # " + scenario.getName()); - } - if (!scenarios.isEmpty()) { - out.println(); - } - } - - private String firstLetterCapitalizedName(Status status) { - String name = status.name(); - return name.substring(0, 1) + name.substring(1).toLowerCase(ROOT); - } - - static class SubCounts { - - public int passed = 0; - public int failed = 0; - public int ambiguous = 0; - public int skipped = 0; - public int pending = 0; - public int undefined = 0; - - int getTotal() { - return passed + failed + ambiguous + skipped + pending + undefined; - } - - } - -} diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/DefaultSummaryPrinterTest.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/DefaultSummaryPrinterTest.java index 217bb3bf18..3f22f77a58 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/DefaultSummaryPrinterTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/DefaultSummaryPrinterTest.java @@ -1,74 +1,57 @@ package io.cucumber.core.plugin; -import io.cucumber.core.eventbus.EventBus; +import io.cucumber.core.backend.StubStepDefinition; +import io.cucumber.core.feature.TestFeatureParser; +import io.cucumber.core.gherkin.Feature; +import io.cucumber.core.options.RuntimeOptionsBuilder; +import io.cucumber.core.runner.StepDurationTimeService; +import io.cucumber.core.runtime.Runtime; +import io.cucumber.core.runtime.StubBackendSupplier; +import io.cucumber.core.runtime.StubFeatureSupplier; import io.cucumber.core.runtime.TimeServiceEventBus; -import io.cucumber.plugin.event.Location; -import io.cucumber.plugin.event.Result; -import io.cucumber.plugin.event.SnippetsSuggestedEvent; -import io.cucumber.plugin.event.SnippetsSuggestedEvent.Suggestion; -import io.cucumber.plugin.event.Status; -import io.cucumber.plugin.event.TestRunFinished; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; -import java.net.URI; -import java.time.Clock; import java.time.Duration; -import java.time.ZoneId; -import java.util.Locale; import java.util.UUID; +import static io.cucumber.core.plugin.Bytes.bytes; import static io.cucumber.core.plugin.IsEqualCompressingLineSeparators.equalCompressingLineSeparators; -import static java.nio.charset.StandardCharsets.UTF_8; -import static java.time.Instant.ofEpochSecond; -import static java.util.Collections.singletonList; +import static io.cucumber.core.plugin.PrettyFormatterStepDefinition.oneReference; +import static io.cucumber.core.plugin.PrettyFormatterStepDefinition.threeReference; +import static io.cucumber.core.plugin.PrettyFormatterStepDefinition.twoReference; import static org.hamcrest.MatcherAssert.assertThat; class DefaultSummaryPrinterTest { - private final ByteArrayOutputStream out = new ByteArrayOutputStream(); - private final DefaultSummaryPrinter summaryPrinter = new DefaultSummaryPrinter(out, Locale.US); - private final EventBus bus = new TimeServiceEventBus( - Clock.fixed(ofEpochSecond(0), ZoneId.of("UTC")), - UUID::randomUUID); - - @BeforeEach - void setup() { - summaryPrinter.setEventPublisher(bus); - } - @Test - void does_not_print_duplicate_snippets() { - bus.send(new SnippetsSuggestedEvent( - bus.getInstant(), - URI.create("classpath:com/example.feature"), - new Location(12, -1), - new Location(13, -1), - new Suggestion("", singletonList("snippet")))); - - bus.send(new SnippetsSuggestedEvent( - bus.getInstant(), - URI.create("classpath:com/example.feature"), - new Location(12, -1), - new Location(14, -1), - new Suggestion("", singletonList("snippet")))); - - bus.send(new TestRunFinished(bus.getInstant(), new Result(Status.PASSED, Duration.ZERO, null))); - - assertThat(new String(out.toByteArray(), UTF_8), equalCompressingLineSeparators("" + + void writesSummary() { + Feature feature = TestFeatureParser.parse("path/test.feature", "" + + "Feature: feature name\n" + + " Scenario: scenario name\n" + + " Given first step\n" + + " When second step\n" + + " Then third step\n"); + + StepDurationTimeService timeService = new StepDurationTimeService(Duration.ofMillis(1128)); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Runtime.builder() + .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) + .withFeatureSupplier(new StubFeatureSupplier(feature)) + .withAdditionalPlugins(timeService, new DefaultSummaryPrinter(out)) + .withRuntimeOptions(new RuntimeOptionsBuilder().setMonochrome().build()) + .withBackendSupplier(new StubBackendSupplier( + new StubStepDefinition("first step", oneReference()), + new StubStepDefinition("second step", twoReference()), + new StubStepDefinition("third step", threeReference()))) + .build() + .run(); + + assertThat(out, bytes(equalCompressingLineSeparators("" + "\n" + - "0 Scenarios\n" + - "0 Steps\n" + - "0m0.000s\n" + - "\n" + - "\n" + - "You can implement missing steps with the snippets below:\n" + - "\n" + - "snippet\n" + - "\n" + - "\n")); - + "1 Scenarios (1 passed)\n" + + "3 Steps (3 passed)\n" + + "0m3.384s\n"))); } } diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/StatsTest.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/StatsTest.java deleted file mode 100755 index fe180747ba..0000000000 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/StatsTest.java +++ /dev/null @@ -1,289 +0,0 @@ -package io.cucumber.core.plugin; - -import io.cucumber.plugin.event.Location; -import io.cucumber.plugin.event.Status; -import io.cucumber.plugin.event.TestCase; -import io.cucumber.plugin.event.TestStep; -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.net.URI; -import java.time.Instant; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.UUID; - -import static java.time.Duration.ofHours; -import static java.time.Duration.ofMillis; -import static java.time.Duration.ofMinutes; -import static java.time.Duration.ofSeconds; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.endsWith; -import static org.hamcrest.CoreMatchers.startsWith; -import static org.hamcrest.MatcherAssert.assertThat; - -class StatsTest { - - private static final Instant ANY_TIME = Instant.ofEpochMilli(1234567890); - - @Test - void should_print_zero_scenarios_zero_steps_if_nothing_has_executed() { - Stats counter = createMonochromeSummaryCounter(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - counter.printStats(new PrintStream(baos)); - - assertThat(baos.toString(), startsWith(String.format( - "0 Scenarios%n" + - "0 Steps%n"))); - } - - private Stats createMonochromeSummaryCounter() { - Stats stats = new Stats(Locale.US); - stats.setMonochrome(true); - return stats; - } - - @Test - void should_only_print_sub_counts_if_not_zero() { - Stats counter = createMonochromeSummaryCounter(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - counter.addStep(Status.PASSED); - counter.addStep(Status.PASSED); - counter.addStep(Status.PASSED); - counter.addScenario(Status.PASSED, createTestCase("classpath:com/example", 42, "scenario designation")); - counter.printStats(new PrintStream(baos)); - - assertThat(baos.toString(), startsWith(String.format( - "1 Scenarios (1 passed)%n" + - "3 Steps (3 passed)%n"))); - } - - @Test - void should_print_sub_counts_in_order_failed_ambiguous_skipped_pending_undefined_passed() { - Stats counter = createMonochromeSummaryCounter(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - addOneStepScenario(counter, Status.PASSED); - addOneStepScenario(counter, Status.FAILED); - addOneStepScenario(counter, Status.AMBIGUOUS); - addOneStepScenario(counter, Status.PENDING); - addOneStepScenario(counter, Status.UNDEFINED); - addOneStepScenario(counter, Status.SKIPPED); - counter.printStats(new PrintStream(baos)); - - assertThat(baos.toString(), containsString(String.format("" + - "6 Scenarios (1 failed, 1 ambiguous, 1 skipped, 1 pending, 1 undefined, 1 passed)%n" + - "6 Steps (1 failed, 1 ambiguous, 1 skipped, 1 pending, 1 undefined, 1 passed)%n"))); - } - - private void addOneStepScenario(Stats counter, Status status) { - counter.addStep(status); - counter.addScenario(status, createTestCase("classpath:com/example", 14, "scenario designation")); - } - - @Test - void should_print_sub_counts_in_order_failed_ambiguous_skipped_undefined_passed_in_color() { - Stats counter = createColorSummaryCounter(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - addOneStepScenario(counter, Status.PASSED); - addOneStepScenario(counter, Status.FAILED); - addOneStepScenario(counter, Status.AMBIGUOUS); - addOneStepScenario(counter, Status.PENDING); - addOneStepScenario(counter, Status.UNDEFINED); - addOneStepScenario(counter, Status.SKIPPED); - counter.printStats(new PrintStream(baos)); - - String colorSubCounts = "" + - AnsiEscapes.RED + "1 failed" + AnsiEscapes.RESET + ", " + - AnsiEscapes.RED + "1 ambiguous" + AnsiEscapes.RESET + ", " + - AnsiEscapes.CYAN + "1 skipped" + AnsiEscapes.RESET + ", " + - AnsiEscapes.YELLOW + "1 pending" + AnsiEscapes.RESET + ", " + - AnsiEscapes.YELLOW + "1 undefined" + AnsiEscapes.RESET + ", " + - AnsiEscapes.GREEN + "1 passed" + AnsiEscapes.RESET; - assertThat(baos.toString(), containsString(String.format("" + - "6 Scenarios (" + colorSubCounts + ")%n" + - "6 Steps (" + colorSubCounts + ")%n"))); - } - - private Stats createColorSummaryCounter() { - return new Stats(Locale.US); - } - - @Test - void should_print_zero_m_zero_s_if_nothing_has_executed() { - Stats counter = createMonochromeSummaryCounter(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - counter.printStats(new PrintStream(baos)); - - assertThat(baos.toString(), endsWith(String.format( - "0m0.000s%n"))); - } - - @Test - void should_report_the_difference_between_finish_time_and_start_time() { - Stats counter = createMonochromeSummaryCounter(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - counter.setStartTime(ANY_TIME); - counter.setFinishTime(ANY_TIME.plus(ofMillis(4))); - counter.printStats(new PrintStream(baos)); - - assertThat(baos.toString(), endsWith(String.format( - "0m0.004s%n"))); - } - - @Test - void should_print_minutes_seconds_and_milliseconds() { - Stats counter = createMonochromeSummaryCounter(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - counter.setStartTime(ANY_TIME); - counter.setFinishTime(ANY_TIME.plus(ofMinutes(1)).plus(ofSeconds(1)).plus(ofMillis(1))); - counter.printStats(new PrintStream(baos)); - - assertThat(baos.toString(), endsWith(String.format( - "1m1.001s%n"))); - } - - @Test - void should_print_minutes_instead_of_hours() { - Stats counter = createMonochromeSummaryCounter(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - counter.setStartTime(ANY_TIME); - counter.setFinishTime(ANY_TIME.plus(ofHours(1)).plus(ofMinutes(1))); - counter.printStats(new PrintStream(baos)); - - assertThat(baos.toString(), endsWith(String.format( - "61m0.000s%n"))); - } - - @Test - void should_use_locale_for_decimal_separator() { - Stats counter = new Stats(Locale.GERMANY); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - counter.setStartTime(ANY_TIME); - counter.setFinishTime(ANY_TIME.plus(ofMinutes(1)).plus(ofSeconds(1)).plus(ofMillis(1))); - counter.printStats(new PrintStream(baos)); - - assertThat(baos.toString(), endsWith(String.format("1m1,001s%n"))); - } - - @Test - void should_print_failed_ambiguous_scenarios() { - Stats counter = createMonochromeSummaryCounter(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - counter.addStep(Status.FAILED); - counter.addScenario(Status.FAILED, createTestCase("path/file.feature", 3, "Scenario: scenario_name")); - counter.addStep(Status.AMBIGUOUS); - counter.addScenario(Status.AMBIGUOUS, createTestCase("path/file.feature", 3, "Scenario: scenario_name")); - counter.addStep(Status.UNDEFINED); - counter.addScenario(Status.UNDEFINED, createTestCase("path/file.feature", 3, "Scenario: scenario_name")); - counter.addStep(Status.PENDING); - counter.addScenario(Status.PENDING, createTestCase("path/file.feature", 3, "Scenario: scenario_name")); - counter.printStats(new PrintStream(baos)); - - assertThat(baos.toString(), startsWith(String.format("" + - "Failed scenarios:%n" + - "path/file.feature:3 # Scenario: scenario_name%n" + - "%n" + - "Ambiguous scenarios:%n" + - "path/file.feature:3 # Scenario: scenario_name%n" + - "%n" + - "Pending scenarios:%n" + - "path/file.feature:3 # Scenario: scenario_name%n" + - "%n" + - "Undefined scenarios:%n" + - "path/file.feature:3 # Scenario: scenario_name%n" + - "%n" + - "4 Scenarios"))); - } - - @Test - void should_print_failed_ambiguous_pending_undefined_scenarios() { - Stats counter = createMonochromeSummaryCounter(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - counter.addStep(Status.FAILED); - counter.addScenario(Status.FAILED, createTestCase("path/file.feature", 3, "Scenario: scenario_name")); - counter.addStep(Status.AMBIGUOUS); - counter.addScenario(Status.AMBIGUOUS, createTestCase("path/file.feature", 3, "Scenario: scenario_name")); - counter.addStep(Status.UNDEFINED); - counter.addScenario(Status.UNDEFINED, createTestCase("path/file.feature", 3, "Scenario: scenario_name")); - counter.addStep(Status.PENDING); - counter.addScenario(Status.PENDING, createTestCase("path/file.feature", 3, "Scenario: scenario_name")); - counter.printStats(new PrintStream(baos)); - - assertThat(baos.toString(), startsWith(String.format("" + - "Failed scenarios:%n" + - "path/file.feature:3 # Scenario: scenario_name%n" + - "%n" + - "Ambiguous scenarios:%n" + - "path/file.feature:3 # Scenario: scenario_name%n" + - "%n" + - "Pending scenarios:%n" + - "path/file.feature:3 # Scenario: scenario_name%n" + - "%n" + - "Undefined scenarios:%n" + - "path/file.feature:3 # Scenario: scenario_name%n" + - "%n" + - "4 Scenarios"))); - } - - private static TestCase createTestCase(String uri, int line, String name) { - return new TestCase() { - @Override - public Integer getLine() { - return getLocation().getLine(); - } - - @Override - public Location getLocation() { - return new Location(line, -1); - } - - @Override - public String getKeyword() { - return "Scenario"; - } - - @Override - public String getName() { - return name; - } - - @Override - public String getScenarioDesignation() { - return null; - } - - @Override - public List getTags() { - return Collections.emptyList(); - } - - @Override - public List getTestSteps() { - return Collections.emptyList(); - } - - @Override - public URI getUri() { - return URI.create(uri); - } - - @Override - public UUID getId() { - return UUID.randomUUID(); - } - }; - } - -}