From 67fa64408ba643349e699f1471051c3256894c8e Mon Sep 17 00:00:00 2001 From: Nicolai Parlog Date: Thu, 26 May 2022 17:06:31 +0200 Subject: [PATCH 1/8] Polish documentation --- docs/default-locale-timezone.adoc | 2 +- docs/disable-if-test-fails.adoc | 4 ++++ docs/disable-parameterized-tests.adoc | 8 +++++--- docs/disabled-until.adoc | 6 ++++-- docs/issue.adoc | 6 ++++-- docs/json-argument-source.adoc | 1 - docs/project-page.adoc | 2 -- docs/report-entries.adoc | 1 - docs/standard-input-output.adoc | 7 +++---- docs/temp-directory.adoc | 1 - docs/vintage-test.adoc | 4 ++-- .../junitpioneer/jupiter/DisableIfTestFailsExtension.java | 2 +- src/main/java/org/junitpioneer/jupiter/DisabledUntil.java | 1 + 13 files changed, 25 insertions(+), 20 deletions(-) diff --git a/docs/default-locale-timezone.adoc b/docs/default-locale-timezone.adoc index a21ef825f..f51ea1c8b 100644 --- a/docs/default-locale-timezone.adoc +++ b/docs/default-locale-timezone.adoc @@ -42,7 +42,7 @@ NOTE: A class-level configuration means that the specified locale is set before == `@DefaultTimeZone` -The default `TimeZone` is specified according to the https://docs.oracle.com/javase/8/docs/api/java/util/TimeZone.html#getTimeZone(java.lang.String)[TimeZone.getTimeZone(String)] method. +The default `TimeZone` is specified according to the https://docs.oracle.com/javase/8/docs/api/java/util/TimeZone.html#getTimeZone-java.lang.String-[TimeZone.getTimeZone(String)] method. [source,java,indent=0] ---- diff --git a/docs/disable-if-test-fails.adoc b/docs/disable-if-test-fails.adoc index 57973fc64..bd15cc971 100644 --- a/docs/disable-if-test-fails.adoc +++ b/docs/disable-if-test-fails.adoc @@ -62,3 +62,7 @@ That means if... * a test in `SpecificTests` fails ... then, only remaining tests in `SpecificTests` are disabled and other implementations of `Tests` remain unaffected, i.e. their tests are not disabled. + +== Thread-Safety + +This extension is safe to use during https://junit.org/junit5/docs/current/user-guide/#writing-tests-parallel-execution[parallel test execution], but no guarantee can be given how soon after a failed test, other tests will be disabled, i.e. how soon other threads observe that "their" tests are supposed to be disabled. diff --git a/docs/disable-parameterized-tests.adoc b/docs/disable-parameterized-tests.adoc index eb72b53c8..5bb6c104b 100644 --- a/docs/disable-parameterized-tests.adoc +++ b/docs/disable-parameterized-tests.adoc @@ -9,7 +9,6 @@ These are as follows: - DisableIfDisplayName - DisableIfArgument - == DisableIfDisplayName The `@DisableIfDisplayName` annotation can be used to selectively disable parameterized tests based on their display names, which are dynamically registered on runtime. @@ -39,7 +38,6 @@ include::{demo}[tag=disable_parameterized_regex] NOTE: Since JUnit Pioneer 1.5.0, using both `matches` and `contains` in a single annotation is no longer permitted. The reason is that it's not clear from reading the annotation whether it's *and* or *or* semantics, i.e. whether the display name needs to both match the regex *and* contain the substring to be disabled or whether fulfilling one criterion suffices. - == DisableIfArgument This extension can be used to selectively disable parameterized tests based on their arguments (converted with `toString()`). @@ -111,7 +109,7 @@ The `matches` attribute works analogous for `@DisableIfAnyArgument`. `@DisableIfArgument` requires you to target a specific parameter. You can do this in three ways: -- By a `name` https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Parameter.html#isNamePresent--[if parameter naming information is present]. +- By a `name` https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Parameter.html#isNamePresent - [if parameter naming information is present]. - By an explicit `index`, starting from 0. - By an implicit index. @@ -177,3 +175,7 @@ Just like with `contains`, if any argument matches any expression from `matches` NOTE: While the documentation uses `String` values for demonstration purposes, you can use it to disable tests with other parameter types. However, the arguments will be converted to `String` with `Object#toString()` before evaluation. Make sure that your parameter types have a meaningful `toString` method. + +== Thread-Safety + +This extension is safe to use during https://junit.org/junit5/docs/current/user-guide/#writing-tests-parallel-execution[parallel test execution]. diff --git a/docs/disabled-until.adoc b/docs/disabled-until.adoc index 1e0bc16af..ad9fc2aa1 100644 --- a/docs/disabled-until.adoc +++ b/docs/disabled-until.adoc @@ -3,8 +3,6 @@ :xp-demo-dir: ../src/demo/java :demo: {xp-demo-dir}/org/junitpioneer/jupiter/DisableUntilExtensionDemo.java -== Introduction - It's sometimes useful to disable a test. One can imagine many reasons for doing so, maybe a remote dependency of the test is broken or not yet deployed. Maybe the test is still in development and unstable. @@ -47,3 +45,7 @@ The `@DisabledUntil` annotation can only be used once per class or method. The test will be skipped only if the date specified by `date` is the future. If `date` is today or in the past, the test will be executed normally but a warning entry will be published to the https://junit-pioneer.org/docs/report-entries[test report]. + +== Thread-Safety + +This extension is safe to use during https://junit.org/junit5/docs/current/user-guide/#writing-tests-parallel-execution[parallel test execution]. diff --git a/docs/issue.adoc b/docs/issue.adoc index c755a4e18..d9eb6441f 100644 --- a/docs/issue.adoc +++ b/docs/issue.adoc @@ -3,8 +3,6 @@ :xp-demo-dir: ../src/demo/java :demo: {xp-demo-dir}/org/junitpioneer/jupiter/IssueExtensionDemo.java -== Introduction - The main reason developers write tests is to ensure the functionality of a requirement or to avoid technical problems. The `@Issue` annotation allows marking tests with a `String`, referencing a related issue (like a requirement, or a bugfix) of an issue-tracker (like Jira or Redmine). @@ -51,3 +49,7 @@ include::{demo}[tag=issue_processor_sample] NOTE: The implementing class must be registered to the Java ecosystem as a service. For further information about registering a service, please see the Java https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html[`ServiceLoader` documentation]. + +== Thread-Safety + +This extension is safe to use during https://junit.org/junit5/docs/current/user-guide/#writing-tests-parallel-execution[parallel test execution]. diff --git a/docs/json-argument-source.adoc b/docs/json-argument-source.adoc index ed6d33410..bb489cd3c 100644 --- a/docs/json-argument-source.adoc +++ b/docs/json-argument-source.adoc @@ -197,7 +197,6 @@ That means (1) it must be on the module path and (2) it must be resolved. The steps above ensure that your build tool knows about the parser and should accomplish (1), but if no other module depends on the parser (directly or indirectly), (2) requires additional work. In that case, you need to manually resolve the module by applying the command line option `--add-modules=com.fasterxml.jackson.databind` to the Java process that executes the tests. - == Thread-Safety This extension is safe to use during https://junit.org/junit5/docs/current/user-guide/#writing-tests-parallel-execution[parallel test execution]. diff --git a/docs/project-page.adoc b/docs/project-page.adoc index d10b5ccf1..8074fc934 100644 --- a/docs/project-page.adoc +++ b/docs/project-page.adoc @@ -9,7 +9,6 @@ excerpt: 'JUnit 5 extension pack, pushing the frontiers on Jupiter.
JUnit Pioneer is an https://nipafx.dev/junit-5-extension-model/[extension pack] for https://junit.org/junit5/[JUnit 5] or, https://nipafx.dev/junit-5-architecture-jupiter/[to be more precise], for the Jupiter engine. It offers https://junit-pioneer.org/docs/[a wide variety of extensions] and is continuously released with more. - == Quick start Determine the latest version (e.g. on https://search.maven.org/artifact/org.junit-pioneer/junit-pioneer[Maven Central]) and add Pioneer as a test dependency. @@ -35,7 +34,6 @@ testCompile group: 'org.junit-pioneer', name: 'junit-pioneer', version: /* ... * Done, you're good to go! đź‘Ť - == Contributing There are various ways to help us improve JUnit Pioneer if you're interested: diff --git a/docs/report-entries.adoc b/docs/report-entries.adoc index bb7f49723..51ea01c3f 100644 --- a/docs/report-entries.adoc +++ b/docs/report-entries.adoc @@ -108,7 +108,6 @@ Then the extension will publish `"21 - Hello - 21"` and `"42 - World - 42"`. Accessing test parameters in the key of the report entry is unsupported. - == Thread-Safety This extension is safe to use during https://junit.org/junit5/docs/current/user-guide/#writing-tests-parallel-execution[parallel test execution]. diff --git a/docs/standard-input-output.adoc b/docs/standard-input-output.adoc index eafa64e38..d35a4921e 100644 --- a/docs/standard-input-output.adoc +++ b/docs/standard-input-output.adoc @@ -13,12 +13,13 @@ This becomes particularly important when running tests in parallel, where other The extension consists of two parts: * The annotation `@StdIo`. It allows defining input that is read from `System.in` without having to wait for user input. -* Parameters `StdIn` and `StdOut`, which you can have injected into your test. Their `capturedLines()` methods allow you to access lines read from `System.in` or written to `System.out`, so you can verify them with common assertions. +* Parameters `StdIn` and `StdOut`, which you can have injected into your test. + Their `capturedLines()` methods allow you to access lines read from `System.in` or written to `System.out`, so you can verify them with common assertions. For example, after calling `System.out.println("Hello")` and `System.out.println("World")`, the `StdOut::capturedLines` method would return an array ["Hello", "World"]. With `System.out.print("Hello")` and `System.out.println("World")` (note that the first method does not print a line break), it would return `["HelloWorld"]`. -Here's which combinations of this annotation (with or without values for the read lines) and parameters are valid: +Here are the valid combinations of the annotation (with or without values for the read lines) and parameters: `@StdIo("...")`:: In this case `System.in` gets replaced and the code under test will read the specified lines (in the snippet, that's just the line `"..."`). @@ -62,7 +63,6 @@ include::{demo}[tag=stdio_both_replaced_and_verify] The remaining combinations of the annotation, its values, and `StdIn`/`StdOut` are considered misconfigurations and lead to exceptions. - == Thread-Safety Since `System.in` and `System.out` are global state, reading and writing them during https://junit.org/junit5/docs/current/user-guide/#writing-tests-parallel-execution[parallel test execution] can lead to unpredictable results and flaky tests. @@ -77,7 +77,6 @@ Tests that cover code that reads or writes `System.in` or `System.out` need to b Tests annotated in this way will never execute in parallel with tests annotated with `@StdIo`. - == Edge cases and unexpected behavior === Empty input (with or without `StdIn`) diff --git a/docs/temp-directory.adoc b/docs/temp-directory.adoc index c1bfdf4fe..45916641b 100644 --- a/docs/temp-directory.adoc +++ b/docs/temp-directory.adoc @@ -58,4 +58,3 @@ class MyTests { == Thread-Safety This extension's thread-safety is undetermined, we recommend to be cautious when using it during https://junit.org/junit5/docs/current/user-guide/#writing-tests-parallel-execution[parallel test execution]. -(We're working on it.) diff --git a/docs/vintage-test.adoc b/docs/vintage-test.adoc index 1428efb54..b65e996b3 100644 --- a/docs/vintage-test.adoc +++ b/docs/vintage-test.adoc @@ -6,7 +6,7 @@ The annotation `org.junitpioneer.vintage.@Test` is a drop-in replacement for https://junit.org/junit4/javadoc/4.12/org/junit/Test.html[JUnit 4's `org.junit.@Test` annotation], but marks the method as a regular JUnit Jupiter test. You can make use of it when migrating tests from JUnit 4 to Jupiter by doing a fulltext search/replace of `import org.junit.Test` with `import org.junitpioneer.vintage.Test`. -That means you do not need to run JUnit 5's Vintage engine to execute such tests--Jupiter suffices. +That means you do not need to run JUnit 5's Vintage engine to execute such tests - Jupiter suffices. This should be seen as an intermediate step towards a full migration and should be followed up by gradual/manual replacement of `org.junitpioneer.vintage.Test` with Jupiter's `org.junit.jupiter.api.Test`. To emphasize its character as a temporary solution and to reduce https://github.com/junit-pioneer/junit-pioneer/issues/137[risk of accidental use], it's marked as deprecated. @@ -55,7 +55,7 @@ include::{demo}[tag=vintage_test_timeout] ---- Like in JUnit 4, `timeout` aborts long-running tests. -The following tests does not run indefinitely--it failes after 100 milliseconds: +The following tests does not run indefinitely - it fails after 100 milliseconds: [source,java,indent=0] ---- diff --git a/src/main/java/org/junitpioneer/jupiter/DisableIfTestFailsExtension.java b/src/main/java/org/junitpioneer/jupiter/DisableIfTestFailsExtension.java index c9fbcbf80..5cdbb7b87 100644 --- a/src/main/java/org/junitpioneer/jupiter/DisableIfTestFailsExtension.java +++ b/src/main/java/org/junitpioneer/jupiter/DisableIfTestFailsExtension.java @@ -40,7 +40,7 @@ class DisableIfTestFailsExtension implements TestExecutionExceptionHandler, Exec * * Setting the information needs to be thread safe, so only positive results (i.e. tests must be disabled) * will be set. The easiest way to do that is to simply use absence/presence of a key in the store - * as indicator, which means the specific value doesn't. + * as indicator, which means the specific value doesn't matter. */ private static final Namespace NAMESPACE = Namespace.create(DisableIfTestFailsExtension.class); diff --git a/src/main/java/org/junitpioneer/jupiter/DisabledUntil.java b/src/main/java/org/junitpioneer/jupiter/DisabledUntil.java index 51bd5dae9..7bbc09e6a 100644 --- a/src/main/java/org/junitpioneer/jupiter/DisabledUntil.java +++ b/src/main/java/org/junitpioneer/jupiter/DisabledUntil.java @@ -28,6 +28,7 @@ *

