Skip to content

testintel: add missing test for Decoder3d.forward (created by linxwang) - #1097

Merged
Xiaoming-AMD merged 15 commits into
mainfrom
testintel/test/cand-c45125ae274daf08773e3cdcf1dd30c1
Sep 8, 2026
Merged

testintel: add missing test for Decoder3d.forward (created by linxwang)#1097
Xiaoming-AMD merged 15 commits into
mainfrom
testintel/test/cand-c45125ae274daf08773e3cdcf1dd30c1

Conversation

@jiagaoxiang

@jiagaoxiang jiagaoxiang commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Same-repo refile of #1084 so Primus-CI-TAS runs against AMD-AGI/Primus (fork PRs do not receive Docker Hub credentials, so build-docker / torch unit tests never ran).

This PR was created by linxwang via Test Gap Resolver.

The feat_cache/feat_idx chunked-decode bookkeeping in Decoder3d.forward (and the CausalConv3d/Resample/ResidualBlock layers it drives) had no test coverage, leaving index-drift or cache-shape regressions undetected.

This PR adds a focused unit test for Decoder3d.forward covering the real per-frame WanVAE_.decode protocol: a shared feat_cache, feat_idx reset per frame, first_chunk handling, cache-slot/sentinel transitions (the Rep marker), temporal output counts, and deterministic replay after a cache reset. A no-cache/single-shot decode is not a valid oracle here: Resample.upsample3d only performs temporal doubling inside the feat_cache-is-not-None branch, so there is no equivalent non-streaming decode path to compare against (see the review-thread discussion). This PR does not change production code.

TestIntel and others added 10 commits September 3, 2026 02:27
Candidate cand:c45125ae274daf08773e3cdcf1dd30c1 at 2f01706.
…ocol

Per review: import from vae2_2 (matches the actual Wan 2.2 production
decode path), drop the no-cache full-sequence comparisons (not a valid
oracle once temporal upsampling is involved), stop asserting 3 output
channels (direct Decoder3d output is 12 channels; RGB projection happens
later via unpatchify), and replay WanVAE_.decode's real per-frame
protocol: one latent frame per call, one feat_cache list reused across
calls, feat_idx reset to [0] per frame, first_chunk=True only on the
first call, feat_idx[0] == count_conv3d(decoder) after every call, and
the temporal-upsample cache slot's "Rep" sentinel transitioning to a
real tensor after the second chunk. Also verifies first-chunk vs later
chunk frame counts and deterministic replay after reinitializing the
cache.
The file was committed as a single Base64-encoded line, which is not
valid Python and raises a SyntaxError on collection (flagged by
Copilot review). Restore the decoded, unencoded test source; the
content itself is unchanged.
Copilot review (discussion_r3923254187): once a cache slot holds a
tensor, `slot == "Rep"` raises TypeError (PyTorch tensor vs str
comparison) instead of returning False. Gate the comparison on
isinstance(slot, str) so it only fires for the sentinel value itself.
code-lint (3.12) failed because black wants the two "Rep" sentinel
assertions collapsed to single lines (they fit within the line-length
limit once written as one line). Applying the exact reformatting from
the failed job's diff; no test logic changed.
Copilot flagged torch.equal as an overly strict/potentially flaky
comparison for floating-point tensors across backends. Switch the
replay-determinism assertion to torch.testing.assert_close with a
tight tolerance, which still catches real cache/index regressions
without risking backend-dependent bit-exactness flakiness.
The previous commit (fb3db94) that switched the replay-determinism
assertion from torch.equal to torch.testing.assert_close was pushed
with the file truncated mid-statement (missing the closing paren and
the rest of the file), which black's parser correctly rejected with
"Cannot parse: 113:0: EOF in multi-line statement" and failed
code-lint (3.12). Restoring the intended, complete call - verified
with `black --line-length 110 --target-version py38 --check` and
`py_compile` locally before pushing.
Copilot flagged that torch.manual_seed(0) in _make_decoder() mutates
global RNG state, which can leak into other tests run afterward in the
same process. Wrap the seeding in torch.random.fork_rng() so the global
generator is restored once the decoder's weights are initialized.
The previous commit's line-numbered patch miscounted and clobbered the
blank lines plus the `def _decode_streaming(...)` header between
_make_decoder and its docstring. Restoring them; no logic change.
The last two automated patches miscounted line ranges and clobbered
the _decode_streaming body (dropped outputs=[]/for-loop/feat_idx=[0]).
Restoring the full, correct file: the only intended change from the
last known-good version (40f74e0) is wrapping _make_decoder's seeding
in torch.random.fork_rng() per Copilot's global-RNG-mutation finding.
No other logic changed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new tests don’t yet assert the PR’s stated numerical parity goal (streaming vs single-shot) and include an RNG forking pattern that may unnecessarily initialize GPU runtimes in CPU-only unit tests.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds a new unit test module to exercise primus.backends.diffusion.models.wan.vae2_2.Decoder3d.forward under Wan2.2’s streaming/chunked decode protocol, focusing on feat_cache/feat_idx behavior and cache sentinel handling.

