From 563fc468fb8ac97837e09778560c8271f6b5e590 Mon Sep 17 00:00:00 2001 From: Amp Date: Wed, 21 Oct 2026 21:35:27 +0000 Subject: [PATCH] docs(test): define when a test may be mock-only Tests mark themselves mock-only by opening with vm.skip(livePrecompiles), but the criteria lived only in per-test comments. Document them on the livePrecompiles flag: a test is mock-only when it writes storage at a fixed precompile address, asserts a mock slot the Rust implementation lacks, or needs a state that only mock storage writes can build. Also say to branch on livePrecompiles when a behavior can be checked in both worlds, and to gate tests that wait on a base/base change behind an environment flag, so those skips stay visible in base/base's live precompile CI, which leaves mock-only skips out of its skipped count. Amp-Thread-ID: https://ampcode.com/threads/T-f04cecba-e4dc-42c5-94de-8846dbf5444c Co-authored-by: Julian Meyer --- LIVE_PRECOMPILE_TESTING.md | 1 + test/lib/BaseTest.sol | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/LIVE_PRECOMPILE_TESTING.md b/LIVE_PRECOMPILE_TESTING.md index b3adce3a..595a6ad7 100644 --- a/LIVE_PRECOMPILE_TESTING.md +++ b/LIVE_PRECOMPILE_TESTING.md @@ -355,6 +355,7 @@ If this returns garbage / fails, the fork's build is broken or out of date. | The test runner | `script/fork/` (`make fork-tests`) | Python + web3; forwards forge args through `ARGS` | | Forge profile config | `foundry.toml`, `[profile.fork]` | `base = true` enables Rust precompile dispatch | | Mode detection / skip-etch | `test/lib/BaseTest.sol` | auto-probes for live precompiles; `LIVE_PRECOMPILES=true` forces live | +| Mock-only test criteria | `test/lib/BaseTest.sol`, `livePrecompiles` | when a test may open with `vm.skip(livePrecompiles)` | | Slot assertions | `test/unit/**/*.t.sol`, `test/lib/mocks/Mock*Storage.sol` | `vm.load`-based slot-layout assertions paired with surface tests | | Storage helpers | `test/lib/mocks/MockB20Storage.sol`, `MockPolicyRegistryStorage.sol` | the slot-derivation library every assertion uses | | Patched forge + anvil | `~/code/base-anvil/target/.../{forge,anvil}` | built by `cargo build -p forge -p anvil` | diff --git a/test/lib/BaseTest.sol b/test/lib/BaseTest.sol index 563bf3b6..ea54267a 100644 --- a/test/lib/BaseTest.sol +++ b/test/lib/BaseTest.sol @@ -90,6 +90,27 @@ abstract contract BaseTest is Test { /// (`base-forge` / base-anvil), false when the Solidity mocks are /// etched (stock `forge`). Set in setUp; read by tests that are /// only meaningful in one world. + /// @dev **Mock-only tests.** A test that can only run against the Solidity + /// mocks opens with `vm.skip(livePrecompiles)` as its first statement, + /// with a comment naming the criterion below that applies. A test is + /// mock-only when it depends on the mock's own storage, in one of these + /// ways: + /// 1. It writes storage at a fixed precompile address. Forge rejects + /// `vm.store` there (`cannot use precompile ... as an argument`). + /// B20 token addresses are not precompiles, so `vm.store` on a + /// token runs in both worlds. + /// 2. It asserts a mock storage slot that the Rust implementation + /// does not have, such as `MockB20Storage.initializedSlot()`. + /// 3. It needs a state that no real sequence of calls can reach and + /// that it can only build by writing mock storage. + /// If the behavior can be checked in both worlds, branch on + /// `livePrecompiles` instead of skipping (see + /// `B20FactoryTest._assertInitialized`). Do not use this guard to wait + /// for a base/base change: gate that test behind an environment flag, + /// like `POLICY_DISPATCH_FIX` in `dispatch_inactive.t.sol`, so its skip + /// stays visible until the change ships. base/base's live-precompile CI + /// counts only these first-statement guards as mock-only and leaves + /// them out of its skipped count. bool internal livePrecompiles; // -- Setup --