| /* |
| * Copyright 2015-2022 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 v2.0 which |
| * accompanies this distribution and is available at |
| * |
| * https://www.eclipse.org/legal/epl-v20.html |
| */ |
| |
| package org.junit.platform.console; |
| |
| import static java.lang.String.format; |
| import static java.nio.charset.StandardCharsets.UTF_8; |
| import static org.junit.jupiter.api.Assertions.assertLinesMatch; |
| import static org.junit.jupiter.api.Assertions.fail; |
| import static org.junit.jupiter.api.Assumptions.assumeTrue; |
| import static org.junit.jupiter.api.DynamicContainer.dynamicContainer; |
| import static org.junit.jupiter.api.DynamicTest.dynamicTest; |
| import static org.junit.platform.commons.util.ReflectionUtils.findMethods; |
| import static org.junit.platform.commons.util.ReflectionUtils.getFullyQualifiedMethodName; |
| |
| import java.io.File; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.nio.file.Files; |
| import java.nio.file.Paths; |
| import java.util.ArrayList; |
| import java.util.EnumMap; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Optional; |
| |
| import org.junit.jupiter.api.Disabled; |
| import org.junit.jupiter.api.DisplayName; |
| import org.junit.jupiter.api.DynamicNode; |
| import org.junit.jupiter.api.DynamicTest; |
| import org.junit.jupiter.api.Test; |
| import org.junit.jupiter.api.TestFactory; |
| import org.junit.jupiter.api.TestReporter; |
| import org.junit.jupiter.api.function.Executable; |
| import org.junit.platform.console.options.Details; |
| import org.junit.platform.console.options.Theme; |
| import org.opentest4j.TestAbortedException; |
| |
| /** |
| * @since 1.0 |
| */ |
| class ConsoleDetailsTests { |
| |
| @TestFactory |
| @DisplayName("Basic tests and annotations usage") |
| List<DynamicNode> basic() { |
| return scanContainerClassAndCreateDynamicTests(BasicTestCase.class); |
| } |
| |
| @TestFactory |
| @DisplayName("Skipped and disabled tests") |
| List<DynamicNode> skipped() { |
| return scanContainerClassAndCreateDynamicTests(SkipTestCase.class); |
| } |
| |
| @TestFactory |
| @DisplayName("Failed tests") |
| List<DynamicNode> failed() { |
| return scanContainerClassAndCreateDynamicTests(FailTestCase.class); |
| } |
| |
| @TestFactory |
| @DisplayName("Tests publishing report entries") |
| List<DynamicNode> reports() { |
| return scanContainerClassAndCreateDynamicTests(ReportTestCase.class); |
| } |
| |
| private List<DynamicNode> scanContainerClassAndCreateDynamicTests(Class<?> containerClass) { |
| var containerName = containerClass.getSimpleName().replace("TestCase", ""); |
| // String containerName = containerClass.getSimpleName(); |
| List<DynamicNode> nodes = new ArrayList<>(); |
| Map<Details, List<DynamicTest>> map = new EnumMap<>(Details.class); |
| for (var method : findMethods(containerClass, m -> m.isAnnotationPresent(Test.class))) { |
| var methodName = method.getName(); |
| var types = method.getParameterTypes(); |
| for (var details : Details.values()) { |
| var tests = map.computeIfAbsent(details, key -> new ArrayList<>()); |
| for (var theme : Theme.values()) { |
| var caption = containerName + "-" + methodName + "-" + details + "-" + theme; |
| String[] args = { // |
| "--include-engine", "junit-jupiter", // |
| "--details", details.name(), // |
| "--details-theme", theme.name(), // |
| "--disable-ansi-colors", // |
| "--disable-banner", // |
| "--include-classname", containerClass.getCanonicalName(), // |
| "--select-method", getFullyQualifiedMethodName(containerClass, methodName, types) // |
| }; |
| var displayName = methodName + "() " + theme.name(); |
| var dirName = "console/details/" + containerName.toLowerCase(); |
| var outName = caption + ".out.txt"; |
| var runner = new Runner(dirName, outName, args); |
| var source = toUri(dirName, outName).orElse(null); |
| tests.add(dynamicTest(displayName, source, runner)); |
| } |
| } |
| } |
| var source = new File("src/test/resources/console/details").toURI(); |
| map.forEach((details, tests) -> nodes.add(dynamicContainer(details.name(), source, tests.stream()))); |
| return nodes; |
| } |
| |
| @DisplayName("Basic") |
| static class BasicTestCase { |
| |
| @Test |
| void empty() { |
| } |
| |
| @Test |
| @DisplayName(".oO fancy display name Oo.") |
| void changeDisplayName() { |
| } |
| |
| } |
| |
| @DisplayName("Skip") |
| static class SkipTestCase { |
| |
| @Test |
| @Disabled("single line skip reason") |
| void skipWithSingleLineReason() { |
| } |
| |
| @Test |
| @Disabled("multi\nline\nfail\nmessage") |
| void skipWithMultiLineMessage() { |
| } |
| |
| } |
| |
| @DisplayName("Fail") |
| static class FailTestCase { |
| |
| @Test |
| void failWithSingleLineMessage() { |
| fail("single line fail message"); |
| } |
| |
| @Test |
| void failWithMultiLineMessage() { |
| fail("multi\nline\nfail\nmessage"); |
| } |
| |
| } |
| |
| @DisplayName("Report") |
| static class ReportTestCase { |
| |
| @Test |
| void reportSingleMessage(TestReporter reporter) { |
| reporter.publishEntry("foo"); |
| } |
| |
| @Test |
| void reportMultipleMessages(TestReporter reporter) { |
| reporter.publishEntry("foo"); |
| reporter.publishEntry("bar"); |
| } |
| |
| @Test |
| void reportSingleEntryWithSingleMapping(TestReporter reporter) { |
| reporter.publishEntry("foo", "bar"); |
| } |
| |
| @Test |
| void reportMultiEntriesWithSingleMapping(TestReporter reporter) { |
| reporter.publishEntry("foo", "bar"); |
| reporter.publishEntry("far", "boo"); |
| } |
| |
| @Test |
| void reportMultiEntriesWithMultiMappings(TestReporter reporter) { |
| Map<String, String> values = new LinkedHashMap<>(); |
| values.put("user name", "dk38"); |
| values.put("award year", "1974"); |
| reporter.publishEntry(values); |
| reporter.publishEntry("single", "mapping"); |
| Map<String, String> more = new LinkedHashMap<>(); |
| more.put("user name", "st77"); |
| more.put("award year", "1977"); |
| more.put("last seen", "2001"); |
| reporter.publishEntry(more); |
| } |
| |
| } |
| |
| private static class Runner implements Executable { |
| |
| private final String dirName; |
| private final String outName; |
| private final String[] args; |
| |
| private Runner(String dirName, String outName, String... args) { |
| this.dirName = dirName; |
| this.outName = outName; |
| this.args = args; |
| } |
| |
| @Override |
| public void execute() throws Throwable { |
| var wrapper = new ConsoleLauncherWrapper(); |
| var result = wrapper.execute(Optional.empty(), args); |
| |
| var optionalUri = toUri(dirName, outName); |
| if (optionalUri.isEmpty()) { |
| if (Boolean.getBoolean("org.junit.platform.console.ConsoleDetailsTests.writeResultOut")) { |
| // do not use Files.createTempDirectory(prefix) as we want one folder for one container |
| var temp = Paths.get(System.getProperty("java.io.tmpdir"), dirName.replace('/', '-')); |
| Files.createDirectories(temp); |
| var path = Files.writeString(temp.resolve(outName), result.out); |
| throw new TestAbortedException( |
| format("resource `%s` not found\nwrote console stdout to: %s/%s", dirName, outName, path)); |
| } |
| fail("could not load resource named `" + dirName + "/" + outName + "`"); |
| } |
| |
| var path = Paths.get(optionalUri.get()); |
| assumeTrue(Files.exists(path), "path does not exist: " + path); |
| assumeTrue(Files.isReadable(path), "can not read: " + path); |
| |
| var expectedLines = Files.readAllLines(path, UTF_8); |
| var actualLines = List.of(result.out.split("\\R")); |
| |
| assertLinesMatch(expectedLines, actualLines); |
| } |
| } |
| |
| static Optional<URI> toUri(String dirName, String outName) { |
| var resourceName = dirName + "/" + outName; |
| var url = ConsoleDetailsTests.class.getClassLoader().getResource(resourceName); |
| if (url == null) { |
| return Optional.empty(); |
| } |
| try { |
| return Optional.of(url.toURI()); |
| } |
| catch (URISyntaxException e) { |
| return Optional.empty(); |
| } |
| } |
| |
| } |