From 0591ece73cc7a25bc611d40bd7a3f657ad6d0751 Mon Sep 17 00:00:00 2001 From: k911 Date: Mon, 18 Mar 2019 21:29:31 +0100 Subject: [PATCH] test(code-coverage): Gather code coverage in server processes --- .travis.yml | 1 + .../DependencyInjection/SwooleExtension.php | 45 ++++++++++---- .../Bundle/Resources/config/services.yaml | 6 ++ src/Server/Api/ApiServerClient.php | 11 +++- src/Server/Api/ApiServerRequestHandler.php | 14 ++--- .../WithServerShutdownHandler.php | 26 ++++++++ .../Configurator/WithWorkerStopHandler.php | 26 ++++++++ .../ServerShutdownHandlerInterface.php | 17 ++++++ src/Server/LifecycleHandler/SigIntHandler.php | 11 ++++ .../WorkerHandler/HMRWorkerStartHandler.php | 8 ++- .../WorkerHandler/NoOpWorkerStartHandler.php | 18 ++++++ .../WorkerHandler/NoOpWorkerStopHandler.php | 18 ++++++ .../WorkerStartHandlerInterface.php | 10 ++-- .../WorkerStopHandlerInterface.php | 20 +++++++ .../Coverage/CodeCoverageManager.php | 7 ++- .../Symfony/CoverageBundle/CoverageBundle.php | 59 +++++++++++++++++++ .../CoverageFinishOnServerShutdown.php | 34 +++++++++++ .../CoverageFinishOnServerWorkerStop.php | 36 +++++++++++ .../CoverageStartOnServerStart.php | 33 +++++++++++ .../CoverageStartOnServerWorkerStart.php | 34 +++++++++++ .../{000-list-swoole.sh => 000-symfony.sh} | 1 + tests/Server/003-start-replace-content-hmr.sh | 4 +- 22 files changed, 410 insertions(+), 29 deletions(-) create mode 100644 src/Server/Configurator/WithServerShutdownHandler.php create mode 100644 src/Server/Configurator/WithWorkerStopHandler.php create mode 100644 src/Server/LifecycleHandler/ServerShutdownHandlerInterface.php create mode 100644 src/Server/WorkerHandler/NoOpWorkerStartHandler.php create mode 100644 src/Server/WorkerHandler/NoOpWorkerStopHandler.php create mode 100644 src/Server/WorkerHandler/WorkerStopHandlerInterface.php create mode 100644 tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageFinishOnServerShutdown.php create mode 100644 tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageFinishOnServerWorkerStop.php create mode 100644 tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageStartOnServerStart.php create mode 100644 tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageStartOnServerWorkerStart.php rename tests/Server/{000-list-swoole.sh => 000-symfony.sh} (82%) diff --git a/.travis.yml b/.travis.yml index ad03237e..827af7e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,7 @@ matrix: after_success: - composer merge-code-coverage - ./cc-test-reporter after-build -t clover --exit-code $TRAVIS_TEST_RESULT + - bash <(curl -s https://codecov.io/bash) env: - COVERAGE=1 - DEPLOY=1 diff --git a/src/Bridge/Symfony/Bundle/DependencyInjection/SwooleExtension.php b/src/Bridge/Symfony/Bundle/DependencyInjection/SwooleExtension.php index 919952b0..ea2cd9a4 100644 --- a/src/Bridge/Symfony/Bundle/DependencyInjection/SwooleExtension.php +++ b/src/Bridge/Symfony/Bundle/DependencyInjection/SwooleExtension.php @@ -13,8 +13,13 @@ use K911\Swoole\Server\Config\Socket; use K911\Swoole\Server\Config\Sockets; use K911\Swoole\Server\Configurator\ConfiguratorInterface; +use K911\Swoole\Server\Configurator\WithServerShutdownHandler; +use K911\Swoole\Server\Configurator\WithServerStartHandler; use K911\Swoole\Server\Configurator\WithWorkerStartHandler; +use K911\Swoole\Server\Configurator\WithWorkerStopHandler; use K911\Swoole\Server\HttpServerConfiguration; +use K911\Swoole\Server\LifecycleHandler\ServerShutdownHandlerInterface; +use K911\Swoole\Server\LifecycleHandler\ServerStartHandlerInterface; use K911\Swoole\Server\RequestHandler\AdvancedStaticFilesServer; use K911\Swoole\Server\RequestHandler\RequestHandlerInterface; use K911\Swoole\Server\Runtime\BootableInterface; @@ -22,8 +27,10 @@ use K911\Swoole\Server\Runtime\HMR\InotifyHMR; use K911\Swoole\Server\WorkerHandler\HMRWorkerStartHandler; use K911\Swoole\Server\WorkerHandler\WorkerStartHandlerInterface; +use K911\Swoole\Server\WorkerHandler\WorkerStopHandlerInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; @@ -131,9 +138,29 @@ private function registerHttpServerConfiguration(array $config, ContainerBuilder $this->registerHttpServerHMR($hmr, $container); + if ($container->has(ServerStartHandlerInterface::class)) { + $container->autowire(WithServerStartHandler::class) + ->setAutoconfigured(true) + ->setPublic(false); + + $container->getDefinition('swoole_bundle.server.http_server.configurator.with_sigint_handler') + ->addArgument(new Reference(ServerStartHandlerInterface::class)); + } + + if ($container->has(ServerShutdownHandlerInterface::class)) { + $container->autowire(WithServerShutdownHandler::class) + ->setAutoconfigured(true) + ->setPublic(false); + } + if ($container->has(WorkerStartHandlerInterface::class)) { - $container->register(WithWorkerStartHandler::class) - ->setAutowired(true) + $container->autowire(WithWorkerStartHandler::class) + ->setAutoconfigured(true) + ->setPublic(false); + } + + if ($container->has(WorkerStopHandlerInterface::class)) { + $container->autowire(WithWorkerStopHandler::class) ->setAutoconfigured(true) ->setPublic(false); } @@ -146,18 +173,16 @@ private function registerHttpServerHMR(string $hmr, ContainerBuilder $container) } if ('inotify' === $hmr) { - $container->register(HotModuleReloaderInterface::class, InotifyHMR::class) - ->setAutowired(true) + $container->autowire(HotModuleReloaderInterface::class, InotifyHMR::class) ->setAutoconfigured(true) ->setPublic(false); } - if (!$container->has(WorkerStartHandlerInterface::class)) { - $container->register(WorkerStartHandlerInterface::class, HMRWorkerStartHandler::class) - ->setAutowired(true) - ->setAutoconfigured(true) - ->setPublic(false); - } + $container->autowire(HMRWorkerStartHandler::class) + ->setAutoconfigured(false) + ->setPublic(false) + ->setArgument('$decorated', new Reference(HMRWorkerStartHandler::class.'.inner', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)) + ->setDecoratedService(WorkerStartHandlerInterface::class); } private function resolveAutoHMR(): string diff --git a/src/Bridge/Symfony/Bundle/Resources/config/services.yaml b/src/Bridge/Symfony/Bundle/Resources/config/services.yaml index 39cb65dc..b2d5cc3a 100644 --- a/src/Bridge/Symfony/Bundle/Resources/config/services.yaml +++ b/src/Bridge/Symfony/Bundle/Resources/config/services.yaml @@ -45,6 +45,12 @@ services: 'K911\Swoole\Server\Config\Sockets': + 'K911\Swoole\Server\WorkerHandler\WorkerStartHandlerInterface': + class: K911\Swoole\Server\WorkerHandler\NoOpWorkerStartHandler + + 'K911\Swoole\Server\WorkerHandler\WorkerStopHandlerInterface': + class: K911\Swoole\Server\WorkerHandler\NoOpWorkerStopHandler + 'K911\Swoole\Server\HttpServerConfiguration': 'K911\Swoole\Server\Configurator\WithHttpServerConfiguration': diff --git a/src/Server/Api/ApiServerClient.php b/src/Server/Api/ApiServerClient.php index c340a49c..dba16e77 100644 --- a/src/Server/Api/ApiServerClient.php +++ b/src/Server/Api/ApiServerClient.php @@ -68,7 +68,16 @@ private function sendRequest(string $path, string $method = ApiServerInterface:: $headers = ['accept' => 'application/json']; if (!empty($data)) { $headers['content-type'] = 'application/json'; - $client->setData(\json_encode($data, JSON_THROW_ON_ERROR)); + + $options = \defined('JSON_THROW_ON_ERROR') ? \JSON_THROW_ON_ERROR : 0; + $json = \json_encode($data, $options); + + // TODO: Drop on PHP 7.3 Migration + if (!\defined('JSON_THROW_ON_ERROR') && false === $json) { + throw new \RuntimeException(\json_last_error_msg(), \json_last_error()); + } + + $client->setData($json); } $client->setHeaders($headers); diff --git a/src/Server/Api/ApiServerRequestHandler.php b/src/Server/Api/ApiServerRequestHandler.php index df8fbaa4..02bbf574 100644 --- a/src/Server/Api/ApiServerRequestHandler.php +++ b/src/Server/Api/ApiServerRequestHandler.php @@ -145,14 +145,12 @@ private function sendResponse(Response $response, int $statusCode = 200, ?array $response->header('Content-Type', 'application/json'); $response->status($statusCode); - if (\defined('JSON_THROW_ON_ERROR')) { - $json = \json_encode($data, \JSON_THROW_ON_ERROR); - } else { - // TODO: Drop on PHP 7.3 Migration - $json = \json_encode($data); - if (false === $json) { - throw new \RuntimeException(\json_last_error_msg(), \json_last_error()); - } + $options = \defined('JSON_THROW_ON_ERROR') ? \JSON_THROW_ON_ERROR : 0; + $json = \json_encode($data, $options); + + // TODO: Drop on PHP 7.3 Migration + if (!\defined('JSON_THROW_ON_ERROR') && false === $json) { + throw new \RuntimeException(\json_last_error_msg(), \json_last_error()); } $response->end($json); diff --git a/src/Server/Configurator/WithServerShutdownHandler.php b/src/Server/Configurator/WithServerShutdownHandler.php new file mode 100644 index 00000000..376272e3 --- /dev/null +++ b/src/Server/Configurator/WithServerShutdownHandler.php @@ -0,0 +1,26 @@ +handler = $handler; + } + + /** + * {@inheritdoc} + */ + public function configure(Server $server): void + { + $server->on('shutdown', [$this->handler, 'handle']); + } +} diff --git a/src/Server/Configurator/WithWorkerStopHandler.php b/src/Server/Configurator/WithWorkerStopHandler.php new file mode 100644 index 00000000..edd46172 --- /dev/null +++ b/src/Server/Configurator/WithWorkerStopHandler.php @@ -0,0 +1,26 @@ +handler = $handler; + } + + /** + * {@inheritdoc} + */ + public function configure(Server $server): void + { + $server->on('close', [$this->handler, 'handle']); + } +} diff --git a/src/Server/LifecycleHandler/ServerShutdownHandlerInterface.php b/src/Server/LifecycleHandler/ServerShutdownHandlerInterface.php new file mode 100644 index 00000000..d8005169 --- /dev/null +++ b/src/Server/LifecycleHandler/ServerShutdownHandlerInterface.php @@ -0,0 +1,17 @@ +decorated = $decorated; + } + /** * {@inheritdoc} */ @@ -16,5 +23,9 @@ public function handle(Server $server): void { // 2 => SIGINT Process::signal(2, [$server, 'shutdown']); + + if ($this->decorated instanceof ServerStartHandlerInterface) { + $this->decorated->handle($server); + } } } diff --git a/src/Server/WorkerHandler/HMRWorkerStartHandler.php b/src/Server/WorkerHandler/HMRWorkerStartHandler.php index 74ca9923..dae7baf1 100644 --- a/src/Server/WorkerHandler/HMRWorkerStartHandler.php +++ b/src/Server/WorkerHandler/HMRWorkerStartHandler.php @@ -11,11 +11,13 @@ final class HMRWorkerStartHandler implements WorkerStartHandlerInterface { private $hmr; private $interval; + private $decorated; - public function __construct(HotModuleReloaderInterface $hmr, int $interval = 2000) + public function __construct(HotModuleReloaderInterface $hmr, int $interval = 2000, ?WorkerStartHandlerInterface $decorated = null) { $this->hmr = $hmr; $this->interval = $interval; + $this->decorated = $decorated; } /** @@ -23,6 +25,10 @@ public function __construct(HotModuleReloaderInterface $hmr, int $interval = 200 */ public function handle(Server $worker, int $workerId): void { + if ($this->decorated instanceof WorkerStartHandlerInterface) { + $this->decorated->handle($worker, $workerId); + } + if ($worker->taskworker) { return; } diff --git a/src/Server/WorkerHandler/NoOpWorkerStartHandler.php b/src/Server/WorkerHandler/NoOpWorkerStartHandler.php new file mode 100644 index 00000000..cacdc0e1 --- /dev/null +++ b/src/Server/WorkerHandler/NoOpWorkerStartHandler.php @@ -0,0 +1,18 @@ +taskworker) { - * echo "Hello from task worker process"; - * } - * ``` + * ```php + * if($server->taskworker) { + * echo "Hello from task worker process"; + * } + * ``` * * @param Server $worker * @param int $workerId diff --git a/src/Server/WorkerHandler/WorkerStopHandlerInterface.php b/src/Server/WorkerHandler/WorkerStopHandlerInterface.php new file mode 100644 index 00000000..59c9b72f --- /dev/null +++ b/src/Server/WorkerHandler/WorkerStopHandlerInterface.php @@ -0,0 +1,20 @@ +codeCoverage = $codeCoverage; @@ -60,7 +62,7 @@ public function start(?string $testName = null): void public function stop(): void { - if (!$this->enabled) { + if (!$this->enabled || $this->finished) { return; } @@ -69,7 +71,7 @@ public function stop(): void public function finish(?string $fileName = null, ?string $path = null): void { - if (!$this->enabled) { + if (!$this->enabled || $this->finished) { return; } @@ -78,6 +80,7 @@ public function finish(?string $fileName = null, ?string $path = null): void $this->writer->process($this->codeCoverage, \sprintf('%s/%s', $path ?? $this->coveragePath, $fileName)); $this->codeCoverage->clear(); + $this->finished = true; } private function initalizeCodeCoverage(ParameterBagInterface $parameterBag, CodeCoverage $codeCoverage): void diff --git a/tests/Fixtures/Symfony/CoverageBundle/CoverageBundle.php b/tests/Fixtures/Symfony/CoverageBundle/CoverageBundle.php index ace98e5a..f9179c52 100644 --- a/tests/Fixtures/Symfony/CoverageBundle/CoverageBundle.php +++ b/tests/Fixtures/Symfony/CoverageBundle/CoverageBundle.php @@ -4,12 +4,27 @@ namespace K911\Swoole\Tests\Fixtures\Symfony\CoverageBundle; +use K911\Swoole\Server\Configurator\WithServerShutdownHandler; +use K911\Swoole\Server\Configurator\WithServerStartHandler; +use K911\Swoole\Server\Configurator\WithWorkerStartHandler; +use K911\Swoole\Server\Configurator\WithWorkerStopHandler; +use K911\Swoole\Server\LifecycleHandler\ServerShutdownHandlerInterface; +use K911\Swoole\Server\LifecycleHandler\ServerStartHandlerInterface; +use K911\Swoole\Server\WorkerHandler\WorkerStartHandlerInterface; +use K911\Swoole\Server\WorkerHandler\WorkerStopHandlerInterface; use K911\Swoole\Tests\Fixtures\Symfony\CoverageBundle\Coverage\CodeCoverageManager; use K911\Swoole\Tests\Fixtures\Symfony\CoverageBundle\EventListeners\CoverageFinishOnConsoleTerminate; use K911\Swoole\Tests\Fixtures\Symfony\CoverageBundle\EventListeners\CoverageStartOnConsoleCommandEventListener; +use K911\Swoole\Tests\Fixtures\Symfony\CoverageBundle\ServerLifecycle\CoverageFinishOnServerShutdown; +use K911\Swoole\Tests\Fixtures\Symfony\CoverageBundle\ServerLifecycle\CoverageFinishOnServerWorkerStop; +use K911\Swoole\Tests\Fixtures\Symfony\CoverageBundle\ServerLifecycle\CoverageStartOnServerStart; +use K911\Swoole\Tests\Fixtures\Symfony\CoverageBundle\ServerLifecycle\CoverageStartOnServerWorkerStart; use SebastianBergmann\CodeCoverage\CodeCoverage; use SebastianBergmann\CodeCoverage\Report\PHP; +use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\Bundle\Bundle; class CoverageBundle extends Bundle @@ -21,6 +36,7 @@ public function build(ContainerBuilder $container): void $container->autowire(CodeCoverageManager::class); $this->registerSingleProcessCoverageFlow($container); + $this->registerServerCoverageFlow($container); } private function registerSingleProcessCoverageFlow(ContainerBuilder $container): void @@ -37,4 +53,47 @@ private function registerSingleProcessCoverageFlow(ContainerBuilder $container): ->setAutoconfigured(true) ->addTag('kernel.event_listener', ['event' => 'console.terminate']); } + + private function registerServerCoverageFlow(ContainerBuilder $container): void + { + $container->register(CoverageStartOnServerStart::class) + ->setPublic(false) + ->setAutowired(false) + ->setAutoconfigured(true) + ->addArgument(new Reference(CodeCoverageManager::class)); + + if ($container->has(ServerStartHandlerInterface::class)) { + $container->getDefinition(CoverageStartOnServerStart::class) + ->addArgument(new Reference(CoverageStartOnServerStart::class.'.inner')) + ->setDecoratedService(ServerStartHandlerInterface::class); + } else { + $container->setAlias(ServerStartHandlerInterface::class, new Alias(CoverageStartOnServerStart::class, false)); + } + + $container->register(CoverageFinishOnServerShutdown::class) + ->setPublic(false) + ->setAutowired(false) + ->setAutoconfigured(true) + ->addArgument(new Reference(CodeCoverageManager::class)); + + if ($container->has(ServerShutdownHandlerInterface::class)) { + $container->getDefinition(CoverageFinishOnServerShutdown::class) + ->addArgument(new Reference(CoverageFinishOnServerShutdown::class.'.inner')) + ->setDecoratedService(ServerShutdownHandlerInterface::class); + } else { + $container->setAlias(ServerShutdownHandlerInterface::class, new Alias(CoverageFinishOnServerShutdown::class, false)); + } + + $container->autowire(CoverageStartOnServerWorkerStart::class) + ->setPublic(false) + ->setAutoconfigured(true) + ->setArgument('$decorated', new Reference(CoverageStartOnServerWorkerStart::class.'.inner')) + ->setDecoratedService(WorkerStartHandlerInterface::class); + + $container->autowire(CoverageFinishOnServerWorkerStop::class) + ->setPublic(false) + ->setAutoconfigured(true) + ->setArgument('$decorated', new Reference(CoverageFinishOnServerWorkerStop::class.'.inner')) + ->setDecoratedService(WorkerStopHandlerInterface::class); + } } diff --git a/tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageFinishOnServerShutdown.php b/tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageFinishOnServerShutdown.php new file mode 100644 index 00000000..320ad450 --- /dev/null +++ b/tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageFinishOnServerShutdown.php @@ -0,0 +1,34 @@ +codeCoverageManager = $codeCoverageManager; + $this->decorated = $decorated; + } + + /** + * {@inheritdoc} + */ + public function handle(Server $server): void + { + if ($this->decorated instanceof ServerShutdownHandlerInterface) { + $this->decorated->handle($server); + } + + $this->codeCoverageManager->stop(); + $this->codeCoverageManager->finish('test_server'); + } +} diff --git a/tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageFinishOnServerWorkerStop.php b/tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageFinishOnServerWorkerStop.php new file mode 100644 index 00000000..dbf0128a --- /dev/null +++ b/tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageFinishOnServerWorkerStop.php @@ -0,0 +1,36 @@ +codeCoverageManager = $codeCoverageManager; + $this->decorated = $decorated; + } + + /** + * {@inheritdoc} + */ + public function handle(Server $worker, int $workerId): void + { + if ($this->decorated instanceof WorkerStopHandlerInterface) { + $this->decorated->handle($worker, $workerId); + } + + $this->codeCoverageManager->stop(); + $this->codeCoverageManager->finish(\sprintf('test_fd_%d', $workerId)); + + $this->codeCoverageManager->start(\sprintf('test_fd_%d', $workerId + 1)); + } +} diff --git a/tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageStartOnServerStart.php b/tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageStartOnServerStart.php new file mode 100644 index 00000000..5aceb1d2 --- /dev/null +++ b/tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageStartOnServerStart.php @@ -0,0 +1,33 @@ +codeCoverageManager = $codeCoverageManager; + $this->decorated = $decorated; + } + + /** + * {@inheritdoc} + */ + public function handle(Server $server): void + { + $this->codeCoverageManager->start('test_server'); + + if ($this->decorated instanceof ServerStartHandlerInterface) { + $this->decorated->handle($server); + } + } +} diff --git a/tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageStartOnServerWorkerStart.php b/tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageStartOnServerWorkerStart.php new file mode 100644 index 00000000..4da9e0a7 --- /dev/null +++ b/tests/Fixtures/Symfony/CoverageBundle/ServerLifecycle/CoverageStartOnServerWorkerStart.php @@ -0,0 +1,34 @@ +codeCoverageManager = $codeCoverageManager; + $this->decorated = $decorated; + } + + /** + * {@inheritdoc} + */ + public function handle(Server $worker, int $workerId): void + { + $this->codeCoverageManager->start(\sprintf('test_worker_%d', $workerId)); + + if ($this->decorated instanceof WorkerStopHandlerInterface) { + $this->decorated->handle($worker, $workerId); + } + } +} diff --git a/tests/Server/000-list-swoole.sh b/tests/Server/000-symfony.sh similarity index 82% rename from tests/Server/000-list-swoole.sh rename to tests/Server/000-symfony.sh index eefdfa58..a2500a17 100755 --- a/tests/Server/000-list-swoole.sh +++ b/tests/Server/000-symfony.sh @@ -2,4 +2,5 @@ cd "$( dirname "${BASH_SOURCE[0]}" )/../Fixtures/Symfony/app"; +./console cache:clear ./console list swoole diff --git a/tests/Server/003-start-replace-content-hmr.sh b/tests/Server/003-start-replace-content-hmr.sh index fc1c6735..967fb199 100755 --- a/tests/Server/003-start-replace-content-hmr.sh +++ b/tests/Server/003-start-replace-content-hmr.sh @@ -35,8 +35,8 @@ if [[ "$RESULT" != "$RESPONSE_TEXT_1" ]]; then fi -# Replace controller after 3 seconds with new response and wait 3 seconds -sleep 3; +# Replace controller after 2 seconds with new response and wait 5 seconds +sleep 2; CONTENTS=${TEMPLATE//"%REPLACE%"/$RESPONSE_TEXT_2} echo "$CONTENTS" > ${CONTROLLER_FILE}; touch ${CONTROLLER_FILE};