Skip to content

Commit

Permalink
feat: inline stress command
Browse files Browse the repository at this point in the history
  • Loading branch information
nunomaduro committed Nov 5, 2023
1 parent 3dd3d03 commit cd354b8
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 67 deletions.
7 changes: 7 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,12 @@
"@test:types",
"@test:unit"
]
},
"extra": {
"pest": {
"plugins": [
"Pest\\Stressless\\Plugin"
]
}
}
}
24 changes: 22 additions & 2 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
*/
Expand All @@ -30,7 +36,7 @@ final class Factory
/**
* Creates a new instance of the run factory.
*
* @param array{stages: array<array{duration: string, target: int}>} $options
* @param array{stages: array<int, array{duration: string, target: int}>} $options
*/
private function __construct(private readonly string $url, private array $options)
{
Expand All @@ -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.
*/
Expand All @@ -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();
}
Expand Down Expand Up @@ -153,6 +169,10 @@ public function __destruct()
return;
}

if ($this->running) {
return;
}

$this->dd();
}
}
15 changes: 3 additions & 12 deletions src/Fluent/StageDurationOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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);
}
}
71 changes: 71 additions & 0 deletions src/Plugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

namespace Pest\Stressless;

use Pest\Contracts\Plugins\HandlesArguments;
use Pest\Plugins\Concerns\HandleArguments;
use Pest\Support\View;

/**
* @internal
*/
final class Plugin implements HandlesArguments
{
use HandleArguments;

/**
* Creates a new instance of the plugin.
*/
public function __construct()
{
// ..
}

/**
* {@inheritdoc}
*/
public function handleArguments(array $arguments): array
{
if (! array_key_exists(1, $arguments)) {
return $arguments;
}
if ($arguments[1] !== 'stress') {
return $arguments;
}
if (! array_key_exists(2, $arguments)) {
View::render('components.badge', [
'type' => '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();
}
}
38 changes: 12 additions & 26 deletions src/ResultPrinters/Detail.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,9 @@ public function print(Result $result): void
</div>
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'
<div class="mx-2 max-w-150 text-right flex text-gray">
Expand All @@ -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);

Expand Down Expand Up @@ -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'];
Expand Down Expand Up @@ -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'];
Expand Down
25 changes: 19 additions & 6 deletions src/ResultPrinters/Progress.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Pest\Stressless\ResultPrinters;

use JsonException;
use Pest\Stressless\Session;
use Pest\Stressless\ValueObjects\Url;
use Symfony\Component\Process\Process;
Expand Down Expand Up @@ -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(<<<HTML
<div class="flex mx-2 max-w-150">
<span class="text-gray">Stress testing <span class="text-cyan font-bold">$domain</span></span>
<span class="flex-1 ml-1 content-repeat-[―] text-gray"></span>
<span class="text-gray ml-1">{$concurrentRequests} concurrent requests for {$duration} seconds</span>
<span class="text-gray ml-1">$options</span>
</div>
HTML);

Expand Down Expand Up @@ -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);
Expand All @@ -83,7 +95,7 @@ public function tail(): void
}

$points[] = $point;
} else {
} catch (JsonException) {
$buffer .= $line;
}
}
Expand All @@ -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);

Expand All @@ -119,6 +130,8 @@ private function printCurrentPoints(array $points): void

$greenDots = str_repeat('', $greenDots);

$average = sprintf('%4.2f', $average);

render(<<<HTML
<div class="flex mx-2 max-w-150">
<span class="text-gray">
Expand Down
6 changes: 3 additions & 3 deletions src/Run.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
/**
* Creates a new run instance.
*
* @param array<string, mixed> $options
* @param array{stages: array{0: array{duration: string, target: int}}} $options
*/
public function __construct(private Url $url, private array $options, private bool $verbose)
{
Expand All @@ -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__),
Expand Down
9 changes: 0 additions & 9 deletions stress/local.php

This file was deleted.

9 changes: 0 additions & 9 deletions stress/nunomaduro.com.php

This file was deleted.

0 comments on commit cd354b8

Please sign in to comment.