Skip to content

[6.x] Pest - #15140

Draft
jasonvarga wants to merge 23 commits into
6.xfrom
pest
Draft

[6.x] Pest#15140
jasonvarga wants to merge 23 commits into
6.xfrom
pest

Conversation

@jasonvarga

Copy link
Copy Markdown
Member

Adds Pest as the test runner for CI. This allows us to use time-based sharding.

jasonvarga and others added 4 commits August 8, 2026 01:00
The provider keyed data sets by URL, and an empty string is one of the
internal URLs, producing an empty-string key. PHPUnit rejects this when
listing tests, which broke any tooling relying on --list-tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
setThemeColors() writes resource_path('preferences.yaml'), and while
setUp() deleted it, nothing cleaned up after the last test. The final
test's colors survived into unrelated tests that assert on preferences,
making the suite order dependent. Delete the file in tearDown() too,
matching DefaultPreferencesTest and PrecedenceTest.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pest runs the existing PHPUnit test suite as-is, with no conversion to
Pest syntax. It's added for its sharding support, which PHPUnit has no
equivalent for.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The test run is around 90% of each job's wall clock, so sharding is
close to linear. Four shards takes the workflow from ~12.8 to ~4
minutes. Windows needs its own include entry per shard, since an
include that overrides a matrix key creates a standalone combination
rather than merging, and so wouldn't inherit the shard dimension.

shards.json holds recorded timings so shards are balanced by duration
rather than test count. Regenerate it with --update-shards.

Adds a php-tests-result job that aggregates every shard, so branch
protection can require one check instead of one per matrix cell.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jasonvarga
jasonvarga requested a review from a team as a code owner August 8, 2026 05:06
@jasonvarga
jasonvarga marked this pull request as draft August 8, 2026 05:10
jasonvarga and others added 17 commits August 8, 2026 10:03
NavItem::display() is a fluent get-or-set, so passing null meant "get".
Chaining create() off it returned the current display (null) instead of
the item, and pushed that null into the registered items.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Matches collections, taxonomies, asset containers, globals, roles, and
user groups, which all humanize the handle when no title is set. Without
it, a title-less form gave a null nav item display and broke the CP nav.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The skeleton at vendor/orchestra/testbench-core/laravel persists for the life of a
process, so anything a test writes there is visible to every test that follows it.
198 of our test files leave files behind, which is how a form file containing only
{} ends up crashing CoreNavTest - it only passes today because of lucky ordering,
and that luck runs out as soon as the suite is split across processes.

Snapshot the skeleton once per process and delete anything new after each test.
Directories the framework owns (bootstrap/cache, storage/framework/views and
friends) are left alone, both because deleting them breaks the app and because
walking them gets expensive. A process that starts against an already dirty
skeleton would bake that dirt into its snapshot, so testbench.yaml declares the
paths our tests are known to write and those get cleared before the first boot -
which also means vendor/bin/testbench package:purge-skeleton cleans up after us.
Making an addon with a fieldtype runs 'npm install' from the testbench app's base
path. That app has no package.json, so npm walks up and installs against ours,
rewriting package-lock.json in the working tree. The other commands that trigger
this already fake the process.
The users it makes have no id, so saving them writes tests/__fixtures__/users/.yaml
into the repo. Point the stache stores at the throwaway directory like the other
tests that save users do.
It was asserting a custom namespace blueprint could be edited without ever creating
one, and only passed because StoreCustomBlueprintTest had left one behind in the
testbench skeleton.
Glide only makes the directory when it actually processes an image, which by
definition never happens here. The test was relying on an earlier one in the file
having made it, so make it up front and keep the assertion that nothing lands in
it.
Temporary. So one shard failing doesn't cancel the rest of the matrix
and we see every remaining failure in a single run. Revert before merge.
Not for merging. Confirms two things on Windows CI:

1. PHP does not recurse into mklink /J junctions via RecursiveDirectoryIterator
   with FOLLOW_SYMLINKS, but does recurse into real symlinks.
2. deleteDirectory() cannot remove directory symlinks, so TemplateFolderTest
   leaks them into the directory TemplatesTest reuses.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Overrides hasChildren() to fall back to is_dir(), which is true for junctions.
Checks that recursion continues from the unresolved pathname so template names
keep their virtual prefix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The override had no effect, so log whether it is invoked at all, and try a
plain recursive scan that leans on is_dir() instead of SPL recursion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
parent::hasChildren() lstat()s the path first, and is_dir() appears to reuse
that cached result. Compare is_dir() before and after clearing the cache.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Its findings are applied in the commits that follow.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both classes shared tests/Fieldtypes/templates-test-tmp, and their teardown used
deleteDirectory(), which cannot remove a symlinked or junctioned directory on
Windows. TemplateFolderTest's symlinks therefore survived into TemplatesTest,
which reused them instead of the links it thought it had created.

Give each class its own directory and a teardown that can remove reparse points.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RecursiveDirectoryIterator treats a junction as a leaf, so a junctioned folder
under a view path was offered as a bogus template and the templates inside it
were invisible. Both the Templates and Template Folder fieldtypes were affected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
jasonvarga and others added 2 commits August 9, 2026 00:30
is_link() lstat's the path, and PHP caches an lstat result as the stat result
when it decides the path isn't a link. A junction isn't reported as a link, so
the following is_dir() was served that bogus mode and answered false, sending
junctions to unlink(), which cannot remove them on Windows.

Try unlink() then rmdir() and recurse into whatever survives both, so no stat
is involved. Cover it with a test, since a teardown leak is otherwise silent.

Co-Authored-By: Claude Opus 5 (1M context) <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.

1 participant