diff --git a/composer.json b/composer.json index 4da3859..3d907b9 100644 --- a/composer.json +++ b/composer.json @@ -50,5 +50,12 @@ "@test:types", "@test:unit" ] + }, + "extra": { + "pest": { + "plugins": [ + "Pest\\Stressless\\Plugin" + ] + } } } diff --git a/src/Factory.php b/src/Factory.php index b8fbb1c..875d1e2 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -4,6 +4,7 @@ namespace Pest\Stressless; +use Pest\Stressless\Fluent\StageDurationOptions; use Pest\Stressless\Fluent\WithOptions; use Pest\Stressless\ValueObjects\Result; use Pest\Stressless\ValueObjects\Url; @@ -22,6 +23,11 @@ final class Factory */ private bool $verbose = false; + /** + * Weather or not the factory is running. + */ + private bool $running = false; + /** * The computed result, if any. */ @@ -30,7 +36,7 @@ final class Factory /** * Creates a new instance of the run factory. * - * @param array{stages: array} $options + * @param array{stages: array} $options */ private function __construct(private readonly string $url, private array $options) { @@ -53,6 +59,14 @@ public function with(int $number): WithOptions return new WithOptions($this, $number); } + /** + * Specifies that the stage should run for the given duration. + */ + public function for(int $duration): StageDurationOptions + { + return new StageDurationOptions($this, 1, $duration); + } + /** * Specifies that run should run with the given number of something to be determined. */ @@ -79,9 +93,11 @@ public function stage(int $requests, int $seconds): self */ public function run(): Result { + $this->running = true; + return $this->result = (new Run( new Url($this->url), - $this->options, + $this->options, // @phpstan-ignore-line $this->verbose, ))->start(); } @@ -153,6 +169,10 @@ public function __destruct() return; } + if ($this->running) { + return; + } + $this->dd(); } } diff --git a/src/Fluent/StageDurationOptions.php b/src/Fluent/StageDurationOptions.php index 62289ab..e834fa3 100644 --- a/src/Fluent/StageDurationOptions.php +++ b/src/Fluent/StageDurationOptions.php @@ -37,10 +37,7 @@ public function second(): Factory */ public function seconds(): Factory { - $this->factory->stage($this->requests, 0); - $this->factory->stage($this->requests, $this->duration); - - return $this->factory; + return $this->factory->stage($this->requests, $this->duration); } /** @@ -58,10 +55,7 @@ public function minute(): Factory */ public function minutes(): Factory { - $this->factory->stage($this->requests, 0); - $this->factory->stage($this->requests, $this->duration * 60); - - return $this->factory; + return $this->factory->stage($this->requests, $this->duration * 60); } /** @@ -79,9 +73,6 @@ public function hour(): Factory */ public function hours(): Factory { - $this->factory->stage($this->requests, 0); - $this->factory->stage($this->requests, $this->duration * 60 * 60); - - return $this->factory; + return $this->factory->stage($this->requests, $this->duration * 60 * 60); } } diff --git a/src/Plugin.php b/src/Plugin.php new file mode 100644 index 0000000..2f03622 --- /dev/null +++ b/src/Plugin.php @@ -0,0 +1,71 @@ + 'ERROR', + 'content' => 'Missing stress domain. Please provide a domain to stress.', + ]); + + exit(0); + } + + $domain = $arguments[2]; + + $duration = 10; + + foreach ($arguments as $argument) { + if (str_starts_with($argument, '--duration=')) { + $duration = (int) str_replace('--duration=', '', $argument); + } + } + + $concurrency = 1; + + foreach ($arguments as $argument) { + if (str_starts_with($argument, '--concurrency=')) { + $concurrency = (int) str_replace('--concurrency=', '', $argument); + } + } + + stress($domain) + ->with($concurrency) + ->concurrentRequests() + ->for($duration) + ->seconds() + ->dd(); + } +} diff --git a/src/ResultPrinters/Detail.php b/src/ResultPrinters/Detail.php index 46b17c7..790c217 100644 --- a/src/ResultPrinters/Detail.php +++ b/src/ResultPrinters/Detail.php @@ -25,29 +25,9 @@ public function print(Result $result): void HTML); - /** - * data_received..................: 22 kB 5.7 kB/s - * data_sent......................: 742 B 198 B/s - * http_req_blocked...............: avg=1.05s min=1.05s med=1.05s max=1.05s p(90)=1.05s p(95)=1.05s - * http_req_connecting............: avg=334.26ms min=334.26ms med=334.26ms max=334.26ms p(90)=334.26ms p(95)=334.26ms - * http_req_duration..............: avg=2.7s min=2.7s med=2.7s max=2.7s p(90)=2.7s p(95)=2.7s - * { expected_response:true }...: avg=2.7s min=2.7s med=2.7s max=2.7s p(90)=2.7s p(95)=2.7s - * http_req_failed................: 0.00% ✓ 0 ✗ 1 - * http_req_receiving.............: avg=112.41µs min=112.41µs med=112.41µs max=112.41µs p(90)=112.41µs p(95)=112.41µs - * http_req_sending...............: avg=294.48µs min=294.48µs med=294.48µs max=294.48µs p(90)=294.48µs p(95)=294.48µs - * http_req_tls_handshaking.......: avg=700.6ms min=700.6ms med=700.6ms max=700.6ms p(90)=700.6ms p(95)=700.6ms - * http_req_waiting...............: avg=2.7s min=2.7s med=2.7s max=2.7s p(90)=2.7s p(95)=2.7s - * http_reqs......................: 1 0.266167/s - * iteration_duration.............: avg=3.75s min=3.75s med=3.75s max=3.75s p(90)=3.75s p(95)=3.75s - * iterations.....................: 1 0.266167/s - * vus............................: 1 min=1 max=1 - * vus_max........................: 1 min=1 max=1 - */ - $metrics = $result->toArray()['metrics']; - - $this->overview($result, $metrics); - $this->server($metrics); - $this->network($metrics); + $this->overview($result); + $this->server($result); + $this->network($result); render(<<<'HTML'
@@ -70,8 +50,10 @@ public function print(Result $result): void /** * Prints the overview's detail. */ - private function overview(Result $result, array $metrics): void + private function overview(Result $result): void { + $metrics = $result->toArray()['metrics']; + $testRunDuration = $result->testRunDuration(); $testRunDuration = sprintf('%4.2f', $testRunDuration / 1000); @@ -118,8 +100,10 @@ private function overview(Result $result, array $metrics): void /** * Prints the network's detail. */ - private function network(array $metrics): void + private function network(Result $result): void { + $metrics = $result->toArray()['metrics']; + $responseDuration = $metrics['http_req_connecting']['values']['avg'] + $metrics['http_req_tls_handshaking']['values']['avg'] + $metrics['http_req_duration']['values']['avg']; @@ -171,8 +155,10 @@ private function network(array $metrics): void /** * Prints the server's detail. */ - private function server(array $metrics): void + private function server(Result $result): void { + $metrics = $result->toArray()['metrics']; + $responseDuration = $metrics['http_req_connecting']['values']['avg'] + $metrics['http_req_tls_handshaking']['values']['avg'] + $metrics['http_req_duration']['values']['avg']; diff --git a/src/ResultPrinters/Progress.php b/src/ResultPrinters/Progress.php index 332fc96..5de6fcf 100644 --- a/src/ResultPrinters/Progress.php +++ b/src/ResultPrinters/Progress.php @@ -4,6 +4,7 @@ namespace Pest\Stressless\ResultPrinters; +use JsonException; use Pest\Stressless\Session; use Pest\Stressless\ValueObjects\Url; use Symfony\Component\Process\Process; @@ -34,11 +35,21 @@ public function tail(): void $concurrentRequests = $this->session->concurrentRequests(); $duration = $this->session->duration(); + $options = 'for '.$duration.' second'; + + if ($duration > 1) { + $options .= 's'; + } + + if ($concurrentRequests > 1) { + $options = $concurrentRequests.' concurrent requests '.$options.' '; + } + render(<< Stress testing $domain - {$concurrentRequests} concurrent requests for {$duration} seconds + $options
HTML); @@ -70,10 +81,11 @@ 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); + try { + /** @var array{data: array{time: string, value: float}}|null $point */ + $point = json_decode($line, true, 512, JSON_THROW_ON_ERROR); + assert(is_array($point)); - if (is_array($point)) { $currentTime = substr($point['data']['time'], 0, 19); if ($lastTime !== $currentTime) { $this->printCurrentPoints($points); @@ -83,7 +95,7 @@ public function tail(): void } $points[] = $point; - } else { + } catch (JsonException) { $buffer .= $line; } } @@ -104,7 +116,6 @@ private function printCurrentPoints(array $points): void if ($points !== []) { $average = array_sum(array_map(fn ($point): float => $point['data']['value'], $points)) / count($points); - $average = round($average, 2); $time = substr($points[0]['data']['time'], 11, 8); @@ -119,6 +130,8 @@ private function printCurrentPoints(array $points): void $greenDots = str_repeat('▉', $greenDots); + $average = sprintf('%4.2f', $average); + render(<< diff --git a/src/Run.php b/src/Run.php index 0eed55d..0d3d1b1 100644 --- a/src/Run.php +++ b/src/Run.php @@ -21,7 +21,7 @@ /** * Creates a new run instance. * - * @param array $options + * @param array{stages: array{0: array{duration: string, target: int}}} $options */ public function __construct(private Url $url, private array $options, private bool $verbose) { @@ -33,8 +33,8 @@ public function __construct(private Url $url, private array $options, private bo */ public function start(): Result { - $concurrentRequests = (int) $this->options['stages'][1]['target']; - $duration = (int) $this->options['stages'][1]['duration']; + $concurrentRequests = $this->options['stages'][0]['target']; + $duration = (int) $this->options['stages'][0]['duration']; $session = new Session( $basePath = dirname(__DIR__), diff --git a/stress/local.php b/stress/local.php deleted file mode 100644 index b678d6f..0000000 --- a/stress/local.php +++ /dev/null @@ -1,9 +0,0 @@ -with(10)->concurrentRequests() - ->for(3)->seconds(); diff --git a/stress/nunomaduro.com.php b/stress/nunomaduro.com.php deleted file mode 100644 index 4dd3120..0000000 --- a/stress/nunomaduro.com.php +++ /dev/null @@ -1,9 +0,0 @@ -with(2)->concurrentRequests() - ->for(3)->seconds();