[THRIFT-5757] Unit tests for php lib
diff --git a/composer.json b/composer.json
index 900fb28..684c1b3 100644
--- a/composer.json
+++ b/composer.json
@@ -26,7 +26,8 @@
         "php-mock/php-mock-phpunit": "^2.10",
         "ext-json": "*",
         "ext-xml": "*",
-        "ext-curl": "*"
+        "ext-curl": "*",
+        "ext-pcntl": "*"
     },
     "autoload": {
         "psr-4": {"Thrift\\": "lib/php/lib/"}
diff --git a/lib/php/lib/Factory/TFramedTransportFactory.php b/lib/php/lib/Factory/TFramedTransportFactory.php
index 485fca5..c0adfd0 100644
--- a/lib/php/lib/Factory/TFramedTransportFactory.php
+++ b/lib/php/lib/Factory/TFramedTransportFactory.php
@@ -1,5 +1,24 @@
 <?php
 
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
 namespace Thrift\Factory;
 
 use Thrift\Transport\TFramedTransport;
@@ -7,7 +26,7 @@
 
 class TFramedTransportFactory implements TTransportFactoryInterface
 {
-    public static function getTransport(TTransport $transport)
+    public function getTransport(TTransport $transport)
     {
         return new TFramedTransport($transport);
     }
diff --git a/lib/php/lib/Factory/TTransportFactory.php b/lib/php/lib/Factory/TTransportFactory.php
index 98ead95..4c0abd0 100644
--- a/lib/php/lib/Factory/TTransportFactory.php
+++ b/lib/php/lib/Factory/TTransportFactory.php
@@ -1,5 +1,24 @@
 <?php
 
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
 namespace Thrift\Factory;
 
 use Thrift\Transport\TTransport;
@@ -7,11 +26,10 @@
 class TTransportFactory implements TTransportFactoryInterface
 {
     /**
-     * @static
      * @param TTransport $transport
      * @return TTransport
      */
-    public static function getTransport(TTransport $transport)
+    public function getTransport(TTransport $transport)
     {
         return $transport;
     }
diff --git a/lib/php/lib/Factory/TTransportFactoryInterface.php b/lib/php/lib/Factory/TTransportFactoryInterface.php
index 614cea8..1c49067 100644
--- a/lib/php/lib/Factory/TTransportFactoryInterface.php
+++ b/lib/php/lib/Factory/TTransportFactoryInterface.php
@@ -1,5 +1,24 @@
 <?php
 
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
 namespace Thrift\Factory;
 
 use Thrift\Transport\TTransport;
@@ -7,9 +26,8 @@
 interface TTransportFactoryInterface
 {
     /**
-     * @static
      * @param TTransport $transport
      * @return TTransport
      */
-    public static function getTransport(TTransport $transport);
+    public function getTransport(TTransport $transport);
 }
diff --git a/lib/php/lib/Server/TSSLServerSocket.php b/lib/php/lib/Server/TSSLServerSocket.php
index ac589b7..e377897 100644
--- a/lib/php/lib/Server/TSSLServerSocket.php
+++ b/lib/php/lib/Server/TSSLServerSocket.php
@@ -49,6 +49,10 @@
     {
         $ssl_host = $this->getSSLHost($host);
         parent::__construct($ssl_host, $port);
+        // Initialize a stream context if not provided
+        if ($context === null) {
+            $context = stream_context_create();
+        }
         $this->context_ = $context;
     }
 
diff --git a/lib/php/lib/Server/TServerTransport.php b/lib/php/lib/Server/TServerTransport.php
index 15a27af..82ee752 100644
--- a/lib/php/lib/Server/TServerTransport.php
+++ b/lib/php/lib/Server/TServerTransport.php
@@ -3,6 +3,7 @@
 namespace Thrift\Server;
 
 use Thrift\Exception\TTransportException;
+use Thrift\Transport\TTransport;
 
 /**
  * Generic class for Server agent.
diff --git a/lib/php/test/Unit/Lib/Factory/TFramedTransportFactoryTest.php b/lib/php/test/Unit/Lib/Factory/TFramedTransportFactoryTest.php
index 3b8b5cc..f8a860c 100644
--- a/lib/php/test/Unit/Lib/Factory/TFramedTransportFactoryTest.php
+++ b/lib/php/test/Unit/Lib/Factory/TFramedTransportFactoryTest.php
@@ -35,7 +35,7 @@
     {
         $transport = $this->createMock(TTransport::class);
         $factory = new TFramedTransportFactory();
-        $framedTransport = $factory::getTransport($transport);
+        $framedTransport = $factory->getTransport($transport);
 
         $this->assertInstanceOf(TFramedTransport::class, $framedTransport);
 
diff --git a/lib/php/test/Unit/Lib/Factory/TTransportFactoryTest.php b/lib/php/test/Unit/Lib/Factory/TTransportFactoryTest.php
index a8a791a..9d3e640 100644
--- a/lib/php/test/Unit/Lib/Factory/TTransportFactoryTest.php
+++ b/lib/php/test/Unit/Lib/Factory/TTransportFactoryTest.php
@@ -34,7 +34,7 @@
     {
         $transport = $this->createMock(TTransport::class);
         $factory = new TTransportFactory();
-        $result = $factory::getTransport($transport);
+        $result = $factory->getTransport($transport);
 
         $this->assertSame($transport, $result);
     }
diff --git a/lib/php/test/Unit/Lib/Server/Fixture/TestProcessor.php b/lib/php/test/Unit/Lib/Server/Fixture/TestProcessor.php
new file mode 100644
index 0000000..8fc6891
--- /dev/null
+++ b/lib/php/test/Unit/Lib/Server/Fixture/TestProcessor.php
@@ -0,0 +1,30 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift\Unit\Lib\Server\Fixture;
+
+class TestProcessor
+{
+    public function process($input, $output)
+    {
+        return true;
+    }
+}
diff --git a/lib/php/test/Unit/Lib/Server/TForkingServerTest.php b/lib/php/test/Unit/Lib/Server/TForkingServerTest.php
new file mode 100644
index 0000000..0a44032
--- /dev/null
+++ b/lib/php/test/Unit/Lib/Server/TForkingServerTest.php
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift\Unit\Lib\Server;
+
+use PHPUnit\Framework\TestCase;
+
+class TForkingServerTest extends TestCase
+{
+    public function testServe(): void
+    {
+        $this->markTestSkipped('Unit test could not be written for class which use pcntl_fork and exit functions');
+    }
+}
diff --git a/lib/php/test/Unit/Lib/Server/TSSLServerSocketTest.php b/lib/php/test/Unit/Lib/Server/TSSLServerSocketTest.php
new file mode 100644
index 0000000..02ae584
--- /dev/null
+++ b/lib/php/test/Unit/Lib/Server/TSSLServerSocketTest.php
@@ -0,0 +1,150 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift\Unit\Lib\Server;
+
+use phpmock\phpunit\PHPMock;
+use PHPUnit\Framework\TestCase;
+use Thrift\Exception\TTransportException;
+use Thrift\Server\TSSLServerSocket;
+use Thrift\Transport\TSocket;
+
+class TSSLServerSocketTest extends TestCase
+{
+    use PHPMock;
+
+    public function testGetSSLHost()
+    {
+        $socket = new TSSLServerSocket();
+
+        $this->assertEquals('ssl://localhost', $socket->getSSLHost('localhost'));
+        $this->assertEquals('ssl://localhost', $socket->getSSLHost('ssl://localhost'));
+        $this->assertEquals('tcp://localhost', $socket->getSSLHost('tcp://localhost'));
+    }
+
+    public function testListenAndClose(): void
+    {
+        $options = [
+            'ssl' => [
+                'verify_peer' => true,
+                'verify_peer_name' => true,
+                'allow_self_signed' => true,
+            ],
+        ];
+        $context = stream_context_create($options);
+        $socket = new TSSLServerSocket('somehost', 999, $context);
+
+        $listener = tmpfile();
+        $this->getFunctionMock('Thrift\Server', 'stream_socket_server')
+             ->expects($this->once())
+             ->with(
+                 'ssl://somehost:999', #$address
+                 $this->anything(), #&$error_code
+                 $this->anything(), #&$error_string
+                 STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, #int $flags
+                 $this->callback(function ($context) use ($options) {
+                     $contextOptions = stream_context_get_options($context);
+
+                     return is_resource($context) && $options === $contextOptions;
+                 })#resource $context
+             )->willReturn($listener);
+
+        $socket->listen();
+
+        $reflection = new \ReflectionClass($socket);
+        $property = $reflection->getProperty('listener_');
+        $property->setAccessible(true);
+
+        $this->assertIsResource($property->getValue($socket));
+
+        $this->getFunctionMock('Thrift\Server', 'fclose')
+             ->expects($this->once())
+             ->with($this->equalTo($listener))
+             ->willReturn(true);
+
+        $socket->close();
+        $this->assertNull($property->getValue($socket));
+    }
+
+    public function testAccept()
+    {
+        $socket = new TSSLServerSocket('somehost', 999);
+        $socket->setAcceptTimeout(1000);
+
+        $listener = tmpfile();
+        $this->getFunctionMock('Thrift\Server', 'stream_socket_server')
+             ->expects($this->once())
+             ->with(
+                 'ssl://somehost:999', #$address
+                 $this->anything(), #&$error_code
+                 $this->anything(), #&$error_string
+                 STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, #int $flags
+                 $this->callback(function ($context) {
+                     $contextOptions = stream_context_get_options($context);
+
+                     return is_resource($context) && $contextOptions === [];
+                 }) #resource $context
+             )->willReturn($listener);
+
+        $transportHandle = tmpfile();
+        $this->getFunctionMock('Thrift\Server', 'stream_socket_accept')
+             ->expects($this->once())
+             ->with(
+                 $this->equalTo($listener),
+                 1
+             )->willReturn($transportHandle);
+
+        $socket->listen();
+        $result = $socket->accept();
+        $this->assertInstanceOf(TSocket::class, $result);
+
+        $reflection = new \ReflectionClass($result);
+        $property = $reflection->getProperty('handle_');
+        $property->setAccessible(true);
+        $this->assertEquals($transportHandle, $property->getValue($result));
+    }
+
+    public function testAcceptFailed()
+    {
+        $socket = new TSSLServerSocket('somehost', 999);
+        $socket->setAcceptTimeout(1000);
+
+        $listener = tmpfile();
+
+        $this->getFunctionMock('Thrift\Server', 'stream_socket_server')
+             ->expects($this->once())
+             ->with('ssl://somehost:999')
+             ->willReturn($listener);
+
+        $this->getFunctionMock('Thrift\Server', 'stream_socket_accept')
+             ->expects($this->once())
+             ->with(
+                 $this->equalTo($listener),
+                 1
+             )->willReturn(null);
+
+        $this->expectException(TTransportException::class);
+        $this->expectExceptionMessage('accept() may not return NULL');
+
+        $socket->listen();
+        $socket->accept();
+    }
+}
diff --git a/lib/php/test/Unit/Lib/Server/TServerSocketTest.php b/lib/php/test/Unit/Lib/Server/TServerSocketTest.php
new file mode 100644
index 0000000..e9d3b96
--- /dev/null
+++ b/lib/php/test/Unit/Lib/Server/TServerSocketTest.php
@@ -0,0 +1,128 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift\Unit\Lib\Server;
+
+use phpmock\phpunit\PHPMock;
+use PHPUnit\Framework\TestCase;
+use Thrift\Exception\TTransportException;
+use Thrift\Server\TServerSocket;
+use Thrift\Transport\TSocket;
+
+class TServerSocketTest extends TestCase
+{
+    use PHPMock;
+
+    public function testSetAcceptTimeout(): void
+    {
+        $socket = new TServerSocket();
+        $socket->setAcceptTimeout(1000);
+
+        $reflection = new \ReflectionClass($socket);
+        $property = $reflection->getProperty('acceptTimeout_');
+        $property->setAccessible(true);
+
+        $this->assertEquals(1000, $property->getValue($socket));
+    }
+
+    public function testListenAndClose(): void
+    {
+        $socket = new TServerSocket('somehost', 999);
+
+        $listener = tmpfile();
+        $this->getFunctionMock('Thrift\Server', 'stream_socket_server')
+             ->expects($this->once())
+             ->with('tcp://somehost:999')
+             ->willReturn($listener);
+
+        $socket->listen();
+
+        $reflection = new \ReflectionClass($socket);
+        $property = $reflection->getProperty('listener_');
+        $property->setAccessible(true);
+
+        $this->assertIsResource($property->getValue($socket));
+
+        $this->getFunctionMock('Thrift\Server', 'fclose')
+             ->expects($this->once())
+             ->with($this->equalTo($listener))
+             ->willReturn(true);
+
+        $socket->close();
+        $this->assertNull($property->getValue($socket));
+    }
+
+    public function testAccept()
+    {
+        $socket = new TServerSocket('somehost', 999);
+        $socket->setAcceptTimeout(1000);
+
+        $listener = tmpfile();
+
+        $this->getFunctionMock('Thrift\Server', 'stream_socket_server')
+             ->expects($this->once())
+             ->with('tcp://somehost:999')
+             ->willReturn($listener);
+
+        $transportHandle = tmpfile();
+        $this->getFunctionMock('Thrift\Server', 'stream_socket_accept')
+             ->expects($this->once())
+             ->with(
+                 $this->equalTo($listener),
+                 1
+             )->willReturn($transportHandle);
+
+        $socket->listen();
+        $result = $socket->accept();
+        $this->assertInstanceOf(TSocket::class, $result);
+
+        $reflection = new \ReflectionClass($result);
+        $property = $reflection->getProperty('handle_');
+        $property->setAccessible(true);
+        $this->assertEquals($transportHandle, $property->getValue($result));
+    }
+
+    public function testAcceptFailed()
+    {
+        $socket = new TServerSocket('somehost', 999);
+        $socket->setAcceptTimeout(1000);
+
+        $listener = tmpfile();
+
+        $this->getFunctionMock('Thrift\Server', 'stream_socket_server')
+             ->expects($this->once())
+             ->with('tcp://somehost:999')
+             ->willReturn($listener);
+
+        $this->getFunctionMock('Thrift\Server', 'stream_socket_accept')
+             ->expects($this->once())
+             ->with(
+                 $this->equalTo($listener),
+                 1
+             )->willReturn(null);
+
+        $this->expectException(TTransportException::class);
+        $this->expectExceptionMessage('accept() may not return NULL');
+
+        $socket->listen();
+        $socket->accept();
+    }
+}
diff --git a/lib/php/test/Unit/Lib/Server/TSimpleServerTest.php b/lib/php/test/Unit/Lib/Server/TSimpleServerTest.php
new file mode 100644
index 0000000..269b838
--- /dev/null
+++ b/lib/php/test/Unit/Lib/Server/TSimpleServerTest.php
@@ -0,0 +1,166 @@
+<?php
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace Test\Thrift\Unit\Lib\Server;
+
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
+use Test\Thrift\Unit\Lib\Server\Fixture\TestProcessor;
+use Thrift\Factory\TProtocolFactory;
+use Thrift\Factory\TTransportFactoryInterface;
+use Thrift\Server\TServerTransport;
+use Thrift\Server\TSimpleServer;
+use Thrift\Transport\TTransport;
+
+class TSimpleServerTest extends TestCase
+{
+    /**
+     * @var object
+     */
+    private $processor;
+    /**
+     * @var MockObject|TServerTransport
+     */
+    private $transport;
+    /**
+     * @var MockObject|TTransportFactoryInterface
+     */
+    private $inputTransportFactory;
+    /**
+     * @var MockObject|TTransportFactoryInterface
+     */
+    private $outputTransportFactory;
+    /**
+     * @var MockObject|TProtocolFactory
+     */
+    private $inputProtocolFactory;
+    /**
+     * @var MockObject|TProtocolFactory
+     */
+    private $outputProtocolFactory;
+    /**
+     * @var TSimpleServer
+     */
+    private $server;
+
+    protected function setUp(): void
+    {
+        $this->processor = $this->createMock(TestProcessor::class);
+        $this->transport = $this->createMock(TServerTransport::class);
+        $this->inputTransportFactory = $this->createMock(TTransportFactoryInterface::class);
+        $this->outputTransportFactory = $this->createMock(TTransportFactoryInterface::class);
+        $this->inputProtocolFactory = $this->createMock(TProtocolFactory::class);
+        $this->outputProtocolFactory = $this->createMock(TProtocolFactory::class);
+
+        $this->server = new TSimpleServer(
+            $this->processor,
+            $this->transport,
+            $this->inputTransportFactory,
+            $this->outputTransportFactory,
+            $this->inputProtocolFactory,
+            $this->outputProtocolFactory
+        );
+    }
+
+    protected function tearDown(): void
+    {
+        unset(
+            $this->processor,
+            $this->transport,
+            $this->inputTransportFactory,
+            $this->outputTransportFactory,
+            $this->inputProtocolFactory,
+            $this->outputProtocolFactory,
+            $this->server
+        );
+    }
+
+    /**
+     * @dataProvider serveDataProvider
+     */
+    public function testServe(
+        $serveLoopCount,
+        array $processLoopResult
+    ): void {
+        $transport = $this->createMock(TTransport::class);
+
+        $this->transport->expects($this->once())
+            ->method('listen');
+        $this->transport->expects($this->exactly($serveLoopCount))
+            ->method('accept')
+            ->willReturn($transport);
+
+        $this->inputTransportFactory->expects($this->exactly($serveLoopCount))
+            ->method('getTransport')
+            ->willReturn($this->createMock(TServerTransport::class));
+        $this->outputTransportFactory->expects($this->exactly($serveLoopCount))
+            ->method('getTransport')
+            ->willReturn($this->createMock(TServerTransport::class));
+
+        $inputProtocol = $this->createMock(TServerTransport::class);
+        $this->inputProtocolFactory->expects($this->exactly($serveLoopCount))
+            ->method('getProtocol')
+            ->willReturn($inputProtocol);
+
+        $outputProtocol = $this->createMock(TServerTransport::class);
+        $this->outputProtocolFactory->expects($this->exactly($serveLoopCount))
+            ->method('getProtocol')
+            ->willReturn($outputProtocol);
+
+        /**
+         * ATTENTION!
+         * it is a hack to stop the server loop in unit test
+         * last call of process can return any value, but should stop server for removing infinite loop
+         **/
+        $processLoopResult[] = $this->returnCallback(function () {
+            $this->server->stop();
+
+            return false;
+        });
+
+        $this->processor->expects($this->exactly(count($processLoopResult)))
+            ->method('process')
+            ->with(
+                $this->equalTo($inputProtocol),
+                $this->equalTo($outputProtocol)
+            )
+            ->willReturnOnConsecutiveCalls(...$processLoopResult);
+
+        $this->server->serve();
+    }
+
+    public function serveDataProvider()
+    {
+        yield 'one serve loop' => [
+            'serveLoopCount' => 1,
+            'processLoopResult' => [
+                true,
+            ]
+        ];
+        yield 'two serve loop' => [
+            'serveLoopCount' => 2,
+            'processLoopResult' => [
+                true,
+                false,
+            ]
+        ];
+    }
+}