Java-to-Rust Migration Testing
Prove observable compatibility and Rust safety properties, not test volume. Treat
the complete source test suite as a non-negotiable compatibility floor: migrate
100% of its test cases without weakening inputs or assertions, copy every test
fixture/data/script byte-for-byte into the target repository, and make both
implementations produce the same per-case observable result. Build three explicit
test ledgers in this order:
Mandatory verification structure
- Require exact Java/Rust path parity; every
MISPLACED or missing file-map.csv target blocks completion.
- Treat E0583/E0761/E0405/E0425/E0659 and broken fixture paths as structural failures after file moves.
- Treat up to 500 physical lines as normal, review authored
.rs files from 501–800 lines for cohesion, and block files above 800 lines; split suites by contract or behavior family rather than arbitrary ranges.
- Allow focused unit tests in
#[cfg(test)] modules beside their implementation. Put public-boundary, cross-module, differential, host, load, and whole-project tests in tests/ or a non-published test/testkit crate.
- Run path audit, fmt, default/all-feature check, Clippy, complete tests, golden/live differential, migration audit, and the project coverage gate without using any one result as parity proof.
Read Directory parity verification and Migration verification SOP.
SOURCE_PARITY — map every source test/case and every source test asset; no
missing, blocked, or not-applicable row permits a completion claim.
RUST_OBLIGATION — test risks introduced by the Rust implementation and replacement components.
VALUE_ADD — add tests justified by uncovered behavior, plausible defects, incidents, mutation survivors, or hostile inputs.
SOURCE_PARITY has two mandatory sections: TEST_CASE and TEST_ASSET.
TEST_CASE preserves the source inputs, assertions, exception/error category,
ordering, state, side effects, and cleanup. TEST_ASSET preserves the source
relative path, target path, and SHA-256 of every copied fixture, script, corpus,
golden file, and data file. Coverage should rise because meaningful contracts are
exercised. A percentage is not the design input and 100% is not migration proof.
Scope and routing
Use this skill for migration-specific test planning, implementation, audit, and acceptance.
This is the Rust testing adapter of the shared target-language migration
profile. Keep source-test dispositions, exact-asset rules, full differential
outcomes, and completion semantics equivalent to zig-java-migration-testing
and future Kotlin/Swift adapters; vary only Rust test/build syntax and Rust risks.
Route:
- module/object/component migration planning to
rust-java-migration;
- general Rust unit, integration, doctest, and fixture mechanics to
rust-testing;
- async model checking to
rust-concurrency;
- profiling and benchmark construction to
rust-performance;
- unsafe and FFI validation to
rust-unsafe-ffi;
- web threat modeling to
rust-web-security.
An audit-only request does not authorize deleting or rewriting tests. Preserve existing behavior and unrelated dirty work. Treat heuristic findings as review candidates.
Required inputs
Resolve or mark unknown:
- pinned Java and Rust SHAs, dirty state, toolchains, profiles, features, targets, and generated-code boundaries;
- the authoritative workspace-topology decision and actual paths of the target
production and
<project>-test packages; do not assume a crates/ directory;
- the complete source test roots and runner configuration, including fixtures,
examples, scripts, test resources, parameter sources, dynamic factories, and
test-support code; the source repository defines the denominator and exclusions
cannot be introduced merely to make parity reach 100%;
- Java test runner and Rust test runner, including parameterized and dynamic test behavior;
- source coverage scope/tool/report and comparable Rust coverage scope/tool/report;
- available oracle: source only, executable Java tests, golden exporter, packaged artifact, live service, or standards suite;
- the current authoritative object ledger generated from the same Java/Rust
baselines; ignore historical-design appendices when reading current states;
- required hosts, real dependencies, concurrency/load model, security boundary, and rollback mechanism;
- deterministic normalization rules for time, identifiers, paths, map order, locale, float precision, and scheduling.
Never use an unpinned remote artifact as the compatibility oracle.
Evidence labels
Use these labels without promotion:
| Level |
Evidence |
Claim allowed |
V0_STATIC |
source/test inventory, call trace, no-stub scan |
structural disposition only |
V1_RUST_LOCAL |
Rust unit/integration/doc/compile tests |
Rust-local behavior passes |
V2_MIRRORED |
Rust test preserves a named Java test's inputs and assertions |
source test represented; not differential |
V3_GOLDEN_DIFF |
pinned Java-generated fixtures compared by Rust |
recorded outputs match; completion requires the full source denominator |
V4_LIVE_DIFF |
pinned Java and Rust execute identical cases |
recorded live behavior matches; completion requires every source case |
V5_HOST |
real framework/process/database/network/filesystem |
named integration boundary works |
V6_NONFUNCTIONAL |
model, mutation, property, fuzz, load, soak, security |
named non-functional claim holds |
V7_ROLLBACK |
gray rollout and rollback rehearsal |
stated recovery path works |
A copied test name, green Rust suite, or 100% line report does not prove V3_GOLDEN_DIFF.
Evidence levels never replace object states. A green V1_RUST_LOCAL suite does
not turn MISSING, MISPLACED, STUB, PARTIAL, or UNVERIFIED into
IMPLEMENTED. DEPENDENCY_REUSED additionally requires an exact pinned
upstream symbol and local integration test; PLATFORM_NA requires platform
evidence rather than a passing test that skips the behavior.
SOP
1. Freeze the verification baseline
Record exact SHAs, commands, tools, profiles, features, targets, test counts, ignored/flaky tests, coverage exclusions, and existing evidence artifacts. Record Java and Rust coverage separately before comparing them.
Read the current fact region of the authoritative object table before designing
tests. Stop at historical-design-appendix-start; old appendix statuses are
context only. Record counts for all strict object states. If any incomplete
state remains, the acceptance report must say “module migration incomplete”
regardless of test results.
Use CodeGraph when indexed to trace each source test through its production entry, collaborators, side effects, and Rust counterpart. Text similarity is insufficient for overloaded methods, registries, interceptors, dynamic dispatch, cleanup, and async paths.
2. Inventory source and target tests
Create one row per Java test method and one row per concrete parameterized,
repeated, template, or dynamic case. Include disabled tests; a disabled source
test remains a required migrated test and its disabled reason is preserved
separately. Create one TEST_ASSET row per file in every source test resource,
fixture, script, corpus, and data root. Copy source assets without editing them;
place target-generated or normalized derivatives beside the immutable copy.
Use dispositions:
| Disposition |
Meaning |
MIRRORED |
same contract represented in one Rust test |
ADAPTED |
same observable contract using a Rust-native fixture/oracle |
SPLIT |
one Java test becomes several focused Rust tests |
MERGED_APPROVED |
several Java tests share one parameterized Rust test without losing cases/assertions |
NOT_APPLICABLE |
JVM-only claim recorded for analysis; blocks 100% lossless source-test completion |
BLOCKED |
named dependency or oracle prevents the test; blocks completion |
MISSING |
no Rust implementation; blocks completion |
MIRRORED, ADAPTED, SPLIT, and MERGED_APPROVED are complete only when
every source case remains identifiable, all preservation flags are true, every
target test exists, and golden/live comparison records MATCH. ADAPTED permits
Rust-native harness mechanics, not a weaker contract. SPLIT and
MERGED_APPROVED may reorganize tests but may not remove a case or assertion.
Do not map by test name alone.
Resolve SKILL_DIR to this skill directory and run from the migration repository:
python3 "$SKILL_DIR/scripts/audit_migration_tests.py" \
--java-root ../java-project/source-module \
--rust-root <selected-target-crate-or-workspace> \
--object-ledger docs/source-module/对象级对照表.md \
--parity-manifest docs/source-module/source-test-parity.json \
--java-test-assets-root src/test/resources \
--fail-on-incomplete
The report inventories tests, flags weak signals, verifies every source-test
manifest row, checks target test files, hashes exact asset copies, and refuses a
completion gate for any object, test, asset, run, or differential gap. Additional
non-standard asset roots must be passed with repeated
--java-test-assets-root. The script validates recorded preservation evidence;
it cannot infer semantic mappings or authorize deletion. It warns on authored
files above 500 physical lines and blocks those above 800; inline unit tests are
valid Rust organization and are not reported as structural violations.
3. Implement the SOURCE_PARITY ledger
For every source row:
- Trace the protected Java contract and production call path.
- Port the fixture and assertions, not merely the method name.
- Preserve valid, boundary, failure, state-transition, and side-effect cases.
- Run the source and target suites from pinned baselines and retain raw per-case
results. A mirror without Java output is useful during implementation but does
not satisfy the final source-parity gate.
- Compare each source case through a pinned golden or live differential oracle;
require
MATCH, zero harness failures, and zero not-run cases.
- Record the Rust test, preservation flags, evidence, commands, artifacts, and
any divergence.
Missing, blocked, or not-applicable source tests remain visible and block a
migration-complete conclusion. Source tests are a compatibility floor, not the
complete Rust plan.
Whole-project migration test module
For every repository/product-level migration completion claim, create one
non-published workspace package as the whole-project acceptance authority. It
is especially important for multiple crates, bindings/adapters, or a source
system/templatesuite. Name it
<project>-test by default; keep the final directory name and Cargo package name
identical, set publish = false, and run it explicitly in CI. Its parent path
follows the recorded workspace topology: a small root-flat workspace uses
<project>-test/; a hybrid may keep it at root or in a real test family; a
contained workspace may use crates/<project>-test/ or
crates/tests/<project>-test/. Local tests inside production
crates prove their own parser/type/API/binding behavior; they do not replace the
whole-project module's source-suite replay, cross-crate workflows, real copied
assets, golden/live differential comparison, or aggregate result artifact.
For FreeMarker use this boundary:
freemarker/ # Rust engine local unit/integration tests
freemarker-pyo3/ # Python binding local/packaging tests
freemarker-test/ # complete Java templatesuite + cross-component acceptance
The freemarker-test package must be a workspace member, depend on public
surfaces under test, and remain outside crates.io publication. Its complete gate
is every source case MATCH, with no threshold pass count, skipped capability,
or ignored case. Read Migration verification SOP
for naming, ownership, layout, and CI rules.
The test package's semantic role is mandatory; one physical parent directory
is not. Never relocate it solely to imitate the Java test-module tree or a
generic Rust example.
Do not use one test per object as a substitute for one real file per source
object. A test that reaches a re-export, compatibility facade, or merged type
does not cure MISPLACED/MISSING. Tests validate semantics only after the
layout and object boundary are factually present.
Treat 500 physical lines as the suite-cohesion review threshold and 800 as the
authored-file blocker. Split by contract, behavior family, fixture family,
adapter, or verification layer while preserving every source case ID and
assertion. Keep compact private-behavior unit tests in #[cfg(test)] modules;
put reusable integration support in tests/common/ or a dedicated testkit crate.
Enable clippy::too_many_lines to review functions over its 100-line default.
4. Implement the RUST_OBLIGATION ledger
Add applicable tests created by the target design:
| Rust mechanism |
Mandatory questions |
Ownership / Drop |
exactly-once cleanup, partial initialization, early return, panic/cancel boundary |
| Async / Tokio |
cancellation, timeout ownership, orphan tasks, shutdown, bounded queues, slow consumers |
| Shared state |
atomicity, poison/recovery policy, no lock held across .await, deadlock/interleaving risks |
Send / Sync |
intended compile contract and supported executor/thread boundary |
| Typed errors |
exact variant, context, retryability, Error::source, public redaction surfaces |
| serde / wire/storage |
rename/default/unknown fields, round trip, bytes, compatibility window |
| Traits / registries |
selection, duplicate registration, missing provider, dynamic dispatch |
| Procedural macros |
compile-pass/fail, generics, visibility, renamed dependencies, diagnostics |
| Feature/platform/MSRV |
supported combinations compile and behave as promised |
| Unsafe / FFI |
invariants, Miri/sanitizer where applicable, ownership across boundary |
| Replacement crate |
risky semantic path, lifecycle, real dependency, upgrade/rollback boundary |
| Framework adapters |
one shared contract suite plus adapter-native routing/body/service behavior |
These tests need not exist in Java because they protect the Rust implementation's correctness.
For a DEPENDENCY_REUSED row, execute the local adapter against the exact
declared upstream symbol/version or commit. The dependency's own unit tests,
documentation examples, or a similar capability name are not local integration
evidence. Assert the source contract's ordering, errors, lifecycle, cancellation,
and metadata that cross the adapter.
5. Implement the VALUE_ADD ledger
Add a test only when it has a reason such as:
- missing boundary or branch revealed by coverage;
- surviving meaningful mutant;
- property/invariant over a broad input space;
- malformed or hostile input found by fuzzing;
- production incident or bug regression;
- concurrency interleaving, load, resource leak, or long-soak risk;
- compatibility behavior absent from the Java suite but required by docs/protocol;
- real-host or rollback behavior that mocks cannot prove.
For each test, record the concrete bug it should catch. Prefer a small parameter/branch matrix over “one test per type/token/method” count targets.
6. Use observable assertions
Strong tests observe exact public behavior:
- value, bytes, order, state transition, call count, side effect, resource release, or typed error;
Display, Debug, serialized error report, logs/tracing fields, transport body, and Error::source() independently where exposed;
- cache hit/miss/eviction metrics or backend-call counts when claiming cache behavior;
- body/trailer/backpressure and disconnect cleanup when claiming streaming lifecycle.
Weak patterns requiring review:
let _ = result, unused parsed AST, or a test that accepts success and failure;
note that during cleanup, converting a genuinely dropped Result to
let _ = expr; preserves semantics and is not itself weak evidence — it only
becomes weak when let _ = is the sole observation of the behavior under test;
- parse-only checks named as semantic/evaluation tests;
- only
is_ok() or is_err() when value/error category matters;
- a cache test that observes only final values;
- tests of fixture
Clone, Debug, constants, constructors, or type existence without a public contract;
- duplicate “coverage burst” tests with no distinct risk;
- tests that merely prove
todo!() or unimplemented!() panics.
Read Vernal case study and test-value rubric before cleanup.
7. Build differential fixtures
Use a versioned case format such as JSON Lines:
{"schema":1,"case_id":"empty-name","input":{"name":""},"expected":{"kind":"error","code":"INVALID_NAME"}}
Retain Java and Rust raw outputs separately. Pin exporter and implementation SHAs, seeds, environment, and normalizer version. Compare success values, error categories, side effects, order, and wire bytes as applicable. Do not normalize unexpected fields away.
Differential comparison is a full source-suite gate, not a representative
sample. The final manifest must record one MATCH per concrete source case and
suite-level Java, Rust, and differential runs with PASS, zero mismatches, zero
harness failures, and zero not-run cases. A temporary subset may guide
development but must be labeled partial and cannot satisfy completion.
8. Reuse conformance suites for adapters
Put shared assertions and failure fixtures in a testkit. Each adapter must provide real native observations. Run the same identity, scope, lifecycle, error, cancellation, streaming, and cleanup contracts for every implementation, then add adapter-native tests.
Prefer event-driven synchronization and bounded timeouts over fixed sleeps. Assert the resource is open while a stream is active and closed after completion, disconnect, timeout, cancellation, or panic.
9. Compare coverage without gaming it
Coverage comparison is valid only when scopes are documented and reasonably comparable:
- production files/modules included;
- generated code and approved exclusions;
- line/region/branch semantics of each tool;
- features, targets, test types, and profile;
- source and target baselines.
Acceptance order:
- every source test and concrete case has a lossless target implementation;
- every source test asset has a byte-identical target copy verified by SHA-256;
- both complete suites pass and every source case has differential
MATCH;
- every high-risk source contract has adequate evidence;
- every applicable Rust obligation is tested;
- meaningful mutants and uncovered branches are reviewed;
- comparable Rust coverage exceeds the Java baseline if the project requires it.
Do not weaken assertions, duplicate tests, exclude difficult files, or add trivial getters to reach a number. A user-mandated 100% gate may be enforced, but report what it proves and what it does not.
10. Audit test value before removing or merging
Score each test manually:
| Question |
Pass signal |
| Traceable? |
source test, contract, risk, bug, or incident is named |
| Observable? |
assertion checks externally meaningful behavior |
| Mutation-sensitive? |
a plausible defect would make it fail |
| Production path? |
exercises real production logic at the right boundary |
| Deterministic? |
synchronization/seed/environment are controlled |
| Distinct? |
adds a branch, case, platform, failure, or invariant |
| Right level? |
unit/integration/host/load test matches the boundary |
Classify KEEP, IMPROVE, MERGE, or REMOVE_PROPOSED; require review before deletion. Coverage loss alone may justify replacement but not retention of a meaningless assertion.
11. Run layered gates
Run applicable gates from cheapest to most diagnostic:
cargo fmt --all -- --check
cargo check --workspace --all-targets --all-features
cargo test --workspace --all-features
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --doc --workspace
Then run targeted compile-fail, platform/MSRV, differential, real-dependency, real-host, model/Loom, Miri/sanitizer, mutation, property/fuzz, load/soak, security, and rollback gates. Record exact command, environment, result, and artifact.
Run every gate at the workspace MSRV: a default rustc older than the workspace
rust-version fails with rustc X is not supported by the following packages
rather than testing your code — use rustup run <ver> cargo ... and never
silently lower the manifest. Use --keep-going so one failing crate does not
hide the remaining errors (errors surface in layers; fix the first layer and
re-run). Require both default and --all-features check runs to reach zero
warnings: feature-gated crates and code paths only compile under
--all-features, and a check gate with warnings must be reported as a gap, not
a pass.
Use the project-specific coverage command. For mutation candidates:
"$SKILL_DIR/scripts/run_mutation_test.sh" <selected-target-crate>
Interpret survivors individually; do not impose one universal mutation score.
12. Report acceptance honestly
Report separately:
- source-test lossless implementation and per-case differential coverage;
- source behavior evidence by
V0–V4;
- Rust-obligation completion;
- value-add tests and defect/risk rationale;
- line/branch/region coverage with comparable scope;
- mutation, property, fuzz, concurrency, load, security, host, and rollback evidence;
- failures, flaky/ignored tests, stubs, exclusions, missing targets, and external boundaries.
- current object-state counts and the explicit list of all
MISSING/MISPLACED/STUB/PARTIAL/UNVERIFIED blockers;
- compiler and Clippy warnings, ignored/doctests, feature/target gaps, and tests
that were not executed;
Red lines
- Do not call mirrored tests differential.
- Do not replace source-test disposition with raw test-count parity.
- Do not exclude, disable, weaken, merge away, or mark a source test not
applicable to manufacture 100% parity.
- Do not rewrite copied source fixtures or data in place; retain a byte-identical
copy and generate target-specific derivatives separately.
- Do not accept two independently green suites as result parity; require
per-case Java/Rust golden or live comparison.
- Do not substitute local tests in production or binding crates for the
non-published
<project>-test whole-project acceptance package, and do not
use pass thresholds, skips, or ignored cases as its completion gate.
- Do not hardcode
<project>-test under crates/ or at repository root without
checking the migration roadmap's project-driven workspace topology.
- Do not write tests solely to increase coverage or file count.
- Do not keep an authored
.rs file above 800 physical lines; review every file above 500 and split weakly cohesive suites.
- Do not compress code, remove useful comments, or split arbitrary line ranges to evade a size gate.
- Do not place integration, differential, host, load, or whole-project suites in production modules; focused inline unit tests remain valid.
- Do not call parse success semantic equivalence.
- Do not accept generic
is_err() when the error contract is observable.
- Do not auto-delete tests from names, body length, or heuristics.
- Do not hide mismatches through broad normalization or snapshot regeneration.
- Do not replace real dependency/host tests with mocks when the contract crosses that boundary.
- Do not use fixed sleeps as the only async coordination mechanism.
- Do not count production stubs as implemented because their tests compile.
- Do not mark a module complete from Cargo/JUnit test totals, coverage, or a
green CI job while the authoritative object ledger has any incomplete state.
- Do not run a completion gate without a source-test parity manifest. Missing
test mappings, incomplete parameter expansion, false preservation flags,
missing target files, asset hash differences, non-
MATCH results, harness
failures, or not-run cases all block completion.
- Do not read completion states from a historical appendix or stale duplicate
migration document.
- Do not call dependency reuse verified from upstream tests or semantic
similarity; require the exact dependency symbol and a local integration test.
- Do not report the check gate as passed while
cargo check (default or
--all-features) still emits warnings, or when the run used an unsupported
toolchain instead of the workspace MSRV.
On-demand resources
- Migration verification SOP
- Directory parity verification and post-migration checks
- Cross-language migration profile
- Test categories
- Test-value rubric
- Vernal positive and negative examples
- Migration test ledger template
- Machine-readable source-test parity manifest
- Worked audit report
scripts/audit_migration_tests.py — Java/Rust test inventory and weak-signal audit
scripts/run_mutation_test.sh — mutation-test wrapper
Completion criteria
- Every Java test and every concrete parameterized/dynamic/repeated case in the
frozen source test roots has a target-language implementation and source trace;
the source denominator is 100% accounted for without completion exclusions.
- Every source fixture, resource, script, corpus, golden file, and test data file
has a byte-identical copy in the target repository with matching SHA-256.
- Every mapped case attests preservation of contract, inputs, assertions,
fixture state, and cleanup; split/merged/adapted forms lose nothing.
- The complete pinned Java and Rust suites pass, and the complete golden/live
differential report contains only
MATCH with zero harness failures and zero
not-run cases.
- The
<project>-test package is a workspace member with publish = false,
lives at the path selected by the recorded workspace topology, exercises
public component surfaces, and owns the complete suite/differential
command plus aggregate artifact; local crate tests remain subsystem evidence.
- The authoritative current object ledger was checked, its baselines match the
test run, and no incomplete object state was hidden by the test summary.
- Every high-risk contract has an oracle, evidence label, and result.
- Applicable Rust ownership, async, error, serialization, feature, adapter, and unsafe obligations are tested.
- Added tests name a distinct risk or plausible defect.
- Every authored
.rs file is at most 800 physical lines; files above 500 and functions above the Clippy 100-line signal have recorded cohesion reviews.
- Focused unit tests may be colocated in
#[cfg(test)] modules; integration, differential, host, load, and whole-project suites live under tests/ or a non-published test package.
- Coverage scopes are comparable and any numeric gate is reported as a signal, not parity proof.
- Stubs, warnings, flaky/skipped tests, missing platforms, real-host gaps, and unverified boundaries remain visible.
- A module completion claim is emitted only when its object ledger, source-test
case ledger, source-asset ledger, complete differential result, Rust
obligations, and required host/non-functional gates all permit it.
1---2name: rust-java-migration-testing3description: Design, implement, audit, and report lossless Java-to-Rust migration tests without promoting green tests into false completion claims. Use when porting 100% of JUnit tests and concrete parameterized/dynamic cases, SHA-256-verifying source fixtures/resources/scripts/data, requiring complete per-case golden or live differential MATCH results, validating object/test ledgers, reviewing oversized Rust test files, organizing inline unit versus integration tests, adding Rust-specific obligations, comparing coverage, or building property, fuzz, mutation, concurrency, lifecycle, adapter, host, load, security, and rollback evidence. Enforces a 500-line cohesion-review threshold, an 800-line authored-file blocker, and idiomatic Rust test placement.4---56# Java-to-Rust Migration Testing78Prove observable compatibility and Rust safety properties, not test volume. Treat9the complete source test suite as a non-negotiable compatibility floor: migrate10100% of its test cases without weakening inputs or assertions, copy every test11fixture/data/script byte-for-byte into the target repository, and make both12implementations produce the same per-case observable result. Build three explicit13test ledgers in this order:1415## Mandatory verification structure1617- Require exact Java/Rust path parity; every `MISPLACED` or missing `file-map.csv` target blocks completion.18- Treat E0583/E0761/E0405/E0425/E0659 and broken fixture paths as structural failures after file moves.19- Treat up to 500 physical lines as normal, review authored `.rs` files from 501–800 lines for cohesion, and block files above 800 lines; split suites by contract or behavior family rather than arbitrary ranges.20- Allow focused unit tests in `#[cfg(test)]` modules beside their implementation. Put public-boundary, cross-module, differential, host, load, and whole-project tests in `tests/` or a non-published test/testkit crate.21- Run path audit, fmt, default/all-feature check, Clippy, complete tests, golden/live differential, migration audit, and the project coverage gate without using any one result as parity proof.2223Read [Directory parity verification](references/directory-parity-verification.md) and [Migration verification SOP](references/migration-verification-sop.md).24251. `SOURCE_PARITY` — map every source test/case and every source test asset; no26 missing, blocked, or not-applicable row permits a completion claim.272. `RUST_OBLIGATION` — test risks introduced by the Rust implementation and replacement components.283. `VALUE_ADD` — add tests justified by uncovered behavior, plausible defects, incidents, mutation survivors, or hostile inputs.2930`SOURCE_PARITY` has two mandatory sections: `TEST_CASE` and `TEST_ASSET`.31`TEST_CASE` preserves the source inputs, assertions, exception/error category,32ordering, state, side effects, and cleanup. `TEST_ASSET` preserves the source33relative path, target path, and SHA-256 of every copied fixture, script, corpus,34golden file, and data file. Coverage should rise because meaningful contracts are35exercised. A percentage is not the design input and 100% is not migration proof.3637## Scope and routing3839Use this skill for migration-specific test planning, implementation, audit, and acceptance.4041This is the Rust testing adapter of the shared target-language migration42profile. Keep source-test dispositions, exact-asset rules, full differential43outcomes, and completion semantics equivalent to `zig-java-migration-testing`44and future Kotlin/Swift adapters; vary only Rust test/build syntax and Rust risks.4546Route:4748- module/object/component migration planning to `rust-java-migration`;49- general Rust unit, integration, doctest, and fixture mechanics to `rust-testing`;50- async model checking to `rust-concurrency`;51- profiling and benchmark construction to `rust-performance`;52- unsafe and FFI validation to `rust-unsafe-ffi`;53- web threat modeling to `rust-web-security`.5455An audit-only request does not authorize deleting or rewriting tests. Preserve existing behavior and unrelated dirty work. Treat heuristic findings as review candidates.5657## Required inputs5859Resolve or mark unknown:6061- pinned Java and Rust SHAs, dirty state, toolchains, profiles, features, targets, and generated-code boundaries;62- the authoritative workspace-topology decision and actual paths of the target63 production and `<project>-test` packages; do not assume a `crates/` directory;64- the complete source test roots and runner configuration, including fixtures,65 examples, scripts, test resources, parameter sources, dynamic factories, and66 test-support code; the source repository defines the denominator and exclusions67 cannot be introduced merely to make parity reach 100%;68- Java test runner and Rust test runner, including parameterized and dynamic test behavior;69- source coverage scope/tool/report and comparable Rust coverage scope/tool/report;70- available oracle: source only, executable Java tests, golden exporter, packaged artifact, live service, or standards suite;71- the current authoritative object ledger generated from the same Java/Rust72 baselines; ignore historical-design appendices when reading current states;73- required hosts, real dependencies, concurrency/load model, security boundary, and rollback mechanism;74- deterministic normalization rules for time, identifiers, paths, map order, locale, float precision, and scheduling.7576Never use an unpinned remote artifact as the compatibility oracle.7778## Evidence labels7980Use these labels without promotion:8182| Level | Evidence | Claim allowed |83|---|---|---|84| `V0_STATIC` | source/test inventory, call trace, no-stub scan | structural disposition only |85| `V1_RUST_LOCAL` | Rust unit/integration/doc/compile tests | Rust-local behavior passes |86| `V2_MIRRORED` | Rust test preserves a named Java test's inputs and assertions | source test represented; not differential |87| `V3_GOLDEN_DIFF` | pinned Java-generated fixtures compared by Rust | recorded outputs match; completion requires the full source denominator |88| `V4_LIVE_DIFF` | pinned Java and Rust execute identical cases | recorded live behavior matches; completion requires every source case |89| `V5_HOST` | real framework/process/database/network/filesystem | named integration boundary works |90| `V6_NONFUNCTIONAL` | model, mutation, property, fuzz, load, soak, security | named non-functional claim holds |91| `V7_ROLLBACK` | gray rollout and rollback rehearsal | stated recovery path works |9293A copied test name, green Rust suite, or 100% line report does not prove `V3_GOLDEN_DIFF`.9495Evidence levels never replace object states. A green `V1_RUST_LOCAL` suite does96not turn `MISSING`, `MISPLACED`, `STUB`, `PARTIAL`, or `UNVERIFIED` into97`IMPLEMENTED`. `DEPENDENCY_REUSED` additionally requires an exact pinned98upstream symbol and local integration test; `PLATFORM_NA` requires platform99evidence rather than a passing test that skips the behavior.100101## SOP102103### 1. Freeze the verification baseline104105Record exact SHAs, commands, tools, profiles, features, targets, test counts, ignored/flaky tests, coverage exclusions, and existing evidence artifacts. Record Java and Rust coverage separately before comparing them.106107Read the current fact region of the authoritative object table before designing108tests. Stop at `historical-design-appendix-start`; old appendix statuses are109context only. Record counts for all strict object states. If any incomplete110state remains, the acceptance report must say “module migration incomplete”111regardless of test results.112113Use CodeGraph when indexed to trace each source test through its production entry, collaborators, side effects, and Rust counterpart. Text similarity is insufficient for overloaded methods, registries, interceptors, dynamic dispatch, cleanup, and async paths.114115### 2. Inventory source and target tests116117Create one row per Java test method and one row per concrete parameterized,118repeated, template, or dynamic case. Include disabled tests; a disabled source119test remains a required migrated test and its disabled reason is preserved120separately. Create one `TEST_ASSET` row per file in every source test resource,121fixture, script, corpus, and data root. Copy source assets without editing them;122place target-generated or normalized derivatives beside the immutable copy.123124Use dispositions:125126| Disposition | Meaning |127|---|---|128| `MIRRORED` | same contract represented in one Rust test |129| `ADAPTED` | same observable contract using a Rust-native fixture/oracle |130| `SPLIT` | one Java test becomes several focused Rust tests |131| `MERGED_APPROVED` | several Java tests share one parameterized Rust test without losing cases/assertions |132| `NOT_APPLICABLE` | JVM-only claim recorded for analysis; blocks 100% lossless source-test completion |133| `BLOCKED` | named dependency or oracle prevents the test; blocks completion |134| `MISSING` | no Rust implementation; blocks completion |135136`MIRRORED`, `ADAPTED`, `SPLIT`, and `MERGED_APPROVED` are complete only when137every source case remains identifiable, all preservation flags are true, every138target test exists, and golden/live comparison records `MATCH`. `ADAPTED` permits139Rust-native harness mechanics, not a weaker contract. `SPLIT` and140`MERGED_APPROVED` may reorganize tests but may not remove a case or assertion.141Do not map by test name alone.142143Resolve `SKILL_DIR` to this skill directory and run from the migration repository:144145```bash146python3 "$SKILL_DIR/scripts/audit_migration_tests.py" \147 --java-root ../java-project/source-module \148 --rust-root <selected-target-crate-or-workspace> \149 --object-ledger docs/source-module/对象级对照表.md \150 --parity-manifest docs/source-module/source-test-parity.json \151 --java-test-assets-root src/test/resources \152 --fail-on-incomplete153```154155The report inventories tests, flags weak signals, verifies every source-test156manifest row, checks target test files, hashes exact asset copies, and refuses a157completion gate for any object, test, asset, run, or differential gap. Additional158non-standard asset roots must be passed with repeated159`--java-test-assets-root`. The script validates recorded preservation evidence;160it cannot infer semantic mappings or authorize deletion. It warns on authored161files above 500 physical lines and blocks those above 800; inline unit tests are162valid Rust organization and are not reported as structural violations.163164### 3. Implement the `SOURCE_PARITY` ledger165166For every source row:1671681. Trace the protected Java contract and production call path.1692. Port the fixture and assertions, not merely the method name.1703. Preserve valid, boundary, failure, state-transition, and side-effect cases.1714. Run the source and target suites from pinned baselines and retain raw per-case172 results. A mirror without Java output is useful during implementation but does173 not satisfy the final source-parity gate.1745. Compare each source case through a pinned golden or live differential oracle;175 require `MATCH`, zero harness failures, and zero not-run cases.1766. Record the Rust test, preservation flags, evidence, commands, artifacts, and177 any divergence.178179Missing, blocked, or not-applicable source tests remain visible and block a180migration-complete conclusion. Source tests are a compatibility floor, not the181complete Rust plan.182183#### Whole-project migration test module184185For every repository/product-level migration completion claim, create one186non-published workspace package as the whole-project acceptance authority. It187is especially important for multiple crates, bindings/adapters, or a source188system/templatesuite. Name it189`<project>-test` by default; keep the final directory name and Cargo package name190identical, set `publish = false`, and run it explicitly in CI. Its parent path191follows the recorded workspace topology: a small root-flat workspace uses192`<project>-test/`; a hybrid may keep it at root or in a real test family; a193contained workspace may use `crates/<project>-test/` or194`crates/tests/<project>-test/`. Local tests inside production195crates prove their own parser/type/API/binding behavior; they do not replace the196whole-project module's source-suite replay, cross-crate workflows, real copied197assets, golden/live differential comparison, or aggregate result artifact.198199For FreeMarker use this boundary:200201```text202freemarker/ # Rust engine local unit/integration tests203freemarker-pyo3/ # Python binding local/packaging tests204freemarker-test/ # complete Java templatesuite + cross-component acceptance205```206207The `freemarker-test` package must be a workspace member, depend on public208surfaces under test, and remain outside crates.io publication. Its complete gate209is every source case `MATCH`, with no threshold pass count, skipped capability,210or ignored case. Read [Migration verification SOP](references/migration-verification-sop.md)211for naming, ownership, layout, and CI rules.212213The test package's semantic role is mandatory; one physical parent directory214is not. Never relocate it solely to imitate the Java test-module tree or a215generic Rust example.216217Do not use one test per object as a substitute for one real file per source218object. A test that reaches a re-export, compatibility facade, or merged type219does not cure `MISPLACED`/`MISSING`. Tests validate semantics only after the220layout and object boundary are factually present.221222Treat 500 physical lines as the suite-cohesion review threshold and 800 as the223authored-file blocker. Split by contract, behavior family, fixture family,224adapter, or verification layer while preserving every source case ID and225assertion. Keep compact private-behavior unit tests in `#[cfg(test)]` modules;226put reusable integration support in `tests/common/` or a dedicated testkit crate.227Enable `clippy::too_many_lines` to review functions over its 100-line default.228229### 4. Implement the `RUST_OBLIGATION` ledger230231Add applicable tests created by the target design:232233| Rust mechanism | Mandatory questions |234|---|---|235| Ownership / `Drop` | exactly-once cleanup, partial initialization, early return, panic/cancel boundary |236| Async / Tokio | cancellation, timeout ownership, orphan tasks, shutdown, bounded queues, slow consumers |237| Shared state | atomicity, poison/recovery policy, no lock held across `.await`, deadlock/interleaving risks |238| `Send` / `Sync` | intended compile contract and supported executor/thread boundary |239| Typed errors | exact variant, context, retryability, `Error::source`, public redaction surfaces |240| serde / wire/storage | rename/default/unknown fields, round trip, bytes, compatibility window |241| Traits / registries | selection, duplicate registration, missing provider, dynamic dispatch |242| Procedural macros | compile-pass/fail, generics, visibility, renamed dependencies, diagnostics |243| Feature/platform/MSRV | supported combinations compile and behave as promised |244| Unsafe / FFI | invariants, Miri/sanitizer where applicable, ownership across boundary |245| Replacement crate | risky semantic path, lifecycle, real dependency, upgrade/rollback boundary |246| Framework adapters | one shared contract suite plus adapter-native routing/body/service behavior |247248These tests need not exist in Java because they protect the Rust implementation's correctness.249250For a `DEPENDENCY_REUSED` row, execute the local adapter against the exact251declared upstream symbol/version or commit. The dependency's own unit tests,252documentation examples, or a similar capability name are not local integration253evidence. Assert the source contract's ordering, errors, lifecycle, cancellation,254and metadata that cross the adapter.255256### 5. Implement the `VALUE_ADD` ledger257258Add a test only when it has a reason such as:259260- missing boundary or branch revealed by coverage;261- surviving meaningful mutant;262- property/invariant over a broad input space;263- malformed or hostile input found by fuzzing;264- production incident or bug regression;265- concurrency interleaving, load, resource leak, or long-soak risk;266- compatibility behavior absent from the Java suite but required by docs/protocol;267- real-host or rollback behavior that mocks cannot prove.268269For each test, record the concrete bug it should catch. Prefer a small parameter/branch matrix over “one test per type/token/method” count targets.270271### 6. Use observable assertions272273Strong tests observe exact public behavior:274275- value, bytes, order, state transition, call count, side effect, resource release, or typed error;276- `Display`, `Debug`, serialized error report, logs/tracing fields, transport body, and `Error::source()` independently where exposed;277- cache hit/miss/eviction metrics or backend-call counts when claiming cache behavior;278- body/trailer/backpressure and disconnect cleanup when claiming streaming lifecycle.279280Weak patterns requiring review:281282- `let _ = result`, unused parsed AST, or a test that accepts success and failure;283 note that during cleanup, converting a genuinely dropped `Result` to284 `let _ = expr;` preserves semantics and is not itself weak evidence — it only285 becomes weak when `let _ =` is the sole observation of the behavior under test;286- parse-only checks named as semantic/evaluation tests;287- only `is_ok()` or `is_err()` when value/error category matters;288- a cache test that observes only final values;289- tests of fixture `Clone`, `Debug`, constants, constructors, or type existence without a public contract;290- duplicate “coverage burst” tests with no distinct risk;291- tests that merely prove `todo!()` or `unimplemented!()` panics.292293Read [Vernal case study](references/vernal-testing-case-study.md) and [test-value rubric](references/test-value-rubric.md) before cleanup.294295### 7. Build differential fixtures296297Use a versioned case format such as JSON Lines:298299```json300{"schema":1,"case_id":"empty-name","input":{"name":""},"expected":{"kind":"error","code":"INVALID_NAME"}}301```302303Retain Java and Rust raw outputs separately. Pin exporter and implementation SHAs, seeds, environment, and normalizer version. Compare success values, error categories, side effects, order, and wire bytes as applicable. Do not normalize unexpected fields away.304305Differential comparison is a full source-suite gate, not a representative306sample. The final manifest must record one `MATCH` per concrete source case and307suite-level Java, Rust, and differential runs with `PASS`, zero mismatches, zero308harness failures, and zero not-run cases. A temporary subset may guide309development but must be labeled partial and cannot satisfy completion.310311### 8. Reuse conformance suites for adapters312313Put shared assertions and failure fixtures in a testkit. Each adapter must provide real native observations. Run the same identity, scope, lifecycle, error, cancellation, streaming, and cleanup contracts for every implementation, then add adapter-native tests.314315Prefer event-driven synchronization and bounded timeouts over fixed sleeps. Assert the resource is open while a stream is active and closed after completion, disconnect, timeout, cancellation, or panic.316317### 9. Compare coverage without gaming it318319Coverage comparison is valid only when scopes are documented and reasonably comparable:320321- production files/modules included;322- generated code and approved exclusions;323- line/region/branch semantics of each tool;324- features, targets, test types, and profile;325- source and target baselines.326327Acceptance order:3283291. every source test and concrete case has a lossless target implementation;3302. every source test asset has a byte-identical target copy verified by SHA-256;3313. both complete suites pass and every source case has differential `MATCH`;3324. every high-risk source contract has adequate evidence;3335. every applicable Rust obligation is tested;3346. meaningful mutants and uncovered branches are reviewed;3357. comparable Rust coverage exceeds the Java baseline if the project requires it.336337Do not weaken assertions, duplicate tests, exclude difficult files, or add trivial getters to reach a number. A user-mandated 100% gate may be enforced, but report what it proves and what it does not.338339### 10. Audit test value before removing or merging340341Score each test manually:342343| Question | Pass signal |344|---|---|345| Traceable? | source test, contract, risk, bug, or incident is named |346| Observable? | assertion checks externally meaningful behavior |347| Mutation-sensitive? | a plausible defect would make it fail |348| Production path? | exercises real production logic at the right boundary |349| Deterministic? | synchronization/seed/environment are controlled |350| Distinct? | adds a branch, case, platform, failure, or invariant |351| Right level? | unit/integration/host/load test matches the boundary |352353Classify `KEEP`, `IMPROVE`, `MERGE`, or `REMOVE_PROPOSED`; require review before deletion. Coverage loss alone may justify replacement but not retention of a meaningless assertion.354355### 11. Run layered gates356357Run applicable gates from cheapest to most diagnostic:358359```bash360cargo fmt --all -- --check361cargo check --workspace --all-targets --all-features362cargo test --workspace --all-features363cargo clippy --workspace --all-targets --all-features -- -D warnings364cargo test --doc --workspace365```366367Then run targeted compile-fail, platform/MSRV, differential, real-dependency, real-host, model/Loom, Miri/sanitizer, mutation, property/fuzz, load/soak, security, and rollback gates. Record exact command, environment, result, and artifact.368369Run every gate at the workspace MSRV: a default rustc older than the workspace370`rust-version` fails with `rustc X is not supported by the following packages`371rather than testing your code — use `rustup run <ver> cargo ...` and never372silently lower the manifest. Use `--keep-going` so one failing crate does not373hide the remaining errors (errors surface in layers; fix the first layer and374re-run). Require **both** default and `--all-features` check runs to reach zero375warnings: feature-gated crates and code paths only compile under376`--all-features`, and a check gate with warnings must be reported as a gap, not377a pass.378379Use the project-specific coverage command. For mutation candidates:380381```bash382"$SKILL_DIR/scripts/run_mutation_test.sh" <selected-target-crate>383```384385Interpret survivors individually; do not impose one universal mutation score.386387### 12. Report acceptance honestly388389Report separately:390391- source-test lossless implementation and per-case differential coverage;392- source behavior evidence by `V0`–`V4`;393- Rust-obligation completion;394- value-add tests and defect/risk rationale;395- line/branch/region coverage with comparable scope;396- mutation, property, fuzz, concurrency, load, security, host, and rollback evidence;397- failures, flaky/ignored tests, stubs, exclusions, missing targets, and external boundaries.398- current object-state counts and the explicit list of all399 `MISSING`/`MISPLACED`/`STUB`/`PARTIAL`/`UNVERIFIED` blockers;400- compiler and Clippy warnings, ignored/doctests, feature/target gaps, and tests401 that were not executed;402403## Red lines404405- Do not call mirrored tests differential.406- Do not replace source-test disposition with raw test-count parity.407- Do not exclude, disable, weaken, merge away, or mark a source test not408 applicable to manufacture 100% parity.409- Do not rewrite copied source fixtures or data in place; retain a byte-identical410 copy and generate target-specific derivatives separately.411- Do not accept two independently green suites as result parity; require412 per-case Java/Rust golden or live comparison.413- Do not substitute local tests in production or binding crates for the414 non-published `<project>-test` whole-project acceptance package, and do not415 use pass thresholds, skips, or ignored cases as its completion gate.416- Do not hardcode `<project>-test` under `crates/` or at repository root without417 checking the migration roadmap's project-driven workspace topology.418- Do not write tests solely to increase coverage or file count.419- Do not keep an authored `.rs` file above 800 physical lines; review every file above 500 and split weakly cohesive suites.420- Do not compress code, remove useful comments, or split arbitrary line ranges to evade a size gate.421- Do not place integration, differential, host, load, or whole-project suites in production modules; focused inline unit tests remain valid.422- Do not call parse success semantic equivalence.423- Do not accept generic `is_err()` when the error contract is observable.424- Do not auto-delete tests from names, body length, or heuristics.425- Do not hide mismatches through broad normalization or snapshot regeneration.426- Do not replace real dependency/host tests with mocks when the contract crosses that boundary.427- Do not use fixed sleeps as the only async coordination mechanism.428- Do not count production stubs as implemented because their tests compile.429- Do not mark a module complete from Cargo/JUnit test totals, coverage, or a430 green CI job while the authoritative object ledger has any incomplete state.431- Do not run a completion gate without a source-test parity manifest. Missing432 test mappings, incomplete parameter expansion, false preservation flags,433 missing target files, asset hash differences, non-`MATCH` results, harness434 failures, or not-run cases all block completion.435- Do not read completion states from a historical appendix or stale duplicate436 migration document.437- Do not call dependency reuse verified from upstream tests or semantic438 similarity; require the exact dependency symbol and a local integration test.439- Do not report the check gate as passed while `cargo check` (default or440 `--all-features`) still emits warnings, or when the run used an unsupported441 toolchain instead of the workspace MSRV.442443## On-demand resources444445- [Migration verification SOP](references/migration-verification-sop.md)446- [Directory parity verification and post-migration checks](references/directory-parity-verification.md)447- [Cross-language migration profile](references/cross-language-migration-profile.md)448- [Test categories](references/test-categories.md)449- [Test-value rubric](references/test-value-rubric.md)450- [Vernal positive and negative examples](references/vernal-testing-case-study.md)451- [Migration test ledger template](assets/templates/迁移测试对照表.md)452- [Machine-readable source-test parity manifest](assets/templates/source-test-parity.json)453- [Worked audit report](examples/audit-report.md)454- `scripts/audit_migration_tests.py` — Java/Rust test inventory and weak-signal audit455- `scripts/run_mutation_test.sh` — mutation-test wrapper456457## Completion criteria458459- Every Java test and every concrete parameterized/dynamic/repeated case in the460 frozen source test roots has a target-language implementation and source trace;461 the source denominator is 100% accounted for without completion exclusions.462- Every source fixture, resource, script, corpus, golden file, and test data file463 has a byte-identical copy in the target repository with matching SHA-256.464- Every mapped case attests preservation of contract, inputs, assertions,465 fixture state, and cleanup; split/merged/adapted forms lose nothing.466- The complete pinned Java and Rust suites pass, and the complete golden/live467 differential report contains only `MATCH` with zero harness failures and zero468 not-run cases.469- The `<project>-test` package is a workspace member with `publish = false`,470 lives at the path selected by the recorded workspace topology, exercises471 public component surfaces, and owns the complete suite/differential472 command plus aggregate artifact; local crate tests remain subsystem evidence.473- The authoritative current object ledger was checked, its baselines match the474 test run, and no incomplete object state was hidden by the test summary.475- Every high-risk contract has an oracle, evidence label, and result.476- Applicable Rust ownership, async, error, serialization, feature, adapter, and unsafe obligations are tested.477- Added tests name a distinct risk or plausible defect.478- Every authored `.rs` file is at most 800 physical lines; files above 500 and functions above the Clippy 100-line signal have recorded cohesion reviews.479- Focused unit tests may be colocated in `#[cfg(test)]` modules; integration, differential, host, load, and whole-project suites live under `tests/` or a non-published test package.480- Coverage scopes are comparable and any numeric gate is reported as a signal, not parity proof.481- Stubs, warnings, flaky/skipped tests, missing platforms, real-host gaps, and unverified boundaries remain visible.482- A module completion claim is emitted only when its object ledger, source-test483 case ledger, source-asset ledger, complete differential result, Rust484 obligations, and required host/non-functional gates all permit it.