tests: fix stale steady-state default left behind by #28 - #36

Merged
wshlavacek merged 2 commits into
mainfrom
claude/stoic-turing-c37f0c
Jul 22, 2026
Merged

tests: fix stale steady-state default left behind by #28#36
wshlavacek merged 2 commits into
mainfrom
claude/stoic-turing-c37f0c

Conversation

@wshlavacek

Copy link
Copy Markdown
Collaborator

What

8a0609a ("steady_state: default to method=\"integration\" (#28)") flipped SteadyStateOptions::method from "newton" to "integration" and updated the Python side, but not the C++ one. test_find_steady_state_methods in tests/test_bngsim.cpp kept asserting the old default, so the native suite has reported 49/50 on every branch since — verified on a clean tree at 52c9022, unrelated to any in-flight work.

Two assertions in the function's first block were stale, both for the same reason: the default's method string, and the method_used it echoes back. Also reworded the comment block above the function, which described the two-tier integrate-then-polish path as the default; it now describes the parity early-stop and cites the 1.4-3.9x net cost #28 measured as why the polish stopped being the default.

Why it matters beyond the count

A permanently-red suite is worse than a slightly smaller one — a genuine regression in this file arrives looking exactly like the failure everyone has learned to wave off.

Notes for review

The test's intent is preserved; only the expected default changed. Two things worth confirming:

  • Newton keeps direct coverage without the default block. The "kinsol" block asserts it converges and echoes the canonical method_used == "newton", and the parity block selects "newton" explicitly to compare equilibrium concentrations against integration. The other three blocks are correct as written and are untouched.
  • The residual assertion still bites. It runs at the default tol = 1e-9, not the looser 1e-8 the explicit-integration block uses — the parity early-stop clears the tighter bound on the reversible model on its own, so this wasn't relaxed to make it pass.

Checks

  • tests/test_bngsim (Ninja, Release): 50/50, up from 49/50.
  • pytest -q python/tests via the pre-push hook: passed.

🤖 Generated with Claude Code

