Regression tests for swadm - #350
Conversation
The comment mentions a time for deletion, and that time has come. These tests have been ignored in CI for at least a year and reference a CLI command that no longer exists.
d4fba7d to
542ba2b
Compare
02998ee to
619462a
Compare
c7dc0ee to
f8869e0
Compare
f8869e0 to
995de63
Compare
| use anyhow::bail; | ||
| use regex::Regex; | ||
|
|
||
| const SWADM: &str = env!("CARGO_BIN_EXE_swadm"); |
There was a problem hiding this comment.
It looks like this is pre-existing, but do you have any idea why this is mixed case?
There was a problem hiding this comment.
This was new to me as well. Seems imposed by Cargo: https://doc.rust-lang.org/cargo/reference/environment-variables.html#:~:text=cleaned%20between%20builds.-,CARGO_BIN_EXE_,-%3Cname%3E%20%E2%80%94%20The
| // Modify these or make them configurable if a tested command | ||
| // ever requires longer than `TIMEOUT` to converge. This just | ||
| // avoids requiring more args if nobody cares. | ||
| const TIMEOUT: Duration = Duration::from_secs(2); | ||
| const SLEEP: Duration = Duration::from_millis(100); |
There was a problem hiding this comment.
You could always move sleep/timeout into args and expose a thin wrapper (fn or macro) that supplies the default. Not something I feel strongly about though, so feel free to ignore me if you disagree
There was a problem hiding this comment.
Haha I often write comments trying to exonerate suspicious code only to realize later the comment could just be replaced by less suspicious code. Seems the case here. Thx!
retry now just calls a function retry_with with these defaults.
| /// Anything up until the next match. | ||
| pub const ANY: Pattern = Pattern::regex(r".*?"); |
There was a problem hiding this comment.
This matches 0 or more characters, right? Is that worth calling out in the comment, since the others call out 1 being the minimum match length?
|
|
||
| impl AsRef<str> for Output { | ||
| fn as_ref(&self) -> &str { | ||
| let (Self::Stdout(txt) | Self::Stderr(txt)) = self; |
There was a problem hiding this comment.
this is slick, I didn't know you could do this inside of let destructuring
| const TXEQ_STDOUT: &str = " | ||
| lane 0 lane 1 lane 2 lane 3 | ||
| pre2 0 (111) 1 ( 11) 2 ( 1) 3 ( 11) | ||
| pre1 -1 ( 11) -2 ( 11) -3 ( 11) -4 ( 11) | ||
| main 19 ( 11) 20 ( 11) 21 ( 11) 22 ( 11) | ||
| post1 -2 ( 1) -13 ( -2) -9 (-11) -22 (-123) | ||
| post2 -123 ( 11) 456 ( 11) 0 ( 11) 0 ( 11) | ||
| "; |
There was a problem hiding this comment.
This feels like it would be a good use case for expectorate
There was a problem hiding this comment.
I agree that this particular case is a good use of expectorate. However since it's a test for the actual matcher, using that instead of expectorate is the whole point.
That begs whether expectorate is a better tool than expect_line. I'm putting a comment in the next commit, but my guess is not.
Tx eq is a good motivating case because it's arbitrarily structured and exposes uncontrolled state.
In this case, the values outside parentheses are what we have commanded, and afaik the values inside parentheses are what the hardware itself has decided to do. This occurs in the case of self optimizing tx eq. So we only want to assert the values outside parentheses controlled by swadm command. Hence the PARENS matches asserting that something exists there, but idk what's inside.
In my understanding, expectorate does exact matching, which makes it a poor fit for a fuzzy assert like this. Is that accurate?
There was a problem hiding this comment.
Here's another plausible design:
- Get swadm output into a machine readable format. Either by adding formatted output options or building parsing infra.
- Allow tests to restructure that output to extract unnecessary tokens.
- Store known good versions of that output in files, and use expectorate for diffing.
However, unless someone feels strongly for a solution of that form, I'm disinclined because it seems like more required code per each new test.
| cmd::retry(|| { | ||
| let tx_eq = cmd::swadm(format!("link serdes get txeq {LINK}"))?; | ||
| for label in ["pre2", "pre1", "main", "post1", "post2"] { | ||
| tx_eq.expect_line(pat![ | ||
| label, VAL, PARENS, VAL, PARENS, VAL, PARENS, VAL, PARENS | ||
| ])?; | ||
| } | ||
|
|
||
| Ok(()) | ||
| }) |
There was a problem hiding this comment.
Similar feedback here about expectorate. As someone unfamiliar with the output of swadm link serdes get txeq, I have no idea what this test is looking for. I think being able to look at some output that's checked into get as a comparison would make it much simpler to reason about the test.
There was a problem hiding this comment.
I'm not yet convinced about expectorate, but this is a good point. I'll add an EXAMPLE comment.
There was a problem hiding this comment.
Sure, no worries. I don't know expectorate super well, but I've seen it used for command output checking, both structured and unstructured, so I thought it would be worth evaluating. If you don't feel like it's a good fit, that's fine by me
There was a problem hiding this comment.
Sorry for the repeated pings on this. Looks like we use expectorate in a lot of other crates. If I want people to contribute tests, probably good to use familiar tooling. I'll look for a way to make it work conveniently. Thx for suggesting.
| /// Verifies that the `tx-eq` shorthand and explicit | ||
| /// tap flags are mutually exclusive. |
There was a problem hiding this comment.
what are tap flags? I'm not sure what this test is doing without digging into swadm to figure out what the tap flags are. Maybe a reference to a command, source file, or type name would be helpful?
There was a problem hiding this comment.
This is referring to the flags like --main or --pre1.
The term "tap" comes from signal processing, and refers to one shifted copy of the signal we're operating on. So --pre1 is the gain on a copy of the signal shifted backward in time by one sample. It's one of the gains in the filter we apply to the signal to try to improve quality.
There was a problem hiding this comment.
I'm generally against unwarranted jargon. But tap seems pretty well standardized in tx eq literature, and it's a super convenient name 😅
There was a problem hiding this comment.
Yeah, the term tap holds roughly the same position in signal processing as process does in software. If you're mucking with it, you know what it means :)
taspelund
left a comment
There was a problem hiding this comment.
Overall, the changes LGTM. I would say that the main bit of feedback I have is that it's hard to track what the tests are doing without already knowing that area of the code (what is a tap flag? what does the expected output look like?).
I'd like you to take a look at expectorate and see if that would be a good fit for some of these tests. If it doesn't make sense in this case, then maybe just a multi-line comment showing what swadm output the test is comparing against would be helpful.
528cef5 to
6bc96d2
Compare
0a02c87 to
aba0e5b
Compare
cfzimmerman
left a comment
There was a problem hiding this comment.
🤖 CLAUDE REVIEW
Nice cleanup — replacing the ad-hoc port-link.rs / counters.rs tests with a shared
cmd harness plus expectorate golden files is a real improvement, and the README note
about swadm being a string-typed interface is well placed.
I ran the suite against a live dpd + tofino-model rig (illumos dpd, remote model) to
check the goldens rather than just reading them. All five --ignored tests pass, in both
orderings I tried, and tx_eq_exclusive + cmd::test::pattern_statics pass in the
illumos test job's non-ignored run. So the substance is sound. The findings below are
mostly about the harness making failures hard to read and one way it can silently
weaken itself.
The one I'd fix before merging
EXPECTORATE=overwrite inside retry bakes in transient output
cmd.rs:310 try_expectorate calls expectorate::eq_file(..).eval(..), which under
EXPECTORATE=overwrite writes the file and returns true on the first call
(feature_predicates.rs → assert_contents_impl(.., OverwriteMode::from_env(), ..)).
Every golden comparison in this PR is wrapped in cmd::retry, whose whole purpose is
that the first attempt is expected to observe pre-convergence state.
Demonstrated on the rig:
$ EXPECTORATE=overwrite DENDRITE_TEST_HOST='[::1]' \
cargo test --test cli set_partial_taps -- --ignored
test tx_eq::set_partial_taps ... ok
$ cat swadm/tests/cli/expect/tx_eq_100g_link.txt
Port/Link rear0/0
Speed 100G
The State Up line is gone. The test still passes, forever, having quietly stopped
checking that the link came up. Since regenerating goldens is exactly what someone will
do after an intentional swadm output change, this is a live trap.
Suggestion: have retry (or try_expectorate) refuse to overwrite while retrying — e.g.
retry on the raw command output until a cheap predicate holds, then run the expectorate
compare exactly once; or check OverwriteMode::from_env() and collapse to a single
attempt.
Failure output
A passing run prints a scary diff; a failing one prints ten
cmd.rs:315-316 prints the expectorate diff and then bail!("Match failed"). Wrapped in
retry, every normal run of the tx_eq tests emits:
@@ -1,3 +1,2 @@
Port/Link rear0/0
-State Up
Speed 100G
string doesn't match the contents of file: ".../tx_eq_100g_link.txt" see diffset above
set EXPECTORATE=overwrite if these changes are intentional
test tx_eq::set_partial_taps ... ok
That's from a green run — the link simply wasn't up on the first poll (this happened on
every run I did). Conversely, a genuine mismatch prints ~10 stacked diffs and then fails
with the context-free Match failed, which doesn't even name the file. Worth putting the
file name in the bail! and only printing on the last attempt.
CI
packet-test-common.sh:105 — swadm tests no longer run if any packet test fails
errexit is on when the dpd-client suite runs (line 105), and the swadm block was moved
to the end of the script (lines 122-134). Before this PR the swadm checks ran first, so
both suites always reported. Now one flaky packet test aborts the script and every swadm
result disappears — the opposite of the "run all the swadm tests in CI" goal.
Moving swadm after the packet tests is right (these tests destroy rear0/0), so the fix
is to stop letting the first suite short-circuit the second:
set +o errexit
DENDRITE_TEST_HOST='[::1]' ... cargo test ... -- --ignored --skip succeeds_when_table_fragmented
packet_status=$?
set -o errexit
...swadm block...
exit $packet_statustx_eq_exclusive never runs in the Linux job
It's the only CLI test without #[ignore], and the packet-test job passes -- --ignored,
which runs only ignored tests. So the one test covering clap's conflict output is
excluded from the job the new README points at; it actually runs in the illumos test
job (verified passing). Either mark it #[ignore] for consistency or add a second,
non---ignored invocation.
Smaller things
-
cmd.rs:106—TIMEOUT = 2swith a 100 ms sleep gives only ~5-10 real attempts once
you account for a process spawn plus HTTP round trip per attempt. On my idle rig
create_100g_linkneeded a retry every run to seeState Up; on the CI box, with
tofino-modeland the packet tests' fallout still in flight, 2 s for delete → create →
enable → link-up is thin. Considerretry_withand a larger budget for the link-up wait
specifically. -
tx_eq.rs:71—create_100g_link(port, link)hardcodesrear0/0in the pattern (and
inexpect/tx_eq_100g_link.txt) instead of using itslinkargument, so calling the
helper with any other link fails the compare rather than checking the link asked for. -
link_apply.rs:92— "Ensure a consistent slate for link apply" isn't quite true for
tx-eq.dpd'sdelete_link(dpd/src/link.rs:704) only setsdelete_meand pokes the
reconciler, and the software tx-eq values survive a delete/create: after
link delete rear0/0, waiting for the 404, thenlink create rear0 -s 100g --fec rs
with no tx-eq flags,link serdes get txeqstill reportedmain -22 / post1 5from the
previous test. The tests pass only because both write paths (serdes set txeqand
link apply) send all five taps. Fine today, but the comment oversells it, and any
future test asserting a default tap value after create will be order-dependent. -
tx_eq.rs:59-66vslink_apply.rs:93-107— two different link-teardown helpers, one
of which waits for the delete to land and one of which doesn't. The non-waiting one only
works becausecreate_linkhas an explicit "exists but markeddelete_me→ reset
config" branch (dpd/src/link.rs:583). Worth collapsing into one helper incmd. -
cmd.rs:69/cmd.rs:79— the doc tells the reader to useswadm_exactfor args with
spaces, but it's private, so sibling modules (tx_eq,link_apply) can't call it, and
the intra-doc link points at a private item. Make itpub. -
cmd.rs:87—"12224"duplicatescommon::DEFAULT_DPD_PORT; use the constant. -
swadm/Cargo.toml:30—predicates = "3.1.4"is pinned in the crate while the other
three dev-deps added here went through[workspace.dependencies]. Worth being
consistent. -
Dropping
syn/proc-macro2from[workspace.dependencies]is safe —aal_macros
pins its own versions and nothing used.workspace = truefor either. Just noting I
checked.
09f63a4 to
7a8cbe3
Compare
- Still using --ignored for swadm tests that require tofino_asic dpd.
- No longer filtering swadm tests by name. The formerly-evaded tests
were deleted in the previous commit.
- swadm tests will now modify switch state, so they should come after
packet tests. Each swadm test can handle its own setup, but it's
easier to run packet tests in a fresh state. Another option is
making swadm tests a standalone CI job. But that's probably
not warranted yet for such a small test suite.
- Script cleanup
- Commonize test env vars into a single export
- Shellcheck
- Standardize whitespacing
7a8cbe3 to
4d3ab6a
Compare
|
My responses:
|
Working towards standardized and easily maintainable regression tests in swadm. This defines a structure for tests and adds initial validation for tx equalization and settings-apply.
swadmis a great tool, and I would like it to continuously improve. While working on #145, I observed how easy (and tempting!) it is to make breaking changes. However, a quick search suggests breaking changes would not be well received by scripts and docs. Such changes may be needed someday, but that should be a careful decision and certainly not an accident.This PR adds infrastructure for regression testing swadm changes in CI. These diffs focus on the tx eq settings I'll soon be modifying, but hopefully the structure is easy to extend as other swadm projects arise.