feat(report): Reporter trait + JSON and JUnit reporters (#14) #33
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!33
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/14-reporters-json-junit"
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 #14. Stacked on #32.
Summary
Extracts the
Reportertrait and adds JSON and JUnit XML implementations.TestRunner::run()now routes throughBox<dyn Reporter>so callers pick the format viaRunConfig.format.New types
OutputFormat { Console, Json, Junit }— with aparse(&str)helper used by the CLI in #15.Reportertrait with default no-op impls forstart_run/harness_started/connected; abstracttest_resultandend_run.build_reporter(format, no_color) -> Box<dyn Reporter>factory.JsonReporter— emits a single pretty-printed JSON document at end of run.JunitReporter— emits a<testsuites>XML document at end of run.ConsoleReporternowimpl Reporter(its existing methods are preserved for direct use).JSON shape
Epoch-ms timestamps to avoid pulling in
chrono.JUnit shape
xml_escapecovers< > & " 'and control chars.Runner wire-up
RunConfig.format: OutputFormat(defaultConsole).TestRunner::format(OutputFormat)setter.run()buildsBox<dyn Reporter>viabuild_reporterand dispatchesstart_run / harness_started / connected / test_result / end_runthrough the trait. Both sequential and parallel phases go through the same reporter.Checklist (from issue #14)
Reporter trait
start_run,harness_started,connected,test_result,end_runJSON
testsarray with{name, tags, status, duration_ms, error?}passed/failed/skipped/filtered/duration_msstarted_at_ms/finished_at_msfor timingserde_json::to_string_pretty)JUnit
<testsuites>with one<testsuite>per run<testcase>with<failure>/<skipped>children as appropriatetimeattribute per testcase and suiteGeneral
Reportertrait's default impls forharness_started/connectedare no-ops for JSON and JUnit)--no-colorTest plan
src/report.rs(4 total for JSON/JUnit +OutputFormat::parse)just qagreen — 98 unit + 5 integration = 103/103Notes for the reviewer
chrono. A future PR can swap to ISO 8601 without changing the JSON structure (just add the extra string field).JunitReporterstoresTestCaseResultclones with the error wrapped asTestError::Connection(formatted_message)becauseTestErrorisn'tClone. The wrapping is invisible to users — we only ever read the error back as a formatted string atend_runtime.Reporter::start_runmethod has a default no-op so reporters that don't care (Console, JUnit) don't have to override it. OnlyJsonReporterneeds it (to capturestarted_at_ms).testsaccumulator directly. A true end-to-end round trip (parse the printed stdout) would need stdout capture, which is brittle and deferred.Review PR #33 —
Reportertrait + JSON / JUnit reportersCode qualité globale : solide. Trait bien défini, factory propre, reporters complets, tests unitaires pertinents. Quelques observations à décharger.
1. Délégation redondante dans
impl Reporter for ConsoleReporterLes méthodes implémentent le trait en appelant les méthodes intrinsèques du même nom avec la syntaxe chemin complet. C'est valide, mais verbose.
self.harness_started(binary, duration)est équivalent ici et plus idiomatique. La seule raison d'utiliser le chemin complet serait d'éviter une récursion infinie si le nom de méthode est identique dans le trait et dans l'impl ; mais comme les méthodes intrinsèques préexistaient, Rust résoud sans ambiguïté. Mineur, pas un bug.2.
started_at_ms = 0sistart_run()n'est pas appeléJsonReporterinitialisestarted_at_ms: 0(via#[derive(Default)]). Si un consommateur appelle directementtest_result+end_runsansstart_run, la sortie JSON contiendra"started_at_ms": 0. Le runner appelle désormaisstart_run()en tête derun(), donc en pratique ça ne se produit pas. Mais le contrat du trait pourrait rendrestart_runobligatoire en le retirant de l'impl par défaut, ou au moins documenter l'invariant.3. Perte de type d'erreur dans
JunitReporterL'erreur est convertie en
Displaystring puis re-wrappée dansTestError::Connection. Toutes les infos de type (Assertion,Timeout,RpcError,Skip,Other) sont perdues. Pour JUnit XML, seul le message apparaît dans<failure message="...">, donc en pratique ça ne pose pas de problème. C'est une décision pragmatique acceptable mais mérite un commentaire explicite (pourquoiConnectionspécifiquement plutôt qu'un variant dédié commeTestError::StringOnly).4. Nom de suite JUnit codé en dur :
"ws-rpc-test"Le nom de la
<testsuite>est toujours"ws-rpc-test". Ce serait plus flexible de l'exposer dansRunConfigou de le passer àbuild_reporter(). Pas bloquant pour v0.1 mais à garder en tête si le framework est utilisé pour d'autres projets.5.
OutputFormat::parse()vsFromStrChoix de design valide — évite d'importer le trait au point d'usage. Fonctionnellement identique. Pas d'objection.
Points positifs
xml_escapecouvre correctement les 5 entités XML + les caractères de contrôle interdits en XML 1.0.error_jsonpréserve fidèlement la structure de chaque variant deTestError.build_reporter()factory centralisée : propre et extensible.reporter.start_run()avant tout, etreporter.end_run()au lieu dereporter.summary(): bonne transition vers le trait.✅ Aucun bug bloquant. Peut merger.
14ddd2dfdato552ba2b9ed