From 416eb761daf12809fff0980ad228c92082edecc3 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Sat, 18 Nov 2023 00:46:33 +0100 Subject: [PATCH] Add `#[MaximumDuration]` attribute --- CHANGELOG.md | 6 +++ README.md | 21 +++++++-- src/Attribute/MaximumDuration.php | 35 +++++++++++++++ src/Subscriber/TestPassedSubscriber.php | 41 ++++++++++++++++- .../MaximumDuration/Fifty/SleeperTest.php | 40 +++++++++++++++++ .../Version10/MaximumDuration/Fifty/test.phpt | 13 +++--- test/Unit/Attribute/MaximumDurationTest.php | 45 +++++++++++++++++++ 7 files changed, 190 insertions(+), 11 deletions(-) create mode 100644 src/Attribute/MaximumDuration.php create mode 100644 test/Unit/Attribute/MaximumDurationTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index fa10b56f..1a1ad5d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), For a full diff see [`2.4.0...main`][2.4.0...main]. +### Added + +- Added `Attribute\MaximumDuration` to allow configuration of maximum duration with attributes on test method level ([#367]), by [@HypeMC] + ## [`2.4.0`][2.4.0] For a full diff see [`2.3.2...2.4.0`][2.3.2...2.4.0]. @@ -195,5 +199,7 @@ For a full diff see [`7afa59c...1.0.0`][7afa59c...1.0.0]. [#354]: https://github.com/ergebnis/phpunit-slow-test-detector/pull/354 [#355]: https://github.com/ergebnis/phpunit-slow-test-detector/pull/355 [#357]: https://github.com/ergebnis/phpunit-slow-test-detector/pull/357 +[#367]: https://github.com/ergebnis/phpunit-slow-test-detector/pull/367 +[@HypeMC]: https://github.com/HypeMC [@localheinz]: https://github.com/localheinz diff --git a/README.md b/README.md index 9d3ad5a9..0cba9c5a 100644 --- a/README.md +++ b/README.md @@ -157,9 +157,13 @@ The following example configures the maximum count of slow tests to three, and t #### Configuring the maximum duration per test case -You can configure the maximum duration for a single test case with a `@maximumDuration` (or `@slowThreshold`) annotation in the DocBlock. +You can configure the maximum duration for a single test case with -The following example configures the maximum durations for single test cases to 5.000 and 4.000 ms: +- an `Attribute\MaximumDuration` attribute +- a `@maximumDuration` annotation in the DocBlock +- a `@slowThreshold` annotation in the DocBlock + +The following example configures the maximum durations for single test cases to 5.000 ms, 4.000 ms, and 3.000 ms: ```php = $milliseconds) { + throw Exception\InvalidMilliseconds::notGreaterThanZero($milliseconds); + } + } + + public function milliseconds(): int + { + return $this->milliseconds; + } +} diff --git a/src/Subscriber/TestPassedSubscriber.php b/src/Subscriber/TestPassedSubscriber.php index 8a82214c..61389059 100644 --- a/src/Subscriber/TestPassedSubscriber.php +++ b/src/Subscriber/TestPassedSubscriber.php @@ -13,6 +13,7 @@ namespace Ergebnis\PHPUnit\SlowTestDetector\Subscriber; +use Ergebnis\PHPUnit\SlowTestDetector\Attribute; use Ergebnis\PHPUnit\SlowTestDetector\Collector; use Ergebnis\PHPUnit\SlowTestDetector\Duration; use Ergebnis\PHPUnit\SlowTestDetector\SlowTest; @@ -64,6 +65,44 @@ public function notify(Event\Test\Passed $event): void } private function resolveMaximumDuration(Event\Code\Test $test): Duration + { + $maximumDurationFromAttribute = self::resolveMaximumDurationFromAttribute($test); + + if ($maximumDurationFromAttribute instanceof Duration) { + return $maximumDurationFromAttribute; + } + + $maximumDurationFromAnnotation = self::resolveMaximumDurationFromAnnotation($test); + + if ($maximumDurationFromAnnotation instanceof Duration) { + return $maximumDurationFromAnnotation; + } + + return $this->maximumDuration; + } + + private static function resolveMaximumDurationFromAttribute(Event\Code\Test $test): ?Duration + { + /** @var Event\Code\TestMethod $test */ + $methodReflection = new \ReflectionMethod( + $test->className(), + $test->methodName(), + ); + + $attributeReflections = $methodReflection->getAttributes(Attribute\MaximumDuration::class); + + if ([] !== $attributeReflections) { + $attributeReflection = \reset($attributeReflections); + + $attribute = $attributeReflection->newInstance(); + + return Duration::fromMilliseconds($attribute->milliseconds()); + } + + return null; + } + + private static function resolveMaximumDurationFromAnnotation(Event\Code\Test $test): ?Duration { $annotations = [ 'maximumDuration', @@ -96,6 +135,6 @@ private function resolveMaximumDuration(Event\Code\Test $test): Duration return Duration::fromMilliseconds((int) $maximumDuration); } - return $this->maximumDuration; + return null; } } diff --git a/test/EndToEnd/Version10/MaximumDuration/Fifty/SleeperTest.php b/test/EndToEnd/Version10/MaximumDuration/Fifty/SleeperTest.php index 94f9fafb..3db3e29e 100644 --- a/test/EndToEnd/Version10/MaximumDuration/Fifty/SleeperTest.php +++ b/test/EndToEnd/Version10/MaximumDuration/Fifty/SleeperTest.php @@ -13,6 +13,7 @@ namespace Ergebnis\PHPUnit\SlowTestDetector\Test\EndToEnd\Version10\MaximumDuration\Fifty; +use Ergebnis\PHPUnit\SlowTestDetector\Attribute; use Ergebnis\PHPUnit\SlowTestDetector\Test; use PHPUnit\Framework; @@ -167,4 +168,43 @@ public function testSleeperSleepsLongerThanMaximumDurationFromSlowThresholdAnnot self::assertSame($milliseconds, $sleeper->milliseconds()); } + + #[Attribute\MaximumDuration(140)] + public function testSleeperSleepsShorterThanMaximumDurationFromMaximumDurationAttribute(): void + { + $milliseconds = 130; + + $sleeper = Test\Fixture\Sleeper::fromMilliseconds($milliseconds); + + $sleeper->sleep(); + + self::assertSame($milliseconds, $sleeper->milliseconds()); + } + + #[Attribute\MaximumDuration(160)] + public function testSleeperSleepsLongerThanMaximumDurationFromMaximumDurationAttribute(): void + { + $milliseconds = 160; + + $sleeper = Test\Fixture\Sleeper::fromMilliseconds($milliseconds); + + $sleeper->sleep(); + + self::assertSame($milliseconds, $sleeper->milliseconds()); + } + + /** + * @maximumDuration 20 + */ + #[Attribute\MaximumDuration(50)] + public function testSleeperSleepsShorterThanMaximumDurationFromMaximumDurationAttributeWhenMaximumDurationAnnotationIsPresent(): void + { + $milliseconds = 40; + + $sleeper = Test\Fixture\Sleeper::fromMilliseconds($milliseconds); + + $sleeper->sleep(); + + self::assertSame($milliseconds, $sleeper->milliseconds()); + } } diff --git a/test/EndToEnd/Version10/MaximumDuration/Fifty/test.phpt b/test/EndToEnd/Version10/MaximumDuration/Fifty/test.phpt index b165b42e..b4203358 100644 --- a/test/EndToEnd/Version10/MaximumDuration/Fifty/test.phpt +++ b/test/EndToEnd/Version10/MaximumDuration/Fifty/test.phpt @@ -21,17 +21,18 @@ Runtime: %s Configuration: %Stest/EndToEnd/Version10/MaximumDuration/Fifty/phpunit.xml Random Seed: %s -.......... 10 / 10 (100%) +............. 13 / 13 (100%) -Detected 6 tests that took longer than expected. +Detected 7 tests that took longer than expected. 1. 0.2%s (0.200) Ergebnis\PHPUnit\SlowTestDetector\Test\EndToEnd\Version10\MaximumDuration\Fifty\SleeperTest::testSleeperSleepsLongerThanMaximumDurationFromSlowThresholdAnnotation 2. 0.2%s (0.180) Ergebnis\PHPUnit\SlowTestDetector\Test\EndToEnd\Version10\MaximumDuration\Fifty\SleeperTest::testSleeperSleepsLongerThanMaximumDurationFromMaximumDurationAnnotationWhenSlowThresholdAnnotationIsPresentBeforeMaximumDuration 3. 0.1%s (0.160) Ergebnis\PHPUnit\SlowTestDetector\Test\EndToEnd\Version10\MaximumDuration\Fifty\SleeperTest::testSleeperSleepsLongerThanMaximumDurationFromMaximumDurationAnnotationWhenSlowThresholdAnnotationIsPresentAfterMaximumDuration -4. 0.1%s (0.150) Ergebnis\PHPUnit\SlowTestDetector\Test\EndToEnd\Version10\MaximumDuration\Fifty\SleeperTest::testSleeperSleepsLongerThanMaximumDurationFromMaximumDurationAnnotation -5. 0.1%s (0.050) Ergebnis\PHPUnit\SlowTestDetector\Test\EndToEnd\Version10\MaximumDuration\Fifty\SleeperTest::testSleeperSleepsWithDocBlockWithMaximumDurationAnnotationWhereValueIsNotAnInt -6. 0.0%s (0.050) Ergebnis\PHPUnit\SlowTestDetector\Test\EndToEnd\Version10\MaximumDuration\Fifty\SleeperTest::testSleeperSleepsWithDocBlockWithoutSlowThresholdAnnotation +4. 0.1%s (0.160) Ergebnis\PHPUnit\SlowTestDetector\Test\EndToEnd\Version10\MaximumDuration\Fifty\SleeperTest::testSleeperSleepsLongerThanMaximumDurationFromMaximumDurationAttribute +5. 0.1%s (0.150) Ergebnis\PHPUnit\SlowTestDetector\Test\EndToEnd\Version10\MaximumDuration\Fifty\SleeperTest::testSleeperSleepsLongerThanMaximumDurationFromMaximumDurationAnnotation +6. 0.1%s (0.050) Ergebnis\PHPUnit\SlowTestDetector\Test\EndToEnd\Version10\MaximumDuration\Fifty\SleeperTest::testSleeperSleepsWithDocBlockWithMaximumDurationAnnotationWhereValueIsNotAnInt +7. 0.0%s (0.050) Ergebnis\PHPUnit\SlowTestDetector\Test\EndToEnd\Version10\MaximumDuration\Fifty\SleeperTest::testSleeperSleepsWithDocBlockWithoutSlowThresholdAnnotation Time: %s, Memory: %s -OK (10 tests, 10 assertions) +OK (13 tests, 13 assertions) diff --git a/test/Unit/Attribute/MaximumDurationTest.php b/test/Unit/Attribute/MaximumDurationTest.php new file mode 100644 index 00000000..0589b839 --- /dev/null +++ b/test/Unit/Attribute/MaximumDurationTest.php @@ -0,0 +1,45 @@ +expectException(Exception\InvalidMilliseconds::class); + + Duration::fromMilliseconds($milliseconds); + } + + public function testConstructorSetsValue(): void + { + $milliseconds = self::faker()->numberBetween(1); + + $maximumDuration = new Attribute\MaximumDuration($milliseconds); + + self::assertSame($milliseconds, $maximumDuration->milliseconds()); + } +}