Skip to content

test(v3/windows): keep the WebView2 lifetime tests valid under checkptr (-race) - #6348

Merged
leaanthony merged 5 commits into
wailsapp:masterfrom
juggler-ai:test/webview2-lifetime-checkptr
Nov 2, 2026
Merged

leaanthony merged 5 commits into
wailsapp:masterfrom
juggler-ai:test/webview2-lifetime-checkptr

Conversation

@julianstorer

@julianstorer julianstorer commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Description

go test -race ./internal/webview2/pkg/edge/ on Windows aborts in the handler-lifetime tests added in #6184 and #6222. -race enables checkptr, which rejects turning a uintptr back into a Go pointer, and those tests rebuild handler pointers from the addresses they hold. CI runs the Windows tests without -race, so this has gone unnoticed, but anyone running the package with -race or -d=checkptr hits it.

This is a test-only change. The tests stand in for native code and now hold a handler the way native code does:

  • as its address (a uintptr, which keeps nothing alive) plus the vtable read while the handler was reachable, and call AddRef/Release through that;
  • match reference counts by comparing addresses, instead of rebuilding the pointer to look them up;
  • take COM out-parameters as typed callback arguments.

None of them converts an integer back into a pointer any more. Each test asserts the same things as before. The moveFocusRequested handler added in #6306 is covered the same way as the others.

There is no production change, so there is no changelog entry.

Type of change

  • Bug fix (non-breaking change which fixes an issue): tests only

How Has This Been Tested?

  • Windows: go test -race ./internal/webview2/pkg/edge/ aborts with a checkptr failure on master and passes with this change (GitHub Actions windows-latest, under -race); go test ./internal/webview2/pkg/edge/ passes as before.
  • macOS / Linux: not applicable (Windows-only package); GOOS=windows go vet and GOOS=windows go test -c pass for amd64, arm64 and 386.

Test Configuration

Built from source at master b6167d9 + this commit, Go 1.26.3.

Checklist:

  • My code follows the general coding style of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (none needed)
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Summary by CodeRabbit

  • Tests
    • Updated WebView2 tests to more reliably verify handler lifetimes and reference counting, including cases where handlers remain active after being abandoned.
    • Added coverage to verify that weakly referenced windows can be collected while related objects remain active.
    • Improved test coverage of controller interface responses and runtime error reporting, using typed interface results.

go test -race enables checkptr, which aborts the process when a uintptr
is converted back into a pointer into the Go heap or stack without a
pointer in the same expression to derive it from. The edge tests that
stand in for native code made that conversion: they held a handler as a
bare address, as WebView2 does, and turned it back into a pointer to
read its vtable or look up its reference count. The QueryInterface stubs
did the same with their out-parameter. Under -race the package dies with
"checkptr: pointer arithmetic result points to invalid allocation" in
TestLiveDelayedControllerCallbackAfterAbandon.

A native reference is now a comRef: the address as an integer, which
still does not keep the handler alive, and the vtable read from the
handler while it was reachable. AddRef and Release call through that
vtable, reference counts are matched by address, and the abandoned
controller test captures the handler's Invoke slot before dropping it.
The QueryInterface stubs take the out-parameter as a typed pointer,
as the handlers' own callbacks take this.

What the tests verify is unchanged, and no production code changes.
@github-actions github-actions Bot added Bug Something isn't working v3 labels Oct 25, 2026
@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 16ef013 and 32fb9f0.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between f650070 and 16ef013.

Files selected for processing: 1
  • v3/internal/webview2/pkg/edge/com_refs_test.go

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Walkthrough

WebView2 lifetime tests now use comRef to track handler references without rebuilding Go pointers from integer addresses. Two QueryInterface mocks now use typed output parameters instead of unsafe pointer conversions.

Changes

WebView2 test reference handling

Layer / File(s) Summary
COM reference tracking and abandoned-handler tests
v3/internal/webview2/pkg/edge/chromium_lifetime_test.go, v3/internal/webview2/pkg/edge/com_refs_test.go
comRef stores the handler address and vtable. Its methods access reference counts and call AddRef and Release through the vtable. The tests use comRef to inspect handler counts and test weakly referenced window collection. The abandoned-handler test captures and invokes the handler’s Invoke procedure.
Lifetime test adoption and typed mocks
v3/internal/webview2/pkg/edge/chromium_lifetime_live_test.go, v3/internal/webview2/pkg/edge/chromium_recovery_test.go, v3/internal/webview2/pkg/edge/chromium_runtime_errors_test.go
Live lifetime tests use comRef to track handlers and check reference counts. The recovery and runtime-error tests assign controllers through typed QueryInterface output parameters.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: leaanthony, taliesin-ai

Merge Risk: Minimal · previous review 16ef0

The lifetime tests retain handlers while native references remain outstanding. No merge-blocking risk is identified.

Pre-merge checks: Passed 5
Check name Status Explanation
Title check Passed The title clearly and concisely identifies the Windows WebView2 lifetime test fix for checkptr and race-enabled testing.
Description check Passed The description explains the failure, motivation, implementation, test scope, environment, and checklist status. It is sufficiently complete for a test-only change, although it does not provide a link…
Docstring Coverage Passed Docstring coverage is inconclusive: files from earlier reviews still need to be rechecked within the check's analysis limits. Not blocking.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description explains the failure, motivation, implementation, test scope, environment, and checklist status. It is sufficiently complete for a test-only change, although it does not provide a linked issue number or explicit dependency statement.

Finishing Touches
Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

A rabbit checks each handler’s hold,
And counts the refs without pointers old.
Typed outputs pass their value through,
Weak windows fade from every view.
The test burrow rests, its checks complete.

julianstorer and others added 2 commits November 2, 2026 09:26
…ion batches

The WebView2 lifetime tests prove that an abandoned Chromium no longer
retains its window by waiting for a weak pointer to the window to become
nil. Go does not promise that for a tiny pointer-free object: the tiny
allocator batches such objects into one allocation slot, and a weak
pointer into a batch stays non-nil while any object in it is referenced.

hostWindow was a single int, so without -race (which disables the tiny
allocator) it could share a slot with an allocation the replacement
Chromium keeps alive. TestLiveReplacementReleasesAbandonedHandlers then
failed intermittently with "abandoned Chromium retained its window".

hostWindow carries a pointer field, which keeps it out of any batch.
TestHostWindowIsCollectedBesideLiveObjects allocates windows beside live
pointer-free neighbours and requires each to be collected; it fails on
every run without the field.

No production code changes.
@leaanthony

Copy link
Copy Markdown
Member

Amazing! Thank you!

@leaanthony
leaanthony merged commit 2eddde8 into wailsapp:master Nov 2, 2026
32 checks passed
@julianstorer
julianstorer deleted the test/webview2-lifetime-checkptr branch November 3, 2026 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something isn't working v3

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants