Next Generation WASM Microkernel Operating System
1// Copyright 2025 Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use alloc::boxed::Box;
9use core::sync::atomic::Ordering;
10
11use ktest::{Test, TestInfo};
12
13use super::args::FormatSetting;
14use super::{Conclusion, Outcome};
15
16pub struct Printer {
17 format: FormatSetting,
18}
19
20impl Printer {
21 pub fn new(format: FormatSetting) -> Self {
22 Self { format }
23 }
24
25 pub(crate) fn print_title(&self, num_tests: u64) {
26 match self.format {
27 FormatSetting::Pretty | FormatSetting::Terse => {
28 let plural_s = if num_tests == 1 { "" } else { "s" };
29
30 tracing::info!("\nrunning {num_tests} test{plural_s}");
31 }
32 FormatSetting::Json => tracing::info!(
33 r#"{{ "type": "suite", "event": "started", "test_count": {num_tests} }}"#,
34 ),
35 }
36 }
37
38 pub(crate) fn print_test(&self, info: &TestInfo) {
39 let TestInfo { module, name, .. } = info;
40 match self.format {
41 FormatSetting::Pretty => {
42 tracing::info!("test {module}::{name} ... ",);
43 }
44 FormatSetting::Terse => {
45 // In terse mode, nothing is printed before the job. Only
46 // `print_single_outcome` prints one character.
47 }
48 FormatSetting::Json => {
49 tracing::info!(
50 r#"{{ "type": "test", "event": "started", "name": "{module}::{name}" }}"#,
51 )
52 }
53 }
54 }
55
56 pub(crate) fn print_single_outcome(&self, info: &TestInfo, outcome: &Outcome) {
57 let TestInfo { module, name, .. } = info;
58 match self.format {
59 FormatSetting::Pretty => {
60 self.print_outcome_pretty(outcome);
61 }
62 FormatSetting::Terse => {
63 let c = match outcome {
64 Outcome::Passed => '.',
65 Outcome::Failed { .. } => 'F',
66 Outcome::Ignored => 'i',
67 };
68
69 tracing::info!("{c}");
70 }
71 FormatSetting::Json => {
72 tracing::info!(
73 r#"{{ "type": "test", "name": "{module}::{name}", "event": "{}" }}"#,
74 match outcome {
75 Outcome::Passed => "ok",
76 Outcome::Failed(_) => "failed",
77 Outcome::Ignored => "ignored",
78 }
79 );
80 }
81 }
82 }
83
84 fn print_outcome_pretty(&self, outcome: &Outcome) {
85 match outcome {
86 Outcome::Passed => tracing::info!("ok"),
87 Outcome::Failed(_) => {
88 tracing::info!("FAILED");
89 }
90 Outcome::Ignored => tracing::info!("ignored"),
91 }
92 }
93
94 pub(crate) fn print_list(&self, tests: &[Test], ignored: bool) {
95 for test in tests {
96 // libtest prints out:
97 // * all tests without `--ignored`
98 // * just the ignored tests with `--ignored`
99 if ignored && !test.info.ignored {
100 continue;
101 }
102
103 tracing::info!("{}::{}: test", test.info.module, test.info.name,);
104 }
105 }
106
107 pub(crate) fn print_summary(&self, conclusion: &Conclusion) {
108 match self.format {
109 FormatSetting::Pretty | FormatSetting::Terse => {
110 let outcome = if conclusion.has_failed() {
111 Outcome::Failed(Box::new(()))
112 } else {
113 Outcome::Passed
114 };
115
116 tracing::info!("test result: ");
117 self.print_outcome_pretty(&outcome);
118 tracing::info!(
119 "{} passed; {} failed; {} ignored; {} measured; \
120 {} filtered out",
121 conclusion.num_passed.load(Ordering::Acquire),
122 conclusion.num_failed.load(Ordering::Acquire),
123 conclusion.num_ignored.load(Ordering::Acquire),
124 conclusion.num_measured.load(Ordering::Acquire),
125 conclusion.num_filtered_out.load(Ordering::Acquire),
126 );
127 }
128 FormatSetting::Json => {
129 tracing::info!(
130 concat!(
131 r#"{{ "type": "suite", "event": "{}", "passed": {}, "failed": {},"#,
132 r#" "ignored": {}, "measured": {}, "filtered_out": {} }}"#,
133 ),
134 if conclusion.has_failed() {
135 "failed"
136 } else {
137 "ok"
138 },
139 conclusion.num_passed.load(Ordering::Acquire),
140 conclusion.num_failed.load(Ordering::Acquire),
141 conclusion.num_ignored.load(Ordering::Acquire),
142 conclusion.num_measured.load(Ordering::Acquire),
143 conclusion.num_filtered_out.load(Ordering::Acquire),
144 )
145 }
146 }
147 }
148}