feat(runner): name/tag filter, skip_if, fail-fast (#11) #29
No reviewers
Labels
No labels
area:assertions
area:cli
area:client
area:harness
area:meta
area:reporting
area:runner
type:user-story
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/ws-rpc-test!29
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/11-filter-skip-failfast"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #11. Stacked on #28.
Summary
Adds everything the runner needs to run a subset of the suite and bail early on failure.
Filtering
runner.filter("gallery*")— glob-like pattern over test names. Supportsexact,prefix*,*suffix,*contains*,prefix*suffix, and*. Implemented inline withstarts_with/ends_with/contains— no regex dep.runner.filter_tag("backend")— exact tag match.report.filteredand produce no output line.Skip
Err(TestError::Skip("reason"))since #9) — re-exercised by new tests.runner.test(...).skip_if(|| cond_at_runtime, "reason"). Condition is aFn() -> bool + Send + Sync + 'staticevaluated at run-time (not registration) so it can read env / fs state. When true, hooks and body are skipped, result isSkipped(reason).Fail-fast
runner.fail_fast(true)— after the firstFailedtest, remaining tests are reported asSkippedwithskip_reason = "fail-fast".after_eachstill runs for the failing test;after_allstill runs at the end.Precedence inside the run loop
skip_if— recorded as Skipped(reason)before_each→ body →after_eachChecklist (from issue #11)
Filtering
RunConfig.filter: Option<String>globRunConfig.tag: Option<String>exact--filter+--tagis AND (wiring to CLI happens in #15)report.filtered, produce no outputSkip
Err(TestError::Skip(...)).skip_if(|| cond, "reason")evaluated at run timeFail-fast
Skipped("fail-fast")(not silently dropped)after_each/after_allstill run per the existing hook contractTest plan (11 new tests)
glob_match_{exact,prefix,suffix,surround,middle,star_all}— 6 pure-function tests, no fixture neededfilter_by_name_skips_non_matches_counted_as_filteredfilter_by_tag_and_name_uses_and_semantics— 3 tests; only one matches both filtersskip_if_true_skips_test_at_runtime— asserts reason preservedskip_if_false_runs_test_normallyfail_fast_skips_remaining_tests— 4 tests, second fails, last two recorded asSkipped("fail-fast")just qagreen locally — 85/85 unit testsNotes for the reviewer
TestCase.skip_conditionis stored asOption<(Box<dyn Fn() -> bool + Send + Sync>, String)>. The Fn-pointer trick means.skip_ifclosures are'static+Send + Sync; that matches the rest of the runner's closure constraints.RunConfig::Default::default()is now a derive, with a separatewith_defaults()helper that sets the 60 s timeout. CallingTestRunner::new()always useswith_defaults(); derive-default is only for callers who want the zero-field-config shape.skip_ifevaluating beforefail_fastwould be the other reasonable ordering. I put fail-fast first because it represents "the whole run is done" whereasskip_ifis per-test — the user probably wants fail-fast to win in all cases.Review — filter, skip_if, fail-fast (#11)
🐛 Bug :
RunConfig::default()a un timeout de 0sLa PR a remplacé l'
impl Default for RunConfigmanuel (qui retournaittimeout: DEFAULT_TEST_TIMEOUT) par#[derive(Default)]. OrDuration::default() == Duration::ZERO, donc :TestRunner::new()est correct (il appelleRunConfig::with_defaults()), maisTestRunner::default()— qui est#[derive(Default)]— crée unRunConfig::default()avec un timeout nul. Tout utilisateur qui écrit :obtiendra des échecs silencieux immédiats.
Fix suggéré : supprimer
#[derive(Default)]surRunConfiget réimplémenter manuellement pour déléguer àwith_defaults():Ça rend
with_defaults()redondant (on peut le garder comme alias ou le supprimer).glob_match— bienImplémentation O(n), sans dépendance, supporte les patterns courants documentés. La limitation multi-
*est explicite.skip_if— évaluation lazyLa condition est évaluée à l'exécution (pas à l'enregistrement) — correct et documenté.
RunConfig::name_matches+tag_matchesSéparation propre des deux filtres. Sémantique AND (les deux doivent passer) — conforme au test
filter_by_tag_and_name_uses_and_semantics.Tests glob
Coverage complète : exact, prefix, suffix, surround, middle,
*-all — excellent.Bloquant : timeout 0s via
RunConfig::default().🐛 Bug bloquant :
RunConfig::default()retourne maintenanttimeout: Duration::ZERO(0 secondes) suite au passage deimpl Defaultmanuel à#[derive(Default)].TestRunner::default()hérite de ce bug — chaque test timeout immédiatement. Fix : réimplémenterDefault for RunConfigmanuellement en déléguant àwith_defaults().