Repository navigation
fix: pass Cowlib 2.22.0 cookie and date upstream suites natively - #5
Merged
Merged
Conversation
OTP's EUnit server calls filename:flatten/1 for watched paths, so a native EUnit project failed to build with "function filename:flatten/1 is not exported". Nera's filename module already defined it privately; it is now exported with OTP's behaviour. The filename_flatten fixture checks strings, binaries, atoms, nested and improper lists, function_clause on a non-list, apply/3 and a remote fun against BEAM's results.
~W and ~P printed the literal text "~W" or "~P". EUnit's reporter formats a cancelled test group with ~tP and passes that text to io:fwrite/2 again, so the leftover directive made the reporter fail with badarg. Both directives now take a term and a depth and print what lies deeper as "...", following io_lib:write/2 for ~W and the pretty printer for ~P; the two differ only for maps. Depth 0 prints "...", a negative depth the whole term, and ~W honours field width as ~w does. The io_format_depth fixture checks 38 results taken from BEAM, including off-heap binaries, sub-binaries, trailing bits, maps, improper lists, field widths and EUnit's second formatting pass.
Cowlib's original cow_cookie and cow_date EUnit suites now run on Cowlib 2.22.0, the release examples/cowlib_demo builds, on BEAM and through nera test with OTP's EUnit: 157/157 and 27/27 on both, with the EUnit framework controls passing. - upstream-2.22.0.json pins the release commit; native_eunit.py and native_date.py take --pin 2.22.0, while 2.20.0 stays the default for the earlier campaigns. - cow_cookie preparation keeps expiry_in_window/3, a test helper the new Max-Age/Expires limit test calls. - native_date.py runs 2.22.0's renamed prop_parse_date/0. It passes on BEAM; natively it is blocked because rand:uniform/1 is a built-in that rand:seed/2 does not control. - The Linux workflow fetches the pinned commit, runs both suites and keeps the report as an artifact. - COMPATIBILITY.md, KNOWN_LIMITS.md, the harness README, the example README and the changelog record the results and limits.
A process in `receive ... after` could wait forever, and a program could exit with status 0 too early. Repeated native runs of Cowlib's cow_cookie suite hit both about once in 30 runs, also on unchanged main. - A timed receive now registers its timer while the process is still locked, before its wait becomes visible. A late registration can no longer replace the timer of the process's next receive, and a timer that expires before the wait is published still wakes the process. - Message, exit and timer senders no longer cancel the receiver's timer after queueing it. The receiver could already be in a later timed receive; the worker that runs it cancels the timer under the process lock. - The idle check treats a process whose exit cleanup is still running as live work, so linked or monitoring processes get their EXIT or DOWN signal before the program stops. Runtime unit tests replay each ordering and fail on the old code.
mremond
force-pushed
the
fm/task-9b04259d
branch
from
November 12, 2026 15:38
43d4536 to
9178345
Compare
… again A process woken with nothing to do, such as by socket readiness or an ignored exit signal, goes back to its wait. It registered its still pending timer again each time, leaving one more heap entry per such wake until the deadline. The timer already belongs to that wait, so it is now kept; a timer that fires meanwhile is still caught by the wait's race check. A unit test wakes a waiting process 100 times and checks that one timer entry remains and the timeout still fires.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Cowlib's own cookie and date test suites had not been run on the 2.22.0 release, so Nera's cookie and date parsing had no upstream check against BEAM.
What changed
Before and after
Before: Natively, EUnit stopped on an undefined `filename:flatten/1` and then on a `badarg` from `~W`. About 1 run in 30 of the cookie suite hung or exited early, on unchanged main too.
After: Both suites pass natively and on BEAM: 157 of 157 cookie tests and 27 of 27 date tests. 1,200 consecutive native cookie-suite runs pass.
This changes the runtime, the standard library and the test harness; there is no screen to show.
Checked
How it was checked
cargo build --release --locked: passed
cargo clippy --workspace --all-targets --locked -- -D warnings: passed
cargo test --workspace --locked --no-fail-fast: passed (nera-runtime library 667 passed, 1 ignored)
python3 scripts/test-allocator-config.py: passed
./target/release/nera test -j 1 --timeout 30: 466 passed, 0 failed, 2 skipped of 468, on the final version
python3 scripts/ci/test_changes.py: passed
Six new runtime tests (stale_receive_timer_cannot_replace_a_later_one, receive_timer_taken_before_waiting_is_published_still_wakes, timer_expiring_during_a_wake_without_mail_still_wakes, waking_without_work_keeps_a_single_timer, exit_cleanup_in_progress_is_not_deadlock, delivery_leaves_the_receive_timer_to_the_worker): 10 of 10 runs pass
tests/io_format_depth.erl and tests/filename_flatten.erl: 10 of 10 runs pass natively; both match BEAM
python3 -m unittest discover -s tests/integration/cowlib -p 'test_*.py': 101 tests, 3 existing skips, 10 of 10 runs pass
tests/integration/cowlib/native_eunit.py --pin 2.22.0 --modules cow_cookie cow_date: passed locally and in the CI Cowlib job
scripts/test-native-timer.py: passed; scripts/test-native-shutdown.py: timings pass, handler output text differs as on unchanged main
scripts/test-cowlib-demo.py: 4 of 4 pass
Platform: macOS arm64, Rust 1.99.0, OTP 29.1.2 with EUnit 2.11, PropEr 1.5.0; CI covers Linux x86_64 and macOS arm64
Risk
Risk above low: Blast radius, medium — Receive timers and the idle check run in every native program
Full risk assessment
Not done
Cowlib's `prop_parse_date/0` property passes on BEAM but cannot run natively yet. `rand:uniform/1` is a built-in that `rand:seed/2` does not control, so seeded runs differ.
A separate cookie probe still prints `Set-Cookie` attributes in another order than BEAM, because Nera orders atom map keys differently. The upstream suite does not depend on that order.
The review noted two older scheduler gaps this change does not touch: a cascade kill of a just-woken process can keep the runnable count too high, and the main process can miss a signal sent during its last slice.