xacli_testing/testing/
execute.rs1use std::collections::VecDeque;
2
3use indexmap::IndexMap;
4
5use super::{ctx::MockContext, test::TestCase};
6use crate::{
7 report::xacli::{
8 TestCaseConfig, TestCaseResult, TestCaseStatus, TestError, TestFailure, TestSuiteResult,
9 TestSuitesResult,
10 },
11 spec, TestingApp,
12};
13
14pub struct ExecuteResult {
15 pub start_time: std::time::Instant,
16 pub duration: std::time::Duration,
17 pub code: i64,
18 pub stdout: String,
19 pub stderr: String,
20}
21
22impl TestingApp {
23 pub fn execute(&self, test_case: TestCase) -> TestCaseResult {
28 let start_time = std::time::Instant::now();
29 let timestamp = std::time::SystemTime::now();
30
31 let config = TestCaseConfig {
33 commands: test_case.commands.clone(),
34 args: test_case.args.clone(),
35 };
36
37 let mut full_args = vec![self.app.info.name.clone()];
39 full_args.extend(test_case.commands.clone());
40 full_args.extend(test_case.args.clone());
41
42 let ctx_info = match self.app.parse(full_args) {
43 Ok(info) => info,
44 Err(e) => {
45 return TestCaseResult {
46 config,
47 timestamp,
48 duration: std::time::Duration::new(0, 0),
49 stdout: String::new(),
50 stderr: e.to_string(),
51 status: TestCaseStatus::Error {
52 error: TestError {
53 message: "Failed to parse arguments".to_string(),
54 },
55 },
56 };
57 }
58 };
59
60 let mut ctx = MockContext::new(
61 ctx_info,
62 VecDeque::from(test_case.input_events.clone()),
63 self.config.tty,
64 );
65
66 let result = self.app.execute_with_ctx(&mut ctx);
67 let code = match &result {
68 Ok(_) => 0,
69 Err(_) => 1,
70 };
71
72 let end_time = std::time::Instant::now();
73 let duration = end_time.duration_since(start_time);
74
75 let stdout = ctx.stdout_plain();
76 let stderr = ctx.stderr_plain();
77
78 let execute_result = ExecuteResult {
80 start_time,
81 duration,
82 code,
83 stdout: stdout.clone(),
84 stderr: stderr.clone(),
85 };
86
87 if let Err(e) = result {
89 return TestCaseResult {
90 config,
91 timestamp,
92 duration,
93 stdout,
94 stderr: format!("{}\n{}", stderr, e),
95 status: TestCaseStatus::Error {
96 error: TestError {
97 message: e.to_string(),
98 },
99 },
100 };
101 }
102
103 let mut all_success = true;
105 let mut error_messages = Vec::new();
106
107 for asserter in &test_case.assertions {
108 if let Ok(result) = asserter.validate(&execute_result) {
109 if !result.success {
110 all_success = false;
111 error_messages.extend(result.messages);
112 }
113 }
114 }
115
116 let status = if all_success {
118 TestCaseStatus::Passed
119 } else {
120 TestCaseStatus::Failed {
121 failure: TestFailure {
122 message: error_messages.join("\n"),
123 },
124 }
125 };
126
127 TestCaseResult {
128 config,
129 timestamp,
130 duration,
131 stdout,
132 stderr,
133 status,
134 }
135 }
136
137 pub fn execute_spec(&self, spec: &spec::SpecFile) -> TestSuitesResult {
142 let mut suites_result = IndexMap::new();
143
144 for (suite_name, suite) in &spec.suite {
145 if suite.features.skip {
147 continue;
148 }
149
150 let mut suite_result = IndexMap::new();
151
152 for (test_name, test_case) in &suite.test {
153 if test_case.features.skip {
155 continue;
156 }
157
158 let test_result = match TestCase::try_from((test_name.as_str(), test_case)) {
160 Ok(testing_case) => {
161 let mut result = self.execute(testing_case);
162 result.config.commands = test_case.commands.clone();
164 result
165 }
166 Err(e) => TestCaseResult {
167 config: TestCaseConfig {
168 commands: test_case.commands.clone(),
169 args: test_case.args.clone(),
170 },
171 timestamp: std::time::SystemTime::now(),
172 duration: std::time::Duration::default(),
173 stdout: String::new(),
174 stderr: e.to_string(),
175 status: TestCaseStatus::Error {
176 error: TestError {
177 message: format!("Failed to convert test case: {}", e),
178 },
179 },
180 },
181 };
182
183 suite_result.insert(test_name.clone(), test_result);
184 }
185
186 if !suite_result.is_empty() {
187 suites_result.insert(
188 suite_name.clone(),
189 TestSuiteResult {
190 tests: suite_result,
191 },
192 );
193 }
194 }
195
196 TestSuitesResult {
197 suites: suites_result,
198 }
199 }
200}