8a0609a flipped SteadyStateOptions::method from "newton" to "integration"
and updated the Python side, but not the C++ one. test_find_steady_state_methods
kept asserting the old default, so the native suite has reported 49/50 on
every branch since -- a standing failure that makes a real regression easy
to wave off as "the known one".
Two assertions in the first block were stale, both for the same reason: the
default's method string, and the method_used it echoes back. Only the expected
default changes here; the rest of the function is correct as written and is
left alone.
Newton keeps its direct coverage without the default block: the "kinsol" block
asserts it converges and echoes the canonical method_used == "newton", and the
parity block selects it explicitly to compare equilibrium concentrations
against integration. The default's residual assertion still runs at the default
tol = 1e-9 rather than the looser 1e-8 the explicit-integration block uses --
the parity early-stop clears the tighter bound on the reversible model on its
own.
The comment block above the function described the two-tier
integrate-then-polish path as the default; it now describes the parity
early-stop and cites the 1.4-3.9x net cost #28 measured as why the polish
stopped being the default.
Checks: 50/50 in tests/test_bngsim (Ninja, Release).
@wshlavacek
wshlavacek merged commit c34c0b0 into mainJul 22, 2026
1 check passed
wshlavacek added a commit that referenced this pull request Jul 22, 2026
tests/test_bngsim had no gate at any stage. Every workflow that compiles the
project passes BNGSIM_BUILD_TESTS=OFF -- mir.yml, windows-nfsim.yml and
windows-tail.yml all do -- and no job ever invoked the binary. The pre-push
hook covers python/tests; the C++ side had no local or remote equivalent, so
the suite's only gate was a developer remembering to build it by hand.
#28 is what that costs. It flipped SteadyStateOptions::method from "newton" to
"integration", updated the Python side, and left the C++ assertion in
test_find_steady_state_methods pinned to the old default. The suite sat at
49/50 on every branch from that merge onward, and no CI run could have said so.
The failure's longevity was a consequence of its invisibility: a permanently-red
suite is worse than a smaller green one, because a genuine regression arrives
looking exactly like the one everyone has learned to wave off.
The job is Linux-only and lean. KLU, NFsim and RuleMonkey are OFF -- test_bngsim
links none of them, and skipping the vendored builds takes the target count from
146 to 81. The wheels/MIR/Windows jobs already cover those subsystems. LAPACK
stays at its default because find_package(LAPACK QUIET) degrades to the
reference dense solver when absent, so Linux builds either way.
Two things fail the job, not one. test_bngsim exits nonzero unless
tests_passed == tests_run, and pipefail carries that through the tee. A second
step then asserts the DENOMINATOR against a floor, which is the failure mode an
exit code cannot see: a suite that goes green because tests stopped being
registered. Every RUN_TEST is unconditionally compiled -- there is no #if around
any of them -- so the total is a real invariant rather than a platform-dependent
count, and the floor only ever moves up.
Verified by building both sides locally with the exact flags: current main
exits 1 at 49/50 naming the stale assertion, and the #36 fix exits 0 at 50/50.
The guard was exercised against four captured logs -- pass, the 49/50 stale
run, a doctored 41/41 shrink, and a no-summary crash -- failing on the last two
and passing the first two.
Not covered: the other native binaries in tests/CMakeLists.txt (test_validation,
test_c_api, test_lapack_dense_linsol, test_seed_count_rounding, the NFsim pair)
stay ungated. Extending this to ctest is a fair follow-up; this closes the gap
that actually drew blood.
wshlavacek added a commit that referenced this pull request Jul 22, 2026
tests/test_bngsim had no gate at any stage. Every workflow that compiles the
project passes BNGSIM_BUILD_TESTS=OFF -- mir.yml, windows-nfsim.yml and
windows-tail.yml all do -- and no job ever invoked the binary. The pre-push
hook covers python/tests; the C++ side had no local or remote equivalent, so
the suite's only gate was a developer remembering to build it by hand.
#28 is what that costs. It flipped SteadyStateOptions::method from "newton" to
"integration", updated the Python side, and left the C++ assertion in
test_find_steady_state_methods pinned to the old default. The suite sat at
49/50 on every branch from that merge until #36 fixed it, and no CI run could
have said so. A permanently-red suite is worse than a smaller green one,
because a genuine regression arrives looking exactly like the one everyone has
learned to wave off.
The job is Linux-only and lean. KLU, NFsim and RuleMonkey are OFF -- test_bngsim
links none of them, and skipping the vendored builds takes the target count from
146 to 81. The wheels/MIR/Windows jobs already cover those subsystems. LAPACK
stays at its default: find_package(LAPACK QUIET) degrades to the reference dense
solver when absent, which the Linux CI run exercised (built-in dense LU, 50/50).
Two things fail the job, not one. test_bngsim exits nonzero unless
tests_passed == tests_run, and pipefail carries that through the tee. A second
step asserts the DENOMINATOR against a floor -- the failure mode an exit code
cannot see: a suite that goes green because tests stopped being registered.
Every RUN_TEST is unconditionally compiled (no #if around any of them), so the
total is a real invariant, and the floor only ever moves up.
Verified both directions: current main exits 1 at 49/50 naming the stale
assertion, the #36 fix exits 0 at 50/50, and the guard was exercised against
four captured logs (pass, the 49/50 stale run, a doctored 41/41 shrink, and a
no-summary crash), failing the last two and passing the first two.
Not covered: the other native binaries in tests/CMakeLists.txt stay ungated.
Extending this to ctest is a fair follow-up; this closes the gap that drew blood.
@wshlavacek
wshlavacek deleted the claude/stoic-turing-c37f0c branch July 22, 2026 22:58
wshlavacek added a commit that referenced this pull request Jul 24, 2026
…#39) (#47)
The native-tests gate added in #38 built and ran test_bngsim only; every
other binary in tests/CMakeLists.txt was built by nothing in CI and run by
nothing anywhere. Driving the suite with `ctest` instead of a single named
target picks up any add_test() binary automatically, and surfaced two latent
breakages that had gone undetected precisely because nothing built them.
tests/CMakeLists.txt:
- Fix test_c_api registration. Its TEST_DATA_DIR + add_test lines had been
copy-paste-slipped onto test_validation, so test_c_api compiled and was
then dropped on the floor with no add_test(). test_c_api reads its model
from argv[1] (not a TEST_DATA_DIR define), so register it as c_api_test
with simple_decay.net; it is model-agnostic and exercises every branch.
- Fix test_seed_count_rounding compile break (present since initial release).
It includes bngsim/platform_compat.hpp (in include/) but only put src/ on
its search path and does not link bngsim, so the standalone target never
compiled. Add include/ to its include directories.
.github/workflows/native-tests.yml:
- Build all targets, then gate with
`ctest --test-dir build --output-on-failure --no-tests=error` so new tests
run without a workflow edit and a zero-test (mis-scoped) run fails.
- Keep the false-green guard in two layers: a ctest binary-count floor for a
binary silently losing its add_test(), plus test_bngsim's own case-count
floor, which ctest cannot see since it treats the binary as one test — the
exact #28/#36 failure mode.
- Document the NFsim tradeoff explicitly: the NFsim tests exist only with
BNGSIM_BUILD_NFSIM=ON, stay off this lean Linux leg, and are covered by
windows-nfsim.yml.
Sign up for freeto 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.

1 participant

@wshlavacek
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

tests: fix stale steady-state default left behind by #28 - #36

Merged
wshlavacek merged 2 commits into
mainfrom
claude/stoic-turing-c37f0c
Jul 22, 2026
Merged

tests: fix stale steady-state default left behind by #28#36
wshlavacek merged 2 commits into
mainfrom
claude/stoic-turing-c37f0c

Conversation

@wshlavacek

Copy link
Copy Markdown
Collaborator

What

8a0609a ("steady_state: default to method=\"integration\" (#28)") flipped SteadyStateOptions::method from "newton" to "integration" and updated the Python side, but not the C++ one. test_find_steady_state_methods in tests/test_bngsim.cpp kept asserting the old default, so the native suite has reported 49/50 on every branch since — verified on a clean tree at 52c9022, unrelated to any in-flight work.

Two assertions in the function's first block were stale, both for the same reason: the default's method string, and the method_used it echoes back. Also reworded the comment block above the function, which described the two-tier integrate-then-polish path as the default; it now describes the parity early-stop and cites the 1.4-3.9x net cost #28 measured as why the polish stopped being the default.

Why it matters beyond the count

A permanently-red suite is worse than a slightly smaller one — a genuine regression in this file arrives looking exactly like the failure everyone has learned to wave off.

Notes for review

The test's intent is preserved; only the expected default changed. Two things worth confirming:

  • Newton keeps direct coverage without the default block. The "kinsol" block asserts it converges and echoes the canonical method_used == "newton", and the parity block selects "newton" explicitly to compare equilibrium concentrations against integration. The other three blocks are correct as written and are untouched.
  • The residual assertion still bites. It runs at the default tol = 1e-9, not the looser 1e-8 the explicit-integration block uses — the parity early-stop clears the tighter bound on the reversible model on its own, so this wasn't relaxed to make it pass.

Checks

  • tests/test_bngsim (Ninja, Release): 50/50, up from 49/50.
  • pytest -q python/tests via the pre-push hook: passed.

🤖 Generated with Claude Code

8a0609a flipped SteadyStateOptions::method from "newton" to "integration"
and updated the Python side, but not the C++ one. test_find_steady_state_methods
kept asserting the old default, so the native suite has reported 49/50 on
every branch since -- a standing failure that makes a real regression easy
to wave off as "the known one".
Two assertions in the first block were stale, both for the same reason: the
default's method string, and the method_used it echoes back. Only the expected
default changes here; the rest of the function is correct as written and is
left alone.
Newton keeps its direct coverage without the default block: the "kinsol" block
asserts it converges and echoes the canonical method_used == "newton", and the
parity block selects it explicitly to compare equilibrium concentrations
against integration. The default's residual assertion still runs at the default
tol = 1e-9 rather than the looser 1e-8 the explicit-integration block uses --
the parity early-stop clears the tighter bound on the reversible model on its
own.
The comment block above the function described the two-tier
integrate-then-polish path as the default; it now describes the parity
early-stop and cites the 1.4-3.9x net cost #28 measured as why the polish
stopped being the default.
Checks: 50/50 in tests/test_bngsim (Ninja, Release).
@wshlavacek
wshlavacek merged commit c34c0b0 into mainJul 22, 2026
1 check passed
wshlavacek added a commit that referenced this pull request Jul 22, 2026
tests/test_bngsim had no gate at any stage. Every workflow that compiles the
project passes BNGSIM_BUILD_TESTS=OFF -- mir.yml, windows-nfsim.yml and
windows-tail.yml all do -- and no job ever invoked the binary. The pre-push
hook covers python/tests; the C++ side had no local or remote equivalent, so
the suite's only gate was a developer remembering to build it by hand.
#28 is what that costs. It flipped SteadyStateOptions::method from "newton" to
"integration", updated the Python side, and left the C++ assertion in
test_find_steady_state_methods pinned to the old default. The suite sat at
49/50 on every branch from that merge onward, and no CI run could have said so.
The failure's longevity was a consequence of its invisibility: a permanently-red
suite is worse than a smaller green one, because a genuine regression arrives
looking exactly like the one everyone has learned to wave off.
The job is Linux-only and lean. KLU, NFsim and RuleMonkey are OFF -- test_bngsim
links none of them, and skipping the vendored builds takes the target count from
146 to 81. The wheels/MIR/Windows jobs already cover those subsystems. LAPACK
stays at its default because find_package(LAPACK QUIET) degrades to the
reference dense solver when absent, so Linux builds either way.
Two things fail the job, not one. test_bngsim exits nonzero unless
tests_passed == tests_run, and pipefail carries that through the tee. A second
step then asserts the DENOMINATOR against a floor, which is the failure mode an
exit code cannot see: a suite that goes green because tests stopped being
registered. Every RUN_TEST is unconditionally compiled -- there is no #if around
any of them -- so the total is a real invariant rather than a platform-dependent
count, and the floor only ever moves up.
Verified by building both sides locally with the exact flags: current main
exits 1 at 49/50 naming the stale assertion, and the #36 fix exits 0 at 50/50.
The guard was exercised against four captured logs -- pass, the 49/50 stale
run, a doctored 41/41 shrink, and a no-summary crash -- failing on the last two
and passing the first two.
Not covered: the other native binaries in tests/CMakeLists.txt (test_validation,
test_c_api, test_lapack_dense_linsol, test_seed_count_rounding, the NFsim pair)
stay ungated. Extending this to ctest is a fair follow-up; this closes the gap
that actually drew blood.
wshlavacek added a commit that referenced this pull request Jul 22, 2026
tests/test_bngsim had no gate at any stage. Every workflow that compiles the
project passes BNGSIM_BUILD_TESTS=OFF -- mir.yml, windows-nfsim.yml and
windows-tail.yml all do -- and no job ever invoked the binary. The pre-push
hook covers python/tests; the C++ side had no local or remote equivalent, so
the suite's only gate was a developer remembering to build it by hand.
#28 is what that costs. It flipped SteadyStateOptions::method from "newton" to
"integration", updated the Python side, and left the C++ assertion in
test_find_steady_state_methods pinned to the old default. The suite sat at
49/50 on every branch from that merge until #36 fixed it, and no CI run could
have said so. A permanently-red suite is worse than a smaller green one,
because a genuine regression arrives looking exactly like the one everyone has
learned to wave off.
The job is Linux-only and lean. KLU, NFsim and RuleMonkey are OFF -- test_bngsim
links none of them, and skipping the vendored builds takes the target count from
146 to 81. The wheels/MIR/Windows jobs already cover those subsystems. LAPACK
stays at its default: find_package(LAPACK QUIET) degrades to the reference dense
solver when absent, which the Linux CI run exercised (built-in dense LU, 50/50).
Two things fail the job, not one. test_bngsim exits nonzero unless
tests_passed == tests_run, and pipefail carries that through the tee. A second
step asserts the DENOMINATOR against a floor -- the failure mode an exit code
cannot see: a suite that goes green because tests stopped being registered.
Every RUN_TEST is unconditionally compiled (no #if around any of them), so the
total is a real invariant, and the floor only ever moves up.
Verified both directions: current main exits 1 at 49/50 naming the stale
assertion, the #36 fix exits 0 at 50/50, and the guard was exercised against
four captured logs (pass, the 49/50 stale run, a doctored 41/41 shrink, and a
no-summary crash), failing the last two and passing the first two.
Not covered: the other native binaries in tests/CMakeLists.txt stay ungated.
Extending this to ctest is a fair follow-up; this closes the gap that drew blood.
@wshlavacek
wshlavacek deleted the claude/stoic-turing-c37f0c branch July 22, 2026 22:58
wshlavacek added a commit that referenced this pull request Jul 24, 2026
…#39) (#47)
The native-tests gate added in #38 built and ran test_bngsim only; every
other binary in tests/CMakeLists.txt was built by nothing in CI and run by
nothing anywhere. Driving the suite with `ctest` instead of a single named
target picks up any add_test() binary automatically, and surfaced two latent
breakages that had gone undetected precisely because nothing built them.
tests/CMakeLists.txt:
- Fix test_c_api registration. Its TEST_DATA_DIR + add_test lines had been
copy-paste-slipped onto test_validation, so test_c_api compiled and was
then dropped on the floor with no add_test(). test_c_api reads its model
from argv[1] (not a TEST_DATA_DIR define), so register it as c_api_test
with simple_decay.net; it is model-agnostic and exercises every branch.
- Fix test_seed_count_rounding compile break (present since initial release).
It includes bngsim/platform_compat.hpp (in include/) but only put src/ on
its search path and does not link bngsim, so the standalone target never
compiled. Add include/ to its include directories.
.github/workflows/native-tests.yml:
- Build all targets, then gate with
`ctest --test-dir build --output-on-failure --no-tests=error` so new tests
run without a workflow edit and a zero-test (mis-scoped) run fails.
- Keep the false-green guard in two layers: a ctest binary-count floor for a
binary silently losing its add_test(), plus test_bngsim's own case-count
floor, which ctest cannot see since it treats the binary as one test — the
exact #28/#36 failure mode.
- Document the NFsim tradeoff explicitly: the NFsim tests exist only with
BNGSIM_BUILD_NFSIM=ON, stay off this lean Linux leg, and are covered by
windows-nfsim.yml.
Sign up for freeto 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.

1 participant

@wshlavacek
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

tests: fix stale steady-state default left behind by #28 - #36

Merged
wshlavacek merged 2 commits into
mainfrom
claude/stoic-turing-c37f0c
Jul 22, 2026
Merged

tests: fix stale steady-state default left behind by #28#36
wshlavacek merged 2 commits into
mainfrom
claude/stoic-turing-c37f0c

Conversation

@wshlavacek

Copy link
Copy Markdown
Collaborator

What

8a0609a ("steady_state: default to method=\"integration\" (#28)") flipped SteadyStateOptions::method from "newton" to "integration" and updated the Python side, but not the C++ one. test_find_steady_state_methods in tests/test_bngsim.cpp kept asserting the old default, so the native suite has reported 49/50 on every branch since — verified on a clean tree at 52c9022, unrelated to any in-flight work.

Two assertions in the function's first block were stale, both for the same reason: the default's method string, and the method_used it echoes back. Also reworded the comment block above the function, which described the two-tier integrate-then-polish path as the default; it now describes the parity early-stop and cites the 1.4-3.9x net cost #28 measured as why the polish stopped being the default.

Why it matters beyond the count

A permanently-red suite is worse than a slightly smaller one — a genuine regression in this file arrives looking exactly like the failure everyone has learned to wave off.

Notes for review

The test's intent is preserved; only the expected default changed. Two things worth confirming:

  • Newton keeps direct coverage without the default block. The "kinsol" block asserts it converges and echoes the canonical method_used == "newton", and the parity block selects "newton" explicitly to compare equilibrium concentrations against integration. The other three blocks are correct as written and are untouched.
  • The residual assertion still bites. It runs at the default tol = 1e-9, not the looser 1e-8 the explicit-integration block uses — the parity early-stop clears the tighter bound on the reversible model on its own, so this wasn't relaxed to make it pass.

Checks

  • tests/test_bngsim (Ninja, Release): 50/50, up from 49/50.
  • pytest -q python/tests via the pre-push hook: passed.

🤖 Generated with Claude Code

8a0609a flipped SteadyStateOptions::method from "newton" to "integration"
and updated the Python side, but not the C++ one. test_find_steady_state_methods
kept asserting the old default, so the native suite has reported 49/50 on
every branch since -- a standing failure that makes a real regression easy
to wave off as "the known one".
Two assertions in the first block were stale, both for the same reason: the
default's method string, and the method_used it echoes back. Only the expected
default changes here; the rest of the function is correct as written and is
left alone.
Newton keeps its direct coverage without the default block: the "kinsol" block
asserts it converges and echoes the canonical method_used == "newton", and the
parity block selects it explicitly to compare equilibrium concentrations
against integration. The default's residual assertion still runs at the default
tol = 1e-9 rather than the looser 1e-8 the explicit-integration block uses --
the parity early-stop clears the tighter bound on the reversible model on its
own.
The comment block above the function described the two-tier
integrate-then-polish path as the default; it now describes the parity
early-stop and cites the 1.4-3.9x net cost #28 measured as why the polish
stopped being the default.
Checks: 50/50 in tests/test_bngsim (Ninja, Release).
@wshlavacek
wshlavacek merged commit c34c0b0 into mainJul 22, 2026
1 check passed
wshlavacek added a commit that referenced this pull request Jul 22, 2026
tests/test_bngsim had no gate at any stage. Every workflow that compiles the
project passes BNGSIM_BUILD_TESTS=OFF -- mir.yml, windows-nfsim.yml and
windows-tail.yml all do -- and no job ever invoked the binary. The pre-push
hook covers python/tests; the C++ side had no local or remote equivalent, so
the suite's only gate was a developer remembering to build it by hand.
#28 is what that costs. It flipped SteadyStateOptions::method from "newton" to
"integration", updated the Python side, and left the C++ assertion in
test_find_steady_state_methods pinned to the old default. The suite sat at
49/50 on every branch from that merge onward, and no CI run could have said so.
The failure's longevity was a consequence of its invisibility: a permanently-red
suite is worse than a smaller green one, because a genuine regression arrives
looking exactly like the one everyone has learned to wave off.
The job is Linux-only and lean. KLU, NFsim and RuleMonkey are OFF -- test_bngsim
links none of them, and skipping the vendored builds takes the target count from
146 to 81. The wheels/MIR/Windows jobs already cover those subsystems. LAPACK
stays at its default because find_package(LAPACK QUIET) degrades to the
reference dense solver when absent, so Linux builds either way.
Two things fail the job, not one. test_bngsim exits nonzero unless
tests_passed == tests_run, and pipefail carries that through the tee. A second
step then asserts the DENOMINATOR against a floor, which is the failure mode an
exit code cannot see: a suite that goes green because tests stopped being
registered. Every RUN_TEST is unconditionally compiled -- there is no #if around
any of them -- so the total is a real invariant rather than a platform-dependent
count, and the floor only ever moves up.
Verified by building both sides locally with the exact flags: current main
exits 1 at 49/50 naming the stale assertion, and the #36 fix exits 0 at 50/50.
The guard was exercised against four captured logs -- pass, the 49/50 stale
run, a doctored 41/41 shrink, and a no-summary crash -- failing on the last two
and passing the first two.
Not covered: the other native binaries in tests/CMakeLists.txt (test_validation,
test_c_api, test_lapack_dense_linsol, test_seed_count_rounding, the NFsim pair)
stay ungated. Extending this to ctest is a fair follow-up; this closes the gap
that actually drew blood.
wshlavacek added a commit that referenced this pull request Jul 22, 2026
tests/test_bngsim had no gate at any stage. Every workflow that compiles the
project passes BNGSIM_BUILD_TESTS=OFF -- mir.yml, windows-nfsim.yml and
windows-tail.yml all do -- and no job ever invoked the binary. The pre-push
hook covers python/tests; the C++ side had no local or remote equivalent, so
the suite's only gate was a developer remembering to build it by hand.
#28 is what that costs. It flipped SteadyStateOptions::method from "newton" to
"integration", updated the Python side, and left the C++ assertion in
test_find_steady_state_methods pinned to the old default. The suite sat at
49/50 on every branch from that merge until #36 fixed it, and no CI run could
have said so. A permanently-red suite is worse than a smaller green one,
because a genuine regression arrives looking exactly like the one everyone has
learned to wave off.
The job is Linux-only and lean. KLU, NFsim and RuleMonkey are OFF -- test_bngsim
links none of them, and skipping the vendored builds takes the target count from
146 to 81. The wheels/MIR/Windows jobs already cover those subsystems. LAPACK
stays at its default: find_package(LAPACK QUIET) degrades to the reference dense
solver when absent, which the Linux CI run exercised (built-in dense LU, 50/50).
Two things fail the job, not one. test_bngsim exits nonzero unless
tests_passed == tests_run, and pipefail carries that through the tee. A second
step asserts the DENOMINATOR against a floor -- the failure mode an exit code
cannot see: a suite that goes green because tests stopped being registered.
Every RUN_TEST is unconditionally compiled (no #if around any of them), so the
total is a real invariant, and the floor only ever moves up.
Verified both directions: current main exits 1 at 49/50 naming the stale
assertion, the #36 fix exits 0 at 50/50, and the guard was exercised against
four captured logs (pass, the 49/50 stale run, a doctored 41/41 shrink, and a
no-summary crash), failing the last two and passing the first two.
Not covered: the other native binaries in tests/CMakeLists.txt stay ungated.
Extending this to ctest is a fair follow-up; this closes the gap that drew blood.
@wshlavacek
wshlavacek deleted the claude/stoic-turing-c37f0c branch July 22, 2026 22:58
wshlavacek added a commit that referenced this pull request Jul 24, 2026
…#39) (#47)
The native-tests gate added in #38 built and ran test_bngsim only; every
other binary in tests/CMakeLists.txt was built by nothing in CI and run by
nothing anywhere. Driving the suite with `ctest` instead of a single named
target picks up any add_test() binary automatically, and surfaced two latent
breakages that had gone undetected precisely because nothing built them.
tests/CMakeLists.txt:
- Fix test_c_api registration. Its TEST_DATA_DIR + add_test lines had been
copy-paste-slipped onto test_validation, so test_c_api compiled and was
then dropped on the floor with no add_test(). test_c_api reads its model
from argv[1] (not a TEST_DATA_DIR define), so register it as c_api_test
with simple_decay.net; it is model-agnostic and exercises every branch.
- Fix test_seed_count_rounding compile break (present since initial release).
It includes bngsim/platform_compat.hpp (in include/) but only put src/ on
its search path and does not link bngsim, so the standalone target never
compiled. Add include/ to its include directories.
.github/workflows/native-tests.yml:
- Build all targets, then gate with
`ctest --test-dir build --output-on-failure --no-tests=error` so new tests
run without a workflow edit and a zero-test (mis-scoped) run fails.
- Keep the false-green guard in two layers: a ctest binary-count floor for a
binary silently losing its add_test(), plus test_bngsim's own case-count
floor, which ctest cannot see since it treats the binary as one test — the
exact #28/#36 failure mode.
- Document the NFsim tradeoff explicitly: the NFsim tests exist only with
BNGSIM_BUILD_NFSIM=ON, stay off this lean Linux leg, and are covered by
windows-nfsim.yml.
Sign up for freeto 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.

1 participant

@wshlavacek
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

tests: fix stale steady-state default left behind by #28 - #36

Merged
wshlavacek merged 2 commits into
mainfrom
claude/stoic-turing-c37f0c
Jul 22, 2026
Merged

tests: fix stale steady-state default left behind by #28#36
wshlavacek merged 2 commits into
mainfrom
claude/stoic-turing-c37f0c

Conversation

@wshlavacek

Copy link
Copy Markdown
Collaborator

What

8a0609a ("steady_state: default to method=\"integration\" (#28)") flipped SteadyStateOptions::method from "newton" to "integration" and updated the Python side, but not the C++ one. test_find_steady_state_methods in tests/test_bngsim.cpp kept asserting the old default, so the native suite has reported 49/50 on every branch since — verified on a clean tree at 52c9022, unrelated to any in-flight work.

Two assertions in the function's first block were stale, both for the same reason: the default's method string, and the method_used it echoes back. Also reworded the comment block above the function, which described the two-tier integrate-then-polish path as the default; it now describes the parity early-stop and cites the 1.4-3.9x net cost #28 measured as why the polish stopped being the default.

Why it matters beyond the count

A permanently-red suite is worse than a slightly smaller one — a genuine regression in this file arrives looking exactly like the failure everyone has learned to wave off.

Notes for review

The test's intent is preserved; only the expected default changed. Two things worth confirming:

  • Newton keeps direct coverage without the default block. The "kinsol" block asserts it converges and echoes the canonical method_used == "newton", and the parity block selects "newton" explicitly to compare equilibrium concentrations against integration. The other three blocks are correct as written and are untouched.
  • The residual assertion still bites. It runs at the default tol = 1e-9, not the looser 1e-8 the explicit-integration block uses — the parity early-stop clears the tighter bound on the reversible model on its own, so this wasn't relaxed to make it pass.

Checks

  • tests/test_bngsim (Ninja, Release): 50/50, up from 49/50.
  • pytest -q python/tests via the pre-push hook: passed.

🤖 Generated with Claude Code

8a0609a flipped SteadyStateOptions::method from "newton" to "integration"
and updated the Python side, but not the C++ one. test_find_steady_state_methods
kept asserting the old default, so the native suite has reported 49/50 on
every branch since -- a standing failure that makes a real regression easy
to wave off as "the known one".
Two assertions in the first block were stale, both for the same reason: the
default's method string, and the method_used it echoes back. Only the expected
default changes here; the rest of the function is correct as written and is
left alone.
Newton keeps its direct coverage without the default block: the "kinsol" block
asserts it converges and echoes the canonical method_used == "newton", and the
parity block selects it explicitly to compare equilibrium concentrations
against integration. The default's residual assertion still runs at the default
tol = 1e-9 rather than the looser 1e-8 the explicit-integration block uses --
the parity early-stop clears the tighter bound on the reversible model on its
own.
The comment block above the function described the two-tier
integrate-then-polish path as the default; it now describes the parity
early-stop and cites the 1.4-3.9x net cost #28 measured as why the polish
stopped being the default.
Checks: 50/50 in tests/test_bngsim (Ninja, Release).
@wshlavacek
wshlavacek merged commit c34c0b0 into mainJul 22, 2026
1 check passed
wshlavacek added a commit that referenced this pull request Jul 22, 2026
tests/test_bngsim had no gate at any stage. Every workflow that compiles the
project passes BNGSIM_BUILD_TESTS=OFF -- mir.yml, windows-nfsim.yml and
windows-tail.yml all do -- and no job ever invoked the binary. The pre-push
hook covers python/tests; the C++ side had no local or remote equivalent, so
the suite's only gate was a developer remembering to build it by hand.
#28 is what that costs. It flipped SteadyStateOptions::method from "newton" to
"integration", updated the Python side, and left the C++ assertion in
test_find_steady_state_methods pinned to the old default. The suite sat at
49/50 on every branch from that merge onward, and no CI run could have said so.
The failure's longevity was a consequence of its invisibility: a permanently-red
suite is worse than a smaller green one, because a genuine regression arrives
looking exactly like the one everyone has learned to wave off.
The job is Linux-only and lean. KLU, NFsim and RuleMonkey are OFF -- test_bngsim
links none of them, and skipping the vendored builds takes the target count from
146 to 81. The wheels/MIR/Windows jobs already cover those subsystems. LAPACK
stays at its default because find_package(LAPACK QUIET) degrades to the
reference dense solver when absent, so Linux builds either way.
Two things fail the job, not one. test_bngsim exits nonzero unless
tests_passed == tests_run, and pipefail carries that through the tee. A second
step then asserts the DENOMINATOR against a floor, which is the failure mode an
exit code cannot see: a suite that goes green because tests stopped being
registered. Every RUN_TEST is unconditionally compiled -- there is no #if around
any of them -- so the total is a real invariant rather than a platform-dependent
count, and the floor only ever moves up.
Verified by building both sides locally with the exact flags: current main
exits 1 at 49/50 naming the stale assertion, and the #36 fix exits 0 at 50/50.
The guard was exercised against four captured logs -- pass, the 49/50 stale
run, a doctored 41/41 shrink, and a no-summary crash -- failing on the last two
and passing the first two.
Not covered: the other native binaries in tests/CMakeLists.txt (test_validation,
test_c_api, test_lapack_dense_linsol, test_seed_count_rounding, the NFsim pair)
stay ungated. Extending this to ctest is a fair follow-up; this closes the gap
that actually drew blood.
wshlavacek added a commit that referenced this pull request Jul 22, 2026
tests/test_bngsim had no gate at any stage. Every workflow that compiles the
project passes BNGSIM_BUILD_TESTS=OFF -- mir.yml, windows-nfsim.yml and
windows-tail.yml all do -- and no job ever invoked the binary. The pre-push
hook covers python/tests; the C++ side had no local or remote equivalent, so
the suite's only gate was a developer remembering to build it by hand.
#28 is what that costs. It flipped SteadyStateOptions::method from "newton" to
"integration", updated the Python side, and left the C++ assertion in
test_find_steady_state_methods pinned to the old default. The suite sat at
49/50 on every branch from that merge until #36 fixed it, and no CI run could
have said so. A permanently-red suite is worse than a smaller green one,
because a genuine regression arrives looking exactly like the one everyone has
learned to wave off.
The job is Linux-only and lean. KLU, NFsim and RuleMonkey are OFF -- test_bngsim
links none of them, and skipping the vendored builds takes the target count from
146 to 81. The wheels/MIR/Windows jobs already cover those subsystems. LAPACK
stays at its default: find_package(LAPACK QUIET) degrades to the reference dense
solver when absent, which the Linux CI run exercised (built-in dense LU, 50/50).
Two things fail the job, not one. test_bngsim exits nonzero unless
tests_passed == tests_run, and pipefail carries that through the tee. A second
step asserts the DENOMINATOR against a floor -- the failure mode an exit code
cannot see: a suite that goes green because tests stopped being registered.
Every RUN_TEST is unconditionally compiled (no #if around any of them), so the
total is a real invariant, and the floor only ever moves up.
Verified both directions: current main exits 1 at 49/50 naming the stale
assertion, the #36 fix exits 0 at 50/50, and the guard was exercised against
four captured logs (pass, the 49/50 stale run, a doctored 41/41 shrink, and a
no-summary crash), failing the last two and passing the first two.
Not covered: the other native binaries in tests/CMakeLists.txt stay ungated.
Extending this to ctest is a fair follow-up; this closes the gap that drew blood.
@wshlavacek
wshlavacek deleted the claude/stoic-turing-c37f0c branch July 22, 2026 22:58
wshlavacek added a commit that referenced this pull request Jul 24, 2026
…#39) (#47)
The native-tests gate added in #38 built and ran test_bngsim only; every
other binary in tests/CMakeLists.txt was built by nothing in CI and run by
nothing anywhere. Driving the suite with `ctest` instead of a single named
target picks up any add_test() binary automatically, and surfaced two latent
breakages that had gone undetected precisely because nothing built them.
tests/CMakeLists.txt:
- Fix test_c_api registration. Its TEST_DATA_DIR + add_test lines had been
copy-paste-slipped onto test_validation, so test_c_api compiled and was
then dropped on the floor with no add_test(). test_c_api reads its model
from argv[1] (not a TEST_DATA_DIR define), so register it as c_api_test
with simple_decay.net; it is model-agnostic and exercises every branch.
- Fix test_seed_count_rounding compile break (present since initial release).
It includes bngsim/platform_compat.hpp (in include/) but only put src/ on
its search path and does not link bngsim, so the standalone target never
compiled. Add include/ to its include directories.
.github/workflows/native-tests.yml:
- Build all targets, then gate with
`ctest --test-dir build --output-on-failure --no-tests=error` so new tests
run without a workflow edit and a zero-test (mis-scoped) run fails.
- Keep the false-green guard in two layers: a ctest binary-count floor for a
binary silently losing its add_test(), plus test_bngsim's own case-count
floor, which ctest cannot see since it treats the binary as one test — the
exact #28/#36 failure mode.
- Document the NFsim tradeoff explicitly: the NFsim tests exist only with
BNGSIM_BUILD_NFSIM=ON, stay off this lean Linux leg, and are covered by
windows-nfsim.yml.
Sign up for freeto 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.

1 participant

@wshlavacek
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

tests: fix stale steady-state default left behind by #28 - #36

Merged
wshlavacek merged 2 commits into
mainfrom
claude/stoic-turing-c37f0c
Jul 22, 2026
Merged

tests: fix stale steady-state default left behind by #28#36
wshlavacek merged 2 commits into
mainfrom
claude/stoic-turing-c37f0c

Conversation

@wshlavacek

Copy link
Copy Markdown
Collaborator

What

8a0609a ("steady_state: default to method=\"integration\" (#28)") flipped SteadyStateOptions::method from "newton" to "integration" and updated the Python side, but not the C++ one. test_find_steady_state_methods in tests/test_bngsim.cpp kept asserting the old default, so the native suite has reported 49/50 on every branch since — verified on a clean tree at 52c9022, unrelated to any in-flight work.

Two assertions in the function's first block were stale, both for the same reason: the default's method string, and the method_used it echoes back. Also reworded the comment block above the function, which described the two-tier integrate-then-polish path as the default; it now describes the parity early-stop and cites the 1.4-3.9x net cost #28 measured as why the polish stopped being the default.

Why it matters beyond the count

A permanently-red suite is worse than a slightly smaller one — a genuine regression in this file arrives looking exactly like the failure everyone has learned to wave off.

Notes for review

The test's intent is preserved; only the expected default changed. Two things worth confirming:

  • Newton keeps direct coverage without the default block. The "kinsol" block asserts it converges and echoes the canonical method_used == "newton", and the parity block selects "newton" explicitly to compare equilibrium concentrations against integration. The other three blocks are correct as written and are untouched.
  • The residual assertion still bites. It runs at the default tol = 1e-9, not the looser 1e-8 the explicit-integration block uses — the parity early-stop clears the tighter bound on the reversible model on its own, so this wasn't relaxed to make it pass.

Checks

  • tests/test_bngsim (Ninja, Release): 50/50, up from 49/50.
  • pytest -q python/tests via the pre-push hook: passed.

🤖 Generated with Claude Code

8a0609a flipped SteadyStateOptions::method from "newton" to "integration"
and updated the Python side, but not the C++ one. test_find_steady_state_methods
kept asserting the old default, so the native suite has reported 49/50 on
every branch since -- a standing failure that makes a real regression easy
to wave off as "the known one".
Two assertions in the first block were stale, both for the same reason: the
default's method string, and the method_used it echoes back. Only the expected
default changes here; the rest of the function is correct as written and is
left alone.
Newton keeps its direct coverage without the default block: the "kinsol" block
asserts it converges and echoes the canonical method_used == "newton", and the
parity block selects it explicitly to compare equilibrium concentrations
against integration. The default's residual assertion still runs at the default
tol = 1e-9 rather than the looser 1e-8 the explicit-integration block uses --
the parity early-stop clears the tighter bound on the reversible model on its
own.
The comment block above the function described the two-tier
integrate-then-polish path as the default; it now describes the parity
early-stop and cites the 1.4-3.9x net cost #28 measured as why the polish
stopped being the default.
Checks: 50/50 in tests/test_bngsim (Ninja, Release).
@wshlavacek
wshlavacek merged commit c34c0b0 into mainJul 22, 2026
1 check passed
wshlavacek added a commit that referenced this pull request Jul 22, 2026
tests/test_bngsim had no gate at any stage. Every workflow that compiles the
project passes BNGSIM_BUILD_TESTS=OFF -- mir.yml, windows-nfsim.yml and
windows-tail.yml all do -- and no job ever invoked the binary. The pre-push
hook covers python/tests; the C++ side had no local or remote equivalent, so
the suite's only gate was a developer remembering to build it by hand.
#28 is what that costs. It flipped SteadyStateOptions::method from "newton" to
"integration", updated the Python side, and left the C++ assertion in
test_find_steady_state_methods pinned to the old default. The suite sat at
49/50 on every branch from that merge onward, and no CI run could have said so.
The failure's longevity was a consequence of its invisibility: a permanently-red
suite is worse than a smaller green one, because a genuine regression arrives
looking exactly like the one everyone has learned to wave off.
The job is Linux-only and lean. KLU, NFsim and RuleMonkey are OFF -- test_bngsim
links none of them, and skipping the vendored builds takes the target count from
146 to 81. The wheels/MIR/Windows jobs already cover those subsystems. LAPACK
stays at its default because find_package(LAPACK QUIET) degrades to the
reference dense solver when absent, so Linux builds either way.
Two things fail the job, not one. test_bngsim exits nonzero unless
tests_passed == tests_run, and pipefail carries that through the tee. A second
step then asserts the DENOMINATOR against a floor, which is the failure mode an
exit code cannot see: a suite that goes green because tests stopped being
registered. Every RUN_TEST is unconditionally compiled -- there is no #if around
any of them -- so the total is a real invariant rather than a platform-dependent
count, and the floor only ever moves up.
Verified by building both sides locally with the exact flags: current main
exits 1 at 49/50 naming the stale assertion, and the #36 fix exits 0 at 50/50.
The guard was exercised against four captured logs -- pass, the 49/50 stale
run, a doctored 41/41 shrink, and a no-summary crash -- failing on the last two
and passing the first two.
Not covered: the other native binaries in tests/CMakeLists.txt (test_validation,
test_c_api, test_lapack_dense_linsol, test_seed_count_rounding, the NFsim pair)
stay ungated. Extending this to ctest is a fair follow-up; this closes the gap
that actually drew blood.
wshlavacek added a commit that referenced this pull request Jul 22, 2026
tests/test_bngsim had no gate at any stage. Every workflow that compiles the
project passes BNGSIM_BUILD_TESTS=OFF -- mir.yml, windows-nfsim.yml and
windows-tail.yml all do -- and no job ever invoked the binary. The pre-push
hook covers python/tests; the C++ side had no local or remote equivalent, so
the suite's only gate was a developer remembering to build it by hand.
#28 is what that costs. It flipped SteadyStateOptions::method from "newton" to
"integration", updated the Python side, and left the C++ assertion in
test_find_steady_state_methods pinned to the old default. The suite sat at
49/50 on every branch from that merge until #36 fixed it, and no CI run could
have said so. A permanently-red suite is worse than a smaller green one,
because a genuine regression arrives looking exactly like the one everyone has
learned to wave off.
The job is Linux-only and lean. KLU, NFsim and RuleMonkey are OFF -- test_bngsim
links none of them, and skipping the vendored builds takes the target count from
146 to 81. The wheels/MIR/Windows jobs already cover those subsystems. LAPACK
stays at its default: find_package(LAPACK QUIET) degrades to the reference dense
solver when absent, which the Linux CI run exercised (built-in dense LU, 50/50).
Two things fail the job, not one. test_bngsim exits nonzero unless
tests_passed == tests_run, and pipefail carries that through the tee. A second
step asserts the DENOMINATOR against a floor -- the failure mode an exit code
cannot see: a suite that goes green because tests stopped being registered.
Every RUN_TEST is unconditionally compiled (no #if around any of them), so the
total is a real invariant, and the floor only ever moves up.
Verified both directions: current main exits 1 at 49/50 naming the stale
assertion, the #36 fix exits 0 at 50/50, and the guard was exercised against
four captured logs (pass, the 49/50 stale run, a doctored 41/41 shrink, and a
no-summary crash), failing the last two and passing the first two.
Not covered: the other native binaries in tests/CMakeLists.txt stay ungated.
Extending this to ctest is a fair follow-up; this closes the gap that drew blood.
@wshlavacek
wshlavacek deleted the claude/stoic-turing-c37f0c branch July 22, 2026 22:58
wshlavacek added a commit that referenced this pull request Jul 24, 2026
…#39) (#47)
The native-tests gate added in #38 built and ran test_bngsim only; every
other binary in tests/CMakeLists.txt was built by nothing in CI and run by
nothing anywhere. Driving the suite with `ctest` instead of a single named
target picks up any add_test() binary automatically, and surfaced two latent
breakages that had gone undetected precisely because nothing built them.
tests/CMakeLists.txt:
- Fix test_c_api registration. Its TEST_DATA_DIR + add_test lines had been
copy-paste-slipped onto test_validation, so test_c_api compiled and was
then dropped on the floor with no add_test(). test_c_api reads its model
from argv[1] (not a TEST_DATA_DIR define), so register it as c_api_test
with simple_decay.net; it is model-agnostic and exercises every branch.
- Fix test_seed_count_rounding compile break (present since initial release).
It includes bngsim/platform_compat.hpp (in include/) but only put src/ on
its search path and does not link bngsim, so the standalone target never
compiled. Add include/ to its include directories.
.github/workflows/native-tests.yml:
- Build all targets, then gate with
`ctest --test-dir build --output-on-failure --no-tests=error` so new tests
run without a workflow edit and a zero-test (mis-scoped) run fails.
- Keep the false-green guard in two layers: a ctest binary-count floor for a
binary silently losing its add_test(), plus test_bngsim's own case-count
floor, which ctest cannot see since it treats the binary as one test — the
exact #28/#36 failure mode.
- Document the NFsim tradeoff explicitly: the NFsim tests exist only with
BNGSIM_BUILD_NFSIM=ON, stay off this lean Linux leg, and are covered by
windows-nfsim.yml.
Sign up for freeto 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.

1 participant

@wshlavacek
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

tests: fix stale steady-state default left behind by #28 - #36

Merged
wshlavacek merged 2 commits into
mainfrom
claude/stoic-turing-c37f0c
Jul 22, 2026
Merged

tests: fix stale steady-state default left behind by #28#36
wshlavacek merged 2 commits into
mainfrom
claude/stoic-turing-c37f0c

Conversation

@wshlavacek

Copy link
Copy Markdown
Collaborator

What

8a0609a ("steady_state: default to method=\"integration\" (#28)") flipped SteadyStateOptions::method from "newton" to "integration" and updated the Python side, but not the C++ one. test_find_steady_state_methods in tests/test_bngsim.cpp kept asserting the old default, so the native suite has reported 49/50 on every branch since — verified on a clean tree at 52c9022, unrelated to any in-flight work.

Two assertions in the function's first block were stale, both for the same reason: the default's method string, and the method_used it echoes back. Also reworded the comment block above the function, which described the two-tier integrate-then-polish path as the default; it now describes the parity early-stop and cites the 1.4-3.9x net cost #28 measured as why the polish stopped being the default.

Why it matters beyond the count

A permanently-red suite is worse than a slightly smaller one — a genuine regression in this file arrives looking exactly like the failure everyone has learned to wave off.

Notes for review

The test's intent is preserved; only the expected default changed. Two things worth confirming:

  • Newton keeps direct coverage without the default block. The "kinsol" block asserts it converges and echoes the canonical method_used == "newton", and the parity block selects "newton" explicitly to compare equilibrium concentrations against integration. The other three blocks are correct as written and are untouched.
  • The residual assertion still bites. It runs at the default tol = 1e-9, not the looser 1e-8 the explicit-integration block uses — the parity early-stop clears the tighter bound on the reversible model on its own, so this wasn't relaxed to make it pass.

Checks

  • tests/test_bngsim (Ninja, Release): 50/50, up from 49/50.
  • pytest -q python/tests via the pre-push hook: passed.

🤖 Generated with Claude Code

8a0609a flipped SteadyStateOptions::method from "newton" to "integration"
and updated the Python side, but not the C++ one. test_find_steady_state_methods
kept asserting the old default, so the native suite has reported 49/50 on
every branch since -- a standing failure that makes a real regression easy
to wave off as "the known one".
Two assertions in the first block were stale, both for the same reason: the
default's method string, and the method_used it echoes back. Only the expected
default changes here; the rest of the function is correct as written and is
left alone.
Newton keeps its direct coverage without the default block: the "kinsol" block
asserts it converges and echoes the canonical method_used == "newton", and the
parity block selects it explicitly to compare equilibrium concentrations
against integration. The default's residual assertion still runs at the default
tol = 1e-9 rather than the looser 1e-8 the explicit-integration block uses --
the parity early-stop clears the tighter bound on the reversible model on its
own.
The comment block above the function described the two-tier
integrate-then-polish path as the default; it now describes the parity
early-stop and cites the 1.4-3.9x net cost #28 measured as why the polish
stopped being the default.
Checks: 50/50 in tests/test_bngsim (Ninja, Release).
@wshlavacek
wshlavacek merged commit c34c0b0 into mainJul 22, 2026
1 check passed
wshlavacek added a commit that referenced this pull request Jul 22, 2026
tests/test_bngsim had no gate at any stage. Every workflow that compiles the
project passes BNGSIM_BUILD_TESTS=OFF -- mir.yml, windows-nfsim.yml and
windows-tail.yml all do -- and no job ever invoked the binary. The pre-push
hook covers python/tests; the C++ side had no local or remote equivalent, so
the suite's only gate was a developer remembering to build it by hand.
#28 is what that costs. It flipped SteadyStateOptions::method from "newton" to
"integration", updated the Python side, and left the C++ assertion in
test_find_steady_state_methods pinned to the old default. The suite sat at
49/50 on every branch from that merge onward, and no CI run could have said so.
The failure's longevity was a consequence of its invisibility: a permanently-red
suite is worse than a smaller green one, because a genuine regression arrives
looking exactly like the one everyone has learned to wave off.
The job is Linux-only and lean. KLU, NFsim and RuleMonkey are OFF -- test_bngsim
links none of them, and skipping the vendored builds takes the target count from
146 to 81. The wheels/MIR/Windows jobs already cover those subsystems. LAPACK
stays at its default because find_package(LAPACK QUIET) degrades to the
reference dense solver when absent, so Linux builds either way.
Two things fail the job, not one. test_bngsim exits nonzero unless
tests_passed == tests_run, and pipefail carries that through the tee. A second
step then asserts the DENOMINATOR against a floor, which is the failure mode an
exit code cannot see: a suite that goes green because tests stopped being
registered. Every RUN_TEST is unconditionally compiled -- there is no #if around
any of them -- so the total is a real invariant rather than a platform-dependent
count, and the floor only ever moves up.
Verified by building both sides locally with the exact flags: current main
exits 1 at 49/50 naming the stale assertion, and the #36 fix exits 0 at 50/50.
The guard was exercised against four captured logs -- pass, the 49/50 stale
run, a doctored 41/41 shrink, and a no-summary crash -- failing on the last two
and passing the first two.
Not covered: the other native binaries in tests/CMakeLists.txt (test_validation,
test_c_api, test_lapack_dense_linsol, test_seed_count_rounding, the NFsim pair)
stay ungated. Extending this to ctest is a fair follow-up; this closes the gap
that actually drew blood.
wshlavacek added a commit that referenced this pull request Jul 22, 2026
tests/test_bngsim had no gate at any stage. Every workflow that compiles the
project passes BNGSIM_BUILD_TESTS=OFF -- mir.yml, windows-nfsim.yml and
windows-tail.yml all do -- and no job ever invoked the binary. The pre-push
hook covers python/tests; the C++ side had no local or remote equivalent, so
the suite's only gate was a developer remembering to build it by hand.
#28 is what that costs. It flipped SteadyStateOptions::method from "newton" to
"integration", updated the Python side, and left the C++ assertion in
test_find_steady_state_methods pinned to the old default. The suite sat at
49/50 on every branch from that merge until #36 fixed it, and no CI run could
have said so. A permanently-red suite is worse than a smaller green one,
because a genuine regression arrives looking exactly like the one everyone has
learned to wave off.
The job is Linux-only and lean. KLU, NFsim and RuleMonkey are OFF -- test_bngsim
links none of them, and skipping the vendored builds takes the target count from
146 to 81. The wheels/MIR/Windows jobs already cover those subsystems. LAPACK
stays at its default: find_package(LAPACK QUIET) degrades to the reference dense
solver when absent, which the Linux CI run exercised (built-in dense LU, 50/50).
Two things fail the job, not one. test_bngsim exits nonzero unless
tests_passed == tests_run, and pipefail carries that through the tee. A second
step asserts the DENOMINATOR against a floor -- the failure mode an exit code
cannot see: a suite that goes green because tests stopped being registered.
Every RUN_TEST is unconditionally compiled (no #if around any of them), so the
total is a real invariant, and the floor only ever moves up.
Verified both directions: current main exits 1 at 49/50 naming the stale
assertion, the #36 fix exits 0 at 50/50, and the guard was exercised against
four captured logs (pass, the 49/50 stale run, a doctored 41/41 shrink, and a
no-summary crash), failing the last two and passing the first two.
Not covered: the other native binaries in tests/CMakeLists.txt stay ungated.
Extending this to ctest is a fair follow-up; this closes the gap that drew blood.
@wshlavacek
wshlavacek deleted the claude/stoic-turing-c37f0c branch July 22, 2026 22:58
wshlavacek added a commit that referenced this pull request Jul 24, 2026
…#39) (#47)
The native-tests gate added in #38 built and ran test_bngsim only; every
other binary in tests/CMakeLists.txt was built by nothing in CI and run by
nothing anywhere. Driving the suite with `ctest` instead of a single named
target picks up any add_test() binary automatically, and surfaced two latent
breakages that had gone undetected precisely because nothing built them.
tests/CMakeLists.txt:
- Fix test_c_api registration. Its TEST_DATA_DIR + add_test lines had been
copy-paste-slipped onto test_validation, so test_c_api compiled and was
then dropped on the floor with no add_test(). test_c_api reads its model
from argv[1] (not a TEST_DATA_DIR define), so register it as c_api_test
with simple_decay.net; it is model-agnostic and exercises every branch.
- Fix test_seed_count_rounding compile break (present since initial release).
It includes bngsim/platform_compat.hpp (in include/) but only put src/ on
its search path and does not link bngsim, so the standalone target never
compiled. Add include/ to its include directories.
.github/workflows/native-tests.yml:
- Build all targets, then gate with
`ctest --test-dir build --output-on-failure --no-tests=error` so new tests
run without a workflow edit and a zero-test (mis-scoped) run fails.
- Keep the false-green guard in two layers: a ctest binary-count floor for a
binary silently losing its add_test(), plus test_bngsim's own case-count
floor, which ctest cannot see since it treats the binary as one test — the
exact #28/#36 failure mode.
- Document the NFsim tradeoff explicitly: the NFsim tests exist only with
BNGSIM_BUILD_NFSIM=ON, stay off this lean Linux leg, and are covered by
windows-nfsim.yml.
Sign up for freeto 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.

1 participant

@wshlavacek
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

tests: fix stale steady-state default left behind by #28 - #36

Merged
wshlavacek merged 2 commits into
mainfrom
claude/stoic-turing-c37f0c
Jul 22, 2026
Merged

tests: fix stale steady-state default left behind by #28#36
wshlavacek merged 2 commits into
mainfrom
claude/stoic-turing-c37f0c

Conversation

@wshlavacek

Copy link
Copy Markdown
Collaborator

What

8a0609a ("steady_state: default to method=\"integration\" (#28)") flipped SteadyStateOptions::method from "newton" to "integration" and updated the Python side, but not the C++ one. test_find_steady_state_methods in tests/test_bngsim.cpp kept asserting the old default, so the native suite has reported 49/50 on every branch since — verified on a clean tree at 52c9022, unrelated to any in-flight work.

Two assertions in the function's first block were stale, both for the same reason: the default's method string, and the method_used it echoes back. Also reworded the comment block above the function, which described the two-tier integrate-then-polish path as the default; it now describes the parity early-stop and cites the 1.4-3.9x net cost #28 measured as why the polish stopped being the default.

Why it matters beyond the count

A permanently-red suite is worse than a slightly smaller one — a genuine regression in this file arrives looking exactly like the failure everyone has learned to wave off.

Notes for review

The test's intent is preserved; only the expected default changed. Two things worth confirming:

  • Newton keeps direct coverage without the default block. The "kinsol" block asserts it converges and echoes the canonical method_used == "newton", and the parity block selects "newton" explicitly to compare equilibrium concentrations against integration. The other three blocks are correct as written and are untouched.
  • The residual assertion still bites. It runs at the default tol = 1e-9, not the looser 1e-8 the explicit-integration block uses — the parity early-stop clears the tighter bound on the reversible model on its own, so this wasn't relaxed to make it pass.

Checks

  • tests/test_bngsim (Ninja, Release): 50/50, up from 49/50.
  • pytest -q python/tests via the pre-push hook: passed.

🤖 Generated with Claude Code

8a0609a flipped SteadyStateOptions::method from "newton" to "integration"
and updated the Python side, but not the C++ one. test_find_steady_state_methods
kept asserting the old default, so the native suite has reported 49/50 on
every branch since -- a standing failure that makes a real regression easy
to wave off as "the known one".
Two assertions in the first block were stale, both for the same reason: the
default's method string, and the method_used it echoes back. Only the expected
default changes here; the rest of the function is correct as written and is
left alone.
Newton keeps its direct coverage without the default block: the "kinsol" block
asserts it converges and echoes the canonical method_used == "newton", and the
parity block selects it explicitly to compare equilibrium concentrations
against integration. The default's residual assertion still runs at the default
tol = 1e-9 rather than the looser 1e-8 the explicit-integration block uses --
the parity early-stop clears the tighter bound on the reversible model on its
own.
The comment block above the function described the two-tier
integrate-then-polish path as the default; it now describes the parity
early-stop and cites the 1.4-3.9x net cost #28 measured as why the polish
stopped being the default.
Checks: 50/50 in tests/test_bngsim (Ninja, Release).
@wshlavacek
wshlavacek merged commit c34c0b0 into mainJul 22, 2026
1 check passed
wshlavacek added a commit that referenced this pull request Jul 22, 2026
tests/test_bngsim had no gate at any stage. Every workflow that compiles the
project passes BNGSIM_BUILD_TESTS=OFF -- mir.yml, windows-nfsim.yml and
windows-tail.yml all do -- and no job ever invoked the binary. The pre-push
hook covers python/tests; the C++ side had no local or remote equivalent, so
the suite's only gate was a developer remembering to build it by hand.
#28 is what that costs. It flipped SteadyStateOptions::method from "newton" to
"integration", updated the Python side, and left the C++ assertion in
test_find_steady_state_methods pinned to the old default. The suite sat at
49/50 on every branch from that merge onward, and no CI run could have said so.
The failure's longevity was a consequence of its invisibility: a permanently-red
suite is worse than a smaller green one, because a genuine regression arrives
looking exactly like the one everyone has learned to wave off.
The job is Linux-only and lean. KLU, NFsim and RuleMonkey are OFF -- test_bngsim
links none of them, and skipping the vendored builds takes the target count from
146 to 81. The wheels/MIR/Windows jobs already cover those subsystems. LAPACK
stays at its default because find_package(LAPACK QUIET) degrades to the
reference dense solver when absent, so Linux builds either way.
Two things fail the job, not one. test_bngsim exits nonzero unless
tests_passed == tests_run, and pipefail carries that through the tee. A second
step then asserts the DENOMINATOR against a floor, which is the failure mode an
exit code cannot see: a suite that goes green because tests stopped being
registered. Every RUN_TEST is unconditionally compiled -- there is no #if around
any of them -- so the total is a real invariant rather than a platform-dependent
count, and the floor only ever moves up.
Verified by building both sides locally with the exact flags: current main
exits 1 at 49/50 naming the stale assertion, and the #36 fix exits 0 at 50/50.
The guard was exercised against four captured logs -- pass, the 49/50 stale
run, a doctored 41/41 shrink, and a no-summary crash -- failing on the last two
and passing the first two.
Not covered: the other native binaries in tests/CMakeLists.txt (test_validation,
test_c_api, test_lapack_dense_linsol, test_seed_count_rounding, the NFsim pair)
stay ungated. Extending this to ctest is a fair follow-up; this closes the gap
that actually drew blood.
wshlavacek added a commit that referenced this pull request Jul 22, 2026
tests/test_bngsim had no gate at any stage. Every workflow that compiles the
project passes BNGSIM_BUILD_TESTS=OFF -- mir.yml, windows-nfsim.yml and
windows-tail.yml all do -- and no job ever invoked the binary. The pre-push
hook covers python/tests; the C++ side had no local or remote equivalent, so
the suite's only gate was a developer remembering to build it by hand.
#28 is what that costs. It flipped SteadyStateOptions::method from "newton" to
"integration", updated the Python side, and left the C++ assertion in
test_find_steady_state_methods pinned to the old default. The suite sat at
49/50 on every branch from that merge until #36 fixed it, and no CI run could
have said so. A permanently-red suite is worse than a smaller green one,
because a genuine regression arrives looking exactly like the one everyone has
learned to wave off.
The job is Linux-only and lean. KLU, NFsim and RuleMonkey are OFF -- test_bngsim
links none of them, and skipping the vendored builds takes the target count from
146 to 81. The wheels/MIR/Windows jobs already cover those subsystems. LAPACK
stays at its default: find_package(LAPACK QUIET) degrades to the reference dense
solver when absent, which the Linux CI run exercised (built-in dense LU, 50/50).
Two things fail the job, not one. test_bngsim exits nonzero unless
tests_passed == tests_run, and pipefail carries that through the tee. A second
step asserts the DENOMINATOR against a floor -- the failure mode an exit code
cannot see: a suite that goes green because tests stopped being registered.
Every RUN_TEST is unconditionally compiled (no #if around any of them), so the
total is a real invariant, and the floor only ever moves up.
Verified both directions: current main exits 1 at 49/50 naming the stale
assertion, the #36 fix exits 0 at 50/50, and the guard was exercised against
four captured logs (pass, the 49/50 stale run, a doctored 41/41 shrink, and a
no-summary crash), failing the last two and passing the first two.
Not covered: the other native binaries in tests/CMakeLists.txt stay ungated.
Extending this to ctest is a fair follow-up; this closes the gap that drew blood.
@wshlavacek
wshlavacek deleted the claude/stoic-turing-c37f0c branch July 22, 2026 22:58
wshlavacek added a commit that referenced this pull request Jul 24, 2026
…#39) (#47)
The native-tests gate added in #38 built and ran test_bngsim only; every
other binary in tests/CMakeLists.txt was built by nothing in CI and run by
nothing anywhere. Driving the suite with `ctest` instead of a single named
target picks up any add_test() binary automatically, and surfaced two latent
breakages that had gone undetected precisely because nothing built them.
tests/CMakeLists.txt:
- Fix test_c_api registration. Its TEST_DATA_DIR + add_test lines had been
copy-paste-slipped onto test_validation, so test_c_api compiled and was
then dropped on the floor with no add_test(). test_c_api reads its model
from argv[1] (not a TEST_DATA_DIR define), so register it as c_api_test
with simple_decay.net; it is model-agnostic and exercises every branch.
- Fix test_seed_count_rounding compile break (present since initial release).
It includes bngsim/platform_compat.hpp (in include/) but only put src/ on
its search path and does not link bngsim, so the standalone target never
compiled. Add include/ to its include directories.
.github/workflows/native-tests.yml:
- Build all targets, then gate with
`ctest --test-dir build --output-on-failure --no-tests=error` so new tests
run without a workflow edit and a zero-test (mis-scoped) run fails.
- Keep the false-green guard in two layers: a ctest binary-count floor for a
binary silently losing its add_test(), plus test_bngsim's own case-count
floor, which ctest cannot see since it treats the binary as one test — the
exact #28/#36 failure mode.
- Document the NFsim tradeoff explicitly: the NFsim tests exist only with
BNGSIM_BUILD_NFSIM=ON, stay off this lean Linux leg, and are covered by
windows-nfsim.yml.
Sign up for freeto 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.

1 participant

@wshlavacek
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

tests: fix stale steady-state default left behind by #28 - #36

Merged
wshlavacek merged 2 commits into
mainfrom
claude/stoic-turing-c37f0c
Jul 22, 2026
Merged

tests: fix stale steady-state default left behind by #28#36
wshlavacek merged 2 commits into
mainfrom
claude/stoic-turing-c37f0c

Conversation

@wshlavacek

Copy link
Copy Markdown
Collaborator

What

8a0609a ("steady_state: default to method=\"integration\" (#28)") flipped SteadyStateOptions::method from "newton" to "integration" and updated the Python side, but not the C++ one. test_find_steady_state_methods in tests/test_bngsim.cpp kept asserting the old default, so the native suite has reported 49/50 on every branch since — verified on a clean tree at 52c9022, unrelated to any in-flight work.

Two assertions in the function's first block were stale, both for the same reason: the default's method string, and the method_used it echoes back. Also reworded the comment block above the function, which described the two-tier integrate-then-polish path as the default; it now describes the parity early-stop and cites the 1.4-3.9x net cost #28 measured as why the polish stopped being the default.

Why it matters beyond the count

A permanently-red suite is worse than a slightly smaller one — a genuine regression in this file arrives looking exactly like the failure everyone has learned to wave off.

Notes for review

The test's intent is preserved; only the expected default changed. Two things worth confirming:

  • Newton keeps direct coverage without the default block. The "kinsol" block asserts it converges and echoes the canonical method_used == "newton", and the parity block selects "newton" explicitly to compare equilibrium concentrations against integration. The other three blocks are correct as written and are untouched.
  • The residual assertion still bites. It runs at the default tol = 1e-9, not the looser 1e-8 the explicit-integration block uses — the parity early-stop clears the tighter bound on the reversible model on its own, so this wasn't relaxed to make it pass.

Checks

  • tests/test_bngsim (Ninja, Release): 50/50, up from 49/50.
  • pytest -q python/tests via the pre-push hook: passed.

🤖 Generated with Claude Code

8a0609a flipped SteadyStateOptions::method from "newton" to "integration"
and updated the Python side, but not the C++ one. test_find_steady_state_methods
kept asserting the old default, so the native suite has reported 49/50 on
every branch since -- a standing failure that makes a real regression easy
to wave off as "the known one".
Two assertions in the first block were stale, both for the same reason: the
default's method string, and the method_used it echoes back. Only the expected
default changes here; the rest of the function is correct as written and is
left alone.
Newton keeps its direct coverage without the default block: the "kinsol" block
asserts it converges and echoes the canonical method_used == "newton", and the
parity block selects it explicitly to compare equilibrium concentrations
against integration. The default's residual assertion still runs at the default
tol = 1e-9 rather than the looser 1e-8 the explicit-integration block uses --
the parity early-stop clears the tighter bound on the reversible model on its
own.
The comment block above the function described the two-tier
integrate-then-polish path as the default; it now describes the parity
early-stop and cites the 1.4-3.9x net cost #28 measured as why the polish
stopped being the default.
Checks: 50/50 in tests/test_bngsim (Ninja, Release).
@wshlavacek
wshlavacek merged commit c34c0b0 into mainJul 22, 2026
1 check passed
wshlavacek added a commit that referenced this pull request Jul 22, 2026
tests/test_bngsim had no gate at any stage. Every workflow that compiles the
project passes BNGSIM_BUILD_TESTS=OFF -- mir.yml, windows-nfsim.yml and
windows-tail.yml all do -- and no job ever invoked the binary. The pre-push
hook covers python/tests; the C++ side had no local or remote equivalent, so
the suite's only gate was a developer remembering to build it by hand.
#28 is what that costs. It flipped SteadyStateOptions::method from "newton" to
"integration", updated the Python side, and left the C++ assertion in
test_find_steady_state_methods pinned to the old default. The suite sat at
49/50 on every branch from that merge onward, and no CI run could have said so.
The failure's longevity was a consequence of its invisibility: a permanently-red
suite is worse than a smaller green one, because a genuine regression arrives
looking exactly like the one everyone has learned to wave off.
The job is Linux-only and lean. KLU, NFsim and RuleMonkey are OFF -- test_bngsim
links none of them, and skipping the vendored builds takes the target count from
146 to 81. The wheels/MIR/Windows jobs already cover those subsystems. LAPACK
stays at its default because find_package(LAPACK QUIET) degrades to the
reference dense solver when absent, so Linux builds either way.
Two things fail the job, not one. test_bngsim exits nonzero unless
tests_passed == tests_run, and pipefail carries that through the tee. A second
step then asserts the DENOMINATOR against a floor, which is the failure mode an
exit code cannot see: a suite that goes green because tests stopped being
registered. Every RUN_TEST is unconditionally compiled -- there is no #if around
any of them -- so the total is a real invariant rather than a platform-dependent
count, and the floor only ever moves up.
Verified by building both sides locally with the exact flags: current main
exits 1 at 49/50 naming the stale assertion, and the #36 fix exits 0 at 50/50.
The guard was exercised against four captured logs -- pass, the 49/50 stale
run, a doctored 41/41 shrink, and a no-summary crash -- failing on the last two
and passing the first two.
Not covered: the other native binaries in tests/CMakeLists.txt (test_validation,
test_c_api, test_lapack_dense_linsol, test_seed_count_rounding, the NFsim pair)
stay ungated. Extending this to ctest is a fair follow-up; this closes the gap
that actually drew blood.
wshlavacek added a commit that referenced this pull request Jul 22, 2026
tests/test_bngsim had no gate at any stage. Every workflow that compiles the
project passes BNGSIM_BUILD_TESTS=OFF -- mir.yml, windows-nfsim.yml and
windows-tail.yml all do -- and no job ever invoked the binary. The pre-push
hook covers python/tests; the C++ side had no local or remote equivalent, so
the suite's only gate was a developer remembering to build it by hand.
#28 is what that costs. It flipped SteadyStateOptions::method from "newton" to
"integration", updated the Python side, and left the C++ assertion in
test_find_steady_state_methods pinned to the old default. The suite sat at
49/50 on every branch from that merge until #36 fixed it, and no CI run could
have said so. A permanently-red suite is worse than a smaller green one,
because a genuine regression arrives looking exactly like the one everyone has
learned to wave off.
The job is Linux-only and lean. KLU, NFsim and RuleMonkey are OFF -- test_bngsim
links none of them, and skipping the vendored builds takes the target count from
146 to 81. The wheels/MIR/Windows jobs already cover those subsystems. LAPACK
stays at its default: find_package(LAPACK QUIET) degrades to the reference dense
solver when absent, which the Linux CI run exercised (built-in dense LU, 50/50).
Two things fail the job, not one. test_bngsim exits nonzero unless
tests_passed == tests_run, and pipefail carries that through the tee. A second
step asserts the DENOMINATOR against a floor -- the failure mode an exit code
cannot see: a suite that goes green because tests stopped being registered.
Every RUN_TEST is unconditionally compiled (no #if around any of them), so the
total is a real invariant, and the floor only ever moves up.
Verified both directions: current main exits 1 at 49/50 naming the stale
assertion, the #36 fix exits 0 at 50/50, and the guard was exercised against
four captured logs (pass, the 49/50 stale run, a doctored 41/41 shrink, and a
no-summary crash), failing the last two and passing the first two.
Not covered: the other native binaries in tests/CMakeLists.txt stay ungated.
Extending this to ctest is a fair follow-up; this closes the gap that drew blood.
@wshlavacek
wshlavacek deleted the claude/stoic-turing-c37f0c branch July 22, 2026 22:58
wshlavacek added a commit that referenced this pull request Jul 24, 2026
…#39) (#47)
The native-tests gate added in #38 built and ran test_bngsim only; every
other binary in tests/CMakeLists.txt was built by nothing in CI and run by
nothing anywhere. Driving the suite with `ctest` instead of a single named
target picks up any add_test() binary automatically, and surfaced two latent
breakages that had gone undetected precisely because nothing built them.
tests/CMakeLists.txt:
- Fix test_c_api registration. Its TEST_DATA_DIR + add_test lines had been
copy-paste-slipped onto test_validation, so test_c_api compiled and was
then dropped on the floor with no add_test(). test_c_api reads its model
from argv[1] (not a TEST_DATA_DIR define), so register it as c_api_test
with simple_decay.net; it is model-agnostic and exercises every branch.
- Fix test_seed_count_rounding compile break (present since initial release).
It includes bngsim/platform_compat.hpp (in include/) but only put src/ on
its search path and does not link bngsim, so the standalone target never
compiled. Add include/ to its include directories.
.github/workflows/native-tests.yml:
- Build all targets, then gate with
`ctest --test-dir build --output-on-failure --no-tests=error` so new tests
run without a workflow edit and a zero-test (mis-scoped) run fails.
- Keep the false-green guard in two layers: a ctest binary-count floor for a
binary silently losing its add_test(), plus test_bngsim's own case-count
floor, which ctest cannot see since it treats the binary as one test — the
exact #28/#36 failure mode.
- Document the NFsim tradeoff explicitly: the NFsim tests exist only with
BNGSIM_BUILD_NFSIM=ON, stay off this lean Linux leg, and are covered by
windows-nfsim.yml.
Sign up for freeto 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.

1 participant

@wshlavacek