From b2a5e3b52b3b435f157bcbe1220d9cc4ecaa3737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Jensen?= Date: Sun, 11 Jan 2026 17:58:47 +0100 Subject: [PATCH 1/2] Runner class --- composer.json | 4 +- docs/Changelog.md | 2 + docs/Class_Synopsis.md | 46 ++--- docs/Client.md | 10 +- docs/Configuration.md | 7 +- docs/Server.md | 11 +- src/Client.php | 109 +++++----- src/Connection.php | 17 +- src/Exception/RunnerException.php | 16 ++ src/Runtime/Runner.php | 91 +++++++++ src/Server.php | 169 +++++++-------- tests/bootstrap.php | 2 + tests/mock/MockStreamTrait.php | 18 +- tests/suites/client/ClientErrorTest.php | 55 ++--- tests/suites/client/ClientTest.php | 193 ++++++++++-------- tests/suites/client/ConfigErrorTest.php | 1 - tests/suites/client/ConfigTest.php | 125 ++++++------ tests/suites/client/HandshakeTest.php | 62 ++---- .../configuration/ConfigurationTest.php | 5 - tests/suites/connection/ConnectionTest.php | 46 ----- tests/suites/connection/ExceptionTest.php | 45 ---- tests/suites/frame/FrameHandlerTest.php | 1 - tests/suites/frame/FrameTest.php | 5 - tests/suites/http/HttpHandlerTest.php | 1 - tests/suites/http/RequestTest.php | 5 - tests/suites/http/ResponseTest.php | 5 - tests/suites/http/ServerRequestTest.php | 5 - tests/suites/message/BinaryTest.php | 5 - tests/suites/message/CloseTest.php | 5 - tests/suites/message/MessageHandlerTest.php | 1 - tests/suites/message/PingTest.php | 5 - tests/suites/message/PongTest.php | 5 - tests/suites/message/TextTest.php | 5 - tests/suites/middleware/CallbackTest.php | 21 -- tests/suites/middleware/CloseHandlerTest.php | 5 - .../middleware/DeflateCompressorTest.php | 22 +- .../suites/middleware/FollowRedirectTest.php | 9 +- tests/suites/middleware/PingIntervalTest.php | 4 +- tests/suites/middleware/PingResponderTest.php | 3 - tests/suites/middleware/ProcessStackTest.php | 9 - .../middleware/SubprotocolNegotiationTest.php | 23 --- tests/suites/runtime/RunnerTest.php | 76 +++++++ tests/suites/server/ConfigErrorTest.php | 5 - tests/suites/server/ConfigTest.php | 51 ++--- tests/suites/server/HandshakeTest.php | 97 +++++---- tests/suites/server/ServerTest.php | 176 +++++++++------- 46 files changed, 788 insertions(+), 795 deletions(-) create mode 100644 src/Exception/RunnerException.php create mode 100644 src/Runtime/Runner.php create mode 100644 tests/suites/runtime/RunnerTest.php diff --git a/composer.json b/composer.json index 779238f..7c1bb42 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "nyholm/psr7": "^1.4", "phrity/http": "^1.1", "phrity/net-uri": "^2.1", - "phrity/net-stream": "^2.3", + "phrity/net-stream": "dev-ws4 as 2.3.99", "psr/http-factory": "^1.0", "psr/http-message": "^1.1 || ^2.0", "psr/log": "^1.0 || ^2.0 || ^3.0" @@ -39,7 +39,7 @@ "phpstan/phpstan": "^2.0", "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", "phrity/logger-console": "^1.0", - "phrity/net-mock": "^2.3", + "phrity/net-mock": "dev-ws4 as 2.3.99", "phrity/util-errorhandler": "^1.1", "robiningelbrecht/phpunit-coverage-tools": "^1.9", "squizlabs/php_codesniffer": "^4.0" diff --git a/docs/Changelog.md b/docs/Changelog.md index 26a31a9..2c1d168 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -11,6 +11,8 @@ * Configuration class for various settings (@sirn-se) * Identity interface and implementation (@sirn-se) * Using Nyholm PSR HTTP (intermediate solution) (@sirn-se) + * Runner class wraps stream-select handler (@sirn-se) + * StreamFactory as constructor option Client/Server (@sirn-se) * Preparations for v4 (@sirn-se) ## `v3.6` diff --git a/docs/Class_Synopsis.md b/docs/Class_Synopsis.md index 53c148c..aa20212 100644 --- a/docs/Class_Synopsis.md +++ b/docs/Class_Synopsis.md @@ -34,7 +34,7 @@ class WebSocket\Client imlements WebSocket\Runtime\IdentityInterface, Psr\Log\Lo use WebSocket\Trait\SendMethodsTrait; use WebSocket\Trait\StringableTrait; - public method __construct(Psr\Http\Message\UriInterface|string $uri, WebSocket\Configuration|null $configuration = null); + public method __construct(Psr\Http\Message\UriInterface|string $uri, WebSocket\Configuration|null $configuration = null, Phrity\Net\StreamFactory|null $streamFactory = null); public method __toString(): string; public method addHeader(string $name, string $content): self; public method addMiddleware(WebSocket\Middleware\MiddlewareInterface $middleware): self; @@ -85,14 +85,13 @@ class WebSocket\Configuration imlements Psr\Log\LoggerAwareInterface, Stringable public method setTimeout(int|float $timeout): void; } -class WebSocket\Connection imlements WebSocket\Runtime\IdentityInterface, Psr\Log\LoggerAwareInterface, Stringable +class WebSocket\Connection imlements WebSocket\Runtime\IdentityInterface, Psr\Log\LoggerAwareInterface, Phrity\Net\StreamContainerInterface, Stringable { use WebSocket\Trait\ConfigurationTrait; use WebSocket\Trait\SendMethodsTrait; use WebSocket\Trait\StringableTrait; public method __construct(Phrity\Net\SocketStream $stream, bool $pushMasked, bool $pullMaskedRequired, bool $ssl = false, Phrity\Http\HttpFactory|null $httpFactory = null, WebSocket\Configuration|null $configuration = null); - public method __destruct(); public method __toString(): string; public method addMiddleware(WebSocket\Middleware\MiddlewareInterface $middleware): self; public method closeRead(): self; @@ -106,6 +105,7 @@ class WebSocket\Connection imlements WebSocket\Runtime\IdentityInterface, Psr\Lo public method getMeta(string $key): mixed; public method getName(): string|null; public method getRemoteName(): string|null; + public method getStream(): Phrity\Net\SocketStream; public method getTimeout(): int|float; public method isConnected(): bool; public method isReadable(): bool; @@ -171,6 +171,10 @@ class WebSocket\Exception\ReconnectException extends WebSocket\Exception\Excepti public method getUri(): Phrity\Net\Uri|null; } +class WebSocket\Exception\RunnerException extends WebSocket\Exception\Exception +{ +} + class WebSocket\Exception\ServerException extends WebSocket\Exception\Exception { } @@ -412,14 +416,23 @@ class WebSocket\Middleware\SubprotocolNegotiation imlements Psr\Log\LoggerAwareI public method setLogger(Psr\Log\LoggerInterface $logger): void; } -class WebSocket\Server imlements WebSocket\Runtime\IdentityInterface, Psr\Log\LoggerAwareInterface, Stringable +class WebSocket\Runtime\Runner +{ + public method __construct(Phrity\Net\StreamFactory $streamFactory); + public method attach(Phrity\Net\StreamContainerInterface $streamContainer, Closure $onSelect): void; + public method detach(string $identity): void; + public method handle(int|float $timeout): void; + public method select(int|float $timeout): Phrity\Net\StreamCollection; +} + +class WebSocket\Server imlements WebSocket\Runtime\IdentityInterface, Psr\Log\LoggerAwareInterface, Phrity\Net\StreamContainerInterface, Stringable { use WebSocket\Trait\ConfigurationTrait; use WebSocket\Trait\ListenerTrait; use WebSocket\Trait\SendMethodsTrait; use WebSocket\Trait\StringableTrait; - public method __construct(int $port = 80, bool $ssl = false, WebSocket\Configuration|null $configuration = null); + public method __construct(int $port = 80, bool $ssl = false, WebSocket\Configuration|null $configuration = null, Phrity\Net\StreamFactory|null $streamFactory = null); public method __toString(): string; public method addMiddleware(WebSocket\Middleware\MiddlewareInterface $middleware): self; public method disconnect(): void; @@ -431,6 +444,7 @@ class WebSocket\Server imlements WebSocket\Runtime\IdentityInterface, Psr\Log\Lo public method getPort(): int; public method getReadableConnections(): array; public method getScheme(): string; + public method getStream(): Phrity\Net\SocketServer; public method getTimeout(): int|float; public method getWritableConnections(): array; public method isRunning(): bool; @@ -525,27 +539,5 @@ trait WebSocket\Trait\ListenerTrait public method onDisconnect(Closure $closure): self; public method onError(Closure $closure): self; public method onHandshake(Closure $closure): self; - public method onPing(Closure $closure): self; - public method onPong(Closure $closure): self; - public method onText(Closure $closure): self; - public method onTick(Closure $closure): self; -} - -trait WebSocket\Trait\OpcodeTrait -{ -} - -trait WebSocket\Trait\SendMethodsTrait -{ - public method binary(string $message): WebSocket\Message\Binary; - public method close(int $status = 1000, string $message = "ttfn"): WebSocket\Message\Close; - public method ping(string $message = ""): WebSocket\Message\Ping; - public method pong(string $message = ""): WebSocket\Message\Pong; - public method text(string $message): WebSocket\Message\Text; -} - -trait WebSocket\Trait\StringableTrait -{ - public method __toString(): string; } ``` diff --git a/docs/Client.md b/docs/Client.md index f2cbc64..494bb0b 100644 --- a/docs/Client.md +++ b/docs/Client.md @@ -139,9 +139,17 @@ $client->close(1000, "Closing now"); ## Configuration -The Client takes one argument: [URI](http://tools.ietf.org/html/rfc3986) as a class implementing [UriInterface](https://www.php-fig.org/psr/psr-7/#35-psrhttpmessageuriinterface) or as string. +The Client has one required argument: [URI](http://tools.ietf.org/html/rfc3986) as a class implementing [UriInterface](https://www.php-fig.org/psr/psr-7/#35-psrhttpmessageuriinterface) or as string. The client support `ws` (`tcp`) and `wss` (`ssl`) schemas, depending on SSL configuration. +```php +__construct( + Psr\Http\Message\UriInterface|string $uri, + WebSocket\Configuration|null $configuration = null, + Phrity\Net\StreamFactory|null $streamFactory = null, +); +``` + Other options are available using the Configuration class. - Logger diff --git a/docs/Configuration.md b/docs/Configuration.md index bdc95a9..382025d 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -35,6 +35,7 @@ Get and set Configuration; ```php $configuration = $client->getConfiguration(); $client->setConfiguration($configuration); + $configuration = $server->getConfiguration(); $server->setConfiguration($configuration); ``` @@ -49,7 +50,7 @@ type: Psr\Log\LoggerInterface default: Psr\Log\NullLogger ``` -Attach any [PSR-4 compatible](https://www.php-fig.org/psr/psr-3/) logger. +Attach any [PSR-3 compatible](https://www.php-fig.org/psr/psr-3/) logger. ```php // Configuration instance @@ -104,7 +105,7 @@ default: 60 Timeout for various operations can be specified in seconds. This affects how long Client and Server will wait for connection, read and write operations, and listener scope. Default is `60` seconds. Minimum is `0` seconds. Accepts int or float value. -Avoid setting very low values as it will cause a read loop to use all +Avoid setting very low values as it will cause a read loop that uses lots of the available processing power even when there's nothing to read. ```php @@ -127,7 +128,7 @@ type: int<1, max> default: 4096 ``` -Defines the maximum payload per frame size in bytes. +Defines the maximum frame payload size in bytes. Default is `4096` bytes. Minimum is `1` byte. Do not change unless you have a strong reason to do so. diff --git a/docs/Server.md b/docs/Server.md index 177d10a..06f29f9 100644 --- a/docs/Server.md +++ b/docs/Server.md @@ -136,9 +136,18 @@ $server->close(1000, "Closing now"); ## Configuration -The Server takes two arguments; port and ssl. +The Server has two main arguments; port and ssl. By default, ssl is false. If port is not specified, it will use 80 for non-secure and 443 for secure server. +```php +__construct( + int $port = 80, + bool $ssl = false, + WebSocket\Configuration|null $configuration = null, + Phrity\Net\StreamFactory|null $streamFactory = null, +); +``` + Other options are available using the Configuration class. - Logger diff --git a/src/Client.php b/src/Client.php index 605b469..d1d53d7 100644 --- a/src/Client.php +++ b/src/Client.php @@ -39,7 +39,10 @@ use WebSocket\Http\DefaultHttpFactory; use WebSocket\Message\Message; use WebSocket\Middleware\MiddlewareInterface; -use WebSocket\Runtime\IdentityInterface; +use WebSocket\Runtime\{ + IdentityInterface, + Runner, +}; use WebSocket\Trait\{ ConfigurationTrait, ListenerTrait, @@ -71,7 +74,7 @@ class Client implements IdentityInterface, LoggerAwareInterface, Stringable private Connection|null $connection = null; /** @var array $middlewares */ private array $middlewares = []; - private StreamCollection|null $streams = null; + private Runner $runner; private bool $running = false; private HttpFactory $httpFactory; /** @var non-empty-string $identity */ @@ -83,14 +86,19 @@ class Client implements IdentityInterface, LoggerAwareInterface, Stringable /** * @param UriInterface|string $uri A ws/wss-URI * @param Configuration|null $configuration + * @param StreamFactory|null $streamFactory */ - public function __construct(UriInterface|string $uri, Configuration|null $configuration = null) - { + public function __construct( + UriInterface|string $uri, + Configuration|null $configuration = null, + StreamFactory|null $streamFactory = null, + ) { $this->socketUri = $this->parseUri($uri); - $this->setStreamFactory(new StreamFactory()); + $this->streamFactory = $streamFactory ?? new StreamFactory(); $this->httpFactory = new DefaultHttpFactory(); $this->identity = "client/{$this->socketUri->getHost()}"; $this->initConfiguration($configuration); + $this->runner = new Runner($this->streamFactory); } /** @@ -114,9 +122,11 @@ public function getIdentity(): string * Set stream factory to use. * @param StreamFactory $streamFactory * @return self + * @depracated Remove in v4 */ public function setStreamFactory(StreamFactory $streamFactory): self { + trigger_error('Client.setStreamFactory is deprecated and will be removed in v4.', E_USER_DEPRECATED); $this->streamFactory = $streamFactory; return $this; } @@ -316,39 +326,10 @@ public function start(int|float|null $timeout = null): void // Run handler while ($this->running) { - /** @var StreamCollection */ - $streams = $this->streams; try { - // Get streams with readable content - $readables = $streams->waitRead($timeout ?? $this->configuration->getTimeout()); - foreach ($readables as $key => $readable) { - try { - // Read from connection - $message = $connection->pullMessage(); - $this->dispatch($message->getOpcode(), [$this, $connection, $message]); - } catch (MessageLevelInterface $e) { - // Error, but keep connection open - $this->configuration->getLogger()->error("[{scope}] {message}", [ - 'scope' => self::SCOPE, - 'client' => $this->identity, - 'connection' => $connection->getIdentity(), - 'exception' => $e, - 'message' => $e->getMessage(), - ]); - $this->dispatch('error', [$this, $connection, $e]); - } catch (ConnectionLevelInterface $e) { - // Error, disconnect connection - $this->disconnect(); - $this->configuration->getLogger()->error("[{scope}] {message}", [ - 'scope' => self::SCOPE, - 'client' => $this->identity, - 'connection' => $connection->getIdentity(), - 'exception' => $e, - 'message' => $e->getMessage(), - ]); - $this->dispatch('error', [$this, $connection, $e]); - } - } + // Run attached handlers on selected streams + $this->runner->handle($timeout ?? $this->configuration->getTimeout()); + if (!$connection->isConnected()) { $this->running = false; } @@ -475,7 +456,6 @@ public function isWritable(): bool public function connect(): void { $this->disconnect(); - $this->streams = $this->streamFactory->createStreamCollection(); $hostUri = (new Uri()) ->withScheme(match ($this->socketUri->getScheme()) { @@ -503,7 +483,7 @@ public function connect(): void ]); throw new ClientException($error); } - $this->connection = new Connection( + $this->connection = $connection = new Connection( $stream, true, false, @@ -511,17 +491,45 @@ public function connect(): void $this->httpFactory, clone $this->configuration ); - $this->streams->attach($stream, $this->connection->getIdentity()); + + $this->runner->attach($this->connection, function (Runner $runner, Connection $connection) { + try { + // Read from connection + $message = $connection->pullMessage(); + $this->dispatch($message->getOpcode(), [$this, $connection, $message]); + } catch (MessageLevelInterface $e) { + // Error, but keep connection open + $this->configuration->getLogger()->error("[{scope}] {message}", [ + 'scope' => self::SCOPE, + 'client' => $this->identity, + 'connection' => $connection->getIdentity(), + 'exception' => $e, + 'message' => $e->getMessage(), + ]); + $this->dispatch('error', [$this, $connection, $e]); + } catch (ConnectionLevelInterface $e) { + // Error, disconnect connection + $this->disconnect(); + $this->configuration->getLogger()->error("[{scope}] {message}", [ + 'scope' => self::SCOPE, + 'client' => $this->identity, + 'connection' => $connection->getIdentity(), + 'exception' => $e, + 'message' => $e->getMessage(), + ]); + $this->dispatch('error', [$this, $connection, $e]); + } + }); foreach ($this->middlewares as $middleware) { - $this->connection->addMiddleware($middleware); + $connection->addMiddleware($middleware); } if (!$this->isConnected()) { $this->configuration->getLogger()->error("[{scope}] Invalid stream on {uri}", [ 'scope' => self::SCOPE, 'client' => $this->identity, - 'connection' => $this->connection->getIdentity(), + 'connection' => $connection->getIdentity(), 'uri' => $hostUri, ]); throw new ClientException("Invalid stream on \"{$hostUri}\"."); @@ -529,13 +537,13 @@ public function connect(): void try { if (!$this->configuration->isPersistent() || $stream->tell() == 0) { /** @throws ReconnectException */ - $response = $this->performHandshake($this->socketUri, $this->connection); + $response = $this->performHandshake($this->socketUri, $connection); } } catch (ReconnectException $e) { $this->configuration->getLogger()->info("[{scope}] {message}", [ 'scope' => self::SCOPE, 'client' => $this->identity, - 'connection' => $this->connection->getIdentity(), + 'connection' => $connection->getIdentity(), 'exception' => $e, 'message' => $e->getMessage(), ]); @@ -548,16 +556,16 @@ public function connect(): void $this->configuration->getLogger()->info("[{scope}] Client connected to {uri}", [ 'scope' => self::SCOPE, 'client' => $this->identity, - 'connection' => $this->connection->getIdentity(), + 'connection' => $connection->getIdentity(), 'uri' => $this->socketUri, ]); $this->dispatch('handshake', [ $this, - $this->connection, - $this->connection->getHandshakeRequest(), - $this->connection->getHandshakeResponse(), + $connection, + $connection->getHandshakeRequest(), + $connection->getHandshakeResponse(), ]); - $this->dispatch('connect', [$this, $this->connection, $this->connection?->getHandshakeResponse()]); + $this->dispatch('connect', [$this, $connection, $connection->getHandshakeResponse()]); } /** @@ -565,6 +573,9 @@ public function connect(): void */ public function disconnect(): void { + if ($this->connection) { + $this->runner->detach($this->connection->getIdentity()); + } if ($this->connection && $this->isConnected()) { $this->connection->disconnect(); $this->configuration->getLogger()->info("[{scope}] Client disconnected", [ diff --git a/src/Connection.php b/src/Connection.php index 9524a9c..869a2a7 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -12,6 +12,7 @@ use Phrity\Net\{ Context, SocketStream, + StreamContainerInterface, }; use Psr\Http\Message\{ MessageInterface, @@ -55,7 +56,7 @@ * WebSocket\Connection class. * A client/server connection, wrapping socket stream. */ -class Connection implements IdentityInterface, LoggerAwareInterface, Stringable +class Connection implements IdentityInterface, LoggerAwareInterface, StreamContainerInterface, Stringable { use ConfigurationTrait; use SendMethodsTrait; @@ -73,7 +74,6 @@ class Connection implements IdentityInterface, LoggerAwareInterface, Stringable private ResponseInterface|null $handshakeResponse = null; /** @var array $meta */ private array $meta = []; - private bool $closed = false; /** @var non-empty-string $identity */ private string $identity = '*/connection/'; @@ -103,13 +103,6 @@ public function __construct( $this->stream->setTimeout($this->configuration->getTimeout()); } - public function __destruct() - { - if (!$this->closed && $this->isConnected()) { - $this->stream->close(); - } - } - public function __toString(): string { return $this->stringable('%s:%s', $this->localName, $this->remoteName); @@ -258,7 +251,6 @@ public function disconnect(): self 'connection' => $this->identity, ]); $this->stream->close(); - $this->closed = true; return $this; } @@ -428,6 +420,11 @@ public function getHandshakeResponse(): ResponseInterface|null return $this->handshakeResponse; } + public function getStream(): SocketStream + { + return $this->stream; + } + /* ---------- Internal helper methods -------------------------------------------------------------------------- */ diff --git a/src/Exception/RunnerException.php b/src/Exception/RunnerException.php new file mode 100644 index 0000000..615316d --- /dev/null +++ b/src/Exception/RunnerException.php @@ -0,0 +1,16 @@ + $containers */ + private array $containers = []; + + public function __construct(StreamFactory $streamFactory) + { + $this->streamFactory = $streamFactory; + $this->streamCollection = $this->streamFactory->createStreamCollection(); + } + + public function attach(StreamContainerInterface $streamContainer, Closure $onSelect): void + { + $identity = $streamContainer->getIdentity(); + if (array_key_exists($identity, $this->containers)) { + throw new RunnerException("Stream container with identity {$identity} already attached"); + } + $stream = $streamContainer->getStream(); + $this->streamCollection->attach($stream, $identity); + $this->containers[$identity] = (object)[ + 'container' => $streamContainer, + 'stream' => $stream, + 'onSelect' => $onSelect, + ]; + } + + public function detach(string $identity): void + { + if (array_key_exists($identity, $this->containers)) { + $this->streamCollection->detach($identity); + unset($this->containers[$identity]); + } + } + + /** + * @throws ExceptionInterface + */ + public function handle(int|float $timeout): void + { + foreach ($this->select($timeout) as $identity => $stream) { + $container = $this->containers[$identity]; + /** @throws ExceptionInterface */ + call_user_func($container->onSelect, $this, $container->container); + } + } + + public function select(int|float $timeout): StreamCollection + { + return $this->streamCollection->waitRead($timeout); + } +} diff --git a/src/Server.php b/src/Server.php index 2e20016..85acc25 100644 --- a/src/Server.php +++ b/src/Server.php @@ -14,8 +14,10 @@ SocketServer, SocketStream, StreamCollection, + StreamContainerInterface, StreamException, StreamFactory, + StreamInterface, Uri }; use Psr\Http\Message\{ @@ -40,7 +42,10 @@ use WebSocket\Http\DefaultHttpFactory; use WebSocket\Message\Message; use WebSocket\Middleware\MiddlewareInterface; -use WebSocket\Runtime\IdentityInterface; +use WebSocket\Runtime\{ + IdentityInterface, + Runner, +}; use WebSocket\Trait\{ ConfigurationTrait, ListenerTrait, @@ -52,7 +57,7 @@ * WebSocket\Server class. * Entry class for WebSocket server. */ -class Server implements IdentityInterface, LoggerAwareInterface, Stringable +class Server implements IdentityInterface, LoggerAwareInterface, StreamContainerInterface, Stringable { use ConfigurationTrait; /** @use ListenerTrait */ @@ -69,7 +74,8 @@ class Server implements IdentityInterface, LoggerAwareInterface, Stringable // Internal resources private StreamFactory $streamFactory; private SocketServer|null $server = null; - private StreamCollection|null $streams = null; + private Runner $runner; + private bool $running = false; /** @var array $connections */ private array $connections = []; @@ -87,19 +93,25 @@ class Server implements IdentityInterface, LoggerAwareInterface, Stringable * @param int $port Socket port to listen to * @param bool $ssl If SSL should be used * @param Configuration|null $configuration + * @param StreamFactory|null $streamFactory * @throws InvalidArgumentException If invalid port provided */ - public function __construct(int $port = 80, bool $ssl = false, Configuration|null $configuration = null) - { + public function __construct( + int $port = 80, + bool $ssl = false, + Configuration|null $configuration = null, + StreamFactory|null $streamFactory = null, + ) { if ($port < 0 || $port > 65535) { throw new InvalidArgumentException("Invalid port '{$port}' provided"); } $this->port = $port; $this->scheme = $ssl ? 'ssl' : 'tcp'; $this->httpFactory = new DefaultHttpFactory(); - $this->setStreamFactory(new StreamFactory()); + $this->streamFactory = $streamFactory ?? new StreamFactory(); $this->identity = "server/{$port}"; $this->initConfiguration($configuration); + $this->runner = new Runner($this->streamFactory); } /** @@ -123,9 +135,11 @@ public function getIdentity(): string * Set stream factory to use. * @param StreamFactory $streamFactory * @return self + * @depracated Remove in v4 */ public function setStreamFactory(StreamFactory $streamFactory): self { + trigger_error('Server.setStreamFactory is deprecated and will be removed in v4.', E_USER_DEPRECATED); $this->streamFactory = $streamFactory; return $this; } @@ -375,72 +389,20 @@ public function start(int|float|null $timeout = null): void 'server' => $this->identity, ]); - /** @var StreamCollection */ - $streams = $this->streams; - // Run handler while ($this->running) { try { // Clear closed connections $this->detachUnconnected(); - if (is_null($this->streams)) { + + if (!$this->server) { $this->stop(); return; } - // Get streams with readable content - $readables = $this->streams->waitRead($timeout ?? $this->configuration->getTimeout()); - foreach ($readables as $key => $readable) { - try { - $connection = null; - // Accept new client connection - if ($readable instanceof SocketServer) { - $this->acceptSocket($readable); - continue; - } - // Read from connection - $connection = $this->connections[$key]; - $message = $connection->pullMessage(); - $this->dispatch($message->getOpcode(), [$this, $connection, $message]); - } catch (MessageLevelInterface $e) { - // Error, but keep connection open - $this->configuration->getLogger()->error("[{scope}] {message}", [ - 'scope' => self::SCOPE, - 'server' => $this->identity, - 'connection' => $connection->getIdentity(), - 'exception' => $e, - 'message' => $e->getMessage(), - ]); - $this->dispatch('error', [$this, $connection, $e]); - } catch (ConnectionLevelInterface $e) { - // Error, disconnect connection - if ($connection) { - $this->streams()->detach($key); - unset($this->connections[$key]); - $connection->disconnect(); - } - $this->configuration->getLogger()->error("[{scope}] {message}", [ - 'scope' => self::SCOPE, - 'server' => $this->identity, - 'exception' => $e, - 'message' => $e->getMessage(), - ]); - $this->dispatch('error', [$this, $connection, $e]); - } catch (CloseException $e) { - // Should close - if ($connection) { - $connection->close($e->getCloseStatus(), $e->getMessage()); - } - $this->configuration->getLogger()->error("[{scope}] {message}", [ - 'scope' => self::SCOPE, - 'server' => $this->identity, - 'connection' => $connection->getIdentity(), - 'exception' => $e, - 'message' => $e->getMessage(), - ]); - $this->dispatch('error', [$this, $connection, $e]); - } - } + // Run attached handlers on selected streams + $this->runner->handle($timeout ?? $this->configuration->getTimeout()); + foreach ($this->connections as $connection) { $connection->tick(); } @@ -533,44 +495,58 @@ public function disconnect(): void $this->running = false; foreach ($this->connections as $connection) { $connection->disconnect(); + $this->runner->detach($connection->getIdentity()); $this->dispatch('disconnect', [$this, $connection]); } $this->connections = []; if ($this->server) { $this->server->close(); + $this->runner->detach($this->identity); } - $this->server = $this->streams = null; + $this->server = null; $this->configuration->getLogger()->info("[{scope}] Server disconnected", [ 'scope' => self::SCOPE, 'server' => $this->identity, ]); } + public function getStream(): SocketServer + { + return $this->server ?? $this->createSocketServer(); + } + /* ---------- Internal helper methods -------------------------------------------------------------------------- */ // Create socket server - protected function createSocketServer(): void + protected function createSocketServer(): SocketServer { try { $uri = new Uri("{$this->scheme}://0.0.0.0:{$this->port}"); - $this->server = $this->streamFactory->createSocketServer($uri, $this->configuration->getContext()); - $this->streams = $this->streamFactory->createStreamCollection(); - $this->streams->attach($this->server, $this->identity); + $this->server = $server = $this->streamFactory->createSocketServer( + $uri, + $this->configuration->getContext() + ); + $this->runner->attach($this, function (Runner $runner, Server $server) { + $this->acceptSocket($server->getStream()); + }); $this->allowConnections = true; - $this->configuration->getLogger()->info("[server] Starting server on {uri}."); - $this->configuration->getLogger()->info("[{scope}] Server disconnected", [ + $this->configuration->getLogger()->info("[{scope}] Starting server on {uri}", [ 'scope' => self::SCOPE, 'server' => $this->identity, 'uri' => $uri, ]); + return $server; } catch (Throwable $e) { $error = "Server failed to start: {$e->getMessage()}"; throw new ServerException($error); } } - // Accept connection on socket server + /** + * Accept connection on socket server + * @throws ConnectionFailureException + */ protected function acceptSocket(SocketServer $socket): void { $maxConnections = $this->configuration->getMaxConnections(); @@ -601,7 +577,47 @@ protected function acceptSocket(SocketServer $socket): void $this->httpFactory, clone $this->configuration ); - $this->streams()->attach($stream, $connection->getIdentity()); + $this->runner->attach($connection, function (Runner $runner, Connection $connection) { + $key = $connection->getIdentity(); + + try { + $message = $connection->pullMessage(); + $this->dispatch($message->getOpcode(), [$this, $connection, $message]); + } catch (MessageLevelInterface $e) { + // Error, but keep connection open + $this->configuration->getLogger()->error("[{scope}] {message}", [ + 'scope' => self::SCOPE, + 'server' => $this->identity, + 'connection' => $connection->getIdentity(), + 'exception' => $e, + 'message' => $e->getMessage(), + ]); + $this->dispatch('error', [$this, $connection, $e]); + } catch (ConnectionLevelInterface $e) { + // Error, disconnect connection + $this->runner->detach($key); + unset($this->connections[$key]); + $connection->disconnect(); + $this->configuration->getLogger()->error("[{scope}] {message}", [ + 'scope' => self::SCOPE, + 'server' => $this->identity, + 'exception' => $e, + 'message' => $e->getMessage(), + ]); + $this->dispatch('error', [$this, $connection, $e]); + } catch (CloseException $e) { + // Should close + $connection->close($e->getCloseStatus(), $e->getMessage()); + $this->configuration->getLogger()->error("[{scope}] {message}", [ + 'scope' => self::SCOPE, + 'server' => $this->identity, + 'connection' => $connection->getIdentity(), + 'exception' => $e, + 'message' => $e->getMessage(), + ]); + $this->dispatch('error', [$this, $connection, $e]); + } + }); } catch (StreamException $e) { throw new ConnectionFailureException("Server failed to accept: {$e->getMessage()}"); } @@ -636,7 +652,7 @@ protected function detachUnconnected(): void { foreach ($this->connections as $key => $connection) { if (!$connection->isConnected()) { - $this->streams()->detach($key); + $this->runner->detach($key); unset($this->connections[$key]); $this->configuration->getLogger()->info("[{scope}] Disconnected {connection}", [ 'scope' => self::SCOPE, @@ -741,11 +757,4 @@ protected function performHandshake(Connection $connection): ServerRequestInterf return $request; } - - protected function streams(): StreamCollection - { - /** @var StreamCollection $streams */ - $streams = $this->streams; - return $streams; - } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index a999777..3ed538c 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -9,6 +9,8 @@ require dirname(__DIR__) . '/vendor/autoload.php'; +error_reporting(-1); + if (extension_loaded('xdebug')) { die("Tests can not be run with xdebug installed.\n"); } diff --git a/tests/mock/MockStreamTrait.php b/tests/mock/MockStreamTrait.php index 23546f2..94d77ed 100644 --- a/tests/mock/MockStreamTrait.php +++ b/tests/mock/MockStreamTrait.php @@ -38,6 +38,13 @@ trait MockStreamTrait /* ---------- WebSocket Client combined asserts --------------------------------------------------------------- */ + private function expectWsClientCreate(): void + { + $this->expectStreamFactory(); + $this->expectStreamFactoryCreateStreamCollection(); + $this->expectStreamCollection(); + } + /** * @param array $context */ @@ -49,8 +56,6 @@ private function expectWsClientSetup( array $context = [], bool $persistent = false, ): void { - $this->expectStreamFactoryCreateStreamCollection(); - $this->expectStreamCollection(); $this->expectStreamFactoryCreateSocketClient()->addAssert( function ($method, $params) use ($scheme, $host, $port) { $this->assertInstanceOf('Phrity\Net\Uri', $params[0]); @@ -153,6 +158,13 @@ function (string $method, array $params) use ($host, $path, $headers): void { /* ---------- WebSocket Server combined asserts --------------------------------------------------------------- */ + private function expectWsServerCreate(): void + { + $this->expectStreamFactory(); + $this->expectStreamFactoryCreateStreamCollection(); + $this->expectStreamCollection(); + } + /** * @param array $context */ @@ -173,8 +185,6 @@ private function expectWsServerSetup(string $scheme = 'tcp', int $port = 8000, a } $this->expectSocketServerGetMetadata(); - $this->expectStreamFactoryCreateStreamCollection(); - $this->expectStreamCollection(); $this->expectStreamCollectionAttach()->addAssert(function ($method, $params) use ($port) { $this->assertEquals("server/{$port}", $params[1]); }); diff --git a/tests/suites/client/ClientErrorTest.php b/tests/suites/client/ClientErrorTest.php index f7a02a7..5399929 100644 --- a/tests/suites/client/ClientErrorTest.php +++ b/tests/suites/client/ClientErrorTest.php @@ -45,7 +45,6 @@ class ClientErrorTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); } @@ -56,9 +55,8 @@ public function tearDown(): void public function testFailedSocket(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->expectWsClientSetup(); $this->expectSocketClientConnect()->setReturn(function () { @@ -67,15 +65,12 @@ public function testFailedSocket(): void $this->expectException(ClientException::class); $this->expectExceptionMessage('Could not open socket to "tcp://localhost:8000": Client could not connect'); $client->connect(); - - unset($client); } public function testFailedConnection(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->expectWsClientSetup(); $this->expectSocketClientConnect(); @@ -89,21 +84,15 @@ public function testFailedConnection(): void $this->expectSocketStreamIsConnected()->setReturn(function () { return false; }); - $this->expectSocketStreamIsConnected()->setReturn(function () { - return false; - }); $this->expectException(ClientException::class); $this->expectExceptionMessage('Invalid stream on "tcp://localhost:8000".'); $client->connect(); - - unset($client); } public function testReceiveBadOpcode(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->expectWsClientConnect(); $this->expectWsClientPerformHandshake(); @@ -118,20 +107,15 @@ public function testReceiveBadOpcode(): void $this->expectSocketStreamRead()->setReturn(function () { return 'Test message'; }); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $this->expectException(BadOpcodeException::class); $this->expectExceptionMessage("Invalid opcode '15' provided"); $message = $client->receive(); - - unset($client); } public function testBrokenWrite(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->expectWsClientConnect(); $this->expectWsClientPerformHandshake(); @@ -147,18 +131,13 @@ public function testBrokenWrite(): void }); $this->expectException(ConnectionClosedException::class); $this->expectExceptionMessage('Connection has unexpectedly closed'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $client->text('Failing to write'); - - unset($client); } public function testReadTimeout(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->expectWsClientConnect(); $this->expectWsClientPerformHandshake(); @@ -174,18 +153,13 @@ public function testReadTimeout(): void }); $this->expectException(ConnectionTimeoutException::class); $this->expectExceptionMessage('Connection operation timeout'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $client->receive(); - - unset($client); } public function testEmptyRead(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->expectWsClientConnect(); $this->expectWsClientPerformHandshake(); @@ -201,11 +175,6 @@ public function testEmptyRead(): void }); $this->expectException(ConnectionTimeoutException::class); $this->expectExceptionMessage('Connection operation timeout'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $client->receive(); - - $this->expectSocketStreamClose(); - unset($client); } } diff --git a/tests/suites/client/ClientTest.php b/tests/suites/client/ClientTest.php index 5ad6a1f..1233b39 100644 --- a/tests/suites/client/ClientTest.php +++ b/tests/suites/client/ClientTest.php @@ -66,7 +66,6 @@ class ClientTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); } @@ -78,9 +77,8 @@ public function tearDown(): void public function testClientSendReceive(): void { // Creating client - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->assertInstanceOf(Stringable::class, $client); $this->assertEquals('client/localhost', $client->getIdentity()); @@ -129,17 +127,17 @@ public function testClientSendReceive(): void $this->expectSocketStreamIsConnected(); $this->assertTrue($client->isConnected()); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testSendMessages(): void { // Creating client - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->expectWsClientConnect(); $this->expectWsClientPerformHandshake(); @@ -155,16 +153,16 @@ public function testSendMessages(): void }); $client->send(new Text('Sending a message')); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testPayload128(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->setFrameSize(65540); $this->expectWsClientConnect(); @@ -202,16 +200,16 @@ public function testPayload128(): void $this->assertEquals($payload, $message->getContent()); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testPayload65536(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->setFrameSize(65540); $this->expectWsClientConnect(); @@ -285,16 +283,16 @@ public function testPayload65536(): void $this->assertEquals($payload, $message->getContent()); $this->assertEquals(65540, $client->getFrameSize()); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testMultiFrame(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->expectWsClientConnect(); $this->expectWsClientPerformHandshake(); @@ -370,16 +368,16 @@ public function testMultiFrame(): void $this->assertEquals('Multi fragment test', $message->getContent()); $this->assertEquals(8, $client->getFrameSize()); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testPingPong(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->addMiddleware(new PingResponder()); $this->expectWsClientConnect(); @@ -477,16 +475,16 @@ public function testPingPong(): void $message = $client->receive(); $this->assertEquals('Receiving a message', $message->getContent()); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testRemoteClose(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->addMiddleware(new CloseHandler()); $this->expectWsClientConnect(); @@ -521,14 +519,15 @@ public function testRemoteClose(): void $message = $client->receive(); $this->assertInstanceOf(Close::class, $message); - unset($client); + $this->expectStreamCollectionDetach(); + $this->expectSocketStreamIsConnected(); + $client->disconnect(); } public function testAutoConnect(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->addMiddleware(new CloseHandler()); $this->expectWsClientConnect(); @@ -558,14 +557,15 @@ public function testAutoConnect(): void $this->expectSocketStreamClose(); $this->expectSocketStreamIsConnected(); $message = $client->receive(); + $this->assertInstanceOf(Close::class, $message); $this->expectSocketStreamIsConnected(); $this->assertFalse($client->isConnected()); // Implicit reconnect and handshake, receive message + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); - $this->expectWsClientConnect(); $this->expectWsClientPerformHandshake(); $this->expectSocketStreamRead()->addAssert(function (string $method, array $params) { @@ -588,16 +588,16 @@ public function testAutoConnect(): void $this->expectSocketStreamIsConnected(); $this->assertTrue($client->isConnected()); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testFrameFragmentation(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->addMiddleware(new CloseHandler()); $client->addMiddleware(new PingResponder()); @@ -709,14 +709,15 @@ public function testFrameFragmentation(): void $this->assertEquals('Closing', $message->getContent()); $this->assertFalse($client->isConnected()); - unset($client); + $this->expectStreamCollectionDetach(); + $this->expectSocketStreamIsConnected(); + $client->disconnect(); } public function testConvenienceMethods(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->addMiddleware(new CloseHandler()); $client->addMiddleware(new PingResponder()); @@ -777,24 +778,24 @@ public function testConvenienceMethods(): void $this->expectSocketStreamIsConnected(); $this->assertEquals('localhost:8000', $client->getRemoteName()); - - $this->expectSocketStreamIsConnected(); $this->assertEquals('WebSocket\Client(ws://localhost:8000/my/mock/path)', "{$client}"); + $this->expectStreamCollectionDetach(); + $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testDisconnect(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->expectWsClientConnect(); $this->expectWsClientPerformHandshake(); $client->connect(); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); $this->expectSocketStreamIsConnected(); @@ -805,9 +806,8 @@ public function testDisconnect(): void public function testListeners(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $handler = new ErrorHandler(); $client->onHandshake(function ($client, $connection, $request, $response) { @@ -951,16 +951,16 @@ public function testListeners(): void $this->expectSocketStreamIsConnected(); $client->start(); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testAlreadyStarted(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->onHandshake(function ($client, $connection, $request, $response) { $client->start(); @@ -968,18 +968,18 @@ public function testAlreadyStarted(): void }); $this->expectWsClientConnect(); $this->expectWsClientPerformHandshake(); - $this->expectSocketStreamIsConnected(); $client->start(); + $this->expectStreamCollectionDetach(); + $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testRunConnectionClosedException(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->onText(function ($client, $connection, $message) { $this->assertInstanceOf(Client::class, $client); @@ -996,19 +996,20 @@ public function testRunConnectionClosedException(): void })->setReturn(function () { throw new ConnectionClosedException(); }); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); $this->expectSocketStreamIsConnected(); $client->start(); - unset($client); + $this->expectSocketStreamIsConnected(); + $client->disconnect(); } public function testRunClientException(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->onText(function ($client, $connection, $message) { $this->assertInstanceOf(Client::class, $client); @@ -1025,37 +1026,37 @@ public function testRunClientException(): void })->setReturn(function () { throw new ClientException(); }); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); $client->start(); - unset($client); + + $this->expectSocketStreamIsConnected(); + $client->disconnect(); } public function testRunExternalException(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->expectWsClientConnect(); $this->expectWsClientPerformHandshake(); $this->expectWsSelectConnections(['*/connection/12345/8000'])->setReturn(function () { throw new StreamException(1000); }); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); $this->expectException(StreamException::class); $this->expectExceptionMessage('Stream is detached.'); $client->start(); - - unset($client); } public function testCloseException(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->addMiddleware(new CloseHandler()); $client->onText(function ($client, $connection, $message) { throw new CloseException(); @@ -1084,16 +1085,17 @@ public function testCloseException(): void $this->expectSocketStreamIsWritable(); $this->expectSocketStreamClose(); $this->expectSocketStreamIsConnected(); + $this->expectStreamCollectionDetach(); $client->start(); - unset($client); + $this->expectSocketStreamIsConnected(); + $client->disconnect(); } public function testReconnectException(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->addMiddleware(new CloseHandler()); $client->onText(function ($client, $connection, $message) { throw new ReconnectException(new Uri('ws://localhost:8000/my/mock/path')); @@ -1127,18 +1129,19 @@ public function testReconnectException(): void $this->expectSocketStreamRead()->setReturn(function () { return base64_decode('ggA='); // binary }); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamIsConnected(); $client->start(); - unset($client); + $this->expectSocketStreamIsConnected(); + $client->disconnect(); } public function testUnresolvableError(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->onTick(function ($client) { /** @@ -1155,19 +1158,18 @@ public function testUnresolvableError(): void return base64_decode('gQA='); }); $this->expectSocketStreamIsConnected(); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); $this->expectException(Error::class); $this->expectExceptionMessage('Class "WebSocket\Test\Client\UnexistingClass" not found'); $client->start(); - unset($client); } public function testDeprecatedMeta(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $errorHandler = new ErrorHandler(); $errorHandler->withAll(function () use ($client) { @@ -1178,6 +1180,27 @@ public function testDeprecatedMeta(): void $errors[0]->getMessage() ); }); - unset($client); + + $client->disconnect(); + } + + public function testDeprecatedSetStreamFactory(): void + { + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); + + $this->expectStreamFactory(); + $errorHandler = new ErrorHandler(); + $errorHandler->withAll(function () use ($client) { + $factory = new StreamFactory(); + $this->assertSame($client, $client->setStreamFactory($factory)); + }, function (array $errors) { + $this->assertEquals( + 'Client.setStreamFactory is deprecated and will be removed in v4.', + $errors[0]->getMessage() + ); + }); + + $client->disconnect(); } } diff --git a/tests/suites/client/ConfigErrorTest.php b/tests/suites/client/ConfigErrorTest.php index f2c56a4..12ec6b4 100644 --- a/tests/suites/client/ConfigErrorTest.php +++ b/tests/suites/client/ConfigErrorTest.php @@ -42,7 +42,6 @@ class ConfigErrorTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); } diff --git a/tests/suites/client/ConfigTest.php b/tests/suites/client/ConfigTest.php index 3e4c43c..c80b08b 100644 --- a/tests/suites/client/ConfigTest.php +++ b/tests/suites/client/ConfigTest.php @@ -50,7 +50,6 @@ class ConfigTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); } @@ -61,32 +60,35 @@ public function tearDown(): void public function testUriStringExtended(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path?my_query=yes#my_fragment'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client( + 'ws://localhost:8000/my/mock/path?my_query=yes#my_fragment', + streamFactory: new StreamFactory() + ); $this->expectWsClientConnect(); $this->expectWsClientPerformHandshake('localhost:8000', '/my/mock/path?my_query=yes'); $client->connect(); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testUriStringWithoutPath(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000', streamFactory: new StreamFactory()); $this->expectWsClientConnect(); $this->expectWsClientPerformHandshake('localhost:8000', '/'); $client->connect(); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testUriInstanceRelativePath(): void @@ -94,17 +96,17 @@ public function testUriInstanceRelativePath(): void $uri = new Uri('ws://localhost:8000'); $uri = $uri->withPath('my/mock/path'); - $this->expectStreamFactory(); - $client = new Client($uri); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client($uri, streamFactory: new StreamFactory()); $this->expectWsClientConnect(); $this->expectWsClientPerformHandshake('localhost:8000', '/my/mock/path'); $client->connect(); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testUriInstanceWsDefaultPort(): void @@ -112,17 +114,17 @@ public function testUriInstanceWsDefaultPort(): void $uri = new Uri('ws://localhost'); $uri = $uri->withPath('my/mock/path'); - $this->expectStreamFactory(); - $client = new Client($uri); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client($uri, streamFactory: new StreamFactory()); $this->expectWsClientConnect(port: 80); $this->expectWsClientPerformHandshake('localhost', '/my/mock/path'); $client->connect(); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testUriInstanceWssDefaultPort(): void @@ -130,17 +132,17 @@ public function testUriInstanceWssDefaultPort(): void $uri = new Uri('wss://localhost'); $uri = $uri->withPath('my/mock/path'); - $this->expectStreamFactory(); - $client = new Client($uri); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client($uri, streamFactory: new StreamFactory()); $this->expectWsClientConnect(scheme: 'ssl', port: 443); $this->expectWsClientPerformHandshake('localhost', '/my/mock/path'); $client->connect(); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } /** @return array */ @@ -166,9 +168,8 @@ public static function uriStringAuthorizationDataProvider(): array #[DataProvider('uriStringAuthorizationDataProvider')] public function testUriStringAuthorization(string $uriAuth, string $expectedCredentials): void { - $this->expectStreamFactory(); - $client = new Client("wss://{$uriAuth}@localhost:8000/my/mock/path"); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client("wss://{$uriAuth}@localhost:8000/my/mock/path", streamFactory: new StreamFactory()); $this->expectWsClientConnect(scheme: 'ssl'); $this->expectWsClientPerformHandshake( @@ -178,44 +179,41 @@ public function testUriStringAuthorization(string $uriAuth, string $expectedCred ); $client->connect(); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testUriInstanceImplementation(): void { $uri = new MockUri(); - $this->expectStreamFactory(); - $client = new Client($uri); - $client->setStreamFactory(new StreamFactory()); - - unset($client); + $this->expectWsClientCreate(); + $client = new Client($uri, streamFactory: new StreamFactory()); } public function testTimeoutOption(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->setTimeout(300); $this->expectWsClientConnect(timeout: 300); $this->expectWsClientPerformHandshake('localhost:8000', '/my/mock/path'); $client->connect(); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testContextOption(): void { $errorHandler = new ErrorHandler(); - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $errorHandler->withAll(function () use ($client) { $client->setContext(['ssl' => ['verify_peer' => false]]); @@ -230,9 +228,10 @@ public function testContextOption(): void $this->expectWsClientPerformHandshake('localhost:8000', '/my/mock/path'); $client->connect(); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testContextClass(): void @@ -243,9 +242,8 @@ public function testContextClass(): void $this->expectContextSetOption(); $context->setOptions(['ssl' => ['verify_peer' => false]]); - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->onHandshake(function (Client $client, Connection $connection) { $this->expectSocketStreamGetContext(); $connectionContext = $connection->getContext(); @@ -263,16 +261,16 @@ public function testContextClass(): void $this->assertSame($context, $client->getContext()); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testHeadersOption(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->addHeader('Generic-header', 'Generic content'); $this->expectWsClientConnect(); @@ -283,32 +281,32 @@ public function testHeadersOption(): void ); $client->connect(); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testPersistentOption(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $client->setPersistent(true); $this->expectWsClientConnect(persistent: true); $this->expectWsClientPerformHandshake(); $client->connect(); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testConfigUnconnectedClient(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->assertFalse($client->isConnected()); $client->setLogger(new NullLogger()); @@ -320,9 +318,8 @@ public function testConfigUnconnectedClient(): void public function testConfigConnectedClient(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->expectWsClientConnect(); $this->expectWsClientPerformHandshake(); @@ -335,25 +332,22 @@ public function testConfigConnectedClient(): void $this->assertEquals(300, $params[0]); }); $client->setTimeout(300); - - $this->expectSocketStreamIsConnected(); $client->setFrameSize(64); $this->assertEquals(64, $client->getFrameSize()); + $this->expectStreamCollectionDetach(); + $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testHttpFactories(): void { $httpFactory = new Psr17Factory(); - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->assertSame($client, $client->setHttpFactory(HttpFactory::create($httpFactory))); - - unset($client); } public function testConfiguration(): void @@ -368,7 +362,8 @@ public function testConfiguration(): void frameSize: 64, persistent: true, ); - $client = new Client('ws://localhost:8000/my/mock/path'); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->assertInstanceOf(Configuration::class, $client->getConfiguration()); $this->assertSame($client, $client->setConfiguration($configuration)); } diff --git a/tests/suites/client/HandshakeTest.php b/tests/suites/client/HandshakeTest.php index 061935a..0b947fd 100644 --- a/tests/suites/client/HandshakeTest.php +++ b/tests/suites/client/HandshakeTest.php @@ -46,7 +46,6 @@ class HandshakeTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); } @@ -58,9 +57,8 @@ public function tearDown(): void public function testHandshakeResponse(): void { // Creating client - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->assertFalse($client->isConnected()); $this->assertEquals(4096, $client->getFrameSize()); @@ -74,17 +72,17 @@ public function testHandshakeResponse(): void $this->assertEquals(101, $response->getStatusCode()); $this->assertEquals('Switching Protocols', $response->getReasonPhrase()); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testHandshakeResponseVariant(): void { // Creating client - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->assertFalse($client->isConnected()); $this->assertEquals(4096, $client->getFrameSize()); @@ -119,16 +117,16 @@ function (string $method, array $params): void { $this->assertEquals(101, $response->getStatusCode()); $this->assertEquals('Switching Protocols', $response->getReasonPhrase()); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } public function testHandshakeConnectionFailure(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->expectWsClientConnect(); $this->expectSocketStreamWrite(); @@ -137,20 +135,15 @@ public function testHandshakeConnectionFailure(): void }); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamGetMetadata(); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $this->expectException(ConnectionFailureException::class); $this->expectExceptionMessage('Connection error'); $client->connect(); - - unset($client); } public function testHandshakeUpgradeStatusFailure(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->expectWsClientConnect(); $this->expectSocketStreamWrite(); @@ -162,18 +155,13 @@ public function testHandshakeUpgradeStatusFailure(): void }); $this->expectException(HandshakeException::class); $this->expectExceptionMessage('Invalid status code 200.'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $client->connect(); - - unset($client); } public function testHandshakeUpgradeHeadersFailure(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->expectWsClientConnect(); $this->expectSocketStreamWrite(); @@ -188,18 +176,13 @@ public function testHandshakeUpgradeHeadersFailure(): void }); $this->expectException(HandshakeException::class); $this->expectExceptionMessage('Connection to \'ws://localhost:8000/my/mock/path\' failed'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $client->connect(); - - unset($client); } public function testHandshakeUpgradeKeyFailure(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->expectWsClientConnect(); $this->expectSocketStreamWrite(); @@ -217,24 +200,20 @@ public function testHandshakeUpgradeKeyFailure(): void }); $this->expectException(HandshakeException::class); $this->expectExceptionMessage('Server sent bad upgrade response'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $client->connect(); - - unset($client); } public function testHandshakeReconnect(): void { - $this->expectStreamFactory(); - $client = new Client('ws://localhost:8000/my/mock/path'); - $client->setStreamFactory(new StreamFactory()); + $this->expectWsClientCreate(); + $client = new Client('ws://localhost:8000/my/mock/path', streamFactory: new StreamFactory()); $this->expectWsClientConnect(); $this->expectSocketStreamWrite(); $this->expectSocketStreamReadLine()->setReturn(function () { throw new ReconnectException(new Uri('ws://localhost:8000/my/new/path')); }); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); @@ -247,8 +226,9 @@ public function testHandshakeReconnect(): void $this->assertEquals(101, $response->getStatusCode()); $this->assertEquals('Switching Protocols', $response->getReasonPhrase()); + $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($client); + $client->disconnect(); } } diff --git a/tests/suites/configuration/ConfigurationTest.php b/tests/suites/configuration/ConfigurationTest.php index a92bde3..ac12663 100644 --- a/tests/suites/configuration/ConfigurationTest.php +++ b/tests/suites/configuration/ConfigurationTest.php @@ -25,11 +25,6 @@ */ class ConfigurationTest extends TestCase { - public function setUp(): void - { - error_reporting(-1); - } - public function testDefaults(): void { $configuration = new Configuration(); diff --git a/tests/suites/connection/ConnectionTest.php b/tests/suites/connection/ConnectionTest.php index 1cf8153..14488c2 100644 --- a/tests/suites/connection/ConnectionTest.php +++ b/tests/suites/connection/ConnectionTest.php @@ -49,7 +49,6 @@ class ConnectionTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); $this->psrFactory = new Psr17Factory(); } @@ -119,31 +118,6 @@ public function testCreate(): void $this->expectSocketStreamGetContext(); $this->assertInstanceOf(Context::class, $connection->getContext()); - - unset($connection); - } - - public function testDestruct(): void - { - $temp = tmpfile(); - - $this->expectSocketStream(); - $this->expectSocketStreamGetMetadata(); - $this->expectContext(); - $stream = new SocketStream($temp); - - $this->expectSocketStreamGetLocalName(); - $this->expectSocketStreamGetRemoteName(); - $this->expectSocketStreamSetTimeout(); - $connection = new Connection($stream, false, false); - - $this->expectSocketStreamIsConnected(); - $this->assertTrue($connection->isConnected()); - - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); - - unset($connection); } public function testHttpMessages(): void @@ -185,8 +159,6 @@ public function testHttpMessages(): void $this->expectSocketStreamClose(); $this->assertSame($connection, $connection->disconnect()); - - unset($connection); } public function testWebSocketMessages(): void @@ -221,8 +193,6 @@ public function testWebSocketMessages(): void $this->expectSocketStreamClose(); $this->assertSame($connection, $connection->disconnect()); - - unset($connection); } public function testSendHttpError(): void @@ -244,11 +214,7 @@ public function testSendHttpError(): void }); $this->expectException(ConnectionClosedException::class); $this->expectExceptionMessage('Connection has unexpectedly closed'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $connection->pushHttp($this->psrFactory->createRequest('GET', '/')); - - unset($connection); } public function testPullHttpError(): void @@ -270,11 +236,7 @@ public function testPullHttpError(): void }); $this->expectException(ConnectionClosedException::class); $this->expectExceptionMessage('Connection has unexpectedly closed'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $connection->pullHttp(); - - unset($connection); } public function testSendMessageError(): void @@ -296,11 +258,7 @@ public function testSendMessageError(): void }); $this->expectException(ConnectionClosedException::class); $this->expectExceptionMessage('Connection has unexpectedly closed'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $connection->send(new Text('Connection error')); - - unset($connection); } public function testPullMessageError(): void @@ -322,10 +280,6 @@ public function testPullMessageError(): void }); $this->expectException(ConnectionClosedException::class); $this->expectExceptionMessage('Connection has unexpectedly closed'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $connection->pullMessage(); - - unset($connection); } } diff --git a/tests/suites/connection/ExceptionTest.php b/tests/suites/connection/ExceptionTest.php index c758fa6..d885dbb 100644 --- a/tests/suites/connection/ExceptionTest.php +++ b/tests/suites/connection/ExceptionTest.php @@ -37,7 +37,6 @@ class ExceptionTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); } @@ -65,11 +64,7 @@ public function testBadOpcodeException(): void $this->expectException(BadOpcodeException::class); $this->expectExceptionMessage('Bad Opcode'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $connection->send(new Text('Bad Opcode')); - - unset($connection); } public function testBadUriException(): void @@ -90,11 +85,7 @@ public function testBadUriException(): void }); $this->expectException(BadUriException::class); $this->expectExceptionMessage('Bad URI'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $connection->send(new Text('Bad URI')); - - unset($connection); } public function testConnectionClosedException(): void @@ -115,11 +106,7 @@ public function testConnectionClosedException(): void }); $this->expectException(ConnectionClosedException::class); $this->expectExceptionMessage('Connection has unexpectedly closed'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $connection->send(new Text('Connection has unexpectedly closed')); - - unset($connection); } public function testConnectionFailureException(): void @@ -140,11 +127,7 @@ public function testConnectionFailureException(): void }); $this->expectException(ConnectionFailureException::class); $this->expectExceptionMessage('Connection error'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $connection->send(new Text('Connection error')); - - unset($connection); } public function testConnectionTimeoutException(): void @@ -165,11 +148,7 @@ public function testConnectionTimeoutException(): void }); $this->expectException(ConnectionTimeoutException::class); $this->expectExceptionMessage('Connection operation timeout'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $connection->send(new Text('Connection operation timeout')); - - unset($connection); } public function testGenericTimeoutException(): void @@ -194,11 +173,7 @@ public function testGenericTimeoutException(): void }); $this->expectException(ConnectionTimeoutException::class); $this->expectExceptionMessage('Connection operation timeout'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $connection->send(new Text('Timeout')); - - unset($connection); } public function testGenericEofException(): void @@ -223,11 +198,7 @@ public function testGenericEofException(): void }); $this->expectException(ConnectionClosedException::class); $this->expectExceptionMessage('Connection has unexpectedly closed'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $connection->send(new Text('Eof')); - - unset($connection); } public function testGenericUnconnectedException(): void @@ -251,11 +222,7 @@ public function testGenericUnconnectedException(): void }); $this->expectException(ConnectionFailureException::class); $this->expectExceptionMessage('Connection error'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $connection->send(new Text('Generic')); - - unset($connection); } public function testGenericConnectedException(): void @@ -280,11 +247,7 @@ public function testGenericConnectedException(): void }); $this->expectException(ConnectionFailureException::class); $this->expectExceptionMessage('Connection error'); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $connection->send(new Text('Generic')); - - unset($connection); } public function testInvalidTimeout(): void @@ -302,11 +265,7 @@ public function testInvalidTimeout(): void $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage("Invalid timeout '-1' provided"); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $connection->setTimeout(-1); - - unset($connection); } public function testInvalidFrameSize(): void @@ -324,11 +283,7 @@ public function testInvalidFrameSize(): void $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage("Invalid frameSize '0' provided"); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); // @phpstan-ignore argument.type $connection->setFrameSize(0); - - unset($connection); } } diff --git a/tests/suites/frame/FrameHandlerTest.php b/tests/suites/frame/FrameHandlerTest.php index f130009..a4cdf31 100644 --- a/tests/suites/frame/FrameHandlerTest.php +++ b/tests/suites/frame/FrameHandlerTest.php @@ -33,7 +33,6 @@ class FrameHandlerTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); } diff --git a/tests/suites/frame/FrameTest.php b/tests/suites/frame/FrameTest.php index 9f66647..bbef677 100644 --- a/tests/suites/frame/FrameTest.php +++ b/tests/suites/frame/FrameTest.php @@ -19,11 +19,6 @@ */ class FrameTest extends TestCase { - public function setUp(): void - { - error_reporting(-1); - } - public function testTextFrame(): void { $frame = new Frame('text', 'Text message', true); diff --git a/tests/suites/http/HttpHandlerTest.php b/tests/suites/http/HttpHandlerTest.php index df416af..39a2bb5 100644 --- a/tests/suites/http/HttpHandlerTest.php +++ b/tests/suites/http/HttpHandlerTest.php @@ -44,7 +44,6 @@ class HttpHandlerTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); $this->psrFactory = new Psr17Factory(); } diff --git a/tests/suites/http/RequestTest.php b/tests/suites/http/RequestTest.php index 8fe0381..fc646ba 100644 --- a/tests/suites/http/RequestTest.php +++ b/tests/suites/http/RequestTest.php @@ -27,11 +27,6 @@ */ class RequestTest extends TestCase { - public function setUp(): void - { - error_reporting(-1); - } - public function testEmptyRequest(): void { $request = new Request(); diff --git a/tests/suites/http/ResponseTest.php b/tests/suites/http/ResponseTest.php index 8045e19..d6d9cfe 100644 --- a/tests/suites/http/ResponseTest.php +++ b/tests/suites/http/ResponseTest.php @@ -25,11 +25,6 @@ */ class ResponseTest extends TestCase { - public function setUp(): void - { - error_reporting(-1); - } - public function testEmptyResponse(): void { $response = new Response(); diff --git a/tests/suites/http/ServerRequestTest.php b/tests/suites/http/ServerRequestTest.php index a68caac..8973fc1 100644 --- a/tests/suites/http/ServerRequestTest.php +++ b/tests/suites/http/ServerRequestTest.php @@ -23,11 +23,6 @@ */ class ServerRequestTest extends TestCase { - public function setUp(): void - { - error_reporting(-1); - } - public function testEmptyRequest(): void { $request = new ServerRequest(); diff --git a/tests/suites/message/BinaryTest.php b/tests/suites/message/BinaryTest.php index 9f31297..0dad99b 100644 --- a/tests/suites/message/BinaryTest.php +++ b/tests/suites/message/BinaryTest.php @@ -22,11 +22,6 @@ */ class BinaryTest extends TestCase { - public function setUp(): void - { - error_reporting(-1); - } - public function testBinaryMessage(): void { $bin = base64_encode('Some content'); diff --git a/tests/suites/message/CloseTest.php b/tests/suites/message/CloseTest.php index 3786e2b..a58a0d1 100644 --- a/tests/suites/message/CloseTest.php +++ b/tests/suites/message/CloseTest.php @@ -23,11 +23,6 @@ */ class CloseTest extends TestCase { - public function setUp(): void - { - error_reporting(-1); - } - public function testCloseMessage(): void { $message = new Close(1000, 'Some content'); diff --git a/tests/suites/message/MessageHandlerTest.php b/tests/suites/message/MessageHandlerTest.php index 619a93a..e95275d 100644 --- a/tests/suites/message/MessageHandlerTest.php +++ b/tests/suites/message/MessageHandlerTest.php @@ -41,7 +41,6 @@ class MessageHandlerTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); } diff --git a/tests/suites/message/PingTest.php b/tests/suites/message/PingTest.php index a9edfb9..62d6ba8 100644 --- a/tests/suites/message/PingTest.php +++ b/tests/suites/message/PingTest.php @@ -23,11 +23,6 @@ */ class PingTest extends TestCase { - public function setUp(): void - { - error_reporting(-1); - } - public function testPingMessage(): void { $message = new Ping('Some content'); diff --git a/tests/suites/message/PongTest.php b/tests/suites/message/PongTest.php index 636b971..21579c7 100644 --- a/tests/suites/message/PongTest.php +++ b/tests/suites/message/PongTest.php @@ -23,11 +23,6 @@ */ class PongTest extends TestCase { - public function setUp(): void - { - error_reporting(-1); - } - public function testPongMessage(): void { $message = new Pong('Some content'); diff --git a/tests/suites/message/TextTest.php b/tests/suites/message/TextTest.php index d33c5cd..2458a7e 100644 --- a/tests/suites/message/TextTest.php +++ b/tests/suites/message/TextTest.php @@ -22,11 +22,6 @@ */ class TextTest extends TestCase { - public function setUp(): void - { - error_reporting(-1); - } - public function testTextMessage(): void { $message = new Text('Some content'); diff --git a/tests/suites/middleware/CallbackTest.php b/tests/suites/middleware/CallbackTest.php index 1bfe403..24eb0b0 100644 --- a/tests/suites/middleware/CallbackTest.php +++ b/tests/suites/middleware/CallbackTest.php @@ -34,7 +34,6 @@ class CallbackTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); $this->psrFactory = new Psr17Factory(); } @@ -76,10 +75,6 @@ public function testIncoming(): void }); $message = $connection->pullMessage(); $this->assertEquals('Changed message', $message->getContent()); - - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); - unset($stream); } public function testOutgoing(): void @@ -106,10 +101,6 @@ public function testOutgoing(): void $this->expectSocketStreamWrite(); $connection->send(new Text('Test message')); - - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); - unset($stream); } public function testHttpIncoming(): void @@ -144,10 +135,6 @@ public function testHttpIncoming(): void /** @var RequestInterface $message */ $message = $connection->pullHttp(); $this->assertEquals('POST', $message->getMethod()); - - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); - unset($stream); } public function testHttpOutgoing(): void @@ -174,10 +161,6 @@ public function testHttpOutgoing(): void /** @var ResponseInterface $message */ $message = $connection->pushHttp($this->psrFactory->createResponse(200)); $this->assertEquals(400, $message->getStatusCode()); - - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); - unset($stream); } public function testTick(): void @@ -198,9 +181,5 @@ public function testTick(): void $stack->handleTick(); })); $connection->tick(); - - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); - unset($stream); } } diff --git a/tests/suites/middleware/CloseHandlerTest.php b/tests/suites/middleware/CloseHandlerTest.php index bab9e66..f9bca66 100644 --- a/tests/suites/middleware/CloseHandlerTest.php +++ b/tests/suites/middleware/CloseHandlerTest.php @@ -26,7 +26,6 @@ class CloseHandlerTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); } @@ -67,8 +66,6 @@ public function testLocalClose(): void $this->expectSocketStreamClose(); $message = $connection->pullMessage(); $this->assertInstanceOf(Close::class, $message); - - unset($stream); } public function testRemoteClose(): void @@ -97,7 +94,5 @@ public function testRemoteClose(): void $this->expectSocketStreamClose(); $message = $connection->pullMessage(); $this->assertInstanceOf(Close::class, $message); - - unset($stream); } } diff --git a/tests/suites/middleware/DeflateCompressorTest.php b/tests/suites/middleware/DeflateCompressorTest.php index 0a6a4c5..dbe27ea 100644 --- a/tests/suites/middleware/DeflateCompressorTest.php +++ b/tests/suites/middleware/DeflateCompressorTest.php @@ -36,7 +36,6 @@ class DeflateCompressorTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); $this->psrFactory = new Psr17Factory(); } @@ -138,9 +137,8 @@ function (string $method, array $params): void { $this->assertNotNull($this->getConfiguration($connection)->inflator); $this->assertSame($inflator, $this->getConfiguration($connection)->inflator); - $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($connection); + $connection->disconnect(); } public function testServerDefault(): void @@ -234,9 +232,8 @@ function (string $method, array $params): void { $this->assertNotNull($connection->getMeta('compressionExtension.configuration')->inflator); $this->assertSame($deflator, $connection->getMeta('compressionExtension.configuration')->deflator); - $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($connection); + $connection->disconnect(); } // Client request compression, but Server declines - do not use compression @@ -311,9 +308,8 @@ function (string $method, array $params): void { $this->assertNull($connection->getMeta('compressionExtension.compressor')); $this->assertNull($connection->getMeta('compressionExtension.configuration')); - $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($connection); + $connection->disconnect(); } public function testClientConfiguration(): void @@ -413,9 +409,8 @@ function (string $method, array $params): void { $this->assertNotNull($this->getConfiguration($connection)->inflator); $this->assertNotSame($inflator, $this->getConfiguration($connection)->inflator); - $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($connection); + $connection->disconnect(); } public function testServerConfiguration(): void @@ -514,9 +509,8 @@ function (string $method, array $params): void { $this->assertNotNull($connection->getMeta('compressionExtension.configuration')->inflator); $this->assertNotSame($deflator, $connection->getMeta('compressionExtension.configuration')->deflator); - $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($connection); + $connection->disconnect(); } public function testClientConfigurationByServer(): void @@ -582,9 +576,8 @@ function (string $method, array $params): void { 'inflator' => null, ], $connection->getMeta('compressionExtension.configuration')); - $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($connection); + $connection->disconnect(); } public function testServerConfigurationByServer(): void @@ -683,9 +676,8 @@ function (string $method, array $params): void { $this->assertNotNull($connection->getMeta('compressionExtension.configuration')->inflator); $this->assertNotSame($deflator, $connection->getMeta('compressionExtension.configuration')->deflator); - $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($connection); + $connection->disconnect(); } public function testDeflateCompressorNoExtension(): void diff --git a/tests/suites/middleware/FollowRedirectTest.php b/tests/suites/middleware/FollowRedirectTest.php index 0f92ae9..c1b2921 100644 --- a/tests/suites/middleware/FollowRedirectTest.php +++ b/tests/suites/middleware/FollowRedirectTest.php @@ -35,7 +35,6 @@ class FollowRedirectTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); } @@ -72,8 +71,6 @@ public function testRedirect(): void $this->expectSocketStreamReadLine()->setReturn(function () { return "\r\n"; }); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $this->expectException(ReconnectException::class); $this->expectExceptionMessage('Reconnect requested: ws://redirect.to/new/target'); $connection->pullHttp(); @@ -105,8 +102,6 @@ public function testMaxRedirect(): void $this->expectSocketStreamReadLine()->setReturn(function () { return "\r\n"; }); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $this->expectException(HandshakeException::class); $this->expectExceptionMessage('Too many redirect attempts, giving up'); $connection->pullHttp(); @@ -136,8 +131,8 @@ public function testNoLocation(): void return "\r\n"; }); $response = $connection->pullHttp(); - $this->expectSocketStreamIsConnected(); + $this->expectSocketStreamClose(); - unset($connection); + $connection->disconnect(); } } diff --git a/tests/suites/middleware/PingIntervalTest.php b/tests/suites/middleware/PingIntervalTest.php index 66c692e..e6f3796 100644 --- a/tests/suites/middleware/PingIntervalTest.php +++ b/tests/suites/middleware/PingIntervalTest.php @@ -25,7 +25,6 @@ class PingIntervalTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); } @@ -70,8 +69,7 @@ public function testPingInterval(): void }); $connection->tick(); - $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($stream); + $connection->disconnect(); } } diff --git a/tests/suites/middleware/PingResponderTest.php b/tests/suites/middleware/PingResponderTest.php index 4c287c5..0678c0d 100644 --- a/tests/suites/middleware/PingResponderTest.php +++ b/tests/suites/middleware/PingResponderTest.php @@ -26,7 +26,6 @@ class PingResponderTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); } @@ -68,7 +67,5 @@ public function testPingAutoResponse(): void $this->expectSocketStreamClose(); $this->assertSame($connection, $connection->disconnect()); - - unset($stream); } } diff --git a/tests/suites/middleware/ProcessStackTest.php b/tests/suites/middleware/ProcessStackTest.php index 12cd0b0..9b725e5 100644 --- a/tests/suites/middleware/ProcessStackTest.php +++ b/tests/suites/middleware/ProcessStackTest.php @@ -30,7 +30,6 @@ class ProcessStackTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); } @@ -84,10 +83,6 @@ public function testIncoming(): void }); $message = $connection->pullMessage(); $this->assertEquals('Test message<-C<-B<-A', $message->getContent()); - - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); - unset($stream); } public function testOutgoing(): void @@ -135,9 +130,5 @@ public function testOutgoing(): void $this->expectSocketStreamWrite(); $connection->send(new Text('Test message')); - - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); - unset($stream); } } diff --git a/tests/suites/middleware/SubprotocolNegotiationTest.php b/tests/suites/middleware/SubprotocolNegotiationTest.php index a6c3c4b..5c782eb 100644 --- a/tests/suites/middleware/SubprotocolNegotiationTest.php +++ b/tests/suites/middleware/SubprotocolNegotiationTest.php @@ -33,7 +33,6 @@ class SubprotocolNegotiationTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); $this->psrFactory = new Psr17Factory(); } @@ -90,10 +89,6 @@ function (string $method, array $params): void { $response = $connection->pullHttp(); $this->assertEquals(['sp-2'], $response->getHeader('Sec-WebSocket-Protocol')); $this->assertEquals('sp-2', $connection->getMeta('subprotocolNegotiation.selected')); - - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); - unset($connection); } public function testClientProtocolNoMatch(): void @@ -140,10 +135,6 @@ function (string $method, array $params): void { $response = $connection->pullHttp(); $this->assertEquals([], $response->getHeader('Sec-WebSocket-Protocol')); $this->assertNull($connection->getMeta('subprotocolNegotiation.selected')); - - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); - unset($connection); } public function testClientProtocolRequire(): void @@ -188,8 +179,6 @@ function (string $method, array $params): void { return "\r\n"; }); $this->expectSocketStreamWrite(); - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); $this->expectException(HandshakeException::class); $this->expectExceptionMessage('Could not resolve subprotocol.'); $connection->pullHttp(); @@ -248,10 +237,6 @@ function (string $method, array $params): void { $response = $connection->pushHttp($response); $this->assertEquals(['sp-2'], $response->getHeader('Sec-WebSocket-Protocol')); $this->assertEquals('sp-2', $connection->getMeta('subprotocolNegotiation.selected')); - - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); - unset($connection); } public function testServerProtocolNoMatch(): void @@ -308,10 +293,6 @@ function (string $method, array $params): void { $response = $connection->pushHttp($response); $this->assertEquals([], $response->getHeader('Sec-WebSocket-Protocol')); $this->assertNull($connection->getMeta('subprotocolNegotiation.selected')); - - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); - unset($connection); } public function testServerProtocolRequire(): void @@ -368,9 +349,5 @@ function (string $method, array $params): void { $response = $connection->pushHttp($response); $this->assertEquals([], $response->getHeader('Sec-WebSocket-Protocol')); $this->assertNull($connection->getMeta('subprotocolNegotiation.selected')); - - $this->expectSocketStreamIsConnected(); - $this->expectSocketStreamClose(); - unset($connection); } } diff --git a/tests/suites/runtime/RunnerTest.php b/tests/suites/runtime/RunnerTest.php new file mode 100644 index 0000000..f369cb6 --- /dev/null +++ b/tests/suites/runtime/RunnerTest.php @@ -0,0 +1,76 @@ +setUpStack(); + } + + public function tearDown(): void + { + $this->tearDownStack(); + } + + public function testHandling(): void + { + $this->expectStreamFactory(); + $this->expectStreamFactoryCreateStreamCollection(); + $this->expectStreamCollection(); + $runner = new Runner(new StreamFactory()); + + $server = new Server(); + $this->expectStreamCollectionAttach(); + $runner->attach($server, function () { + // ignore + }); + $this->expectStreamCollectionWaitRead(); + $this->expectStreamCollection(); + $runner->handle(0); + + $this->expectStreamCollectionDetach(); + $runner->detach($server->getIdentity()); + + $server->disconnect(); + } + + public function testIdentityConflict(): void + { + $this->expectStreamFactory(); + $this->expectStreamFactoryCreateStreamCollection(); + $this->expectStreamCollection(); + $runner = new Runner(new StreamFactory()); + + $server = new Server(); + $this->expectStreamCollectionAttach(); + $runner->attach($server, function () { + // ignore + }); + $this->expectException(RunnerException::class); + $this->expectExceptionMessage('Stream container with identity server/80 already attached'); + $runner->attach($server, function () { + // ignore + }); + } +} diff --git a/tests/suites/server/ConfigErrorTest.php b/tests/suites/server/ConfigErrorTest.php index af22a20..84bf67f 100644 --- a/tests/suites/server/ConfigErrorTest.php +++ b/tests/suites/server/ConfigErrorTest.php @@ -18,11 +18,6 @@ */ class ConfigErrorTest extends TestCase { - public function setUp(): void - { - error_reporting(-1); - } - public function testPortTooLow(): void { $this->expectException(InvalidArgumentException::class); diff --git a/tests/suites/server/ConfigTest.php b/tests/suites/server/ConfigTest.php index 6990a53..5a8d68f 100644 --- a/tests/suites/server/ConfigTest.php +++ b/tests/suites/server/ConfigTest.php @@ -48,7 +48,6 @@ class ConfigTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); } @@ -59,9 +58,8 @@ public function tearDown(): void public function testServerDefaults(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $this->assertSame($server, $server->setStreamFactory(new StreamFactory())); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->assertSame($server, $server->addMiddleware(new Callback())); $this->assertEquals('WebSocket\Server(closed)', "{$server}"); @@ -88,14 +86,15 @@ public function testServerDefaults(): void $this->assertEquals('WebSocket\Server(tcp://0.0.0.0:8000)', "{$server}"); $this->assertFalse($server->isRunning()); - unset($server); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testServerConfiguration(): void { - $this->expectStreamFactory(); - $server = new Server(9000, true); - $this->assertSame($server, $server->setStreamFactory(new StreamFactory())); + $this->expectWsServerCreate(); + $server = new Server(9000, true, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'ssl', port: 9000); $this->expectWsSelectConnections(['server/9000']); @@ -128,17 +127,18 @@ public function testServerConfiguration(): void $this->expectSocketStreamIsWritable(); $this->assertCount(1, $server->getWritableConnections()); - $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($server); + $this->expectStreamCollectionDetach(); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testContextOption(): void { $errorHandler = new ErrorHandler(); - $this->expectStreamFactory(); - $server = new Server(8000); - $this->assertSame($server, $server->setStreamFactory(new StreamFactory())); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $errorHandler->withAll(function () use ($server) { $server->setContext(['ssl' => ['verify_peer' => false]]); @@ -162,7 +162,9 @@ public function testContextOption(): void $this->assertEquals('WebSocket\Server(tcp://0.0.0.0:8000)', "{$server}"); $this->assertFalse($server->isRunning()); - unset($server); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testContextClass(): void @@ -173,9 +175,8 @@ public function testContextClass(): void $this->expectContextSetOption(); $context->setOptions(['ssl' => ['verify_peer' => false]]); - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $server->onHandshake(function (Server $server, Connection $connection) { $this->expectSocketStreamGetContext(); $connectionContext = $connection->getContext(); @@ -194,9 +195,11 @@ public function testContextClass(): void $this->expectWsServerPerformHandshake(); $server->start(); - $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($server); + $this->expectStreamCollectionDetach(); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testHttpFactories(): void @@ -208,13 +211,12 @@ public function testHttpFactories(): void $this->expectContextSetOption(); $context->setOptions(['ssl' => ['verify_peer' => false]]); - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->assertSame($server, $server->setHttpFactory(HttpFactory::create($httpFactory))); - unset($server); + $server->disconnect(); } public function testConfiguration(): void @@ -232,6 +234,7 @@ public function testConfiguration(): void $server = new Server(8000, configuration: $configuration); $this->assertInstanceOf(Configuration::class, $server->getConfiguration()); $this->assertSame($server, $server->setConfiguration($configuration)); - unset($server); + + $server->disconnect(); } } diff --git a/tests/suites/server/HandshakeTest.php b/tests/suites/server/HandshakeTest.php index f694a3f..cf1cb88 100644 --- a/tests/suites/server/HandshakeTest.php +++ b/tests/suites/server/HandshakeTest.php @@ -46,7 +46,6 @@ class HandshakeTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); } @@ -57,9 +56,8 @@ public function tearDown(): void public function testHandshakeRequest(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections(['server/8000']); @@ -69,17 +67,17 @@ public function testHandshakeRequest(): void $this->expectWsServerPerformHandshake(); $server->start(); - $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - - unset($server); + $this->expectStreamCollectionDetach(); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testHandshakeRequestVariant(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections(['server/8000']); @@ -117,17 +115,17 @@ public function testHandshakeRequestVariant(): void }); $server->start(); - $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - - unset($server); + $this->expectStreamCollectionDetach(); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testHandshakeRequestFailure(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections(['server/8000']); @@ -143,14 +141,15 @@ public function testHandshakeRequestFailure(): void $this->expectSocketStreamClose(); $server->start(); - unset($server); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testHandshakeMethodFailure(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections(['server/8000']); @@ -185,14 +184,15 @@ public function testHandshakeMethodFailure(): void $this->expectSocketStreamClose(); $server->start(); - unset($server); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testHandshakeConnectionHeaderFailure(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections(['server/8000']); @@ -227,14 +227,15 @@ public function testHandshakeConnectionHeaderFailure(): void $this->expectSocketStreamClose(); $server->start(); - unset($server); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testHandshakeUpgradeHeaderFailure(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections(['server/8000']); @@ -269,14 +270,15 @@ public function testHandshakeUpgradeHeaderFailure(): void $this->expectSocketStreamClose(); $server->start(); - unset($server); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testHandshakeVersionHeaderFailure(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections(['server/8000']); @@ -311,14 +313,15 @@ public function testHandshakeVersionHeaderFailure(): void $this->expectSocketStreamClose(); $server->start(); - unset($server); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testHandshakeWebSocketKeyHeaderFailure(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections(['server/8000']); @@ -350,14 +353,15 @@ public function testHandshakeWebSocketKeyHeaderFailure(): void $this->expectSocketStreamClose(); $server->start(); - unset($server); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testHandshakeWebSocketKeyInvalidFailure(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections(['server/8000']); @@ -392,14 +396,15 @@ public function testHandshakeWebSocketKeyInvalidFailure(): void $this->expectSocketStreamClose(); $server->start(); - unset($server); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testHandshakeResponseFailure(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections(['server/8000']); @@ -436,6 +441,8 @@ public function testHandshakeResponseFailure(): void $this->expectSocketStreamClose(); $server->start(); - unset($server); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } } diff --git a/tests/suites/server/ServerTest.php b/tests/suites/server/ServerTest.php index c3c1208..d7c698f 100644 --- a/tests/suites/server/ServerTest.php +++ b/tests/suites/server/ServerTest.php @@ -68,7 +68,6 @@ class ServerTest extends TestCase public function setUp(): void { - error_reporting(-1); $this->setUpStack(); } @@ -79,9 +78,9 @@ public function tearDown(): void public function testListeners(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); + $handler = new ErrorHandler(); $this->assertInstanceOf(Stringable::class, $server); $this->assertEquals('WebSocket\Server(closed)', "{$server}"); @@ -241,17 +240,16 @@ public function testListeners(): void $server->start(); $this->expectSocketStreamClose(); + $this->expectStreamCollectionDetach(); $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); $server->disconnect(); - - unset($server); } public function testMiddlewares(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $server->addMiddleware(new Callback()); @@ -265,17 +263,17 @@ public function testMiddlewares(): void $server->addMiddleware(new Callback()); - $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - - unset($server); + $this->expectStreamCollectionDetach(); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testBroadcastSend(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections(['server/8000']); @@ -310,17 +308,17 @@ public function testBroadcastSend(): void $message = $server->close(1000, 'Close message'); $this->assertInstanceOf(Close::class, $message); - $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - - unset($server); + $this->expectStreamCollectionDetach(); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testDetachConnection(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $server->onHandshake(function ($server, $connection, $request, $response) { $connection->disconnect(); @@ -342,14 +340,17 @@ public function testDetachConnection(): void $this->expectSocketStreamClose(); $server->start(); - unset($server); + $this->expectSocketStreamClose(); + $this->expectStreamCollectionDetach(); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testShutdown(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $server->addMiddleware(new CloseHandler()); $server->onHandshake(function ($server, $connection, $request, $response) { @@ -384,7 +385,6 @@ public function testShutdown(): void $this->expectSocketStreamIsReadable(); $this->expectSocketStreamCloseWrite(); $this->expectSocketStreamGetMetadata(); - $this->expectSocketStreamIsConnected(); $this->expectSocketStreamIsConnected(); // The @server handler should be blocked now @@ -418,23 +418,21 @@ public function testShutdown(): void $this->expectSocketStreamIsWritable(); $this->expectSocketStreamClose(); - // Connection detacher $this->expectSocketStreamIsConnected(); $this->expectStreamCollectionDetach(); $this->expectSocketStreamIsConnected(); $this->expectStreamCollectionDetach(); - $this->expectSocketServerClose(); - + $this->expectStreamCollectionDetach(); $server->start(); - unset($server); + + $server->disconnect(); } public function testShutdownEmpty(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $server->addMiddleware(new CloseHandler()); $server->onTick(function ($server) { @@ -443,15 +441,16 @@ public function testShutdownEmpty(): void $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections([]); $this->expectSocketServerClose(); - + $this->expectStreamCollectionDetach(); $server->start(); + + $server->disconnect(); } public function testAlreadyStarted(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $server->onHandshake(function ($server, $connection, $request, $response) { $connection->disconnect(); @@ -466,14 +465,17 @@ public function testAlreadyStarted(): void $this->expectSocketStreamClose(); $server->start(); - unset($server); + $this->expectSocketStreamClose(); + $this->expectStreamCollectionDetach(); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testCreateServerError(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectStreamFactoryCreateSocketServer()->addAssert(function ($method, $params) { throw new StreamException(StreamException::SERVER_SOCKET_ERR, ['uri' => 'test']); @@ -481,15 +483,12 @@ public function testCreateServerError(): void $this->expectException(ServerException::class); $this->expectExceptionMessage('Server failed to start:'); $server->start(); - - unset($server); } public function testServerAccessError(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections(['server/8000']); @@ -499,14 +498,15 @@ public function testServerAccessError(): void }); $server->start(); - unset($server); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testRunBadOpcodeException(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections(['server/8000']); @@ -531,17 +531,16 @@ public function testRunBadOpcodeException(): void $this->assertCount(1, $server->getWritableConnections()); $this->expectSocketStreamClose(); + $this->expectStreamCollectionDetach(); $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); $server->disconnect(); - - unset($server); } public function testRunConnectionClosedException(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections(['server/8000']); @@ -566,16 +565,14 @@ public function testRunConnectionClosedException(): void $this->assertEmpty($server->getWritableConnections()); $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); $server->disconnect(); - - unset($server); } public function testRunServerException(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections(['server/8000']); @@ -600,17 +597,16 @@ public function testRunServerException(): void $this->assertCount(1, $server->getWritableConnections()); $this->expectSocketStreamClose(); + $this->expectStreamCollectionDetach(); $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); $server->disconnect(); - - unset($server); } public function testRunExternalException(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); $this->expectWsSelectConnections(['server/8000']); @@ -622,7 +618,9 @@ public function testRunExternalException(): void throw new StreamException(1000); }); $this->expectSocketStreamClose(); + $this->expectStreamCollectionDetach(); $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); $this->expectException(StreamException::class); $this->expectExceptionMessage('Stream is detached.'); $server->start(); @@ -630,9 +628,8 @@ public function testRunExternalException(): void public function testUnmaskedException(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $server->onError(function ($server, $connection, $exception) { $this->assertInstanceOf(Server::class, $server); @@ -655,18 +652,19 @@ public function testUnmaskedException(): void return base64_decode('gQA='); }); $this->expectSocketStreamWrite(); - $this->expectSocketStreamIsConnected(); $server->start(); $this->expectSocketStreamClose(); - unset($server); + $this->expectStreamCollectionDetach(); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testMaxConnectionsOverflow(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $server->setMaxConnections(1); $this->expectWsServerSetup(scheme: 'tcp', port: 8000); @@ -681,16 +679,17 @@ public function testMaxConnectionsOverflow(): void $server->start(); $this->assertEquals(1, $server->getConnectionCount()); - $this->expectSocketStreamIsConnected(); $this->expectSocketStreamClose(); - unset($server); + $this->expectStreamCollectionDetach(); + $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); + $server->disconnect(); } public function testUnresolvableError(): void { - $this->expectStreamFactory(); - $server = new Server(8000); - $server->setStreamFactory(new StreamFactory()); + $this->expectWsServerCreate(); + $server = new Server(8000, streamFactory: new StreamFactory()); $server->onTick(function ($server) { /** @@ -705,11 +704,30 @@ public function testUnresolvableError(): void $this->expectWsServerAccept(); $this->expectWsServerPerformHandshake(); $this->expectSocketStreamClose(); + $this->expectStreamCollectionDetach(); $this->expectSocketServerClose(); + $this->expectStreamCollectionDetach(); $this->expectException(Error::class); $this->expectExceptionMessage('Class "WebSocket\Test\Server\UnexistingClass" not found'); $server->start(); + } - unset($server); + public function testDeprecatedSetStreamFactory(): void + { + $this->expectStreamFactory(); + $server = new Server(8000); + + $errorHandler = new ErrorHandler(); + $errorHandler->withAll(function () use ($server) { + $factory = new StreamFactory(); + $this->assertSame($server, $server->setStreamFactory($factory)); + }, function (array $errors) { + $this->assertEquals( + 'Server.setStreamFactory is deprecated and will be removed in v4.', + $errors[0]->getMessage() + ); + }); + + $server->disconnect(); } } From dc4c4244815f8ca828ab4314cfb16f01a33d29ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Jensen?= Date: Sun, 11 Jan 2026 19:40:38 +0100 Subject: [PATCH 2/2] runner test fix --- tests/mock/MockStreamContainer.php | 40 +++++++++++++++++++++++++++++ tests/mock/MockStreamTrait.php | 4 ++- tests/suites/runtime/RunnerTest.php | 40 +++++++++++++++++++---------- 3 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 tests/mock/MockStreamContainer.php diff --git a/tests/mock/MockStreamContainer.php b/tests/mock/MockStreamContainer.php new file mode 100644 index 0000000..c13a7b5 --- /dev/null +++ b/tests/mock/MockStreamContainer.php @@ -0,0 +1,40 @@ +stream = $factory->createStream('mock-stream-container'); + } + + public function getStream(): StreamInterface + { + return $this->stream; + } + + public function getIdentity(): string + { + return 'mock-stream-container'; + } +} diff --git a/tests/mock/MockStreamTrait.php b/tests/mock/MockStreamTrait.php index 94d77ed..a159427 100644 --- a/tests/mock/MockStreamTrait.php +++ b/tests/mock/MockStreamTrait.php @@ -12,9 +12,10 @@ ExpectSocketClientTrait, ExpectSocketStreamTrait, ExpectSocketServerTrait, + ExpectStreamTrait, ExpectStreamCollectionTrait, ExpectStreamFactoryTrait, - StackItem + StackItem, }; use Phrity\Net\Mock\StreamCollection; use Phrity\Net\Context; @@ -28,6 +29,7 @@ trait MockStreamTrait use ExpectSocketClientTrait; use ExpectSocketServerTrait; use ExpectSocketStreamTrait; + use ExpectStreamTrait; use ExpectStreamCollectionTrait; use ExpectStreamFactoryTrait; diff --git a/tests/suites/runtime/RunnerTest.php b/tests/suites/runtime/RunnerTest.php index f369cb6..3979591 100644 --- a/tests/suites/runtime/RunnerTest.php +++ b/tests/suites/runtime/RunnerTest.php @@ -13,8 +13,10 @@ use PHPUnit\Framework\TestCase; use WebSocket\Exception\RunnerException; use WebSocket\Runtime\Runner; -use WebSocket\Server; -use WebSocket\Test\MockStreamTrait; +use WebSocket\Test\{ + MockStreamContainer, + MockStreamTrait, +}; /** * Test case for WebSocket\Runtime\Runner @@ -36,13 +38,20 @@ public function tearDown(): void public function testHandling(): void { $this->expectStreamFactory(); + $factory = new StreamFactory(); $this->expectStreamFactoryCreateStreamCollection(); $this->expectStreamCollection(); - $runner = new Runner(new StreamFactory()); + $runner = new Runner($factory); + + $this->expectStreamFactoryCreateStream(); + $this->expectStreamFactoryCreateStreamFromResource(); + $this->expectStream(); + $this->expectStreamGetMetadata(); + $this->expectContext(); + $streamContainer = new MockStreamContainer($factory); - $server = new Server(); $this->expectStreamCollectionAttach(); - $runner->attach($server, function () { + $runner->attach($streamContainer, function () { // ignore }); $this->expectStreamCollectionWaitRead(); @@ -50,26 +59,31 @@ public function testHandling(): void $runner->handle(0); $this->expectStreamCollectionDetach(); - $runner->detach($server->getIdentity()); - - $server->disconnect(); + $runner->detach($streamContainer->getIdentity()); } public function testIdentityConflict(): void { $this->expectStreamFactory(); + $factory = new StreamFactory(); $this->expectStreamFactoryCreateStreamCollection(); $this->expectStreamCollection(); - $runner = new Runner(new StreamFactory()); + $runner = new Runner($factory); + + $this->expectStreamFactoryCreateStream(); + $this->expectStreamFactoryCreateStreamFromResource(); + $this->expectStream(); + $this->expectStreamGetMetadata(); + $this->expectContext(); + $streamContainer = new MockStreamContainer($factory); - $server = new Server(); $this->expectStreamCollectionAttach(); - $runner->attach($server, function () { + $runner->attach($streamContainer, function () { // ignore }); $this->expectException(RunnerException::class); - $this->expectExceptionMessage('Stream container with identity server/80 already attached'); - $runner->attach($server, function () { + $this->expectExceptionMessage('Stream container with identity mock-stream-container already attached'); + $runner->attach($streamContainer, function () { // ignore }); }