Changes:

  • Add a focused streaming decode test that validates cache-slot consumption (feat_idx) and the "Rep" sentinel lifecycle.
  • Add a determinism test to ensure repeated streaming decodes match after cache reset.
File summaries
File Description
tests/unit_tests/backends/diffusion/test_wan_vae_decoder.py New unit tests for Decoder3d.forward streaming/chunked decode cache bookkeeping and determinism.
Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tests/unit_tests/backends/diffusion/test_wan_vae_decoder.py
Comment on lines +97 to +99
out = torch.cat(outputs, dim=2)
assert out.shape == (1, 12, 5, 8, 8)
assert torch.isfinite(out).all()
Address Copilot review comment: torch.random.fork_rng() defaults to
forking all CUDA devices when available, which can trigger GPU
runtime initialization in this CPU-only unit test. Pass devices=[]
to fork only CPU RNG state.
Copilot AI review requested due to automatic review settings September 4, 2026 16:37

@jiagaoxiang jiagaoxiang left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestIntel PR Steward: Replies to Copilot's two findings inline.

# Seed only within a forked RNG scope so this doesn't mutate global RNG
# state and leak into other tests run later in the same process.
with torch.random.fork_rng(devices=[]):
torch.manual_seed(0)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestIntel PR Steward: Good catch — fixed in 8204f01 by passing devices=[] to torch.random.fork_rng(), so the seeding scope no longer forks CUDA RNG state on GPU-enabled runners.


out = torch.cat(outputs, dim=2)
assert out.shape == (1, 12, 5, 8, 8)
assert torch.isfinite(out).all()

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestIntel PR Steward: Declining this one after checking the architecture in vae2_2.py. Resample.forward (mode upsample3d) only performs the temporal 2x doubling inside the if feat_cache is not None: branch — when feat_cache is None it falls through to spatial-only upsampling and the time dimension is left unchanged. So there isn't a "single-shot, no-cache" decode path in this model that reproduces the streamed result: WanVAE_.decode itself always drives Decoder3d frame-by-frame through a shared feat_cache/feat_idx, exactly like this test does. A full-sequence call with a fresh cache and first_chunk=True also isn't equivalent, since the temporal-upsample branch only skips doubling on the very first cache slot access regardless of how many frames are in that call, so it wouldn't reproduce the expected 5-frame streamed output either. Given that, the cache-bookkeeping + determinism assertions already in this PR are the correct/only feasible way to pin down this behavior; a "no-cache reference" comparison would just be comparing against a different, non-equivalent computation. Happy to revisit if you see a concrete construction that sidesteps this.

@jiagaoxiang

Copy link
Copy Markdown
Collaborator Author

TestIntel PR Steward: Addressed the Copilot review from this run (new head 8204f01):

  • RNG fork: fixed — torch.random.fork_rng(devices=[]) now avoids forking CUDA RNG state in this CPU-only test.
  • Numeric streaming-vs-single-shot parity: declined, with rationale posted inline. Resample.forward's upsample3d path only performs temporal doubling inside the feat_cache is not None branch, so there's no "no-cache single-shot" decode in this model to compare against — WanVAE_.decode itself always drives the decoder frame-by-frame through a shared cache, which is exactly what the existing test already exercises.