{@code @DisabledUntil} can be used on the method and class level. It can only be used once per method or class, * but is inherited from higher-level containers.

* + * @since 1.6.0 * @see org.junit.jupiter.api.Disabled */ @Retention(RetentionPolicy.RUNTIME) From 2ce6802b9f489418a351904806bc48a057eb6db0 Mon Sep 17 00:00:00 2001 From: Nicolai Parlog Date: Thu, 26 May 2022 17:08:03 +0200 Subject: [PATCH 2/8] Reorder annotations to put @Test first --- .../jupiter/DisableUntilExtensionDemo.java | 4 ++-- .../org/junitpioneer/jupiter/IssueExtensionDemo.java | 2 +- .../jupiter/StopwatchExtensionTests.java | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/demo/java/org/junitpioneer/jupiter/DisableUntilExtensionDemo.java b/src/demo/java/org/junitpioneer/jupiter/DisableUntilExtensionDemo.java index c6993fe3c..25f29a68c 100644 --- a/src/demo/java/org/junitpioneer/jupiter/DisableUntilExtensionDemo.java +++ b/src/demo/java/org/junitpioneer/jupiter/DisableUntilExtensionDemo.java @@ -15,16 +15,16 @@ public class DisableUntilExtensionDemo { // tag::disable_until_simple[] - @DisabledUntil(date = "2022-01-01") @Test + @DisabledUntil(date = "2022-01-01") void test() { // Test will be skipped if it's 2021-12-31 or earlier } // end::disable_until_simple[] // tag::disable_until_with_reason[] - @DisabledUntil(reason = "The remote server won't be ready until next year", date = "2022-01-01") @Test + @DisabledUntil(reason = "The remote server won't be ready until next year", date = "2022-01-01") void testWithReason() { // Test will be skipped if it's 2021-12-31 or earlier } diff --git a/src/demo/java/org/junitpioneer/jupiter/IssueExtensionDemo.java b/src/demo/java/org/junitpioneer/jupiter/IssueExtensionDemo.java index a18e96d0a..1f2d14606 100644 --- a/src/demo/java/org/junitpioneer/jupiter/IssueExtensionDemo.java +++ b/src/demo/java/org/junitpioneer/jupiter/IssueExtensionDemo.java @@ -17,8 +17,8 @@ public class IssueExtensionDemo { // tag::issue_simple[] - @Issue("REQ-123") @Test + @Issue("REQ-123") void test() { // One of the tests for the issue with the id "REQ-123" } diff --git a/src/test/java/org/junitpioneer/jupiter/StopwatchExtensionTests.java b/src/test/java/org/junitpioneer/jupiter/StopwatchExtensionTests.java index 98abf893f..e155f1d5f 100644 --- a/src/test/java/org/junitpioneer/jupiter/StopwatchExtensionTests.java +++ b/src/test/java/org/junitpioneer/jupiter/StopwatchExtensionTests.java @@ -21,8 +21,8 @@ @DisplayName("Stopwatch extension ") public class StopwatchExtensionTests { - @DisplayName("should be executed with annotation on class level and report an entry for test method") @Test + @DisplayName("should be executed with annotation on class level and report an entry for test method") void runClassLevelAnnotationTest() { ExecutionResults results = PioneerTestKit.executeTestClass(ClassLevelAnnotationTestCases.class); @@ -34,8 +34,8 @@ void runClassLevelAnnotationTest() { } - @DisplayName("should be executed with annotation on class level and test method and report an entry for test method") @Test + @DisplayName("should be executed with annotation on class level and test method and report an entry for test method") void runClassAndMethodLevelAnnotationTest() { String methodName = "stopwatchExtensionShouldBeExecutedWithAnnotationOnClassAndMethodLevel"; @@ -45,8 +45,8 @@ void runClassAndMethodLevelAnnotationTest() { assertStringStartWithUnitAndContainsName(results, methodName); } - @DisplayName("should be executed with annotation on test method and report an entry for test method") @Test + @DisplayName("should be executed with annotation on test method and report an entry for test method") void runMethodLevelAnnotationTest() { String methodName = "stopwatchExtensionShouldBeExecutedOnWithAnnotationOnMethodLevel"; @@ -56,8 +56,8 @@ void runMethodLevelAnnotationTest() { assertStringStartWithUnitAndContainsName(results, methodName); } - @DisplayName("should not be executed and therefore no entry should be published") @Test + @DisplayName("should not be executed and therefore no entry should be published") void runAnnotationTest() { String methodName = "stopwatchExtensionShouldNotBeExecuted"; @@ -89,8 +89,8 @@ void stopwatchExtensionShouldBeExecutedWithAnnotationOnClassLevel() { */ static class MethodLevelAnnotationTestCases { - @Stopwatch @Test + @Stopwatch void stopwatchExtensionShouldBeExecutedOnWithAnnotationOnMethodLevel() { } @@ -102,8 +102,8 @@ void stopwatchExtensionShouldBeExecutedOnWithAnnotationOnMethodLevel() { @Stopwatch static class ClassAndMethodLevelAnnotationTestCases { - @Stopwatch @Test + @Stopwatch void stopwatchExtensionShouldBeExecutedWithAnnotationOnClassAndMethodLevel() { } From a1d0d194ad3211658e2305dcb53b1dfd0df8560b Mon Sep 17 00:00:00 2001 From: Nicolai Parlog Date: Thu, 26 May 2022 17:10:03 +0200 Subject: [PATCH 3/8] Refactor some code --- .../jupiter/DisableIfTestFailsExtension.java | 4 ++-- .../jupiter/issue/IssueExtensionExecutionListener.java | 1 + .../junitpioneer/jupiter/StopwatchExtensionTests.java | 9 ++------- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/junitpioneer/jupiter/DisableIfTestFailsExtension.java b/src/main/java/org/junitpioneer/jupiter/DisableIfTestFailsExtension.java index 5cdbb7b87..36a2f9d08 100644 --- a/src/main/java/org/junitpioneer/jupiter/DisableIfTestFailsExtension.java +++ b/src/main/java/org/junitpioneer/jupiter/DisableIfTestFailsExtension.java @@ -82,8 +82,8 @@ private static Stream findConfigurations(ExtensionContext context .map(DisableIfTestFailsExtension::findConfigurations) .orElse(Stream.empty()); - List stream = Stream.concat(onClassConfig, onParentClassConfigs).collect(toList()); - return stream.stream(); + List configurations = Stream.concat(onClassConfig, onParentClassConfigs).collect(toList()); + return configurations.stream(); } private static Stream createConfigurationFor(ExtensionContext context, diff --git a/src/main/java/org/junitpioneer/jupiter/issue/IssueExtensionExecutionListener.java b/src/main/java/org/junitpioneer/jupiter/issue/IssueExtensionExecutionListener.java index 185966786..9f519ccd6 100644 --- a/src/main/java/org/junitpioneer/jupiter/issue/IssueExtensionExecutionListener.java +++ b/src/main/java/org/junitpioneer/jupiter/issue/IssueExtensionExecutionListener.java @@ -62,6 +62,7 @@ public void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry e if (messages.containsKey(REPORT_ENTRY_KEY)) { String issueId = messages.get(REPORT_ENTRY_KEY); + // because test IDs are unique, there's no risk of overriding previously entered information testCases.put(testId, new IssueTestCaseBuilder(testId).setIssueId(issueId)); } } diff --git a/src/test/java/org/junitpioneer/jupiter/StopwatchExtensionTests.java b/src/test/java/org/junitpioneer/jupiter/StopwatchExtensionTests.java index e155f1d5f..28d04306c 100644 --- a/src/test/java/org/junitpioneer/jupiter/StopwatchExtensionTests.java +++ b/src/test/java/org/junitpioneer/jupiter/StopwatchExtensionTests.java @@ -24,21 +24,18 @@ public class StopwatchExtensionTests { @Test @DisplayName("should be executed with annotation on class level and report an entry for test method") void runClassLevelAnnotationTest() { - + String methodName = "stopwatchExtensionShouldBeExecutedWithAnnotationOnClassLevel"; ExecutionResults results = PioneerTestKit.executeTestClass(ClassLevelAnnotationTestCases.class); assertThat(results).hasNumberOfReportEntries(1); - String methodName = "stopwatchExtensionShouldBeExecutedWithAnnotationOnClassLevel"; assertStringStartWithUnitAndContainsName(results, methodName); - } @Test @DisplayName("should be executed with annotation on class level and test method and report an entry for test method") void runClassAndMethodLevelAnnotationTest() { String methodName = "stopwatchExtensionShouldBeExecutedWithAnnotationOnClassAndMethodLevel"; - ExecutionResults results = PioneerTestKit.executeTestClass(ClassAndMethodLevelAnnotationTestCases.class); assertThat(results).hasNumberOfReportEntries(1); @@ -49,7 +46,6 @@ void runClassAndMethodLevelAnnotationTest() { @DisplayName("should be executed with annotation on test method and report an entry for test method") void runMethodLevelAnnotationTest() { String methodName = "stopwatchExtensionShouldBeExecutedOnWithAnnotationOnMethodLevel"; - ExecutionResults results = PioneerTestKit.executeTestMethod(MethodLevelAnnotationTestCases.class, methodName); assertThat(results).hasNumberOfReportEntries(1); @@ -60,10 +56,9 @@ void runMethodLevelAnnotationTest() { @DisplayName("should not be executed and therefore no entry should be published") void runAnnotationTest() { String methodName = "stopwatchExtensionShouldNotBeExecuted"; - ExecutionResults results = PioneerTestKit.executeTestMethod(NonAnnotationTestCases.class, methodName); - assertThat(results).hasNumberOfReportEntries(0); + assertThat(results).hasNumberOfReportEntries(0); } private void assertStringStartWithUnitAndContainsName(ExecutionResults results, String methodName) { From 8f5f09089bc1083fbab5322e91dd90bcac322a2f Mon Sep 17 00:00:00 2001 From: Nicolai Parlog Date: Thu, 26 May 2022 17:10:28 +0200 Subject: [PATCH 4/8] Refactor and better document Stopwatch extension --- docs/stopwatch.adoc | 36 +++++++++++++++---- .../jupiter/StopwatchExtensionDemo.java | 25 ++++++++++--- .../jupiter/StopwatchExtension.java | 21 ++--------- .../jupiter/StopwatchExtensionTests.java | 9 +++++ 4 files changed, 62 insertions(+), 29 deletions(-) diff --git a/docs/stopwatch.adoc b/docs/stopwatch.adoc index 460f5369f..8e195472b 100644 --- a/docs/stopwatch.adoc +++ b/docs/stopwatch.adoc @@ -3,18 +3,40 @@ :xp-demo-dir: ../src/demo/java :demo: {xp-demo-dir}/org/junitpioneer/jupiter/StopwatchExtensionDemo.java -== Introduction - -Test shall be fast. -To get a feeling how fast or slow a specific test is, the `@Stopwatch` annotation can be used. -It will measure elapsed time and pass the result to the `https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/TestReporter.html[TestReporter]`, which will publish it. +Annotating a test with `@Stopwatch` will measure the time it takes to execute and report the result to the `https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/TestReporter.html[TestReporter]`. +How that information is displayed depends on the tool used to run the tests and how it processes test report entries. == Usage -To measure elapsed time just add the `@Stopwatch` annotation to a class or, like in the following example, to a method: +`@Stopwatch` can be applied to a method to report its execution time: + +[source,java,indent=0] +---- +include::{demo}[tag=method] +---- + +It can also be applied to a class, in which case it reports the execution times of each test as if it were applied to each method individually: [source,java,indent=0] ---- -include::{demo}[tag=stopwatch_demo] +include::{demo}[tag=class] ---- +It can also be applied to a class and a method therein but since a class-level annotation already works as if each method was annotated, the method-level annotations would be redundant. + +== Output + +This is how IntelliJ displays a report entry (in the Run/Debug panel): + +---- +timestamp = 2022-05-26T12:16:14.021646, StopwatchExtension = Execution of 'test()' took [11] ms. +---- + +Other tools may or may not print report entries. + +The most reliable way to gather entries is to create a https://junit.org/junit5/docs/current/api/org.junit.platform.launcher/org/junit/platform/launcher/TestExecutionListener.html[`TestExecutionListener`] and register it with the launcher. +https://junit.org/junit5/docs/current/api/org.junit.platform.engine/org/junit/platform/engine/reporting/ReportEntry.html[Report entries] from the stopwatch extension can be identified by checking their https://junit.org/junit5/docs/current/api/org.junit.platform.engine/org/junit/platform/engine/reporting/ReportEntry.html#getKeyValuePairs()[key-value pairs] - at least one key will start with the string `"StopwatchExtension"`. + +== Thread-Safety + +This extension is safe to use during https://junit.org/junit5/docs/current/user-guide/#writing-tests-parallel-execution[parallel test execution]. diff --git a/src/demo/java/org/junitpioneer/jupiter/StopwatchExtensionDemo.java b/src/demo/java/org/junitpioneer/jupiter/StopwatchExtensionDemo.java index 19c6f7ef0..3122f201c 100644 --- a/src/demo/java/org/junitpioneer/jupiter/StopwatchExtensionDemo.java +++ b/src/demo/java/org/junitpioneer/jupiter/StopwatchExtensionDemo.java @@ -14,12 +14,29 @@ public class StopwatchExtensionDemo { - // tag::stopwatch_demo[] - @Stopwatch + // tag::method[] @Test + @Stopwatch void test() { - // Some test + // execution time will be reported + } + // end::method[] + + // tag::class[] + @Stopwatch + class TestCases { + + @Test + void test_1() { + // execution time will be reported + } + + @Test + void test_s() { + // execution time will be reported + } + } + // end::class[] - // end::stopwatch_demo[] } diff --git a/src/main/java/org/junitpioneer/jupiter/StopwatchExtension.java b/src/main/java/org/junitpioneer/jupiter/StopwatchExtension.java index a50547acc..c8806174f 100644 --- a/src/main/java/org/junitpioneer/jupiter/StopwatchExtension.java +++ b/src/main/java/org/junitpioneer/jupiter/StopwatchExtension.java @@ -36,30 +36,15 @@ public void afterTestExecution(ExtensionContext context) { calculateAndReportElapsedTime(context); } - /** - * Stores the current time for the method in the execution context. - * - * @param context Extension context of the class - */ - void storeNowAsLaunchTime(ExtensionContext context) { + private void storeNowAsLaunchTime(ExtensionContext context) { context.getStore(NAMESPACE).put(context.getUniqueId(), clock.instant().toEpochMilli()); } - /** - * Loads the stored time for method from the execution context. - * - * @param context Extension context of the class - */ - long loadLaunchTime(ExtensionContext context) { + private long loadLaunchTime(ExtensionContext context) { return context.getStore(NAMESPACE).get(context.getUniqueId(), long.class); } - /** - * Calculates the elapsed time method and publishes it to the execution context. - * - * @param context Extension context of the class - */ - void calculateAndReportElapsedTime(ExtensionContext context) { + private void calculateAndReportElapsedTime(ExtensionContext context) { long launchTime = loadLaunchTime(context); long elapsedTime = clock.instant().toEpochMilli() - launchTime; diff --git a/src/test/java/org/junitpioneer/jupiter/StopwatchExtensionTests.java b/src/test/java/org/junitpioneer/jupiter/StopwatchExtensionTests.java index 28d04306c..a65e24cee 100644 --- a/src/test/java/org/junitpioneer/jupiter/StopwatchExtensionTests.java +++ b/src/test/java/org/junitpioneer/jupiter/StopwatchExtensionTests.java @@ -12,6 +12,7 @@ import static org.junitpioneer.testkit.assertion.PioneerAssert.assertThat; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junitpioneer.testkit.ExecutionResults; @@ -61,6 +62,14 @@ void runAnnotationTest() { assertThat(results).hasNumberOfReportEntries(0); } + @Test + @DisplayName("should not change the report entry key") + void verifyReportEntryKey() { + // the store key is mentioned in the documentation and changing it would break + // `TestExecutionListener` implementations that use it to filter stopwatch report entries + Assertions.assertThat(StopwatchExtension.STORE_KEY).startsWith("StopwatchExtension"); + } + private void assertStringStartWithUnitAndContainsName(ExecutionResults results, String methodName) { ReportEntryContentAssert reportEntry = assertThat(results).hasNumberOfReportEntries(1); reportEntry.firstValue().matches(String.format("Execution of '%s\\(\\)' took \\[[0-9]*\\] ms.", methodName)); From 5a4cfbc976d98749153b81270bd45b4dd951cc2e Mon Sep 17 00:00:00 2001 From: Nicolai Parlog Date: Fri, 27 May 2022 13:41:09 +0200 Subject: [PATCH 5/8] Update docs/stopwatch.adoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mihály Verhás --- docs/stopwatch.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/stopwatch.adoc b/docs/stopwatch.adoc index 8e195472b..459d134e8 100644 --- a/docs/stopwatch.adoc +++ b/docs/stopwatch.adoc @@ -3,7 +3,7 @@ :xp-demo-dir: ../src/demo/java :demo: {xp-demo-dir}/org/junitpioneer/jupiter/StopwatchExtensionDemo.java -Annotating a test with `@Stopwatch` will measure the time it takes to execute and report the result to the `https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/TestReporter.html[TestReporter]`. +Annotating a test with `@Stopwatch` will measure the time it takes to execute and report the result to the https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/TestReporter.html[`TestReporter`]. How that information is displayed depends on the tool used to run the tests and how it processes test report entries. == Usage From 87d12de0b252551059ec3ad1d26efb9e76c8ae0e Mon Sep 17 00:00:00 2001 From: Nicolai Parlog Date: Fri, 27 May 2022 13:44:10 +0200 Subject: [PATCH 6/8] Revert erronous change that broke a link --- docs/disable-parameterized-tests.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/disable-parameterized-tests.adoc b/docs/disable-parameterized-tests.adoc index 5bb6c104b..57bb4ee3a 100644 --- a/docs/disable-parameterized-tests.adoc +++ b/docs/disable-parameterized-tests.adoc @@ -109,7 +109,7 @@ The `matches` attribute works analogous for `@DisableIfAnyArgument`. `@DisableIfArgument` requires you to target a specific parameter. You can do this in three ways: -- By a `name` https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Parameter.html#isNamePresent - [if parameter naming information is present]. +- By a `name` https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Parameter.html#isNamePresent--[if parameter naming information is present]. - By an explicit `index`, starting from 0. - By an implicit index. From 492099652fd1a5ff73cb822703cb497e21ad6ff7 Mon Sep 17 00:00:00 2001 From: Nicolai Parlog Date: Fri, 27 May 2022 13:52:49 +0200 Subject: [PATCH 7/8] Link to JUnit 5 user guide instead of Javadoc (for test execution listener) --- docs/stopwatch.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/stopwatch.adoc b/docs/stopwatch.adoc index 459d134e8..1d7e65a89 100644 --- a/docs/stopwatch.adoc +++ b/docs/stopwatch.adoc @@ -34,7 +34,7 @@ timestamp = 2022-05-26T12:16:14.021646, StopwatchExtension = Execution of 'test( Other tools may or may not print report entries. -The most reliable way to gather entries is to create a https://junit.org/junit5/docs/current/api/org.junit.platform.launcher/org/junit/platform/launcher/TestExecutionListener.html[`TestExecutionListener`] and register it with the launcher. +The most reliable way to gather entries is to create a https://junit.org/junit5/docs/current/user-guide/#launcher-api-listeners-custom[`TestExecutionListener`] and register it with the launcher. https://junit.org/junit5/docs/current/api/org.junit.platform.engine/org/junit/platform/engine/reporting/ReportEntry.html[Report entries] from the stopwatch extension can be identified by checking their https://junit.org/junit5/docs/current/api/org.junit.platform.engine/org/junit/platform/engine/reporting/ReportEntry.html#getKeyValuePairs()[key-value pairs] - at least one key will start with the string `"StopwatchExtension"`. == Thread-Safety From 4560ec06784d17b6e6c59055e72e4463d67d75fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mih=C3=A1ly=20Verh=C3=A1s?= Date: Fri, 27 May 2022 14:03:08 +0200 Subject: [PATCH 8/8] Fix minor typo --- docs/vintage-test.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/vintage-test.adoc b/docs/vintage-test.adoc index b65e996b3..5729bf51d 100644 --- a/docs/vintage-test.adoc +++ b/docs/vintage-test.adoc @@ -55,7 +55,7 @@ include::{demo}[tag=vintage_test_timeout] ---- Like in JUnit 4, `timeout` aborts long-running tests. -The following tests does not run indefinitely - it fails after 100 milliseconds: +The following test does not run indefinitely - it fails after 100 milliseconds: [source,java,indent=0] ----