diff --git a/src/Runtime/Runner.php b/src/Runtime/Runner.php index bb044ee..df53c9c 100644 --- a/src/Runtime/Runner.php +++ b/src/Runtime/Runner.php @@ -52,7 +52,11 @@ public function __construct(StreamFactory $streamFactory) public function attach(StreamContainerInterface $streamContainer, Closure $onSelect, string $identity): void { if (array_key_exists($identity, $this->containers)) { - throw new RunnerException("Stream container with identity {$identity} already attached"); + // On repeated identity, check if actually readable (detach if not) + if ($this->containers[$identity]->stream->isReadable()) { + throw new RunnerException("Stream container with identity {$identity} already attached"); + } + $this->detach($identity); } $stream = $streamContainer->getStream(); $this->streamCollection->attach($stream, $identity); diff --git a/tests/suites/runtime/RunnerTest.php b/tests/suites/runtime/RunnerTest.php index d55786b..cdb95db 100644 --- a/tests/suites/runtime/RunnerTest.php +++ b/tests/suites/runtime/RunnerTest.php @@ -62,6 +62,36 @@ public function testHandling(): void $runner->detach($streamContainer->getIdentity()); } + public function testIdentityConflictResolvable(): void + { + $this->expectStreamFactory(); + $factory = new StreamFactory(); + $this->expectStreamFactoryCreateStreamCollection(); + $this->expectStreamCollection(); + $runner = new Runner($factory); + + $this->expectStreamFactoryCreateStream(); + $this->expectStreamFactoryCreateStreamFromResource(); + $this->expectStream(); + $this->expectStreamGetMetadata(); + $this->expectContext(); + $streamContainer = new MockStreamContainer($factory); + + $this->expectStreamCollectionAttach(); + $runner->attach($streamContainer, function () { + // ignore + }, $streamContainer->getIdentity()); + + $this->expectStreamIsReadable()->setReturn(function () { + return false; + }); + $this->expectStreamCollectionDetach(); + $this->expectStreamCollectionAttach(); + $runner->attach($streamContainer, function () { + // ignore + }, $streamContainer->getIdentity()); + } + public function testIdentityConflict(): void { $this->expectStreamFactory(); @@ -81,6 +111,10 @@ public function testIdentityConflict(): void $runner->attach($streamContainer, function () { // ignore }, $streamContainer->getIdentity()); + + $this->expectStreamIsReadable()->setReturn(function () { + return true; + }); $this->expectException(RunnerException::class); $this->expectExceptionMessage('Stream container with identity mock-stream-container already attached'); $runner->attach($streamContainer, function () {