Skip to content

perf: read each test file once when detecting browser tests - #305

Open
blakedeckard wants to merge 1 commit into
pestphp:5.xfrom
blakedeckard:claude/project-thread-cj3v7g
Open

blakedeckard wants to merge 1 commit into
pestphp:5.xfrom
blakedeckard:claude/project-thread-cj3v7g

Conversation

@blakedeckard

Copy link
Copy Markdown

What:

  • Bug Fix (performance)
  • New Feature

Description:

BrowserTestIdentifier::usesFunction() runs twice for every test as it is registered: once for visit and once for debug. Each call re-reads the whole test file with file() and tokenizes the test's source. A file with N tests is read 2N times. This happens in every project that has the plugin installed, even ones with no browser tests.

This PR:

  • Caches the lines of the file currently being registered. Tests are registered file by file, so caching one file is enough, and memory stays at one file's worth.
  • Skips tokenizing when the test's source doesn't contain the function name. It checks with mb_stripos(..., encoding: 'UTF-8'), so the result doesn't depend on mb_internal_encoding(). The token check that follows already requires that name to appear, compared case-insensitively, so detection results can't change.

The class is no longer readonly, because readonly classes can't declare static properties.

Benchmark: usesFunction() for visit and debug on 6,000 test closures across 60 files, with no browser tests, on PHP 8.4. The time is three runs each. Memory was measured with memory_get_usage() and memory_get_peak_usage() around the loop; the test source totals about 590 KB.

Time Memory retained after Peak memory during
Before 166 / 166 / 169 ms 0 KB 36 KB
After 9 / 9 / 9 ms 15 KB 51 KB

The saving grows with longer test files, because the old code re-read the whole file for each test.

Related:

#238 changes a different line in the same method (it accepts ( before visit). This PR applies cleanly on top of it, and the two can merge in either order.

BrowserTestIdentifier::usesFunction() runs twice for every test as it is
registered, once for "visit" and once for "debug". Each call re-read the
whole test file with file() and tokenized the test's source, so a file with
N tests was read 2N times, even in suites that never use the browser.

- Cache the lines of the test file currently being registered. Tests are
  registered file by file, so one file is enough and memory stays at one
  file's worth.
- Skip tokenizing when the test's source does not contain the function name
  at all. The token check below already requires that name to appear
  (case-insensitively), so this can't change the result. The search is done
  as UTF-8 so it doesn't depend on mb_internal_encoding().

The class is no longer readonly, since readonly classes can't declare static
properties.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants