From 2228e684a2b28d0f05da09bf5b4b377545b3e33a Mon Sep 17 00:00:00 2001 From: Aleksandar Babic Date: Tue, 23 Dec 2014 12:09:51 +0100 Subject: [PATCH 1/2] catch socket_connect exception + tests --- .gitignore | 2 + composer.json | 11 +++++ phpunit.php | 5 +++ phpunit.xml | 24 +++++++++++ src/Connection/Node.php | 13 ++++-- tests/Connection/NodeTest.php | 65 +++++++++++++++++++++++++++++ tests/Stubs/ListeningServerStub.php | 27 ++++++++++++ tests/TestCase.php | 6 +++ 8 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 .gitignore create mode 100644 phpunit.php create mode 100644 phpunit.xml create mode 100644 tests/Connection/NodeTest.php create mode 100644 tests/Stubs/ListeningServerStub.php create mode 100644 tests/TestCase.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..88e99d5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +vendor +composer.lock \ No newline at end of file diff --git a/composer.json b/composer.json index aa20e71..13654ee 100755 --- a/composer.json +++ b/composer.json @@ -11,12 +11,23 @@ { "name": "Evseev Nikolay", "email": "evseevnn@gmail.com" + }, + { + "name": "Aleksandar Babic", + "email": "salebab@gmail.com" } ], "require": { "php": ">=5.4.0" }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, "autoload": { "psr-4": { "Cassandra\\": "src/" } + }, + + "autoload-dev" : { + "psr-4": { "Cassandra\\": "tests/"} } } diff --git a/phpunit.php b/phpunit.php new file mode 100644 index 0000000..39a1434 --- /dev/null +++ b/phpunit.php @@ -0,0 +1,5 @@ + + + + ./tests/ + + + + + src + + vendor + + + + \ No newline at end of file diff --git a/src/Connection/Node.php b/src/Connection/Node.php index aefdbe0..cf37e9b 100644 --- a/src/Connection/Node.php +++ b/src/Connection/Node.php @@ -61,9 +61,16 @@ public function getConnection() { foreach($this->_options['socket'] as $optname => $optval) socket_set_option($this->socket, SOL_SOCKET, $optname, $optval); - - if (!socket_connect($this->socket, $this->_options['host'], $this->_options['port'])) - throw new Exception("Unable to connect to Cassandra node: {$this->_options['host']}:{$this->_options['port']}"); + + try { + $connect = socket_connect($this->socket, $this->_options['host'], $this->_options['port']); + + if ($connect === false) + throw new \Exception(socket_strerror(socket_last_error($this->socket))); + + } catch(\Exception $e) { + throw new Exception("Unable to connect to Cassandra node: {$this->_options['host']}:{$this->_options['port']}", 0, $e); + } return $this->socket; } diff --git a/tests/Connection/NodeTest.php b/tests/Connection/NodeTest.php new file mode 100644 index 0000000..203b8bf --- /dev/null +++ b/tests/Connection/NodeTest.php @@ -0,0 +1,65 @@ +listen(19042); + } + + public static function tearDownAfterClass() + { + self::$server->shutdown(); + } + + public function testNewInstance() + { + $node = new Node("localhost:19042"); + + $this->assertInstanceOf('Cassandra\Connection\Node', $node); + + return $node; + } + + /** + * @depends testNewInstance + * @param Node $node + */ + public function testGetOptions(Node $node) + { + $this->assertArrayHasKey("host", $node->getOptions()); + $this->assertArrayHasKey("port", $node->getOptions()); + } + + /** + * @depends testNewInstance + * @param Node $node + */ + public function testGetConnectionSuccess(Node $node) + { + $connection = $node->getConnection(); + + $this->assertTrue(is_resource($connection)); + } + + /** + * @throws Exception + */ + public function testGetConnectionException() + { + $node = new Node("invalidhost:9042"); + + $this->setExpectedException('Cassandra\Connection\Exception', "invalidhost:9042"); + + $node->getConnection(); + } +} diff --git a/tests/Stubs/ListeningServerStub.php b/tests/Stubs/ListeningServerStub.php new file mode 100644 index 0000000..e740155 --- /dev/null +++ b/tests/Stubs/ListeningServerStub.php @@ -0,0 +1,27 @@ +sock = socket_create(AF_INET, SOCK_STREAM, 0); + + // Bind the socket to an address/port + if(!socket_bind($this->sock, 'localhost', $port)) { + throw new RuntimeException('Could not bind to address'); + } + + socket_set_nonblock($this->sock); + + // Start listening for connections + socket_listen($this->sock); + } + + + public function shutdown() + { + socket_shutdown($this->sock); + } +} \ No newline at end of file diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..6b28a89 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,6 @@ + Date: Tue, 23 Dec 2014 14:05:10 +0100 Subject: [PATCH 2/2] Cleanup Node, more tests --- src/Connection/Node.php | 104 +++++++++++++++++++++++----- tests/Connection/NodeTest.php | 44 +++++++++++- tests/Stubs/ListeningServerStub.php | 2 +- 3 files changed, 129 insertions(+), 21 deletions(-) diff --git a/src/Connection/Node.php b/src/Connection/Node.php index cf37e9b..b09c0de 100644 --- a/src/Connection/Node.php +++ b/src/Connection/Node.php @@ -45,40 +45,106 @@ public function __construct($options) { } /** - * + * Create connection and return socket resource + * * @throws Exception * @return resource */ public function getConnection() { - if (!empty($this->socket)) return $this->socket; - $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + if(!$this->isConnected()) { + try { + $this->connect(); + } catch(\Exception $e) { + throw new Exception("Unable to connect to Cassandra node: {$this->_options['host']}:{$this->_options['port']}. Reason: ". $e->getMessage(), 0, $e); + } + } - if ($this->socket === false) - throw new Exception(socket_strerror(socket_last_error())); + return $this->socket; + } - socket_set_option($this->socket, getprotobyname('TCP'), TCP_NODELAY, 1); - - foreach($this->_options['socket'] as $optname => $optval) - socket_set_option($this->socket, SOL_SOCKET, $optname, $optval); + /** + * @return array + */ + public function getOptions() + { + return $this->_options; + } - try { - $connect = socket_connect($this->socket, $this->_options['host'], $this->_options['port']); + /** + * Connect to host/port + * + * @throws Exception + */ + public function connect() + { + $this->createConnection(); + $this->setConnectionOptions(); - if ($connect === false) - throw new \Exception(socket_strerror(socket_last_error($this->socket))); + $result = @socket_connect($this->socket, $this->_options['host'], $this->_options['port']); - } catch(\Exception $e) { - throw new Exception("Unable to connect to Cassandra node: {$this->_options['host']}:{$this->_options['port']}", 0, $e); + if($result === false) { + throw new Exception("Unable to connect: " . socket_strerror(socket_last_error($this->socket))); } + } - return $this->socket; + /** + * Disconnect + */ + public function disconnect() + { + if(is_resource($this->socket)) { + @socket_close($this->socket); + } + + $this->socket = null; } /** - * @return array + * @return bool */ - public function getOptions() { - return $this->_options; + public function isConnected() + { + return is_resource($this->socket); + } + + + /** + * @throws Exception + */ + private function createConnection() + { + $this->socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + + if($this->socket === false) { + throw new Exception("Unable to create socket: ". socket_strerror(socket_last_error())); + } + } + + /** + * @throws Exception + */ + private function setConnectionOptions() + { + $this->setConnectionOption(getprotobyname('tcp'), TCP_NODELAY, 1); + + foreach($this->_options['socket'] as $name => $value) { + $this->setConnectionOption(SOL_SOCKET, $name, $value); + } + } + + /** + * @param int $level + * @param int $name + * @param mixed $value + * @throws Exception + */ + private function setConnectionOption($level, $name, $value) + { + $result = @socket_set_option($this->socket, $level, $name, $value); + + if($result === false) { + throw new Exception("Unable to set socket option: $level, $name"); + } } } diff --git a/tests/Connection/NodeTest.php b/tests/Connection/NodeTest.php index 203b8bf..62ba0e7 100644 --- a/tests/Connection/NodeTest.php +++ b/tests/Connection/NodeTest.php @@ -30,6 +30,20 @@ public function testNewInstance() return $node; } + /** + * @param Node $node + * @depends testNewInstance + */ + public function testIsConnected(Node $node) + { + $this->assertFalse($node->isConnected()); + + $node->connect(); + + $this->assertTrue($node->isConnected()); + } + + /** * @depends testNewInstance * @param Node $node @@ -44,7 +58,7 @@ public function testGetOptions(Node $node) * @depends testNewInstance * @param Node $node */ - public function testGetConnectionSuccess(Node $node) + public function testGetConnectionReturnsResource(Node $node) { $connection = $node->getConnection(); @@ -61,5 +75,33 @@ public function testGetConnectionException() $this->setExpectedException('Cassandra\Connection\Exception', "invalidhost:9042"); $node->getConnection(); + $node->disconnect(); + } + + public function testInvalidOptions() + { + $node = new Node([ + 'host' => 'localhost', + 'port' => 19042, + 'socket' => [ + SO_RCVTIMEO => -1, + ] + ]); + + $this->setExpectedException('Cassandra\Connection\Exception', "Unable to set socket option"); + + $node->connect(); + } + + /** + * + * @depends testNewInstance + * @param Node $node + */ + public function testDisconnect(Node $node) + { + $node->disconnect(); + + $this->assertFalse($node->isConnected()); } } diff --git a/tests/Stubs/ListeningServerStub.php b/tests/Stubs/ListeningServerStub.php index e740155..89fc2dc 100644 --- a/tests/Stubs/ListeningServerStub.php +++ b/tests/Stubs/ListeningServerStub.php @@ -10,7 +10,7 @@ public function listen($port = 9042) // Bind the socket to an address/port if(!socket_bind($this->sock, 'localhost', $port)) { - throw new RuntimeException('Could not bind to address'); + throw new \RuntimeException('Could not bind to address'); } socket_set_nonblock($this->sock);