Skip to content

test(journal): cover the 1.3.0 writers in the fuzzed walk - #346

Merged
siracozmen01 merged 1 commit into
mainfrom
walk-late-writers
Nov 5, 2026
Merged

siracozmen01 merged 1 commit into
mainfrom
walk-late-writers

Conversation

@siracozmen01

Copy link
Copy Markdown
Collaborator

Problem

The seeded API walk (test/walk.h), which test_walk and fuzz_walk drive, calls none of the writers 1.3.0 added. So no fuzzing, and none of the walk's replay, rollback and twin promises, covers m2Body_SetSleepThreshold (or a def's sleepThreshold), m2Chain_SetFriction, m2Chain_SetRestitution, m2WaterVolume_SetUserData, m2MotorJoint_SetMaxForce or m2MotorJoint_SetMaxTorque. A writer that stopped journaling would pass every walk (#345).

Adding them to the canonical walk would move M2_JOURNAL_HASH, and #343 leaves that to the next minor release.

Change

  • test/walk.h gains s_walkLateWriters. When it is set, DoRandomOp gives one draw in ten to DoLateWriterOp, which makes one of these calls:
    • It sets a random body's sleep threshold to 0, 0.02, 0.05, 0.2 or 0.6.
    • It makes a dynamic body with a def threshold from 0 to 0.6.
    • It sets a random chain's friction or restitution.
    • It sets the pool's game data.
    • It sets a motor joint's force or torque budget, picked among the motor joints alone, from 0, 1, 5, 50 or 400 (a quarter of that for torque). With no motor joint it makes one that pulls its bodies 2 m apart, so the budgets bind.
  • test_walk.c and fuzz_walk.c set the flag, and test_golden.c does not. With the flag clear, the walk draws exactly as before, so the canonical seeds and every golden line are unchanged.
  • RunScenario keeps the pool's id. After a replay it checks that the pool's game data matches the recorded world's, since the world hash leaves out what the step never reads.

A test-only change: the library, its formats and its results are unchanged.

Verification

  • test_walk passes: the regression bank's 6 seeds take the new path, and each one runs all three promises.
  • Mutants, each built into a scratch library and run against test_walk:
Mutant Fails
m2Body_SetSleepThreshold records nothing replay lands on the recorded bits
the body create record leaves the def's threshold out replay lands on the recorded bits
a chain's material change records nothing replay lands on the recorded bits
replay drops the pool's game data record replay restores the pool's game data
m2MotorJoint_SetMaxForce records nothing replay lands on the recorded bits

The motor mutant first survived, because the walk reached a motor joint only through RandomJoint and set budgets the motor never used. Picking among the motor joints, making one when there is none, and drawing budgets that bind made it fail.

  • tools/check.sh changed passes. The changed files compile clean with gcc-14 and clang-20 under the strict flags.
  • test_golden prints the 13 lines of test/hashes.txt unchanged in the scalar (linux-clang-20-asan) and AVX2 (linux-clang-20) presets.

Closes #345

@siracozmen01 siracozmen01 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.

Review at 9244afc: ready. Read, not run.

The flag cannot change the canonical draws when it is off. DoRandomOp tests s_walkLateWriters && Pick(10) == 0. With the flag false, && short-circuits, so Pick(10) is never called and the next draw is the one Pick(100) made before. The other additions draw nothing:

  • s_walkPool = NewWaterVolume(...) only keeps the id;
  • the replay check only reads m2WaterVolume_GetUserData from both worlds.

s_walkLateWriters is a static in walk.h, so each test program has its own, false unless set. test_golden.c does not set it, so its seeds walk exactly as before and the 13 lines stand.

Each 1.3.0 writer is reached with values that matter.

  • Sleep threshold, by setter: values from 0 (resolved to the world's) to 0.6 on a random body. They change when a body falls asleep, which the step and the hash read.
  • Sleep threshold, by def: Pick(16) * 0.04, so 0 to 0.6, on a new dynamic body. The body cap is checked first.
  • Chain friction and restitution: values up to 0.9 on one of the world's chains. Those reach contacts on chain shapes, so they matter in any walk that has a chain.
  • Pool user data: the hash leaves it out, so the new check compares it between the recorded and the replayed world. Without that check a lost record would pass, which is why it was added.
  • Motor budgets: motor joints are now picked among motor joints alone, so the setter is never refused for the wrong type. With none, one is made with a 2 m offset and budgets of 20 and 5 that bind. The values 0 to 400 for force, and a quarter of that for torque, include ones below the pull, so a lost budget changes the motion.

The five mutants being caught confirms each writer reaches the replayed bits.

@siracozmen01
siracozmen01 merged commit b442e22 into main Nov 5, 2026
30 checks passed
@siracozmen01
siracozmen01 deleted the walk-late-writers branch November 5, 2026 05:25
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.

The fuzzed walk never calls the 1.3.0 writers

1 participant