aboutsummaryrefslogtreecommitdiff
path: root/tests/bench
diff options
context:
space:
mode:
authorBrett Parson <brett@brett-parson.com>2026-08-29 15:56:31 -0500
committerBrett Parson <brett@brett-parson.com>2026-08-29 15:56:31 -0500
commit965551532d684456b74baf960157c0c2cef7335d (patch)
tree1445afbc47730caa11d08fd84d9d65db132821f5 /tests/bench
parentd039a5d62a9ac6ababae70d9d96422ca59f2b2d4 (diff)
downloadminiroute-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.php107
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";