diff options
| author | Brett Parson <brett@brett-parson.com> | 2026-08-29 15:56:31 -0500 |
|---|---|---|
| committer | Brett Parson <brett@brett-parson.com> | 2026-08-29 15:56:31 -0500 |
| commit | 965551532d684456b74baf960157c0c2cef7335d (patch) | |
| tree | 1445afbc47730caa11d08fd84d9d65db132821f5 /tests/bench | |
| parent | d039a5d62a9ac6ababae70d9d96422ca59f2b2d4 (diff) | |
| download | miniroute-965551532d684456b74baf960157c0c2cef7335d.tar.gz miniroute-965551532d684456b74baf960157c0c2cef7335d.zip | |
Add regression and integration test suite
Port the stress-test findings into PHPUnit: realistic brett-parson.com
route model, known-issue baselines, router boundary cases, and a
standalone scaling benchmark (composer bench).
Diffstat (limited to 'tests/bench')
| -rw-r--r-- | tests/bench/bench.php | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/tests/bench/bench.php b/tests/bench/bench.php new file mode 100644 index 0000000..b7ae94e --- /dev/null +++ b/tests/bench/bench.php @@ -0,0 +1,107 @@ +<?php + +declare(strict_types=1); + +/** + * Miniroute benchmark — registration and dispatch scaling. + * + * Ported from the tmp-miniroute stress test (section 5) so the performance + * characteristics stay measurable as the kernel evolves. Run manually: + * + * composer bench # or: php tests/bench/bench.php + * + * Timings are intentionally NOT PHPUnit assertions — wall-clock numbers are + * too flaky for CI. The v1.0.0 release (miniroute-18h.3) adds hard CI gates + * (register < 5ms for the full table, dispatch < 0.1ms). + * + * Expected shape: registration is super-linear in v0.1.0 (usort per add, + * ~770ms at 600 routes) and collapses once miniroute-uhs.5 (lazy sort) + * lands. Dispatch scales linearly in route count. + */ + +require dirname(__DIR__) . '/bootstrap.php'; + +use BrettParson\MiniRoute\Attribute\Get; +use BrettParson\MiniRoute\Http\RequestInterface; +use BrettParson\MiniRoute\Http\ResponseInterface; +use BrettParson\MiniRoute\Routing\RouteNotFoundException; +use BrettParson\MiniRoute\Routing\Router; +use BrettParson\MiniRoute\Tests\Fixtures\AdminLoggingMiddleware; +use BrettParson\MiniRoute\Tests\Fixtures\CsrfLoggingMiddleware; +use BrettParson\MiniRoute\Tests\Fixtures\FakeResolver; +use BrettParson\MiniRoute\Tests\Fixtures\RealisticAdminController; +use BrettParson\MiniRoute\Tests\Fixtures\RealisticPublicController; +use BrettParson\MiniRoute\Tests\Fixtures\TestRequest; +use BrettParson\MiniRoute\Tests\Fixtures\TestResponse; + +/** @return ResponseInterface|null body, or null on no-match */ +function benchHit(Router $router, string $method, string $path): ?string +{ + try { + return $router->dispatch(new TestRequest($method, $path))->body(); + } catch (RouteNotFoundException) { + return null; + } +} + +function benchMs(float $start): float +{ + return (microtime(true) - $start) * 1000; +} + +echo "miniroute benchmark\n"; +echo str_repeat('-', 64) . "\n"; + +// ── Realistic table: registration + 2000 dispatches ──────────────────────── + +$resolver = new FakeResolver(); +$resolver->bind(RealisticPublicController::class, new RealisticPublicController()); +$resolver->bind(RealisticAdminController::class, new RealisticAdminController()); +$resolver->bind(AdminLoggingMiddleware::class, new AdminLoggingMiddleware(new ArrayObject())); +$resolver->bind(CsrfLoggingMiddleware::class, new CsrfLoggingMiddleware(new ArrayObject())); + +$real = new Router($resolver); +$real->group('/admin', [AdminLoggingMiddleware::class]); +$real->group('/admin', [CsrfLoggingMiddleware::class], ['POST']); + +$start = microtime(true); +$real->registerController(RealisticPublicController::class); +$real->registerController(RealisticAdminController::class); +$regMs = benchMs($start); + +$paths = ['/entries', '/entries/hello-world', '/notes/42', '/admin', '/admin/notes', '/admin/entries/preview', '/feed.xml', '/robots.txt', '/sitemap.xml']; +$start = microtime(true); +for ($i = 0; $i < 2000; $i++) { + benchHit($real, 'GET', $paths[$i % count($paths)]); +} +$dispatchMs = benchMs($start); + +printf("realistic (22 routes): register = %8.3fms | 2000 dispatches = %8.2fms (%0.5fms each)\n", $regMs, $dispatchMs, $dispatchMs / 2000); + +// ── Scaling: synthetic routes at 100 / 300 / 600 ─────────────────────────── + +printf("\n%-22s %-20s %s\n", 'routes', 'register', 'leaf dispatch (each)'); + +foreach ([100, 300, 600] as $n) { + $resolver = new FakeResolver(); + $bench = new Router($resolver); + + $start = microtime(true); + for ($i = 0; $i < $n; $i++) { + $class = 'BenchRoute' . $n . '_' . $i; + eval('final class ' . $class . ' { #[' . Get::class . '("/bench/' . $n . '/' . $i . '")] public function run(): ' . ResponseInterface::class . ' { return new ' . TestResponse::class . '(200, "b"); } }'); + $resolver->bind($class, new $class()); + $bench->registerController($class); + } + $regMs = benchMs($start); + + $start = microtime(true); + for ($i = 0; $i < 200; $i++) { + benchHit($bench, 'GET', '/bench/' . $n . '/' . ($n - 1)); + } + $dispatchMs = benchMs($start) / 200; + + printf("%-22d %8.1fms %s %0.5fms\n", $n, $regMs, str_repeat(' ', 11), $dispatchMs); +} + +echo str_repeat('-', 64) . "\n"; |
