Testing a MapLibre GL Major Upgrade Visually
A MapLibre GL major bump moves pixels everywhere, because the library is doing the drawing: symbol placement, text halos, line joins, tile fade timing and anti-aliasing can all change in one release. Compared against a stored baseline the result is uninterpretable, since it mixes the library change with everything that has drifted since that baseline was blessed. Captured as two cells in one run, it becomes a measurement — and the handful of groups that the release notes do not explain are exactly what the exercise exists to find.
This is a task within Map Library Version Upgrade Testing, under Web Map Visual Testing Fundamentals & Toolchains. The grouping it depends on is described in Triaging a Large Map Baseline Re-Bless.
Prerequisites
Step-by-step procedure
1. Capture both versions in one matrix run
strategy:
fail-fast: false
matrix:
include:
- { version: '4.7.1', major: '4' }
- { version: '5.0.0', major: '5' }
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm install maplibre-gl@$ --no-save
- run: npm run capture -- --version-key=maplibre-$
- uses: actions/upload-artifact@v4
with:
name: captures-$
path: out/captures
2. Diff the cells against each other
npx map-diff \
--baseline out/captures-4.7.1 \
--candidate out/captures-5.0.0 \
--report out/upgrade-report.json
Comparing the new version against the stored baseline instead is the mistake that makes upgrades take weeks: the result then contains the library change plus every environment and style drift since that baseline was blessed, and none of it can be separated.
3. Group the report and read it against the release notes
Read every version’s notes between current and target, not just the target’s. A change introduced in an intermediate release is still new to you, and it is the most common source of a group that appears unexplained.
4. Check the four MapLibre areas that move most between majors
Rather than working purely from the diff, look specifically at the places this library historically changes, because a small change in one of them can hide inside a large group.
- Symbol placement and collision — a label that used to render and now does not is a content change, not an aesthetic one, and it is easy to accept as part of a frame-wide placement group.
- Text halo and glyph rasterisation — halos change width or blend differently, which produces a frame-wide wash that masks smaller label changes underneath it.
- Line joins, caps and casings — visible mainly at high zoom, and often not mentioned in release notes.
- Raster fade and tile transition timing — if a fade duration default changes, captures taken through a settled gate are unaffected while captures taken on a timer are not, which is a useful signal that a scenario’s gate is weaker than it should be.
5. Narrow every regression candidate before filing it
A capture from your application with a proprietary style attached to an upstream issue is very unlikely to be actioned. The narrowing is also where harness bugs surface: a fixture that encoded an internal detail, a readiness predicate that called a method which has moved, a style property that was always invalid and used to be ignored.
6. Bless the new cell as one attributed batch
The annotation names the version pair, the release, the reviewer and each accepted group signature. This is what makes the churn attributable a year later, when someone asks why nine hundred baselines all changed on the same day.
7. Switch the default and keep the old cell for a cycle
The suite now compares against the new cell. The old one stays readable for one release cycle, which costs a little storage and removes the need for a restore if the upgrade is reverted.
Verification
Confirm the procedure worked before wiring it into a blocking gate:
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The two cells differ in scenarios that render no map at all | The new version changed an initialisation API, so the map never constructed and the capture shows an empty container | Check the console output from the failing cell; an upgrade that breaks construction is a code change, not a rendering one |
| Every capture in the new cell times out | An event the readiness predicate waits on is no longer emitted, or is emitted in a different order | Treat a suite-wide timeout as a signal about event semantics rather than infrastructure, and read the notes for event changes |
| The diff is enormous and groups into hundreds of rows | The signature buckets are too narrow for a frame-wide rendering change | Widen the coverage bucket and drop the colour-delta term, per Triaging a Large Map Baseline Re-Bless |
Frequently asked questions
Should the two cells run on the same runner or on parallel runners?
Parallel runners are fine provided the image is pinned by digest, which it should be anyway. What matters is that the image, the fixtures and the commit are identical; the machine is not an input once the GL backend is software-rendered. If the suite is not yet pinned to a digest, run both cells sequentially on one runner until it is, because otherwise the comparison contains the runner difference too.
How many majors can safely be skipped?
One at a time is much cheaper than several at once, because the diff from two majors is the union of both changes and the release notes have to be read together. If several have accumulated, upgrading through each in turn — accepting the churn at each step — is usually faster in total than triaging the combined diff, even though it means more re-blesses.
What if the application uses a fork or a pinned patch?
Then the fork’s own diff against upstream is a third axis, and the honest procedure is to evaluate upstream-to-upstream first, then apply the fork and evaluate again. Combining them produces a diff in which a fork conflict and a library regression are indistinguishable, and the fork is the thing least likely to have release notes.
Does the two-cell comparison replace the normal baseline gate?
No — it is an additional measurement taken on the upgrade branch. The normal gate still runs against the current default cell throughout, which is what keeps the branch honest about whether it broke anything unrelated to the upgrade. The two-cell diff answers a different question, and answering it does not remove the need for the first.
Related
- Up to Map Library Version Upgrade Testing, and the section Web Map Visual Testing Fundamentals & Toolchains.
- Triaging a Large Map Baseline Re-Bless — the grouping this procedure depends on.
- Baseline Management for Tile Servers — the keying model that makes a version a cell rather than an overwrite.
- Cross-Browser Baseline Matrix — the axis a library version multiplies with.