diff --git a/docs/modules/pkl-cli/pages/index.adoc b/docs/modules/pkl-cli/pages/index.adoc index 8bb91adb..6161614b 100644 --- a/docs/modules/pkl-cli/pages/index.adoc +++ b/docs/modules/pkl-cli/pages/index.adoc @@ -546,6 +546,15 @@ When enabled, test failures will show intermediate values in the assertion expre Use `--no-power-assertions` to disable this feature if you prefer simpler output. ==== +[[test-reporter]] +.--test-reporter +[%collapsible] +==== +Default: `spec` + +Example: `--test-reporter minimal` + +Which test reporter to use for CLI output. Possible values are `spec` and `minimal`. +==== + This command also takes <>. [[command-run]] @@ -667,6 +676,14 @@ Force generation of expected examples. + The old expected files will be deleted if present. ==== +.--test-reporter +[%collapsible] +==== +Default: `spec` + +Example: `--test-reporter minimal` + +Which test reporter to use for CLI output. Possible values are `spec` and `minimal`. +==== + This command also takes <>. [[command-project-resolve]] diff --git a/docs/modules/pkl-gradle/pages/index.adoc b/docs/modules/pkl-gradle/pages/index.adoc index 89c11a10..e05a6c24 100644 --- a/docs/modules/pkl-gradle/pages/index.adoc +++ b/docs/modules/pkl-gradle/pages/index.adoc @@ -322,6 +322,15 @@ Default: `false` + Whether to ignore expected example files and generate them again. ==== +[[test-reporter]] +.test-reporter: Property +[%collapsible] +==== +Default: `"spec"` + +Example: `reporter = "minimal"` + +Which test reporter to use for CLI output. Possible values are `"spec"` and `"minimal"`. +==== + [[power-assertions-test]] .powerAssertions: Property [%collapsible] @@ -677,6 +686,14 @@ Default: `false` + Whether to ignore expected example files and generate them again. ==== +.test-reporter: Property +[%collapsible] +==== +Default: `"spec"` + +Example: `reporter = "minimal"` + +Which test reporter to use for CLI output. Possible values are `"spec"` and `"minimal"`. +==== + Common properties: include::../partials/gradle-common-properties.adoc[] diff --git a/pkl-cli/src/main/kotlin/org/pkl/cli/CliTestRunner.kt b/pkl-cli/src/main/kotlin/org/pkl/cli/CliTestRunner.kt index bfe00b18..d3418e4d 100644 --- a/pkl-cli/src/main/kotlin/org/pkl/cli/CliTestRunner.kt +++ b/pkl-cli/src/main/kotlin/org/pkl/cli/CliTestRunner.kt @@ -15,6 +15,7 @@ */ package org.pkl.cli +import java.io.StringWriter import java.io.Writer import org.pkl.commons.cli.* import org.pkl.core.Closeables @@ -22,8 +23,9 @@ import org.pkl.core.EvaluatorBuilder import org.pkl.core.ModuleSource.uri import org.pkl.core.PklException import org.pkl.core.TestResults -import org.pkl.core.stdlib.test.report.JUnitReport -import org.pkl.core.stdlib.test.report.SimpleReport +import org.pkl.core.stdlib.test.report.JUnitReporter +import org.pkl.core.stdlib.test.report.MinimalReporter +import org.pkl.core.stdlib.test.report.SpecReporter import org.pkl.core.util.ErrorMessages class CliTestRunner @@ -64,13 +66,15 @@ constructor( var failed = false var isExampleWrittenFailure = true val moduleNames = mutableSetOf() - val reporter = SimpleReport(useColor) + val reporter = + when (testOptions.reporter) { + TestReporter.SPEC -> SpecReporter(useColor) + TestReporter.MINIMAL -> MinimalReporter(useColor) + } val allTestResults = mutableListOf() val junitDir = testOptions.junitDir - if (junitDir != null) { - junitDir.toFile().mkdirs() - } + junitDir?.toFile()?.mkdirs() for ((idx, moduleUri) in sources.withIndex()) { try { @@ -80,8 +84,11 @@ constructor( failed = results.failed() isExampleWrittenFailure = results.isExampleWrittenFailure.and(isExampleWrittenFailure) } - reporter.report(results, consoleWriter) - if (sources.size > 1 && idx != sources.size - 1) { + val tmpWriter = StringWriter() + reporter.report(results, tmpWriter) + val report = tmpWriter.toString() + consoleWriter.write(report) + if (report.isNotEmpty() && sources.size > 1 && idx != sources.size - 1) { consoleWriter.append('\n') } consoleWriter.flush() @@ -101,7 +108,7 @@ constructor( moduleNames += moduleName if (!testOptions.junitAggregateReports) { - JUnitReport().reportToPath(results, junitDir.resolve(moduleName)) + JUnitReporter().reportToPath(results, junitDir.resolve(moduleName)) } } } catch (ex: Exception) { @@ -119,7 +126,7 @@ constructor( } if (testOptions.junitAggregateReports && junitDir != null) { val fileName = "${testOptions.junitAggregateSuiteName}.xml" - JUnitReport(testOptions.junitAggregateSuiteName) + JUnitReporter(testOptions.junitAggregateSuiteName) .summarizeToPath(allTestResults, junitDir.resolve(fileName)) } consoleWriter.append('\n') diff --git a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliTestOptions.kt b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliTestOptions.kt index 0915abf2..c79a0b1f 100644 --- a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliTestOptions.kt +++ b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliTestOptions.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,4 +22,5 @@ class CliTestOptions( val overwrite: Boolean = false, val junitAggregateReports: Boolean = false, val junitAggregateSuiteName: String = "pkl-tests", + val reporter: TestReporter = TestReporter.SPEC, ) diff --git a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/TestReporter.kt b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/TestReporter.kt new file mode 100644 index 00000000..54d4534d --- /dev/null +++ b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/TestReporter.kt @@ -0,0 +1,21 @@ +/* + * Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved. + * + * Licensed 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.pkl.commons.cli + +enum class TestReporter { + SPEC, + MINIMAL, +} diff --git a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/commands/TestOptions.kt b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/commands/TestOptions.kt index 0c0715df..2de7df94 100644 --- a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/commands/TestOptions.kt +++ b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/commands/TestOptions.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,9 +19,11 @@ import com.github.ajalt.clikt.parameters.groups.OptionGroup import com.github.ajalt.clikt.parameters.options.default import com.github.ajalt.clikt.parameters.options.flag import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.types.enum import com.github.ajalt.clikt.parameters.types.path import java.nio.file.Path import org.pkl.commons.cli.CliTestOptions +import org.pkl.commons.cli.TestReporter class TestOptions : OptionGroup() { private val junitReportDir: Path? by @@ -51,7 +53,19 @@ class TestOptions : OptionGroup() { private val overwrite: Boolean by option(names = arrayOf("--overwrite"), help = "Force generation of expected examples.").flag() + private val reporter: TestReporter by + option(names = arrayOf("--test-reporter"), help = "Which test reporter to use for CLI output.") + .enum { it.name.lowercase() } + .single() + .default(TestReporter.SPEC) + val cliTestOptions: CliTestOptions by lazy { - CliTestOptions(junitReportDir, overwrite, junitAggregateReports, junitAggregateSuiteName) + CliTestOptions( + junitReportDir, + overwrite, + junitAggregateReports, + junitAggregateSuiteName, + reporter, + ) } } diff --git a/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/SimpleReport.java b/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/BaseReporter.java similarity index 74% rename from pkl-core/src/main/java/org/pkl/core/stdlib/test/report/SimpleReport.java rename to pkl-core/src/main/java/org/pkl/core/stdlib/test/report/BaseReporter.java index d2b138f9..da855f0d 100644 --- a/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/SimpleReport.java +++ b/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/BaseReporter.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,41 +24,21 @@ import java.util.Locale; import java.util.stream.Collectors; import org.pkl.core.TestResults; import org.pkl.core.TestResults.TestResult; -import org.pkl.core.TestResults.TestSectionResults; import org.pkl.core.util.AnsiStringBuilder; import org.pkl.core.util.AnsiStringBuilder.AnsiCode; import org.pkl.core.util.AnsiTheme; import org.pkl.core.util.StringUtils; -public final class SimpleReport implements TestReport { +public abstract class BaseReporter implements TestReporter { + protected static final String passingMark = "✔ "; + protected static final String failingMark = "✘ "; - private static final String passingMark = "✔ "; - private static final String failingMark = "✘ "; + protected final boolean useColor; - private final boolean useColor; - - public SimpleReport(boolean useColor) { + public BaseReporter(boolean useColor) { this.useColor = useColor; } - @Override - public void report(TestResults results, Writer writer) throws IOException { - var builder = new AnsiStringBuilder(useColor); - - builder.append("module ").append(results.moduleName()).append('\n'); - - if (results.error() != null) { - var rendered = results.error().exception().getMessage(); - appendPadded(builder, rendered, " "); - builder.append('\n'); - } else { - reportResults(results.facts(), builder); - reportResults(results.examples(), builder); - } - - writer.append(builder.toString()); - } - @Override public void summarize(List allTestResults, Writer writer) throws IOException { var totalTests = 0; @@ -91,16 +71,7 @@ public final class SimpleReport implements TestReport { writer.append(builder.toString()); } - private void reportResults(TestSectionResults section, AnsiStringBuilder builder) { - if (!section.results().isEmpty()) { - builder.append(" ").append(section.name()).append('\n'); - StringUtils.joinToStringBuilder( - builder, section.results(), "\n", res -> reportResult(res, builder)); - builder.append('\n'); - } - } - - private void reportResult(TestResult result, AnsiStringBuilder builder) { + protected void reportResult(TestResult result, AnsiStringBuilder builder) { builder.append(" "); if (result.isExampleWritten()) { @@ -129,7 +100,7 @@ public final class SimpleReport implements TestReport { } } - private static void appendPadded(AnsiStringBuilder builder, String lines, String padding) { + protected static void appendPadded(AnsiStringBuilder builder, String lines, String padding) { StringUtils.joinToStringBuilder( builder, lines.lines().collect(Collectors.toList()), diff --git a/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/JUnitReport.java b/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/JUnitReporter.java similarity index 98% rename from pkl-core/src/main/java/org/pkl/core/stdlib/test/report/JUnitReport.java rename to pkl-core/src/main/java/org/pkl/core/stdlib/test/report/JUnitReporter.java index 41c62175..3a1169ae 100644 --- a/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/JUnitReport.java +++ b/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/JUnitReporter.java @@ -37,15 +37,15 @@ import org.pkl.core.stdlib.PklConverter; import org.pkl.core.stdlib.xml.RendererNodes.Renderer; import org.pkl.core.util.EconomicMaps; -public final class JUnitReport implements TestReport { +public final class JUnitReporter implements TestReporter { private final String aggregateSuiteName; - public JUnitReport(String aggregateSuiteName) { + public JUnitReporter(String aggregateSuiteName) { this.aggregateSuiteName = aggregateSuiteName; } - public JUnitReport() { + public JUnitReporter() { this(""); } diff --git a/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/MinimalReporter.java b/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/MinimalReporter.java new file mode 100644 index 00000000..39af8437 --- /dev/null +++ b/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/MinimalReporter.java @@ -0,0 +1,67 @@ +/* + * Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved. + * + * Licensed 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.pkl.core.stdlib.test.report; + +import java.io.IOException; +import java.io.Writer; +import java.util.List; +import org.pkl.core.TestResults; +import org.pkl.core.TestResults.TestResult; +import org.pkl.core.TestResults.TestSectionResults; +import org.pkl.core.util.AnsiStringBuilder; +import org.pkl.core.util.StringUtils; + +/** Minimal reporter. Only reports failures and errors. */ +public final class MinimalReporter extends BaseReporter { + + public MinimalReporter(boolean useColor) { + super(useColor); + } + + @Override + public void report(TestResults results, Writer writer) throws IOException { + var builder = new AnsiStringBuilder(useColor); + + if (results.error() != null) { + builder.append("module ").append(results.moduleName()).append('\n'); + + var rendered = results.error().exception().getMessage(); + appendPadded(builder, rendered, " "); + builder.append('\n'); + } else { + var factFailures = results.facts().results().stream().filter(TestResult::isFailure).toList(); + var exampleFailures = + results.examples().results().stream().filter(TestResult::isFailure).toList(); + if (!factFailures.isEmpty() || !exampleFailures.isEmpty()) { + builder.append("module ").append(results.moduleName()).append('\n'); + + reportResults(results.facts(), factFailures, builder); + reportResults(results.examples(), exampleFailures, builder); + } + } + + writer.append(builder.toString()); + } + + private void reportResults( + TestSectionResults section, List results, AnsiStringBuilder builder) { + if (!results.isEmpty()) { + builder.append(" ").append(section.name()).append('\n'); + StringUtils.joinToStringBuilder(builder, results, "\n", res -> reportResult(res, builder)); + builder.append('\n'); + } + } +} diff --git a/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/SpecReporter.java b/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/SpecReporter.java new file mode 100644 index 00000000..1a7e9e02 --- /dev/null +++ b/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/SpecReporter.java @@ -0,0 +1,57 @@ +/* + * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. + * + * Licensed 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.pkl.core.stdlib.test.report; + +import java.io.IOException; +import java.io.Writer; +import org.pkl.core.TestResults; +import org.pkl.core.TestResults.TestSectionResults; +import org.pkl.core.util.AnsiStringBuilder; +import org.pkl.core.util.StringUtils; + +public final class SpecReporter extends BaseReporter { + + public SpecReporter(boolean useColor) { + super(useColor); + } + + @Override + public void report(TestResults results, Writer writer) throws IOException { + var builder = new AnsiStringBuilder(useColor); + + builder.append("module ").append(results.moduleName()).append('\n'); + + if (results.error() != null) { + var rendered = results.error().exception().getMessage(); + appendPadded(builder, rendered, " "); + builder.append('\n'); + } else { + reportResults(results.facts(), builder); + reportResults(results.examples(), builder); + } + + writer.append(builder.toString()); + } + + private void reportResults(TestSectionResults section, AnsiStringBuilder builder) { + if (!section.results().isEmpty()) { + builder.append(" ").append(section.name()).append('\n'); + StringUtils.joinToStringBuilder( + builder, section.results(), "\n", res -> reportResult(res, builder)); + builder.append('\n'); + } + } +} diff --git a/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/TestReport.java b/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/TestReporter.java similarity index 95% rename from pkl-core/src/main/java/org/pkl/core/stdlib/test/report/TestReport.java rename to pkl-core/src/main/java/org/pkl/core/stdlib/test/report/TestReporter.java index 03031992..b263512f 100644 --- a/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/TestReport.java +++ b/pkl-core/src/main/java/org/pkl/core/stdlib/test/report/TestReporter.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ import org.pkl.core.PklBugException; import org.pkl.core.TestResults; import org.pkl.core.util.StringBuilderWriter; -public interface TestReport { +public interface TestReporter { void report(TestResults results, Writer writer) throws IOException; diff --git a/pkl-core/src/test/kotlin/org/pkl/core/stdlib/MinimalReportTest.kt b/pkl-core/src/test/kotlin/org/pkl/core/stdlib/MinimalReportTest.kt new file mode 100644 index 00000000..560a510b --- /dev/null +++ b/pkl-core/src/test/kotlin/org/pkl/core/stdlib/MinimalReportTest.kt @@ -0,0 +1,158 @@ +/* + * Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved. + * + * Licensed 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.pkl.core.stdlib + +import java.io.StringWriter +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.pkl.core.TestResults +import org.pkl.core.TestResults.TestResult +import org.pkl.core.TestResults.TestSectionResults +import org.pkl.core.stdlib.test.report.MinimalReporter + +class MinimalReportTest { + + @Test + fun `report with only passing tests does not show module or test names`() { + val resultsBuilder = TestResults.Builder("module1", "module1") + resultsBuilder.setFactsSection( + TestSectionResults( + TestResults.TestSectionName.FACTS, + listOf(TestResult("passing fact", 1, emptyList(), emptyList(), false)), + ) + ) + resultsBuilder.setExamplesSection( + TestSectionResults( + TestResults.TestSectionName.EXAMPLES, + listOf(TestResult("passing example", 1, emptyList(), emptyList(), false)), + ) + ) + val testResults = resultsBuilder.build() + + val writer = StringWriter() + val minimalReport = MinimalReporter(false) + minimalReport.report(testResults, writer) + + assertThat(writer.toString()).isEmpty() + } + + @Test + fun `report with failures shows module name and only failed tests`() { + val resultsBuilder = TestResults.Builder("module1", "module1") + resultsBuilder.setFactsSection( + TestSectionResults( + TestResults.TestSectionName.FACTS, + listOf( + TestResult("passing fact", 1, emptyList(), emptyList(), false), + TestResult( + "failing fact", + 1, + listOf(TestResults.Failure("Fact Failure", "failed")), + emptyList(), + false, + ), + ), + ) + ) + resultsBuilder.setExamplesSection( + TestSectionResults( + TestResults.TestSectionName.EXAMPLES, + listOf(TestResult("passing example", 1, emptyList(), emptyList(), false)), + ) + ) + val testResults = resultsBuilder.build() + + val writer = StringWriter() + val minimalReport = MinimalReporter(false) + minimalReport.report(testResults, writer) + + val output = writer.toString() + assertThat(output).contains("module module1") + assertThat(output).contains("failing fact") + assertThat(output).doesNotContain("passing fact") + assertThat(output).doesNotContain("passing example") + assertThat(output).doesNotContain("examples") + } + + @Test + fun `summarize includes stats even when all tests pass`() { + val resultsBuilder = TestResults.Builder("module1", "module1") + resultsBuilder.setFactsSection( + TestSectionResults( + TestResults.TestSectionName.FACTS, + listOf(TestResult("passing fact", 1, emptyList(), emptyList(), false)), + ) + ) + resultsBuilder.setExamplesSection( + TestSectionResults(TestResults.TestSectionName.EXAMPLES, emptyList()) + ) + val testResults = listOf(resultsBuilder.build()) + + val writer = StringWriter() + val minimalReport = MinimalReporter(false) + minimalReport.summarize(testResults, writer) + + val output = writer.toString() + assertThat(output).contains("100.0% tests pass") + assertThat(output).contains("1 passed") + } + + @Test + fun `summarize method should generate correct output for failures`() { + val resultsBuilder = TestResults.Builder("module1", "module1") + resultsBuilder.setFactsSection( + TestSectionResults( + TestResults.TestSectionName.FACTS, + listOf( + TestResult( + "example1", + 321919, + listOf(TestResults.Failure("Fact Failure", "failed")), + emptyList(), + false, + ) + ), + ) + ) + resultsBuilder.setExamplesSection( + TestSectionResults( + TestResults.TestSectionName.EXAMPLES, + listOf( + TestResult( + "example1", + 432525, + listOf(TestResults.Failure("Output Mismatch", "does not match")), + emptyList(), + false, + ) + ), + ) + ) + val testResults = listOf(resultsBuilder.build()) + + val writer = StringWriter() + val minimalReport = MinimalReporter(false) + minimalReport.summarize(testResults, writer) + + val expectedOutput = + """ + 0.0% tests pass [2/2 failed], 99.9% asserts pass [2/754444 failed] + """ + .trimIndent() + + assertThat(writer.toString().trimIndent()).isEqualTo(expectedOutput) + } +} diff --git a/pkl-core/src/test/kotlin/org/pkl/core/stdlib/SimpleReportTest.kt b/pkl-core/src/test/kotlin/org/pkl/core/stdlib/SimpleReportTest.kt index 133fdc4e..d79c3f50 100644 --- a/pkl-core/src/test/kotlin/org/pkl/core/stdlib/SimpleReportTest.kt +++ b/pkl-core/src/test/kotlin/org/pkl/core/stdlib/SimpleReportTest.kt @@ -16,19 +16,18 @@ package org.pkl.core.stdlib import java.io.StringWriter -import java.util.* import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.pkl.core.TestResults import org.pkl.core.TestResults.TestResult import org.pkl.core.TestResults.TestSectionResults -import org.pkl.core.stdlib.test.report.SimpleReport +import org.pkl.core.stdlib.test.report.SpecReporter class SimpleReportTest { @Test fun `summarize method should generate correct output`() { - var resultsBuilder = TestResults.Builder("module1", "module1") + val resultsBuilder = TestResults.Builder("module1", "module1") resultsBuilder.setFactsSection( TestSectionResults( TestResults.TestSectionName.FACTS, @@ -60,7 +59,7 @@ class SimpleReportTest { val testResults = listOf(resultsBuilder.build()) val writer = StringWriter() - val simpleReport = SimpleReport(false) + val simpleReport = SpecReporter(false) simpleReport.summarize(testResults, writer) val expectedOutput = diff --git a/pkl-gradle/src/main/java/org/pkl/gradle/PklPlugin.java b/pkl-gradle/src/main/java/org/pkl/gradle/PklPlugin.java index 1e28b28d..10091ae2 100644 --- a/pkl-gradle/src/main/java/org/pkl/gradle/PklPlugin.java +++ b/pkl-gradle/src/main/java/org/pkl/gradle/PklPlugin.java @@ -100,6 +100,7 @@ public class PklPlugin implements Plugin { spec.getOutputPath() .convention(project.getLayout().getBuildDirectory().dir("generated/pkl/packages")); spec.getOverwrite().convention(false); + spec.getReporter().convention("spec"); var packageTask = createTask(project, ProjectPackageTask.class, spec); packageTask.configure( task -> { @@ -108,6 +109,7 @@ public class PklPlugin implements Plugin { task.getSkipPublishCheck().set(spec.getSkipPublishCheck()); task.getJunitReportsDir().set(spec.getJunitReportsDir()); task.getOverwrite().set(spec.getOverwrite()); + task.getTestReporter().set(spec.getReporter()); }); project .getPluginManager() @@ -278,12 +280,14 @@ public class PklPlugin implements Plugin { configureBaseSpec(project, spec); spec.getOverwrite().convention(false); + spec.getTestReporter().convention("spec"); var testTask = createModulesTask(project, TestTask.class, spec); testTask.configure( task -> { task.getJunitReportsDir().set(spec.getJunitReportsDir()); task.getOverwrite().set(spec.getOverwrite()); + task.getTestReporter().set(spec.getTestReporter()); }); project diff --git a/pkl-gradle/src/main/java/org/pkl/gradle/spec/ProjectPackageSpec.java b/pkl-gradle/src/main/java/org/pkl/gradle/spec/ProjectPackageSpec.java index 0212b3f9..42d4d261 100644 --- a/pkl-gradle/src/main/java/org/pkl/gradle/spec/ProjectPackageSpec.java +++ b/pkl-gradle/src/main/java/org/pkl/gradle/spec/ProjectPackageSpec.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,4 +29,6 @@ public interface ProjectPackageSpec extends BasePklSpec { Property getOverwrite(); Property getSkipPublishCheck(); + + Property getReporter(); } diff --git a/pkl-gradle/src/main/java/org/pkl/gradle/spec/TestSpec.java b/pkl-gradle/src/main/java/org/pkl/gradle/spec/TestSpec.java index 53dfbf60..e9765eee 100644 --- a/pkl-gradle/src/main/java/org/pkl/gradle/spec/TestSpec.java +++ b/pkl-gradle/src/main/java/org/pkl/gradle/spec/TestSpec.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,4 +22,6 @@ public interface TestSpec extends ModulesSpec { DirectoryProperty getJunitReportsDir(); Property getOverwrite(); + + Property getTestReporter(); } diff --git a/pkl-gradle/src/main/java/org/pkl/gradle/task/ProjectPackageTask.java b/pkl-gradle/src/main/java/org/pkl/gradle/task/ProjectPackageTask.java index b1ff4358..5046fec3 100644 --- a/pkl-gradle/src/main/java/org/pkl/gradle/task/ProjectPackageTask.java +++ b/pkl-gradle/src/main/java/org/pkl/gradle/task/ProjectPackageTask.java @@ -34,6 +34,7 @@ import org.gradle.api.tasks.PathSensitivity; import org.gradle.api.tasks.UntrackedTask; import org.pkl.cli.CliProjectPackager; import org.pkl.commons.cli.CliTestOptions; +import org.pkl.commons.cli.TestReporter; @UntrackedTask(because = "Output names are known only after execution") public abstract class ProjectPackageTask extends BasePklTask { @@ -62,6 +63,10 @@ public abstract class ProjectPackageTask extends BasePklTask { @Optional public abstract Property getSkipPublishCheck(); + @Input + @Optional + public abstract Property getTestReporter(); + public ProjectPackageTask() { this.getJunitAggregateSuiteName().convention("pkl-tests"); } @@ -75,6 +80,18 @@ public abstract class ProjectPackageTask extends BasePklTask { if (projectDirectories.isEmpty()) { throw new InvalidUserDataException("No project directories specified."); } + TestReporter testReporter; + try { + testReporter = TestReporter.valueOf(getTestReporter().getOrElse("SPEC").toUpperCase()); + } catch (IllegalArgumentException e) { + throw new InvalidUserDataException( + "Invalid reporter: '%s'. Valid reporter options: %s" + .formatted( + getTestReporter().get(), + TestReporter.getEntries().stream() + .map(it -> it.name().toLowerCase()) + .collect(Collectors.joining(", ")))); + } new CliProjectPackager( getCliBaseOptions(), @@ -83,7 +100,8 @@ public abstract class ProjectPackageTask extends BasePklTask { mapAndGetOrNull(getJunitReportsDir(), it -> it.getAsFile().toPath()), getOverwrite().get(), getJunitAggregateReports().getOrElse(false), - getJunitAggregateSuiteName().get()), + getJunitAggregateSuiteName().get(), + testReporter), getOutputPath().get().getAsFile().getAbsolutePath(), getSkipPublishCheck().getOrElse(false), new PrintWriter(System.out), diff --git a/pkl-gradle/src/main/java/org/pkl/gradle/task/TestTask.java b/pkl-gradle/src/main/java/org/pkl/gradle/task/TestTask.java index ac6a82c3..d562806b 100644 --- a/pkl-gradle/src/main/java/org/pkl/gradle/task/TestTask.java +++ b/pkl-gradle/src/main/java/org/pkl/gradle/task/TestTask.java @@ -18,6 +18,8 @@ package org.pkl.gradle.task; import static org.pkl.gradle.utils.PluginUtils.mapAndGetOrNull; import java.io.PrintWriter; +import java.util.stream.Collectors; +import org.gradle.api.InvalidUserDataException; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.provider.Property; import org.gradle.api.tasks.CacheableTask; @@ -26,6 +28,7 @@ import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.OutputDirectory; import org.pkl.cli.CliTestRunner; import org.pkl.commons.cli.CliTestOptions; +import org.pkl.commons.cli.TestReporter; @CacheableTask public abstract class TestTask extends ModulesTask { @@ -43,6 +46,10 @@ public abstract class TestTask extends ModulesTask { @Input public abstract Property getOverwrite(); + @Input + @Optional + public abstract Property getTestReporter(); + public TestTask() { this.getJunitAggregateSuiteName().convention("pkl-tests"); this.getPowerAssertions().convention(true); @@ -50,13 +57,26 @@ public abstract class TestTask extends ModulesTask { @Override protected void doRunTask() { + TestReporter testReporter; + try { + testReporter = TestReporter.valueOf(getTestReporter().getOrElse("SPEC").toUpperCase()); + } catch (IllegalArgumentException e) { + throw new InvalidUserDataException( + "Invalid reporter: '%s'. Valid reporter options: %s" + .formatted( + getTestReporter().get(), + TestReporter.getEntries().stream() + .map(it -> it.name().toLowerCase()) + .collect(Collectors.joining(", ")))); + } new CliTestRunner( getCliBaseOptions(), new CliTestOptions( mapAndGetOrNull(getJunitReportsDir(), it -> it.getAsFile().toPath()), getOverwrite().get(), getJunitAggregateReports().getOrElse(false), - getJunitAggregateSuiteName().get()), + getJunitAggregateSuiteName().get(), + testReporter), new PrintWriter(System.out), new PrintWriter(System.err)) .run();