/** * {@inheritdoc} * * @throws TransitionPatternMismatch */ public static function new(string $transition) : Transition { $isMatch = preg_match(self::getPattern(), $transition, $matches); $withExpectedNamedCaptures = isset($matches['current_state'], $matches['input'], $matches['next_state']); if (!$isMatch || !$withExpectedNamedCaptures) { throw new TransitionPatternMismatch($transition, self::$pattern); } return new self(FlyweightState::named($matches['current_state']), FlyweightInput::named($matches['input']), FlyweightState::named($matches['next_state'])); }
/** * @dataProvider movesProvider * * @param string $currentStateName * @param array $inputNames * @param string $expectedStateName */ public function testResolvesStateTransitionsProperly(string $currentStateName, array $inputNames, string $expectedStateName) { $table = new ChessMatchTransitionTable(); $currentState = FlyweightState::named($currentStateName); $expectedState = FlyweightState::named($expectedStateName); foreach ($inputNames as $inputName) { $currentState = $table->resolve($currentState, FlyweightInput::named($inputName)); } $this->assertSame($expectedState, $currentState); }
public function testCanCreateTransitionsUsingConstructor() { $currentState = FlyweightState::named('FOO'); $input = FlyweightInput::named('BAR'); $nextState = FlyweightState::named('BAZ'); $transition = new DefaultTransition($currentState, $input, $nextState); $this->assertInstanceOf(Transition::class, $transition); $this->assertSame($currentState, $transition->getCurrentState()); $this->assertSame($input, $transition->getInput()); $this->assertSame($nextState, $transition->getNextState()); }
public function testAdditionalExceptionPropertiesAreAccessibleViaGeters() { $input = FlyweightInput::named('BAR'); $object = new StatefulObject(); $object->setCurrentState(FlyweightState::named('FOO')); $context = new Context(); $nextState = FlyweightState::named('FOOBAR'); $previous = new \Exception(); $exception = new StateTransitionFailed($input, $object, $context, $nextState, $previous); $this->assertSame($input, $exception->getInput()); $this->assertSame($object, $exception->getObject()); $this->assertSame($context, $exception->getContext()); $this->assertSame($nextState, $exception->getNextState()); $this->assertSame($previous, $exception->getPrevious()); }
public function testExceptionsInListenersPropagateOutToApplication() { $this->expectException(\Exception::class); $this->expectExceptionMessage('kaboom!'); $object = new StatefulObject(); $object->setCurrentState(FlyweightState::named('WHITES_TURN')); $originalException = new \Exception('kaboom!'); $statemachine = new Statemachine(new ChessMatchTransitionTable()); $statemachine->before(FlyweightInput::named('WHITE_MOVES'), FlyweightState::named('WHITES_TURN'), CallbackListener::fromCallable(function (Event $event) use($originalException) { throw $originalException; })); $statemachine->trigger(FlyweightInput::named('WHITE_MOVES'), $object); }
/** * Returns events to be emitted whenever a state transition is attempted * * @param Input $input * @param State $currentState * * @return Generator */ private function eventProvider(Input $input, State $currentState) : Generator { $anyInput = FlyweightInput::any(); $anyState = FlyweightState::any(); (yield Event::named($this->getEventName('before', $input, $currentState))); (yield Event::named($this->getEventName('before', $anyInput, $currentState))); (yield Event::named($this->getEventName('before', $input, $anyState))); (yield Event::named($this->getEventName('before', $anyInput, $anyState))); (yield Event::named($this->getEventName('on', $anyInput, $anyState))); (yield Event::named($this->getEventName('after', $input, $currentState))); (yield Event::named($this->getEventName('after', $anyInput, $currentState))); (yield Event::named($this->getEventName('after', $input, $anyState))); (yield Event::named($this->getEventName('after', $anyInput, $anyState))); }