then ->integer(SUT::OPCODE_CONTINUATION_FRAME) ->isEqualTo(0x0) ->integer(SUT::OPCODE_TEXT_FRAME) ->isEqualTo(0x1) ->integer(SUT::OPCODE_BINARY_FRAME) ->isEqualTo(0x2) ->integer(SUT::OPCODE_CONNECTION_CLOSE) ->isEqualTo(0x8) ->integer(SUT::OPCODE_PING) ->isEqualTo(0x9) ->integer(SUT::OPCODE_PONG) ->isEqualTo(0xa); } public function case_close_codes() { $this ->then ->integer(SUT::CLOSE_NORMAL) ->isEqualTo(1000) ->integer(SUT::CLOSE_GOING_AWAY) ->isEqualTo(1001) ->integer(SUT::CLOSE_PROTOCOL_ERROR) ->isEqualTo(1002) ->integer(SUT::CLOSE_DATA_ERROR) ->isEqualTo(1003) ->integer(SUT::CLOSE_STATUS_ERROR) ->isEqualTo(1005) ->integer(SUT::CLOSE_ABNORMAL) ->isEqualTo(1006) ->integer(SUT::CLOSE_MESSAGE_ERROR) ->isEqualTo(1007) ->integer(SUT::CLOSE_POLICY_ERROR) ->isEqualTo(1008) ->integer(SUT::CLOSE_MESSAGE_TOO_BIG) ->isEqualTo(1009) ->integer(SUT::CLOSE_EXTENSION_MISSING) ->isEqualTo(1010) ->integer(SUT::CLOSE_SERVER_ERROR) ->isEqualTo(1011) ->integer(SUT::CLOSE_TLS) ->isEqualTo(1015); } public function case_constructor() { $this ->given($socket = new Socket\Client('tcp://*:1234')) ->when($result = new SUT($socket)) ->then ->object($result->getConnection()) ->isIdenticalTo($socket) ->string($socket->getNodeName()) ->isEqualTo(Websocket\Node::class) ->let($listener = $this->invoke($result)->getListener()) ->object($listener) ->isInstanceOf(Event\Listener::class) ->boolean($listener->listenerExists('open')) ->isTrue() ->boolean($listener->listenerExists('message')) ->isTrue() ->boolean($listener->listenerExists('binary-message')) ->isTrue() ->boolean($listener->listenerExists('ping')) ->isTrue() ->boolean($listener->listenerExists('close')) ->isTrue() ->boolean($listener->listenerExists('error')) ->isTrue(); } public function case_run_do_handshake() { $self = $this; $this ->given( $socket = new Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $connection->on( 'open', function (Event\Bucket $bucket) use (&$calledA, $self) { $calledA = true; $self ->variable($bucket->getData()) ->isNull(); return; } ), $this->calling($node)->getHandshake = FAILED, $this->calling($connection)->doHandshake = function () use (&$calledB) { $calledB = true; return; } ) ->when($result = $this->invoke($connection)->_run($node)) ->then ->variable($result) ->isNull() ->boolean($calledA) ->isTrue() ->boolean($calledB) ->isTrue(); } public function case_run_cannot_read_the_frame() { $self = $this; $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation(new Websocket\Protocol\Rfc6455($socket)), $this->calling($socket)->read[1] = chr( (0x1 << 7) | (0x1 << 6) | (0x0 << 5) | (0x0 << 4) | Websocket\Connection::OPCODE_TEXT_FRAME ), $this->calling($socket)->read[2] = chr( (0x1 << 7) | 42 ), $this->calling($socket)->getCurrentNode = $node, $this->calling($node)->getHandshake = SUCCEED, $this->calling($connection)->close = function ($_code, $_reason) use (&$called, $self) { $called = true; $self ->integer($_code) ->isEqualTo(SUT::CLOSE_PROTOCOL_ERROR) ->string($_reason) ->isNotEmpty(); return; } ) ->when($result = $this->invoke($connection)->_run($node)) ->then ->variable($result) ->isNull() ->boolean($called) ->isTrue(); } public function case_run_client_messages_must_be_masked() { $self = $this; $this ->given( $socket = new \Mock\Hoa\Socket\Server('tcp://*:1234'), $connection = new \Mock\Hoa\Websocket\Server($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $this->calling($node)->getHandshake = SUCCEED, $this->calling($protocol)->readFrame = [ 'fin' => 0x1, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => SUT::OPCODE_TEXT_FRAME, 'mask' => 0x0, 'length' => 6, 'message' => 'foobar' ], $this->calling($connection)->close = function ($_code, $_reason) use (&$called, $self) { $called = true; $self ->integer($_code) ->isEqualTo(SUT::CLOSE_MESSAGE_ERROR) ->string($_reason) ->isEqualTo('All messages from the client must be masked.'); return; } ) ->when($result = $this->invoke($connection)->_run($node)) ->then ->variable($result) ->isNull() ->boolean($called) ->isTrue(); } public function case_run_ping_opcode() { $self = $this; $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $this->calling($node)->getHandshake = SUCCEED, $this->calling($protocol)->readFrame = [ 'fin' => 0x1, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => SUT::OPCODE_PING, 'mask' => 0x1, 'length' => 6, 'message' => 'foobar' ], $this->calling($protocol)->writeFrame = function ($_message, $_opcode, $_mask) use (&$calledA, $self) { $calledA = true; $self ->string($_message) ->isEqualTo('foobar') ->integer($_opcode) ->isEqualTo(SUT::OPCODE_PONG) ->boolean($_mask) ->isTrue(); return; }, $connection->on( 'ping', function (Event\Bucket $bucket) use (&$calledB, $self) { $calledB = true; $self ->array($bucket->getData()) ->isEqualTo([ 'message' => 'foobar' ]); return; } ) ) ->when($result = $this->invoke($connection)->_run($node)) ->then ->variable($result) ->isNull() ->boolean($calledA) ->isTrue() ->boolean($calledB) ->isTrue(); } public function case_run_ping_opcode_not_fin() { return $this->_case_run_ping_opcode_with_invalid_frame([ 'fin' => 0x0, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => SUT::OPCODE_PING, 'mask' => 0x1, 'length' => 6, 'message' => 'foobar' ]); } public function case_run_ping_opcode_with_length_too_big() { return $this->_case_run_ping_opcode_with_invalid_frame([ 'fin' => 0x1, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => SUT::OPCODE_PING, 'mask' => 0x1, 'length' => 0x7e, 'message' => 'foobar…' ]); } protected function _case_run_ping_opcode_with_invalid_frame(array $frame) { $self = $this; $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $this->calling($node)->getHandshake = SUCCEED, $this->calling($protocol)->readFrame = $frame, $this->calling($protocol)->writeFrame = function ($_message, $_opcode, $_mask) use (&$calledA) { $calledA = true; return; }, $this->calling($connection)->close = function ($_code, $_reason) use (&$calledB, $self) { $calledB = true; $self ->integer($_code) ->isEqualTo(SUT::CLOSE_PROTOCOL_ERROR) ->variable($_reason) ->isNull(); return; }, $connection->on( 'ping', function (Event\Bucket $bucket) use (&$calledC) { $calledC = true; return; } ) ) ->when($result = $this->invoke($connection)->_run($node)) ->then ->variable($result) ->isNull() ->variable($calledA) ->isNull() ->boolean($calledB) ->isTrue() ->variable($calledC) ->isNull(); } public function case_run_pong_opcode() { $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $this->calling($node)->getHandshake = SUCCEED, $this->calling($protocol)->readFrame = [ 'fin' => 0x1, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => SUT::OPCODE_PONG, 'mask' => 0x1, 'length' => 6, 'message' => 'foobar' ], $this->calling($protocol)->writeFrame = function ($_message, $_opcode, $_mask) use (&$called) { $called = true; return; } ) ->when($result = $this->invoke($connection)->_run($node)) ->then ->variable($result) ->isNull() ->variable($called) ->isNull(); } public function case_run_pong_opcode_not_fin() { $self = $this; $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $this->calling($node)->getHandshake = SUCCEED, $this->calling($protocol)->readFrame = [ 'fin' => 0x0, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => SUT::OPCODE_PONG, 'mask' => 0x1, 'length' => 6, 'message' => 'foobar' ], $this->calling($protocol)->writeFrame = function ($_message, $_opcode, $_mask) use (&$calledA) { $calledA = true; return; }, $this->calling($connection)->close = function ($_code, $_reason) use (&$calledB, $self) { $calledB = true; $self ->integer($_code) ->isEqualTo(SUT::CLOSE_PROTOCOL_ERROR) ->variable($_reason) ->isNull(); return; } ) ->when($result = $this->invoke($connection)->_run($node)) ->then ->variable($result) ->isNull() ->variable($calledA) ->isNull() ->boolean($calledB) ->isTrue(); } public function case_run_text_frame_opcode_with_fragments() { return $this->_case_run_x_frame_opcode_with_fragments(SUT::OPCODE_TEXT_FRAME); } public function case_run_binary_frame_opcode_with_fragments() { return $this->_case_run_x_frame_opcode_with_fragments(SUT::OPCODE_BINARY_FRAME); } protected function _case_run_x_frame_opcode_with_fragments($opcode) { $self = $this; $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $this->calling($node)->getHandshake = SUCCEED, $this->calling($node)->getNumberOfFragments = 42, $this->calling($protocol)->readFrame = [ 'fin' => 0x1, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => $opcode, 'mask' => 0x1, 'length' => 6, 'message' => 'foobar' ], $this->calling($protocol)->writeFrame = function ($_message, $_opcode, $_mask) use (&$calledA) { $calledA = true; return; }, $this->calling($connection)->close = function ($_code, $_reason) use (&$calledB, $self) { $calledB = true; $self ->integer($_code) ->isEqualTo(SUT::CLOSE_PROTOCOL_ERROR) ->variable($_reason) ->isNull(); return; } ) ->when($result = $this->invoke($connection)->_run($node)) ->then ->variable($result) ->isNull() ->variable($calledA) ->isNull() ->boolean($calledB) ->isTrue(); } public function case_run_text_frame_opcode_with_invalid_message_encoding() { $self = $this; $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $this->calling($node)->getHandshake = SUCCEED, $this->calling($protocol)->readFrame = [ 'fin' => 0x1, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => SUT::OPCODE_TEXT_FRAME, 'mask' => 0x1, 'length' => 6, 'message' => iconv('UTF-8', 'UTF-16', '😄') ], $this->calling($protocol)->writeFrame = function ($_message, $_opcode, $_mask) use (&$calledA) { $calledA = true; return; }, $this->calling($connection)->close = function ($_code, $_reason) use (&$calledB, $self) { $calledB = true; $self ->integer($_code) ->isEqualTo(SUT::CLOSE_MESSAGE_ERROR) ->variable($_reason) ->isNull(); return; } ) ->when($result = $this->invoke($connection)->_run($node)) ->then ->variable($result) ->isNull() ->variable($calledA) ->isNull() ->boolean($calledB) ->isTrue(); } public function case_run_text_frame_opcode() { return $this->_case_run_x_frame_opcode(SUT::OPCODE_TEXT_FRAME); } public function case_run_binary_frame_opcode() { return $this->_case_run_x_frame_opcode(SUT::OPCODE_BINARY_FRAME); } protected function _case_run_x_frame_opcode($opcode) { $self = $this; $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $this->calling($node)->getHandshake = SUCCEED, $this->calling($protocol)->readFrame = [ 'fin' => 0x1, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => $opcode, 'mask' => 0x1, 'length' => 6, 'message' => 'foobar' ], $this->calling($protocol)->writeFrame = function ($_message, $_opcode, $_mask) use (&$calledA) { $calledA = true; return; }, $this->calling($connection)->close = function ($_code, $_reason) use (&$calledB) { $calledB = true; return; }, $connection->on( SUT::OPCODE_TEXT_FRAME === $opcode ? 'message' : 'binary-message', function (Event\Bucket $bucket) use (&$calledC, $self) { $calledC = true; $self ->array($bucket->getData()) ->isEqualTo([ 'message' => 'foobar' ]); return; } ) ) ->when($result = $this->invoke($connection)->_run($node)) ->then ->variable($result) ->isNull() ->variable($calledA) ->isNull() ->variable($calledB) ->isNull() ->boolean($calledC) ->isTrue(); } public function case_run_text_frame_opcode_with_an_exception_from_the_listener() { return $this->_case_run_x_frame_opcode_with_an_exception_from_the_listener(SUT::OPCODE_TEXT_FRAME); } public function case_run_binary_frame_opcode_with_an_exception_from_the_listener() { return $this->_case_run_x_frame_opcode_with_an_exception_from_the_listener(SUT::OPCODE_BINARY_FRAME); } protected function _case_run_x_frame_opcode_with_an_exception_from_the_listener($opcode) { $self = $this; $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $this->calling($node)->getHandshake = SUCCEED, $this->calling($protocol)->readFrame = [ 'fin' => 0x1, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => $opcode, 'mask' => 0x1, 'length' => 6, 'message' => 'foobar' ], $this->calling($protocol)->writeFrame = function ($_message, $_opcode, $_mask) use (&$calledA) { $calledA = true; return; }, $this->calling($connection)->close = function ($_code, $_reason) use (&$calledB) { $calledB = true; return; }, $connection->on( SUT::OPCODE_TEXT_FRAME === $opcode ? 'message' : 'binary-message', function (Event\Bucket $bucket) use (&$calledC, $self) { $calledC = true; $self ->array($bucket->getData()) ->isEqualTo([ 'message' => 'foobar' ]); throw new \RuntimeException('bang'); } ), $connection->on( 'error', function (Event\Bucket $bucket) use (&$calledD, $self) { $calledD = true; $self ->let($data = $bucket->getData()) ->array($data) ->hasSize(1) ->hasKey('exception') ->exception($data['exception']) ->isInstanceOf(\RuntimeException::class) ->hasMessage('bang'); return; } ) ) ->when($result = $this->invoke($connection)->_run($node)) ->then ->variable($result) ->isNull() ->variable($calledA) ->isNull() ->variable($calledB) ->isNull() ->boolean($calledC) ->isTrue() ->boolean($calledD) ->isTrue(); } public function case_run_incomplete_text_frame_opcode() { return $this->_case_run_incomplete_x_frame_opcode(SUT::OPCODE_TEXT_FRAME); } public function case_run_incomplete_binary_frame_opcode() { return $this->_case_run_incomplete_x_frame_opcode(SUT::OPCODE_BINARY_FRAME); } protected function _case_run_incomplete_x_frame_opcode($opcode) { $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $this->calling($node)->getHandshake = SUCCEED, $this->calling($protocol)->readFrame = [ 'fin' => 0x0, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => $opcode, 'mask' => 0x1, 'length' => 6, 'message' => 'foobar' ], $this->calling($protocol)->writeFrame = function ($_message, $_opcode, $_mask) use (&$calledA) { $calledA = true; return; }, $this->calling($connection)->close = function ($_code, $_reason) use (&$calledB) { $calledB = true; return; }, $connection->on( 'message', function (Event\Bucket $bucket) use (&$calledC) { $calledC = true; return; } ) ) ->when($result = $this->invoke($connection)->_run($node)) ->then ->variable($result) ->isNull() ->variable($calledA) ->isNull() ->variable($calledB) ->isNull() ->variable($calledC) ->isNull() ->boolean($node->isMessageComplete()) ->isFalse() ->integer($node->getNumberOfFragments()) ->isEqualTo(1) ->string($node->getFragmentedMessage()) ->isEqualTo('foobar') ->boolean($node->isBinary()) ->isEqualTo(SUT::OPCODE_BINARY_FRAME === $opcode); } public function case_run_continuation_frame_opcode_with_fragments() { $self = $this; $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $this->calling($node)->getHandshake = SUCCEED, $this->calling($node)->getNumberOfFragments = 42, $this->calling($protocol)->readFrame = [ 'fin' => 0x0, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => SUT::OPCODE_CONTINUATION_FRAME, 'mask' => 0x1, 'length' => 6, 'message' => 'foobar' ], $this->calling($connection)->close = function ($_code, $_reason) use (&$called) { $called = true; $self ->integer($_code) ->isEqualTo(SUT::CLOSE_PROTOCOL_ERROR) ->variable($_reason) ->isNull(); return; } ) ->when($result = $this->invoke($connection)->_run($node)) ->then ->variable($result) ->isNull() ->variable($called) ->isNull(); } public function case_run_text_frame_and_continuation_frame_opcodes() { return $this->_case_run_x_frame_and_continuation_frame_opcodes(SUT::OPCODE_TEXT_FRAME); } public function case_run_binary_frame_and_continuation_frame_opcodes() { return $this->_case_run_x_frame_and_continuation_frame_opcodes(SUT::OPCODE_BINARY_FRAME); } protected function _case_run_x_frame_and_continuation_frame_opcodes($opcode) { $self = $this; $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $this->calling($node)->getHandshake = SUCCEED, $this->calling($protocol)->readFrame[1] = [ 'fin' => 0x0, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => $opcode, 'mask' => 0x1, 'length' => 3, 'message' => 'foo' ], $this->calling($protocol)->readFrame[2] = [ 'fin' => 0x0, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => SUT::OPCODE_CONTINUATION_FRAME, 'mask' => 0x1, 'length' => 3, 'message' => 'bar' ], $this->calling($protocol)->readFrame[3] = [ 'fin' => 0x1, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => SUT::OPCODE_CONTINUATION_FRAME, 'mask' => 0x1, 'length' => 3, 'message' => 'baz' ], $this->calling($socket)->getCurrentNode = $node, $connection->on( SUT::OPCODE_TEXT_FRAME === $opcode ? 'message' : 'binary-message', function (Event\Bucket $bucket) use (&$called, $self) { $called = true; $self ->array($bucket->getData()) ->isEqualTo([ 'message' => 'foobarbaz' ]); return; } ) ) ->when($result = $this->invoke($connection)->_run($node)) ->then ->integer($node->getNumberOfFragments()) ->isEqualTo(1) ->string($node->getFragmentedMessage()) ->isEqualTo('foo') ->boolean($node->isMessageComplete()) ->isFalse() ->variable($called) ->isNull() ->when($result = $this->invoke($connection)->_run($node)) ->then ->integer($node->getNumberOfFragments()) ->isEqualTo(2) ->string($node->getFragmentedMessage()) ->isEqualTo('foobar') ->boolean($node->isMessageComplete()) ->isFalse() ->variable($called) ->isNull() ->when($result = $this->invoke($connection)->_run($node)) ->then ->integer($node->getNumberOfFragments()) ->isEqualTo(0) ->variable($node->getFragmentedMessage()) ->isNull() ->boolean($node->isMessageComplete()) ->isTrue() ->boolean($called) ->isTrue(); } public function case_run_text_frame_and_continuation_frame_opcodes_with_an_exception_from_the_listener() { return $this->_case_run_x_frame_and_continuation_frame_opcodes_with_an_exception_from_the_listener(SUT::OPCODE_TEXT_FRAME); } public function case_run_binary_frame_and_continuation_frame_opcodes_with_an_exception_from_the_listener() { return $this->_case_run_x_frame_and_continuation_frame_opcodes_with_an_exception_from_the_listener(SUT::OPCODE_BINARY_FRAME); } protected function _case_run_x_frame_and_continuation_frame_opcodes_with_an_exception_from_the_listener($opcode) { $self = $this; $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $this->calling($node)->getHandshake = SUCCEED, $this->calling($protocol)->readFrame[1] = [ 'fin' => 0x0, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => $opcode, 'mask' => 0x1, 'length' => 3, 'message' => 'foo' ], $this->calling($protocol)->readFrame[2] = [ 'fin' => 0x0, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => SUT::OPCODE_CONTINUATION_FRAME, 'mask' => 0x1, 'length' => 3, 'message' => 'bar' ], $this->calling($protocol)->readFrame[3] = [ 'fin' => 0x1, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => SUT::OPCODE_CONTINUATION_FRAME, 'mask' => 0x1, 'length' => 3, 'message' => 'baz' ], $this->calling($socket)->getCurrentNode = $node, $connection->on( SUT::OPCODE_TEXT_FRAME === $opcode ? 'message' : 'binary-message', function (Event\Bucket $bucket) use (&$calledA, $self) { $calledA = true; $self ->array($bucket->getData()) ->isEqualTo([ 'message' => 'foobarbaz' ]); throw new \RuntimeException('bang'); } ), $connection->on( 'error', function (Event\Bucket $bucket) use (&$calledB, $self) { $calledB = true; $self ->let($data = $bucket->getData()) ->array($data) ->hasSize(1) ->hasKey('exception') ->exception($data['exception']) ->isInstanceOf(\RuntimeException::class) ->hasMessage('bang'); return; } ) ) ->when($result = $this->invoke($connection)->_run($node)) ->then ->integer($node->getNumberOfFragments()) ->isEqualTo(1) ->string($node->getFragmentedMessage()) ->isEqualTo('foo') ->boolean($node->isMessageComplete()) ->isFalse() ->variable($calledA) ->isNull() ->variable($calledB) ->isNull() ->when($result = $this->invoke($connection)->_run($node)) ->then ->integer($node->getNumberOfFragments()) ->isEqualTo(2) ->string($node->getFragmentedMessage()) ->isEqualTo('foobar') ->boolean($node->isMessageComplete()) ->isFalse() ->variable($calledA) ->isNull() ->variable($calledB) ->isNull() ->when($result = $this->invoke($connection)->_run($node)) ->then ->integer($node->getNumberOfFragments()) ->isEqualTo(0) ->variable($node->getFragmentedMessage()) ->isNull() ->boolean($node->isMessageComplete()) ->isTrue() ->boolean($calledA) ->isTrue() ->boolean($calledB) ->isTrue(); } public function case_run_text_frame_and_continuation_frame_opcodes_with_invalid_message_encoding() { $self = $this; $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $this->calling($node)->getHandshake = SUCCEED, $this->calling($protocol)->readFrame[1] = [ 'fin' => 0x0, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => SUT::OPCODE_TEXT_FRAME, 'mask' => 0x1, 'length' => 3, 'message' => 'foo' ], $this->calling($protocol)->readFrame[2] = [ 'fin' => 0x1, 'rsv1' => 0x0, 'rsv2' => 0x0, 'rsv3' => 0x0, 'opcode' => SUT::OPCODE_CONTINUATION_FRAME, 'mask' => 0x1, 'length' => 6, 'message' => iconv('UTF-8', 'UTF-16', '😄') ], $this->calling($socket)->getCurrentNode = $node, $this->calling($connection)->close = function ($_code, $_reason) use (&$called, $self) { $called = true; $self ->integer($_code) ->isEqualTo(SUT::CLOSE_MESSAGE_ERROR) ->variable($_reason) ->isNull(); return; } ) ->when($result = $this->invoke($connection)->_run($node)) ->then ->integer($node->getNumberOfFragments()) ->isEqualTo(1) ->string($node->getFragmentedMessage()) ->isEqualTo('foo') ->boolean($node->isMessageComplete()) ->isFalse() ->variable($called) ->isNull() ->when($result = $this->invoke($connection)->_run($node)) ->then ->integer($node->getNumberOfFragments()) ->isEqualTo(0) ->variable($node->getFragmentedMessage()) ->isNull() ->boolean($node->isMessageComplete()) ->isTrue() ->boolean($called) ->isTrue(); } public function case__send() { $self = $this; $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $message = 'foobar', $opcode = SUT::OPCODE_TEXT_FRAME, $end = true, $this->calling($node)->getHandshake = true, $this->calling($protocol)->send = function ($_message, $_opcode, $_end, $_mask) use (&$called, $self, $message, $opcode, $end) { $called = true; $self ->string($_message) ->isEqualTo($message) ->integer($_opcode) ->isEqualTo($opcode) ->boolean($_end) ->isEqualTo($end) ->boolean($_mask) ->isFalse(); return 42; }, $closure = $this->invoke($connection)->_send($message, $node) ) ->when($result = new \ReflectionFunction($closure)) ->then ->integer($result->getNumberOfRequiredParameters()) ->isEqualTo(2) ->when($result = $closure($opcode, $end)) ->then ->integer($result) ->isEqualTo(42) ->boolean($called) ->isTrue(); } public function case__send_with_no_handshake() { $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $message = 'foobar', $opcode = SUT::OPCODE_TEXT_FRAME, $end = true, $this->calling($protocol)->send = function ($_message, $_opcode, $_end, $_mask) use (&$called) { $called = true; return 42; }, $closure = $this->invoke($connection)->_send($message, $node) ) ->when($result = new \ReflectionFunction($closure)) ->then ->integer($result->getNumberOfRequiredParameters()) ->isEqualTo(2) ->when($result = $closure($opcode, $end)) ->then ->variable($result) ->isNull() ->variable($called) ->isNull(); } public function case_close_with_no_protocol() { $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $this->calling($socket)->getCurrentNode = $node ) ->when($result = $connection->close(SUT::CLOSE_DATA_ERROR, 'foo')) ->then ->variable($result) ->isNull() ->boolean($socket->isDisconnected()) ->isTrue(); } public function case_close() { $self = $this; $this ->given( $socket = new \Mock\Hoa\Socket\Client('tcp://*:1234'), $connection = new SUT($socket), $protocol = new \Mock\Hoa\Websocket\Protocol\Rfc6455($socket), $this->mockGenerator->orphanize('__construct'), $node = new \Mock\Hoa\Websocket\Node(), $node->setProtocolImplementation($protocol), $code = SUT::CLOSE_DATA_ERROR, $reason = 'foo', $this->calling($protocol)->close = function ($_code, $_reason) use (&$called, $self, $code, $reason) { $called = true; $self ->integer($_code) ->isEqualTo($code) ->string($_reason) ->isEqualTo($reason); return; }, $this->calling($socket)->getCurrentNode = $node ) ->when($result = $connection->close($code, $reason)) ->then ->variable($result) ->isNull() ->boolean($socket->isDisconnected()) ->isTrue() ->boolean($called) ->isTrue(); } }