This PR is still draft, so no merge action taken. Will keep following CI, reviews, and mergeability.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The new test suite does not currently assert that streaming/chunked decoding matches an equivalent larger-chunk/single-shot decode output as stated in the PR description, leaving the core acceptance criterion unverified.

Review details

Suppressed comments (1)

tests/unit_tests/backends/diffusion/test_wan_vae_decoder.py:99

  • The PR description says this adds coverage that streaming/chunked decode reproduces a non-streaming (larger-chunk / single-shot) decode output, but this test currently only checks shapes and cache sentinel state. Consider adding an output-equality assertion by decoding the same input in fewer (larger) chunks and comparing against the per-frame protocol output; that will catch feat_idx drift/cache-shape regressions even if internal sentinels change.
    out = torch.cat(outputs, dim=2)
    assert out.shape == (1, 12, 5, 8, 8)
    assert torch.isfinite(out).all()
  • Files reviewed: 1/1 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@WangLingxun

Copy link
Copy Markdown
Collaborator

The revised test code now follows the actual Wan 2.2 production protocol and no longer uses a no-cache/single-shot decode as an oracle, which is correct. However, the PR body still says the gap is that streaming/chunked decode does not verify reproduction of a single-shot output. That acceptance criterion is invalid for this decoder: the no-cache path does not execute the same temporal-upsample/cache protocol and can have different temporal behavior.

Please update the PR description before marking it ready. It should instead say that the test covers the real per-frame WanVAE_.decode protocol: shared feat_cache, feat_idx reset per frame, first_chunk handling, cache-slot/sentinel transitions, temporal output counts, and deterministic replay after cache reset. No test-code change is requested here.

@jiagaoxiang

Copy link
Copy Markdown
Collaborator Author

TestIntel PR Steward: Updated the PR description per @WangLingxun's comment — it no longer claims streaming/chunked decode reproduces a single-shot decode output (that oracle is invalid for this decoder, as discussed in the review thread). The description now states the actual coverage: the per-frame WanVAE_.decode protocol — shared feat_cache, feat_idx reset per frame, first_chunk handling, cache-slot/sentinel transitions, temporal output counts, and deterministic replay after a cache reset. No test-code change was requested or made.

Still draft, mergeable_state: behind main, and no failing checks. Will keep following.

@@ -0,0 +1,116 @@
##########################################################################
# Copyright (c) 2025, Advanced Micro Devices, Inc.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a newly added file, so the copyright year should be 2026.

Newly added file should use 2026, not 2025, per review comment.
Copilot AI review requested due to automatic review settings September 7, 2026 04:25
@jiagaoxiang

Copy link
Copy Markdown
Collaborator Author

TestIntel PR Steward: Addressed @WangLingxun's review comment on tests/unit_tests/backends/diffusion/test_wan_vae_decoder.py:2 — the copyright header now reads 2026 instead of 2025, since this is a newly added file. Pushed as bb2afaa.

No other new findings from this poll. Still draft; mergeable_state was behind main but no failing checks. Will keep following CI, reviews, and mergeability.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The new tests generate random inputs using the global RNG (outside the forked RNG scope), which can create cross-test RNG coupling and reduce reproducibility.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

tests/unit_tests/backends/diffusion/test_wan_vae_decoder.py:63

  • The test input x is generated with torch.randn outside a forked RNG scope, which advances the global RNG state and undermines the earlier goal of avoiding RNG leakage into other tests; it also makes failures harder to reproduce.

This issue also appears on line 103 of the same file.

tests/unit_tests/backends/diffusion/test_wan_vae_decoder.py:106

  • This test also constructs x with torch.randn using the global RNG state. Wrapping it in fork_rng (optionally with a fixed seed) avoids cross-test RNG coupling and improves reproducibility.
    decoder = _make_decoder()
    conv_num = count_conv3d(decoder)
    x = torch.randn(1, 4, 3, 4, 4)

  • Files reviewed: 1/1 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Copilot review (2026-09-07) flagged that both tests still draw the
