From 99e7c5f706ff24291ba2c1b8bc9b8fe3c14d615b Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Sat, 4 Nov 2023 13:44:51 -0400 Subject: [PATCH] feat: detail --- src/Blocks/NetworkDuration.php | 8 +- src/Blocks/ResponseDuration.php | 8 +- src/Blocks/ServerDuration.php | 8 +- src/Blocks/SuccessRate.php | 4 + src/ResultPrinters/Blocks.php | 35 +++--- src/ResultPrinters/Detail.php | 201 ++++++++++++++++++++++++++++++++ src/ResultPrinters/Progress.php | 10 +- src/Run.php | 5 + src/ValueObjects/Result.php | 16 +++ stress/local.php | 9 ++ stress/nunomaduro.com.php | 2 +- 11 files changed, 285 insertions(+), 21 deletions(-) create mode 100644 src/ResultPrinters/Detail.php create mode 100644 stress/local.php diff --git a/src/Blocks/NetworkDuration.php b/src/Blocks/NetworkDuration.php index dfb2c68..723b308 100644 --- a/src/Blocks/NetworkDuration.php +++ b/src/Blocks/NetworkDuration.php @@ -33,7 +33,13 @@ public function value(): string + $array['metrics']['http_req_duration']['values']['avg'] - $array['metrics']['http_req_waiting']['values']['avg']; - return sprintf('%4.2f ms', $duration); + $duration = sprintf('%4.2f ms', $duration); + + if (strlen($duration) < 9) { + $duration = str_pad($duration, 9, ' ', STR_PAD_BOTH); + } + + return $duration; } /** diff --git a/src/Blocks/ResponseDuration.php b/src/Blocks/ResponseDuration.php index 292ceb6..250f3df 100644 --- a/src/Blocks/ResponseDuration.php +++ b/src/Blocks/ResponseDuration.php @@ -32,7 +32,13 @@ public function value(): string + $array['metrics']['http_req_tls_handshaking']['values']['avg'] + $array['metrics']['http_req_duration']['values']['avg']; - return sprintf('%4.2f ms', $duration); + $duration = sprintf('%4.2f ms', $duration); + + if (strlen($duration) < 9) { + $duration = str_pad($duration, 9, ' ', STR_PAD_BOTH); + } + + return $duration; } /** diff --git a/src/Blocks/ServerDuration.php b/src/Blocks/ServerDuration.php index c650b08..6222821 100644 --- a/src/Blocks/ServerDuration.php +++ b/src/Blocks/ServerDuration.php @@ -30,7 +30,13 @@ public function value(): string $duration = $array['metrics']['http_req_waiting']['values']['avg']; - return sprintf('%4.2f ms', $duration); + $duration = sprintf('%4.2f ms', $duration); + + if (strlen($duration) < 9) { + $duration = str_pad($duration, 9, ' ', STR_PAD_BOTH); + } + + return $duration; } /** diff --git a/src/Blocks/SuccessRate.php b/src/Blocks/SuccessRate.php index 67752b2..a805804 100644 --- a/src/Blocks/SuccessRate.php +++ b/src/Blocks/SuccessRate.php @@ -30,6 +30,10 @@ public function value(): string $percentage = (float) ($array['metrics']['http_req_failed']['values']['fails'] * 100 / $array['metrics']['http_reqs']['values']['count']); + if ($percentage === 100.0) { + return '100 %'; + } + return sprintf('%4.1f %%', $percentage); } diff --git a/src/ResultPrinters/Blocks.php b/src/ResultPrinters/Blocks.php index 237498b..b15b6e3 100644 --- a/src/ResultPrinters/Blocks.php +++ b/src/ResultPrinters/Blocks.php @@ -32,7 +32,7 @@ private const SUCCESS_RATE = <<<'EOD' <%success_rate_color%>%block_size% - %success_rate% + %success_rate% <%success_rate_color%>%block_size% EOD; @@ -43,9 +43,9 @@ EOD; private const NETWORK_DURATION = <<<'EOD' - <%ttfb_color%>%block_size% - %ttfb% - <%ttfb_color%>%block_size% + <%network_color%>%block_size% + %network% + <%network_color%>%block_size% EOD; private const SERVER_DURATION = <<<'EOD' @@ -57,13 +57,9 @@ public function print(Result $result): void { render(<<<'HTML' -
- - 0-49 - - 50-89 - - 90-100 +
+ +
HTML); @@ -77,11 +73,11 @@ public function print(Result $result): void '%success_rate_color%' => "bg={$successRate->color()}", '%response_time%' => $responseDuration->value(), '%response_time_color%' => "bg={$responseDuration->color()}", - '%ttfb%' => $networkDuration->value(), - '%ttfb_color%' => "bg={$networkDuration->color()}", + '%network%' => $networkDuration->value(), + '%network_color%' => "bg={$networkDuration->color()}", '%server_duration%' => $serverDuration->value(), '%server_duration_color%' => "bg={$serverDuration->color()}", - '%subtitle%' => 'fg=white;options=bold;fg=white', + '%subtitle%' => 'options=bold', ]; $disposition = self::ALL_BLOCKS_IN_ROW; $spaceWidth = $this->getSpaceWidth(terminal()->width(), self::BLOCK_SIZE, $disposition); @@ -154,6 +150,17 @@ public function print(Result $result): void } $table->render(); + + render(<<<'HTML' +
+ + 0-49 + + 50-89 + + 90-100 +
+ HTML); } /** diff --git a/src/ResultPrinters/Detail.php b/src/ResultPrinters/Detail.php new file mode 100644 index 0000000..19ecc53 --- /dev/null +++ b/src/ResultPrinters/Detail.php @@ -0,0 +1,201 @@ +toArray()['metrics']; + + $this->overview($result, $metrics); + $this->server($metrics); + $this->network($metrics); + } + + /** + * Prints the overview's detail. + */ + private function overview(Result $result, array $metrics): void + { + render(<<<'HTML' +
+ + + + + + + 50-89 + + 90-100 + +
); + HTML); + + $testRunDuration = $result->testRunDuration(); + $testRunDuration = sprintf('%4.2f', $testRunDuration / 1000); + + $this->twoColumnDetail('Test Duration', "$testRunDuration s"); + + $requestsTotal = $metrics['http_reqs']['values']['count']; + $requestsRate = round($metrics['http_reqs']['values']['rate'], 2); + $testRunConcurrentUsers = $result->testRunConcurrentUsers(); + + $this->twoColumnDetail('Total Requests', <<$requestsRate reqs/second │ $testRunConcurrentUsers concurrent users + $requestsTotal requests + HTML); + + $successRate = (float) ($metrics['http_req_failed']['values']['fails'] * 100 / $metrics['http_reqs']['values']['count']); + $successRate = sprintf('%4.1f', $successRate); + $successRateColor = $metrics['http_req_failed']['values']['fails'] === $metrics['http_reqs']['values']['count'] + ? 'green' + : 'red'; + + $this->twoColumnDetail('Success Rate', <<$successRate % + HTML); + + $responseDuration = $metrics['http_req_connecting']['values']['avg'] + + $metrics['http_req_tls_handshaking']['values']['avg'] + + $metrics['http_req_duration']['values']['avg']; + + $responseDurationColor = match (true) { + $responseDuration < 200 => 'green', + $responseDuration < 400 => 'yellow', + default => 'red', + }; + + $responseDuration = sprintf('%4.2f', $responseDuration); + + $this->twoColumnDetail('Response Duration', <<$responseDuration ms + HTML); + } + + /** + * Prints the network's detail. + */ + private function network(array $metrics): void + { + $responseDuration = $metrics['http_req_connecting']['values']['avg'] + + $metrics['http_req_tls_handshaking']['values']['avg'] + + $metrics['http_req_duration']['values']['avg']; + + $responseNetworkDuration = $metrics['http_req_connecting']['values']['avg'] + + $metrics['http_req_tls_handshaking']['values']['avg'] + + $metrics['http_req_duration']['values']['avg'] + - $metrics['http_req_waiting']['values']['avg']; + $responseNetworkDurationPercentage = $responseDuration > 0.00 ? round($responseNetworkDuration * 100 / $responseDuration, 2) : 0.00; + $responseNetworkDurationColor = match (true) { + $responseNetworkDuration < 100 => 'green', + $responseNetworkDuration < 200 => 'yellow', + default => 'red', + }; + + $responseNetworkDuration = sprintf('%4.2f', $responseNetworkDuration); + + $this->twoColumnDetail(<<├Network + $responseNetworkDurationPercentage % + HTML, <<$responseNetworkDuration ms + HTML); + + $tlsHandshakingTime = $metrics['http_req_tls_handshaking']['values']['avg']; + $tlsHandshakingTime = sprintf('%4.2f', $tlsHandshakingTime); + + $dnsLookupTime = $metrics['http_req_connecting']['values']['avg']; + $dnsLookupTime = sprintf('%4.2f', $dnsLookupTime); + + $uploadTime = $metrics['http_req_sending']['values']['avg']; + $uploadTime = sprintf('%4.2f', $uploadTime); + + $downloadTime = $metrics['http_req_receiving']['values']['avg']; + $downloadTime = sprintf('%4.2f', $downloadTime); + + foreach ([ + 'TLS Handshaking' => "$tlsHandshakingTime ms", + 'DNS Lookup' => "$dnsLookupTime ms", + 'Upload' => "$uploadTime ms", + 'Download' => "$downloadTime ms", + ] as $title => $value) { + $this->twoColumnDetail('│ ├'.$title, $value); + } + } + + /** + * Prints the server's detail. + */ + private function server(array $metrics): void + { + $responseDuration = $metrics['http_req_connecting']['values']['avg'] + + $metrics['http_req_tls_handshaking']['values']['avg'] + + $metrics['http_req_duration']['values']['avg']; + $responseServerDuration = $metrics['http_req_waiting']['values']['avg']; + $responseServerDurationColor = match (true) { + $responseDuration < 100 => 'green', + $responseDuration < 200 => 'yellow', + default => 'red', + }; + + $responseServerDurationPercentage = $responseDuration > 0.00 ? round($responseServerDuration * 100 / $responseDuration, 2) : 0.00; + $responseServerDuration = sprintf('%4.2f', $responseServerDuration); + + $this->twoColumnDetail(<<├Server + $responseServerDurationPercentage % + HTML, <<$responseServerDuration ms + HTML); + } + + /** + * Prints a two column detail. + */ + private function twoColumnDetail(string $left, string $right): void + { + render(<< + + $left + + + + $right + +
+ HTML); + } +} diff --git a/src/ResultPrinters/Progress.php b/src/ResultPrinters/Progress.php index 61946ba..43be3e2 100644 --- a/src/ResultPrinters/Progress.php +++ b/src/ResultPrinters/Progress.php @@ -36,7 +36,7 @@ public function tail(): void
$date - Running stress test on $domain + Stress testing $domain
HTML); @@ -55,7 +55,11 @@ public function tail(): void $buffer = ''; $lastTime = null; while ($this->process->isRunning()) { - $output = $tail->getIncrementalOutput(); + $output = trim($tail->getIncrementalOutput()); + + if ($output === '') { + continue; + } $output = $buffer.$output; $buffer = ''; @@ -65,7 +69,7 @@ public function tail(): void foreach ($lines as $line) { if (str_starts_with($line, '{"metric":"http_req_duration","type":"Point"')) { /** @var array{data: array{time: string, value: float}}|null $point */ - $point = json_decode($line, true, 512, JSON_THROW_ON_ERROR); + $point = json_decode($line, true); if (is_array($point)) { $currentTime = substr($point['data']['time'], 0, 19); diff --git a/src/Run.php b/src/Run.php index 431f9e3..d11a044 100644 --- a/src/Run.php +++ b/src/Run.php @@ -6,6 +6,7 @@ use Pest\Exceptions\ShouldNotHappen; use Pest\Stressless\ResultPrinters\Blocks; +use Pest\Stressless\ResultPrinters\Detail; use Pest\Stressless\ResultPrinters\Progress; use Pest\Stressless\ValueObjects\Binary; use Pest\Stressless\ValueObjects\Result; @@ -72,6 +73,10 @@ public function start(): Result $blocks = new Blocks(); $blocks->print($result); + + $detail = new Detail(); + + $detail->print($result); } $session->clean(); diff --git a/src/ValueObjects/Result.php b/src/ValueObjects/Result.php index 5f8d1d1..8242d04 100644 --- a/src/ValueObjects/Result.php +++ b/src/ValueObjects/Result.php @@ -226,6 +226,22 @@ public function __construct( // } + /** + * Gets the test time in milliseconds. + */ + public function testRunDuration(): float + { + return $this->array['state']['testRunDurationMs']; + } + + /** + * Gets the test number of concurrent users. + */ + public function testRunConcurrentUsers(): int + { + return $this->array['metrics']['vus_max']['values']['max']; + } + /** * Gets the rate of successful requests, as a percentage, between "0.00" and "100.00". */ diff --git a/stress/local.php b/stress/local.php new file mode 100644 index 0000000..b678d6f --- /dev/null +++ b/stress/local.php @@ -0,0 +1,9 @@ +with(10)->concurrentRequests() + ->for(3)->seconds(); diff --git a/stress/nunomaduro.com.php b/stress/nunomaduro.com.php index 729f875..4dd3120 100644 --- a/stress/nunomaduro.com.php +++ b/stress/nunomaduro.com.php @@ -4,6 +4,6 @@ use function Pest\Stressless\stress; -stress('nunomaduro.com') +stress('pplware.com') ->with(2)->concurrentRequests() ->for(3)->seconds();