From 965551532d684456b74baf960157c0c2cef7335d Mon Sep 17 00:00:00 2001 From: Brett Parson Date: Sat, 29 Aug 2026 15:56:31 -0500 Subject: 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). --- tests/bench/bench.php | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 tests/bench/bench.php (limited to 'tests/bench/bench.php') 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 @@ +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"; -- cgit v1.2.3