decoder input `x` with torch.randn() outside the forked RNG scope,
which advances global RNG state and undermines the earlier fix that
scoped decoder-init seeding to a forked RNG. Wrap the input draws in
torch.random.fork_rng(devices=[]) with a fixed seed too, so runs are
reproducible and isolated from other tests in the same process.
Copilot AI review requested due to automatic review settings September 7, 2026 04:31
@jiagaoxiang

Copy link
Copy Markdown
Collaborator Author

TestIntel PR Steward: Addressed the latest Copilot review (pullrequestreview-5128066056, on bb2afaa): both tests still generated the decoder input x = torch.randn(1, 4, 3, 4, 4) outside the forked RNG scope, which advanced global RNG state despite the earlier fix that scoped decoder-init seeding. Fixed by factoring input generation into a _make_input() helper that draws x inside torch.random.fork_rng(devices=[]) with a fixed seed, mirroring the existing _make_decoder() pattern. Pushed as a7805ce.

Still draft, no failing checks (only the Read the Docs status check, which is green). Will keep following CI, reviews, and mergeability.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new tests’ RNG scoping uses fork_rng(devices=[]) with manual_seed(), which can still leak CUDA/ROCm RNG state on GPU-capable builds and undermine test isolation.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

tests/unit_tests/backends/diffusion/test_wan_vae_decoder.py:41

  • Same RNG-scoping issue as in _make_decoder(): manual_seed() affects CUDA RNG, but fork_rng(devices=[]) doesn't fork/restore it, so this can leak RNG state in GPU-capable test runs. Fork all CUDA devices here too.
    # Same rationale as _make_decoder: keep the random draw inside a forked,
    # seeded RNG scope so it is reproducible and never leaks into the global
    # RNG state that other tests may depend on.
    with torch.random.fork_rng(devices=[]):
        torch.manual_seed(1)
        return torch.randn(1, 4, 3, 4, 4)
  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Lite

# count_conv3d(decoder)).
# Seed only within a forked RNG scope so this doesn't mutate global RNG
# state and leak into other tests run later in the same process.
with torch.random.fork_rng(devices=[]):
Copilot flagged that torch.manual_seed() also seeds CUDA/ROCm RNG state
when a GPU runtime is available, so fork_rng(devices=[]) doesn't restore
that CUDA RNG afterward and can leak into other tests on GPU-capable
runners. Fork torch.cuda.device_count() devices when CUDA is available,
and no devices when it isn't (avoiding CUDA runtime init on CPU-only
runners, per the earlier Copilot comment this replaces).
Copilot AI review requested due to automatic review settings September 7, 2026 04:42
@jiagaoxiang

Copy link
Copy Markdown
Collaborator Author

TestIntel PR Steward: Addressed the latest Copilot review comment on tests/unit_tests/backends/diffusion/test_wan_vae_decoder.py:20 (discussion r3946578926, pullrequestreview-5128094912): torch.manual_seed() also seeds CUDA/ROCm RNG state when a GPU runtime is present, so fork_rng(devices=[]) wasn't restoring it afterward, which could leak into other tests on GPU-capable runners — as flagged, this also applied to the _make_input() call at line ~46.

Fixed by adding a _fork_rng_devices() helper that forks range(torch.cuda.device_count()) when CUDA is available and [] when it isn't, and using it in both _make_decoder() and _make_input(). This keeps the original intent (no CUDA runtime init forced on CPU-only runners, per the first Copilot comment on this same line) while properly scoping/restoring CUDA RNG state when a GPU is present. Pushed as 3bf4791.

Still draft, no failing checks (only the Read the Docs status, which is green). mergeable_state was behind main but that's a metadata field, not a conflict — GitHub reports mergeable: true. Will keep following CI, reviews, and mergeability.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The change is isolated to a new unit test file and the test logic aligns with the current WanVAE_.decode chunked-decode protocol without impacting production code.

Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Copilot AI review requested due to automatic review settings September 7, 2026 06:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review is ineligible. To be eligible to request a review, you need a paid Copilot license, or your organization must enable Copilot code review.

@WangLingxun
WangLingxun marked this pull request as ready for review September 7, 2026 06:07
@Xiaoming-AMD
Xiaoming-AMD merged commit 0114552 into main Sep 8, 2026
10 of 15 checks passed
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.

4 participants