From 2b00ac520e95749b1b9aa74b9c2b2548e4251042 Mon Sep 17 00:00:00 2001 From: henzeb Date: Wed, 14 Dec 2022 13:54:55 +0100 Subject: [PATCH] added features to State and Comparison --- CHANGELOG.md | 7 + README.md | 2 +- docs/comparison.md | 28 ++ docs/constructor.md | 3 - docs/state.md | 26 +- src/Concerns/Comparison.php | 36 +- src/Concerns/Constructor.php | 5 +- src/Concerns/MagicCalls.php | 18 + src/Concerns/State.php | 30 +- src/Helpers/EnumCompare.php | 23 +- src/Helpers/EnumImplements.php | 12 + src/Helpers/EnumMagicCalls.php | 40 +++ src/Helpers/EnumState.php | 25 +- .../UnitEnums/State/StateElevatorEnum.php | 7 + tests/Unit/Concerns/ComparisonTest.php | 66 +++- tests/Unit/Concerns/ConstructorTest.php | 2 +- tests/Unit/Concerns/MappersMakersTest.php | 318 ++++++++++++++++++ tests/Unit/Concerns/StateTest.php | 68 ++++ tests/Unit/Helpers/EnumMagicCallsTest.php | 25 ++ 19 files changed, 695 insertions(+), 46 deletions(-) create mode 100644 src/Concerns/MagicCalls.php create mode 100644 src/Helpers/EnumMagicCalls.php create mode 100644 tests/Unit/Concerns/MappersMakersTest.php create mode 100644 tests/Unit/Helpers/EnumMagicCallsTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index f9e0d6e..91c288a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to `Enumhancer` will be documented in this file +## 1.18.0 - 2022-12-14 + +- Added Magic method functionality to [State](docs/state.md) +- Added `to` and `tryTo` methods to `State` +- Added `is`, `isNot`, `isIn` and `isNotIn` + to [Comparison](docs/comparison.md) + ## 1.17.0 - 2022-12-13 - Added [Flip](docs/mappers.md#flip), allowing to use diff --git a/README.md b/README.md index a1661cb..7436906 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ You can also just use one of the features by using the specific trait for that feature. Note: all traits can be used next to each other, except for `Mappers`, which has -implemented the methods of `Makers`, `Extractor` and `Reporters`. +implemented the methods of `Getters`, `Extractor` and `Reporters`. ### Features diff --git a/docs/comparison.md b/docs/comparison.md index b878b4e..84d2afe 100644 --- a/docs/comparison.md +++ b/docs/comparison.md @@ -67,6 +67,13 @@ YourThirdEnum::ENUM->equals('enum', 'enum2'); //returns true Next to `equals`, you can also handle assertions with `is` and `isNot`. ```php +YourEnum::Enum->is('enum'); // returns true +YourEnum::Enum->isNot('enum'); // returns false + +YourEnum::Enum->is('enum2'); // returns false +YourEnum::Enum->isNot('enum2'); // returns true + +/** magical methods */ YourEnum::ENUM->isEnum(); // returns true YourEnum::ENUM->isNotEnum(); // returns false YourEnum::ENUM->isEnum2(); // returns false @@ -86,6 +93,21 @@ contain that underscore. You also cannot use values with spaces. Tip: Use the @method tag in your docblock to typehint the methods if you like. +### isIn and isNotIn + +`Equals` already can do this, but this might be easier to the eye. + +````php +YourEnum::ENUM->isIn(YourEnum::ENUM, 'your_other_value'); // returns true +YourEnum::ENUM->isIn('ENUM', YourEnum::ENUM2); // returns true +YourEnum::ENUM->isIn('your_value', 'your_other_value'); //returns true +YourEnum::ENUM->isIn('ENUM2', YourEnum::ENUM2); // returns false + + +YourEnum::ENUM->isNotIn('random', 'something_else'); //returns true +YourEnum::ENUM->isNotIn('enum', 'something_else'); //returns false +```` + ## Comparing and mapping Comparison automatically uses [Mappers](mappers.md) whenever available. @@ -126,6 +148,12 @@ object, but do match by name or are mapped using a mapper. Animal::Dog->equals(LatinAnimalName::Canine); // returns true; Animal::Dog->equals(LatinAnimalName::Feline); // returns false; +Animal::Dog->is(LatinAnimalName::Canine); // returns true; +Animal::Dog->is(LatinAnimalName::Feline); // returns false; + +Animal::Dog->isNot(LatinAnimalName::Canine); // returns false; +Animal::Dog->isNot(LatinAnimalName::Feline); // returns true; + Animal::Dog->equals(SomeOtherEnum::Canine); // return true Animal::Dog->equals(SomeOtherEnum::Dog); // return true Animal::Dog->equals(SomeOtherEnum::Something); // throws error diff --git a/docs/constructor.md b/docs/constructor.md index e792115..611aa34 100644 --- a/docs/constructor.md +++ b/docs/constructor.md @@ -27,7 +27,4 @@ enum yourEnum { YourEnum::CALLABLE(); // will return YourEnum::CALLABLE; ``` -Note: Under the hood it is using `__callStatic`, so it may give some unpredicted -behavior when calling a method that doesn't exist. - Note: This trait is not enabled by default when using the `Enhancers` trait. diff --git a/docs/state.md b/docs/state.md index 44755de..b36baad 100644 --- a/docs/state.md +++ b/docs/state.md @@ -30,7 +30,16 @@ elevator::Open->allowsTransition(elevator::Move); // returns false elevator::Open->transitionTo('Close'); // returns elevator::Close elevator::Move->transitionTo(elevator::Close); // throws exception -elevator::Close->transitionTo('Open'); // throws exception +elevator::Close->transitionTo('Open'); // throws IllegalEnumTransitionException + +elevator::Open->to(elevator::Move) // throws IllegalEnumTransitionException +elevator::Open->tryTo(elevator::Move) // returns elevator::Open +elevator::Open->tryTo(elevator::Close) // returns elevator::Close + +/** using magic */ +elevator::Open->toMove() // throws IllegalEnumTransitionException +elevator::Open->tryToMove() // returns elevator::Open +elevator::Open->tryToClose() // returns elevator::Close ``` #### Complex Usage @@ -69,7 +78,7 @@ elevator::Open->transitionTo(elevator::Close) ->transitionTo('Open') ->transitionTo('Close'); //eventually returns elevator::Close -elevator::Move->transitionTo('Open'); //throws exception +elevator::Move->transitionTo('Open'); //throws IllegalEnumTransitionException ``` The array returned by the `customTransitions` method can return an array containing @@ -132,9 +141,20 @@ elevator::Move->allowedTransitions($hook); // returns [] elevator::Move->transitionTo('stop', $hook); // throws IllegalEnumTransitionException elevator::Close->allowTransition('move', $hook); // returns true elevator::Close->transitionTo('move', $hook); // returns increases $floor to 2 + +elevator::Move->tryTo('stop', $hook); // returns elevator::Move +elevator::Move->to('stop', $hook); // throws IllegalEnumTransitionException +elevator::Close->tryTo('move', $hook); // returns elevator::Close +elevator::Close->to('move', $hook); // returns increases $floor to 2 + +/** using magic calls */ +elevator::Move->tryToStop($hook); // returns elevator::Move +elevator::Move->toStop($hook); // throws IllegalEnumTransitionException +elevator::Close->tryToMove($hook); // returns elevator::Close +elevator::Close->toMove($hook); // returns increases $floor to 2 ``` -You can also add a `TransitionHook` directly on to your enum class. +You can also add a `TransitionHook` directly on to your enum object. ```php enum elevator { diff --git a/src/Concerns/Comparison.php b/src/Concerns/Comparison.php index 844eddc..9e89f99 100644 --- a/src/Concerns/Comparison.php +++ b/src/Concerns/Comparison.php @@ -2,39 +2,35 @@ namespace Henzeb\Enumhancer\Concerns; -use BadMethodCallException; -use Henzeb\Enumhancer\Helpers\EnumGetters; use Henzeb\Enumhancer\Helpers\EnumCompare; use UnitEnum; trait Comparison { + use MagicCalls; + public function equals(UnitEnum|string|int|null ...$equals): bool { return EnumCompare::equals($this, ...$equals); } - public function __call(string $name, array $arguments): self|bool + public function is(UnitEnum|string|int|null $equals): bool { - if (EnumCompare::isValidCall(self::class, $name, $arguments)) { - throw new BadMethodCallException(sprintf('Call to undefined method %s::%s(...)', $this::class, $name)); - } - - $nameIsEnum = !EnumGetters::tryGet(self::class, $name, true); - - if (!$nameIsEnum && method_exists(self::class, '__callStatic')) { - return self::__callStatic($name, []); - } + return $this->equals($equals); + } - $value = substr($name, str_starts_with($name, 'isNot') ? 5 : 2); + public function isNot(UnitEnum|string|int|null $equals): bool + { + return !$this->is($equals); + } - if (!EnumGetters::tryGet(self::class, $value, true)) { - throw new BadMethodCallException(sprintf('Call to undefined method %s::%s(...)', $this::class, $name)); - } + public function isIn(UnitEnum|string|int|null ...$equals): bool + { + return $this->equals(...$equals); + } - if (str_starts_with($name, 'isNot')) { - return !$this->equals($value); - } - return $this->equals($value); + public function isNotIn(UnitEnum|string|int|null ...$equals): bool + { + return !$this->equals(...$equals); } } diff --git a/src/Concerns/Constructor.php b/src/Concerns/Constructor.php index f87568b..736e38a 100644 --- a/src/Concerns/Constructor.php +++ b/src/Concerns/Constructor.php @@ -6,8 +6,5 @@ trait Constructor { - public static function __callStatic(string $name, array $arguments) - { - return EnumGetters::get(self::class, $name, true); - } + use MagicCalls; } diff --git a/src/Concerns/MagicCalls.php b/src/Concerns/MagicCalls.php new file mode 100644 index 0000000..ecdc5d8 --- /dev/null +++ b/src/Concerns/MagicCalls.php @@ -0,0 +1,18 @@ +transitionTo($state, $hook); + } + + public function tryTo(self|string|int $state, TransitionHook $hook = null): self + { + if ($this->isTransitionAllowed($state, $hook)) { + return $this->transitionTo($state, $hook); + } + return $this; + } + /** * @param self|string|int $state * @param TransitionHook|null $hook * @return bool - * @throws SyntaxException */ public function isTransitionAllowed(self|string|int $state, TransitionHook $hook = null): bool { /** * @var $this UnitEnum */ - $state = EnumGetters::cast(self::class, $state); + $state = EnumGetters::tryCast(self::class, $state); - return in_array($state, $this->allowedTransitions($hook)); + return $state && in_array($state, $this->allowedTransitions($hook)); } /** diff --git a/src/Helpers/EnumCompare.php b/src/Helpers/EnumCompare.php index 00e8fb1..af5502e 100644 --- a/src/Helpers/EnumCompare.php +++ b/src/Helpers/EnumCompare.php @@ -14,9 +14,24 @@ public static function equals(UnitEnum $compare, UnitEnum|int|string|null ...$wi public static function isValidCall(string $class, $name, array $arguments): bool { - $nameIsEnum = !EnumGetters::tryGet($class, $name, true); - return ((!str_starts_with($name, 'is') && !str_starts_with($name, 'isNot')) - || count($arguments)) - && $nameIsEnum; + EnumCheck::check($class); + + return EnumImplements::comparison($class) + && !count($arguments) && str_starts_with($name, 'is'); + } + + public static function call(UnitEnum $enum, string $name): bool + { + $value = EnumGetters::tryGet( + $enum::class, + substr($name, str_starts_with($name, 'isNot') ? 5 : 2), + true + ); + + if (!$value) { + EnumMagicCalls::throwException($enum::class, $name); + } + + return str_starts_with($name, 'isNot') !== self::equals($enum, $value); } } diff --git a/src/Helpers/EnumImplements.php b/src/Helpers/EnumImplements.php index fb5d846..b8afc61 100644 --- a/src/Helpers/EnumImplements.php +++ b/src/Helpers/EnumImplements.php @@ -2,6 +2,8 @@ namespace Henzeb\Enumhancer\Helpers; +use Henzeb\Enumhancer\Concerns\Comparison; +use Henzeb\Enumhancer\Concerns\Constructor; use Henzeb\Enumhancer\Concerns\Labels; use Henzeb\Enumhancer\Concerns\State; use Henzeb\Enumhancer\Concerns\Mappers; @@ -36,6 +38,16 @@ public static function labels(string $class): bool return self::traitOn($class, Labels::class); } + public static function constructor(string $class): bool + { + return self::traitOn($class, Constructor::class); + } + + public static function comparison(string $class): bool + { + return self::traitOn($class, Comparison::class); + } + private static function classUsesTrait(string $class): array { $results = []; diff --git a/src/Helpers/EnumMagicCalls.php b/src/Helpers/EnumMagicCalls.php new file mode 100644 index 0000000..177feeb --- /dev/null +++ b/src/Helpers/EnumMagicCalls.php @@ -0,0 +1,40 @@ +tryTo($toState, ...$arguments); + } + + return $currentState->to($toState, ...$arguments); + } } diff --git a/tests/Fixtures/UnitEnums/State/StateElevatorEnum.php b/tests/Fixtures/UnitEnums/State/StateElevatorEnum.php index 9eb0b8f..ef0ee32 100644 --- a/tests/Fixtures/UnitEnums/State/StateElevatorEnum.php +++ b/tests/Fixtures/UnitEnums/State/StateElevatorEnum.php @@ -6,6 +6,13 @@ use Henzeb\Enumhancer\Concerns\Properties; use Henzeb\Enumhancer\Contracts\TransitionHook; +/** + * @method tryToClose() + * @method tryToMove() + * @method toClose() + * @method toMove() + * @method doesNotExist() + */ enum StateElevatorEnum { use State, Properties; diff --git a/tests/Unit/Concerns/ComparisonTest.php b/tests/Unit/Concerns/ComparisonTest.php index 87346ec..14f3682 100644 --- a/tests/Unit/Concerns/ComparisonTest.php +++ b/tests/Unit/Concerns/ComparisonTest.php @@ -198,6 +198,70 @@ public function testPassingEnums(): void $this->expectException(BadMethodCallException::class); - $this->assertTrue(EnhancedBackedEnum::ANOTHER_ENUM->isFailure()); + $this->assertTrue(EnhancedBackedEnum::ANOTHER_ENUM->isExpectedToFail()); + } + + public function testIs(): void + { + $this->assertTrue(EnhancedBackedEnum::ANOTHER_ENUM->is('another_enum')); + + $this->assertTrue(EnhancedBackedEnum::ANOTHER_ENUM->is(1)); + + $this->assertTrue(EnhancedBackedEnum::ANOTHER_ENUM->is(EnhancedUnitEnum::ANOTHER_ENUM)); + + $this->assertTrue(EnhancedBackedEnum::ANOTHER_ENUM->is('mapped')); + + $this->assertFalse(EnhancedBackedEnum::ANOTHER_ENUM->is('something else')); + } + + public function testIsNot(): void + { + $this->assertFalse(EnhancedBackedEnum::ANOTHER_ENUM->isNot('another_enum')); + + $this->assertTrue(EnhancedBackedEnum::ANOTHER_ENUM->isNot(2)); + + $this->assertFalse(EnhancedBackedEnum::ANOTHER_ENUM->isNot(EnhancedUnitEnum::ANOTHER_ENUM)); + + $this->assertFalse(EnhancedBackedEnum::ANOTHER_ENUM->isNot('mapped')); + + $this->assertTrue(EnhancedBackedEnum::ANOTHER_ENUM->isNot('something else')); + } + + public function testIsIn(): void + { + $this->assertTrue( + EnhancedBackedEnum::ANOTHER_ENUM->isIn('another_enum', 'somethingElse') + ); + + $this->assertTrue( + EnhancedBackedEnum::ANOTHER_ENUM->isIn(EnhancedUnitEnum::ANOTHER_ENUM, 'somethingElse') + ); + + $this->assertTrue( + EnhancedBackedEnum::ANOTHER_ENUM->isIn(0, 1) + ); + + $this->assertFalse( + EnhancedBackedEnum::ANOTHER_ENUM->isIn(0, 2) + ); + } + + public function testIsNotIn(): void + { + $this->assertTrue( + EnhancedBackedEnum::ANOTHER_ENUM->isNotIn('other_enums', 'somethingElse') + ); + + $this->assertTrue( + EnhancedBackedEnum::ANOTHER_ENUM->isNotIn(EnhancedUnitEnum::ENUM, 'somethingElse') + ); + + $this->assertTrue( + EnhancedBackedEnum::ANOTHER_ENUM->isNotIn(0, 2) + ); + + $this->assertFalse( + EnhancedBackedEnum::ANOTHER_ENUM->isNotIn(0, 1) + ); } } diff --git a/tests/Unit/Concerns/ConstructorTest.php b/tests/Unit/Concerns/ConstructorTest.php index 359606d..25c5d44 100644 --- a/tests/Unit/Concerns/ConstructorTest.php +++ b/tests/Unit/Concerns/ConstructorTest.php @@ -20,7 +20,7 @@ public function testShouldGetEnumUsingStaticCall(): void public function testShouldFailUsingStaticCallToUnknownEnum(): void { - $this->expectError(); + $this->expectException(\BadMethodCallException::class); ConstructableUnitEnum::CANNOT_CALL(); } diff --git a/tests/Unit/Concerns/MappersMakersTest.php b/tests/Unit/Concerns/MappersMakersTest.php new file mode 100644 index 0000000..c0f79ac --- /dev/null +++ b/tests/Unit/Concerns/MappersMakersTest.php @@ -0,0 +1,318 @@ + EnhancedBackedEnum::ENUM + ]; + } + }; + } + + public function testMakeShouldWorkWithoutMapper() + { + $this->assertEquals( + EnhancedBackedEnum::ENUM, + EnhancedBackedEnum::make('ENUM') + ); + } + + public function testMakeShouldErrorWithoutMapper() + { + $this->expectError(); + EnhancedBackedEnum::make('mappedEnum'); + } + + public function testMakeShouldMap() + { + $this->assertEquals( + EnhancedBackedEnum::ENUM, + EnhancedBackedEnum::make('mappedEnum', $this->getMapper()) + ); + } + + public function testMakeShouldMapWithStringMap() + { + $this->assertEquals( + EnhancedBackedEnum::ENUM, + EnhancedBackedEnum::make('mappedEnum', $this->getMapper()::class) + ); + } + + public function testMakeShouldThrowExceptionForNonMap() + { + $this->expectException(\RuntimeException::class); + EnhancedBackedEnum::make('mappedEnum', self::class); + } + + public function testMakeShouldNotMapWhenNull() + { + $this->expectError(); + EnhancedBackedEnum::make(null, $this->getMapper()); + } + + public function testMakeShouldMapWithoutMapperGiven() + { + $this->assertEquals( + EnhancedBackedEnum::ENUM, + EnhancedBackedEnum::make('anotherMappedEnum') + ); + } + + public function testMakeShouldErrorWithMap() + { + $this->expectError(); + EnhancedBackedEnum::make('not existing', $this->getMapper()); + } + + public function testTryMakeShouldWorkWithoutMapper() + { + $this->assertEquals( + EnhancedBackedEnum::ENUM, + EnhancedBackedEnum::tryGet('ENUM') + ); + } + + public function testTryGetShouldReturnNullWithoutMapper() + { + $this->assertNull(EnhancedBackedEnum::tryGet('mappedEnum')); + } + + public function testTryGetShouldNotMapWhenNull() + { + + $this->assertNull( + EnhancedBackedEnum::tryGet(null, $this->getMapper()) + ); + } + + public function testTryGetShouldMap() + { + $this->assertEquals( + EnhancedBackedEnum::ENUM, + EnhancedBackedEnum::tryGet('mappedEnum', $this->getMapper()) + ); + } + + public function testTryGetShouldMapWithoutMapperGiven() + { + $this->assertEquals( + EnhancedBackedEnum::ENUM, + EnhancedBackedEnum::tryGet('anotherMappedEnum') + ); + } + + + public function testTryGetShouldReturnNullWithMap() + { + $this->assertNull(EnhancedBackedEnum::tryGet('not existing', $this->getMapper())); + } + + + public function testGetArrayShouldNotMapWhenNull() + { + $this->expectError(); + EnhancedBackedEnum::makeArray([null], $this->getMapper()); + } + + public function testGetArrayShouldWorkWithoutMapper() + { + $this->assertEquals( + [EnhancedBackedEnum::ENUM], + EnhancedBackedEnum::makeArray(['ENUM']) + ); + } + + public function testGetArrayShouldThrowErrorWorkWithoutMapper() + { + $this->expectError(); + EnhancedBackedEnum::makeArray(['Does Not exist']); + } + + public function testGetArrayShouldWorkWitMapper() + { + $this->assertEquals( + [EnhancedBackedEnum::ENUM], + EnhancedBackedEnum::tryMakeArray(['mappedEnum'], $this->getMapper()) + ); + } + + + public function testGetArrayShouldMapWithoutMapperGiven() + { + $this->assertEquals( + [EnhancedBackedEnum::ENUM], + EnhancedBackedEnum::GetArray(['anotherMappedEnum']) + ); + } + + public function testGetArrayShouldThrowErrorWitMapper() + { + $this->expectError(); + EnhancedBackedEnum::makeArray(['ENUM', 'doesNotExist'], $this->getMapper()); + } + + public function testTryGetArrayShouldWorkWithoutMapper() + { + $this->assertEquals( + [EnhancedBackedEnum::ENUM], + EnhancedBackedEnum::tryMakeArray(['ENUM', 'DoesNotExist']) + ); + } + + public function testTryGetArrayShouldNotMapWhenNull() + { + $this->assertEquals([], EnhancedBackedEnum::tryMakeArray([null], $this->getMapper())); + } + + public function testTryGetArrayShouldWorkWitMapper() + { + $this->assertEquals( + [EnhancedBackedEnum::ENUM], + EnhancedBackedEnum::tryMakeArray(['mappedEnum', 'DoesNotExist'], $this->getMapper()) + ); + } + + public function testtryArrayShouldMapWithoutMapperGiven() + { + $this->assertEquals( + [EnhancedBackedEnum::ENUM], + EnhancedBackedEnum::tryMakeArray(['anotherMappedEnum']) + ); + } + + public function testShouldUseMapperWhenConstructorIsUsed() + { + $this->assertEquals( + EnhancedBackedEnum::ENUM, + EnhancedBackedEnum::anotherMappedEnum() + ); + } + + public function testShouldExtractWithDefaultMappedKey() + { + $this->assertEquals( + [EnhancedBackedEnum::ENUM], + EnhancedBackedEnum::extract('This text contains anotherMappedEnum for you') + ); + } + + public function testShouldExtractWithMappedKey() + { + $this->assertEquals( + [EnhancedBackedEnum::ENUM], + EnhancedBackedEnum::extract('This text contains mappedEnum for you', $this->getMapper()) + ); + } + + public function testShouldExtractWithMappedKeyAndDefaultMappedKey() + { + $this->assertEquals( + [EnhancedBackedEnum::ENUM, EnhancedBackedEnum::ENUM], + EnhancedBackedEnum::extract( + 'This text contains mappedEnum and anotherMappedEnum for you', + $this->getMapper() + ) + ); + } + + public function testShouldAcceptEnumsAsValue(): void + { + //EnhancedUnitEnum::Mapped->name => EnhancedBackedEnum::ANOTHER_ENUM + $this->assertEquals( + EnhancedBackedEnum::ENUM, + EnhancedBackedEnum::tryMake(EnhancedBackedEnum::ENUM) + ); + + $this->assertEquals( + EnhancedBackedEnum::ANOTHER_ENUM, + EnhancedBackedEnum::tryMake(EnhancedUnitEnum::Mapped) + ); + + $this->assertEquals( + EnhancedBackedEnum::ENUM, + EnhancedBackedEnum::tryMake(EnhancedUnitEnum::ENUM) + ); + + $this->assertNull( + EnhancedBackedEnum::tryMake(EnhancedUnitEnum::Unique) + ); + + $this->assertEquals( + EnhancedBackedEnum::ENUM, + EnhancedBackedEnum::make(EnhancedBackedEnum::ENUM) + ); + + $this->assertEquals( + EnhancedBackedEnum::ANOTHER_ENUM, + EnhancedBackedEnum::make(EnhancedUnitEnum::Mapped) + ); + + $this->assertEquals( + EnhancedBackedEnum::ENUM, + EnhancedBackedEnum::make(EnhancedUnitEnum::ENUM) + ); + + $this->expectException(ValueError::class); + EnhancedBackedEnum::make(EnhancedUnitEnum::Unique); + } + + public function testShouldAcceptEnumsAsValueArrays(): void + { + $this->assertEquals( + [EnhancedBackedEnum::ENUM], + EnhancedBackedEnum::tryMakeArray([EnhancedBackedEnum::ENUM]) + ); + + $this->assertEquals( + [EnhancedBackedEnum::ANOTHER_ENUM], + EnhancedBackedEnum::tryMakeArray([EnhancedUnitEnum::Mapped]) + ); + + $this->assertEquals( + [EnhancedBackedEnum::ENUM], + EnhancedBackedEnum::tryMakeArray([EnhancedUnitEnum::ENUM]) + ); + + $this->assertEquals( + [], + EnhancedBackedEnum::tryMakeArray([EnhancedUnitEnum::Unique]) + ); + + $this->assertEquals( + [EnhancedBackedEnum::ENUM], + EnhancedBackedEnum::tryMakeArray([EnhancedBackedEnum::ENUM]) + ); + + $this->assertEquals( + [EnhancedBackedEnum::ENUM], + EnhancedBackedEnum::makeArray([EnhancedUnitEnum::ENUM]) + ); + + $this->assertEquals( + [EnhancedBackedEnum::ANOTHER_ENUM], + EnhancedBackedEnum::makeArray([EnhancedUnitEnum::Mapped]) + ); + + $this->expectException(ValueError::class); + EnhancedBackedEnum::makeArray([EnhancedUnitEnum::Unique]); + } +} diff --git a/tests/Unit/Concerns/StateTest.php b/tests/Unit/Concerns/StateTest.php index b4cf6e5..828b203 100644 --- a/tests/Unit/Concerns/StateTest.php +++ b/tests/Unit/Concerns/StateTest.php @@ -29,6 +29,13 @@ public function testBasicTransition(): void ->transitionTo('Move') ->transitionTo(StateElevatorEnum::Stop) ); + + $this->assertEquals( + StateElevatorEnum::Stop, + StateElevatorEnum::Open->to('close') + ->to('Move') + ->to(StateElevatorEnum::Stop) + ); } public function testComplexTransition(): void @@ -89,6 +96,20 @@ public function testIllegalTransitionsThrowException(mixed $from, mixed $to): vo $from->transitionTo($to); } + /** + * @param StateElevatorEnum|StateElevatorComplexEnum|UnitEnum|string|int $from + * @param StateElevatorEnum|StateElevatorComplexEnum|string|int $to + * @return void + * + * @dataProvider providesNotAllowedTransitionTestcases + */ + public function testIllegalTransitionsToThrowException(mixed $from, mixed $to): void + { + $this->expectException(IllegalEnumTransitionException::class); + + $from->to($to); + } + public function testNullParameterDisablesTransition(): void { $this->assertFalse(StateElevatorDisableTransitionEnum::Open->isTransitionAllowed('close')); @@ -98,6 +119,15 @@ public function testNullParameterDisablesTransition(): void StateElevatorDisableTransitionEnum::Open->transitionTo('close'); } + public function testNullParameterDisablesTransitionWithTo(): void + { + $this->assertFalse(StateElevatorDisableTransitionEnum::Open->isTransitionAllowed('close')); + + $this->expectException(IllegalEnumTransitionException::class); + + StateElevatorDisableTransitionEnum::Open->to('close'); + } + public function testCloseToMoveStillWorksWhenCustomTransitions(): void { $this->assertTrue(StateElevatorDisableTransitionEnum::Close->isTransitionAllowed('move')); @@ -245,4 +275,42 @@ public function allowsOpenClose(): bool StateElevatorEnum::setTransitionHook($hookSuccess); $this->assertTrue(StateElevatorEnum::Open->isTransitionAllowed('close', $hookSuccess)); } + + public function testTryTo(): void + { + $this->assertEquals(StateElevatorEnum::Close, StateElevatorEnum::Open->tryTo('close')); + $this->assertEquals(StateElevatorEnum::Close, StateElevatorEnum::Open->tryTo(StateElevatorEnum::Close)); + + $this->assertEquals(StateElevatorEnum::Open, StateElevatorEnum::Open->tryTo('up')); + + $this->assertEquals(StateElevatorEnum::Open, StateElevatorEnum::Open->tryTo('move')); + + $this->assertEquals(StateElevatorEnum::Open, StateElevatorEnum::Open->tryTo(StateElevatorEnum::Move)); + } + + public function testMagicCalls(): void + { + $this->assertEquals(StateElevatorEnum::Close, StateElevatorEnum::Open->tryToClose()); + + $this->assertEquals(StateElevatorEnum::Close, StateElevatorEnum::Open->toClose()); + + $this->assertEquals(StateElevatorEnum::Open, StateElevatorEnum::Open->tryToMove()); + + $this->expectException(IllegalEnumTransitionException::class); + + StateElevatorEnum::Open->toMove(); + } + + public function testMagicCallsWithHooks(): void + { + $this->assertEquals(StateElevatorEnum::Close, StateElevatorEnum::Open->tryToClose()); + + $this->assertEquals(StateElevatorEnum::Close, StateElevatorEnum::Open->toClose()); + + $this->assertEquals(StateElevatorEnum::Open, StateElevatorEnum::Open->tryToMove()); + + $this->expectException(IllegalEnumTransitionException::class); + + StateElevatorEnum::Open->toMove(); + } } diff --git a/tests/Unit/Helpers/EnumMagicCallsTest.php b/tests/Unit/Helpers/EnumMagicCallsTest.php new file mode 100644 index 0000000..3a51f4b --- /dev/null +++ b/tests/Unit/Helpers/EnumMagicCallsTest.php @@ -0,0 +1,25 @@ +expectException(BadMethodCallException::class); + + StateElevatorEnum::Open->doesNotExist(); + } + + + public function testShouldThrowExceptionWhenMethodNotavailableStatic(): void + { + $this->expectException(BadMethodCallException::class); + + StateElevatorEnum::doesNotExist(); + } +}