Loader cache: build-command tests, discovery timing, and runtime resilience#36
Open
rleeson wants to merge 6 commits into
Open
Loader cache: build-command tests, discovery timing, and runtime resilience#36rleeson wants to merge 6 commits into
rleeson wants to merge 6 commits into
Conversation
Testing: - Add tests/Bin/GenerateClassCacheTest: shells out to the actual tenup-framework-generate-class-cache script (as CI would) and covers the generate path, no-args usage/exit-1, a missing directory failing while valid ones still cache, and multi-directory runs. Backed by small, salient example loader directories under tests/examples/ (a real ModuleInterface module, a plain support class, and a second directory). - Cover the previously untested LoaderDebug branches: legacy_files() detection, every cache_state() variant, and the staleness "up to date" path. - Replace the vacuous assertGreaterThanOrEqual(0, ...) in test_it_can_find_classes_to_register with an assertion that the registered set is non-empty and contains only ModuleInterface implementations. Loader timing: - ModuleInitialization::init_classes() now times discovery (cache read or live scan) and class lookup (reflection/instantiation/registration) separately and records both on the loader debug record. - The debug page shows both timings per loader, and the staleness check reports how long its live discovery ran — so the cache's saving on a given site is measurable. New format_duration() helper picks a sensible unit. phpcs, phpstan (level 10) and phpunit all green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- format_duration(): reject non-finite input (INF/NAN) alongside non-positive, so a misbehaving contributor to the tenup_framework_debug_loaders filter can no longer render "inf s" / "nan s". Makes the "untrusted mixed input" contract in the docblock actually hold. Cover with negative, NAN and INF data cases. - FrameworkTestSetup: reset the ModuleInitialization singleton in setUp(). The trait-level @runTestsInSeparateProcesses annotation does not take effect (PHPUnit ignores it on a used trait; confirmed by suite wall-time and the author's explicit method-level @runInSeparateProcess on the two define() tests), so the singleton's accumulated $classes previously leaked between tests. Prevents order-dependent flakiness as more tests are added. phpcs, phpstan (level 10) and phpunit all green (57 tests / 148 assertions). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…repo & opcache Addresses a Fable re-review of the build-time-cache PR plus the timing addition, targeting mono-repo flexibility, genuine live-timing measurement, and security/perf. Resilience (security/perf): - get_classes() now catches a corrupt/truncated shipped cache (the cache is executable PHP loaded via `require`) and falls back to a live discovery instead of fataling every request until redeploy. Same spirit as #30: a bad cache must never take the site down. Covered by a new test. - record_loader_debug() bails on wp_doing_ajax(): is_admin() is also true on admin-ajax.php, and the debug page re-records on its own GET, so ajax recording was pure waste (often front-end triggered). Timing correctness: - Switch discovery/lookup timing from microtime(true) to the monotonic hrtime(true), so an NTP adjustment mid-request cannot skew a delta. - Rewrite the timing tests that were vacuous (assertGreaterThanOrEqual(0.0) passed the never-wired 0.0 default; the staleness string also matched the "took —." failure rendering). Now: the no-cache case asserts cache_used===false and strictly-positive live discovery time; a new cached case asserts cache_used===true and positive cache-read time; the staleness checks assert a real duration via regex. Mono-repo dedupe: - LoaderDebug::record() keeps one record per directory, so a repeated init_classes() for the same directory refreshes rather than duplicating a card. Docs: - Build-and-Deployment: opcache in-place-deploy staleness caveat, corrupt-cache fallback behaviour, and mixed-framework-version generation guidance for mono-repos. - Debugging: known limitations (per-request visibility, oldest-UI renders on mixed versions). CHANGELOG: fallback note; "identifier" -> "filename" wording fix. phpcs, phpstan (level 10) and phpunit all green (59 tests / 153 assertions). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cache detail now reads "Built <age> ago · <size> · <YYYY-MM-DD HH:MM:SS UTC>", using gmdate() so the build timestamp is unambiguous regardless of site or server timezone. Covered by a cache_detail() test asserting the size and trailing UTC segment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The class table's long file paths pushed it past the card's right border. The card now sizes to its content (min 60em, capped at the admin content width) so it widens when a class list is expanded, and long paths in the meta/class tables wrap (overflow-wrap: anywhere) so nothing spills once the width cap is reached. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description of the Change
Stacked on top of #35 (branches from
fix/issue-30-autoloader-cache-refresh); this PR targets that branch, notdevelop. It adds test coverage that #35 was missing, surfaces discovery timing on the loader debug page, and hardens the runtime read path.tenup-framework-generate-class-cacheend-to-end (generate, no-args usage, missing-directory-continues, multi-directory), backed by small example loader dirs undertests/examples/.legacy_files(), everycache_state()variant,format_duration(),cache_detail(), and the staleness up-to-date path; replaced a vacuousassertGreaterThanOrEqual(0, …)with a real contract assertion; reset theModuleInitializationsingleton between tests (the suite is not process-isolated).init_classes()records class-discovery time (a cache read when cached, a live scan otherwise) and class-lookup time using the monotonichrtime(). The debug page shows both per loader, and the staleness check reports how long its live scan took, so the cache's saving is measurable.Built <age> ago · <size> · <YYYY-MM-DD HH:MM:SS UTC>(UTC viagmdate(), timezone-independent).wp_doing_ajax(); records dedupe by directory.Refs #30
How to test the Change
composer install, thencomposer lint,composer static(PHPStan level 10), andcomposer test— all green (60 tests).composer generate-class-cache -- <dir>(or the shippedvendor/bin/tenup-framework-generate-class-cache <dir>); confirm aclass-loader-cache/class-loader-cache-v2.phpis written.wp-admin, visitadmin.php?page=tenup-framework-loaders: cached loaders show Cache in use with the build age/size/UTC time and a fast discovery time; uncached loaders show Uncached — live discovery. Use Check this cache for staleness to compare live vs. cached timing, and expand a classes loaded list to confirm the card grows to fit it.Also manually validated on a multi-package install where several plugins each require the framework: the build command cached real classes, the runtime read path matched a live scan, and the debug page aggregated all loaders.
Here's an example of the revised debug display with timing information:
Changelog Entry
Credits
Props @rleeson, @darylldoyle
Checklist: