1use super::{SetupScriptPacket, TestPacket};
11use crate::{
12 config::{
13 elements::{FlakyResult, JunitFlakyFailStatus, ReportSkipPolicy},
14 scripts::{ScriptId, SetupScriptConfig},
15 },
16 errors::DisplayErrorChain,
17 list::TestInstance,
18 output_spec::LiveSpec,
19 reporter::{
20 TestOutputDisplay, UnitErrorDescription,
21 events::{
22 ChildExecutionOutputDescription, ErrorSummary, ExecuteStatus, ExecutionResult,
23 ExecutionResultDescription, InfoResponse, OutputErrorSlice, RetryData,
24 SetupScriptEnvMap, SetupScriptExecuteStatus, StressIndex, TestSlotAssignment, UnitKind,
25 UnitState,
26 },
27 },
28 signal::ShutdownEvent,
29 test_output::ChildExecutionOutput,
30 time::StopwatchSnapshot,
31};
32use nextest_metadata::MismatchReason;
33use std::time::Duration;
34use tokio::{
35 sync::{
36 mpsc::{UnboundedReceiver, UnboundedSender},
37 oneshot,
38 },
39 task::JoinError,
40};
41
42#[derive(Debug)]
48pub(super) enum ExecutorEvent<'a> {
49 SetupScriptStarted {
50 stress_index: Option<StressIndex>,
51 script_id: ScriptId,
52 config: &'a SetupScriptConfig,
53 program: String,
54 index: usize,
55 total: usize,
56 req_rx_tx: oneshot::Sender<UnboundedReceiver<RunUnitRequest<'a>>>,
58 },
59 SetupScriptSlow {
60 stress_index: Option<StressIndex>,
61 script_id: ScriptId,
62 config: &'a SetupScriptConfig,
63 program: String,
64 elapsed: Duration,
65 will_terminate: Option<Duration>,
66 },
67 SetupScriptFinished {
68 stress_index: Option<StressIndex>,
69 script_id: ScriptId,
70 config: &'a SetupScriptConfig,
71 program: String,
72 index: usize,
73 total: usize,
74 status: SetupScriptExecuteStatus<LiveSpec>,
75 },
76 Started {
77 stress_index: Option<StressIndex>,
78 test_instance: TestInstance<'a>,
79 slot_assignment: TestSlotAssignment,
80 command_line: Vec<String>,
81 req_rx_tx: oneshot::Sender<UnboundedReceiver<RunUnitRequest<'a>>>,
92 flaky_result: FlakyResult,
94 },
95 Slow {
96 stress_index: Option<StressIndex>,
97 test_instance: TestInstance<'a>,
98 retry_data: RetryData,
99 elapsed: Duration,
100 will_terminate: Option<Duration>,
101 },
102 AttemptFailedWillRetry {
103 stress_index: Option<StressIndex>,
104 test_instance: TestInstance<'a>,
105 failure_output: TestOutputDisplay,
106 run_status: ExecuteStatus<LiveSpec>,
107 delay_before_next_attempt: Duration,
108 },
109 RetryStarted {
110 stress_index: Option<StressIndex>,
111 test_instance: TestInstance<'a>,
112 slot_assignment: TestSlotAssignment,
113 retry_data: RetryData,
114 command_line: Vec<String>,
115 tx: oneshot::Sender<()>,
117 },
118 Finished {
119 stress_index: Option<StressIndex>,
120 test_instance: TestInstance<'a>,
121 success_output: TestOutputDisplay,
122 failure_output: TestOutputDisplay,
123 junit_store_success_output: bool,
124 junit_store_failure_output: bool,
125 junit_flaky_fail_status: JunitFlakyFailStatus,
126 last_run_status: ExecuteStatus<LiveSpec>,
127 },
128 Skipped {
129 stress_index: Option<StressIndex>,
130 test_instance: TestInstance<'a>,
131 reason: MismatchReason,
132 junit_report_skipped: ReportSkipPolicy,
133 },
134}
135
136#[derive(Clone, Copy)]
137pub(super) enum UnitExecuteStatus<'a, 'status> {
138 Test(&'status InternalExecuteStatus<'a>),
139 SetupScript(&'status InternalSetupScriptExecuteStatus<'a>),
140}
141
142impl<'a> UnitExecuteStatus<'a, '_> {
143 pub(super) fn info_response(&self) -> InfoResponse<'a> {
144 match self {
145 Self::Test(status) => status.test.info_response(
146 UnitState::Exited {
147 result: ExecutionResultDescription::from(status.result),
148 time_taken: status.stopwatch_end.active,
149 slow_after: status.slow_after,
150 },
151 status.output.clone(),
152 ),
153 Self::SetupScript(status) => status.script.info_response(
154 UnitState::Exited {
155 result: ExecutionResultDescription::from(status.result),
156 time_taken: status.stopwatch_end.active,
157 slow_after: status.slow_after,
158 },
159 status.output.clone(),
160 ),
161 }
162 }
163}
164
165pub(super) struct InternalExecuteStatus<'a> {
166 pub(super) test: TestPacket<'a>,
167 pub(super) slow_after: Option<Duration>,
168 pub(super) output: ChildExecutionOutput,
169 pub(super) result: ExecutionResult,
170 pub(super) stopwatch_end: StopwatchSnapshot,
171}
172
173impl InternalExecuteStatus<'_> {
174 pub(super) fn into_external(self) -> ExecuteStatus<LiveSpec> {
175 let output: ChildExecutionOutputDescription<LiveSpec> = self.output.into();
176
177 let desc = UnitErrorDescription::new(UnitKind::Test, &output);
180 let error_summary = desc.all_error_list().map(|errors| ErrorSummary {
181 short_message: errors.short_message(),
182 description: DisplayErrorChain::new(errors).to_string(),
183 });
184 let output_error_slice = desc.output_slice().map(|slice| OutputErrorSlice {
185 slice: slice.to_string(),
186 start: slice.combined_subslice().map(|s| s.start).unwrap_or(0),
187 });
188
189 ExecuteStatus {
190 retry_data: self.test.retry_data(),
191 output,
192 result: self.result.into(),
193 start_time: self.stopwatch_end.start_time.fixed_offset(),
194 time_taken: self.stopwatch_end.active,
195 is_slow: self.slow_after.is_some(),
196 delay_before_start: self.test.delay_before_start(),
197 error_summary,
198 output_error_slice,
199 }
200 }
201}
202
203pub(super) struct InternalSetupScriptExecuteStatus<'a> {
204 pub(super) script: SetupScriptPacket<'a>,
205 pub(super) slow_after: Option<Duration>,
206 pub(super) output: ChildExecutionOutput,
207 pub(super) result: ExecutionResult,
208 pub(super) stopwatch_end: StopwatchSnapshot,
209 pub(super) env_map: Option<SetupScriptEnvMap>,
210}
211
212impl InternalSetupScriptExecuteStatus<'_> {
213 pub(super) fn into_external(self) -> SetupScriptExecuteStatus<LiveSpec> {
214 let output: ChildExecutionOutputDescription<LiveSpec> = self.output.into();
215
216 let desc = UnitErrorDescription::new(UnitKind::Script, &output);
221 let error_summary = desc.all_error_list().map(|errors| ErrorSummary {
222 short_message: errors.short_message(),
223 description: DisplayErrorChain::new(errors).to_string(),
224 });
225
226 SetupScriptExecuteStatus {
227 output,
228 result: self.result.into(),
229 start_time: self.stopwatch_end.start_time.fixed_offset(),
230 time_taken: self.stopwatch_end.active,
231 is_slow: self.slow_after.is_some(),
232 env_map: self.env_map,
233 error_summary,
234 }
235 }
236}
237
238#[derive(Clone, Debug)]
240pub(super) enum RunUnitRequest<'a> {
241 Signal(SignalRequest),
242 OtherCancel,
245 Query(RunUnitQuery<'a>),
246}
247
248impl<'a> RunUnitRequest<'a> {
249 pub(super) fn drain(self, status: UnitExecuteStatus<'a, '_>) {
250 match self {
251 #[cfg(unix)]
252 Self::Signal(SignalRequest::Stop(sender)) => {
253 let _ = sender.send(());
255 }
256 #[cfg(unix)]
257 Self::Signal(SignalRequest::Continue) => {}
258 Self::Signal(SignalRequest::Shutdown(_)) => {}
259 Self::OtherCancel => {}
260 Self::Query(RunUnitQuery::GetInfo(tx)) => {
261 _ = tx.send(status.info_response());
263 }
264 }
265 }
266}
267
268#[derive(Clone, Debug)]
269pub(super) enum SignalRequest {
270 #[cfg(unix)]
272 Stop(UnboundedSender<()>),
273 #[cfg(unix)]
274 Continue,
275 Shutdown(ShutdownRequest),
276}
277
278#[derive(Copy, Clone, Debug, Eq, PartialEq)]
279pub(super) enum ShutdownRequest {
280 Once(ShutdownEvent),
281 Twice,
282}
283
284#[derive(Clone, Debug)]
285pub(super) enum RunUnitQuery<'a> {
286 GetInfo(UnboundedSender<InfoResponse<'a>>),
287}
288
289#[derive(Clone, Copy, Debug, Eq, PartialEq)]
290pub(super) enum InternalTerminateReason {
291 Timeout,
292 Signal(ShutdownRequest),
293}
294
295pub(super) enum RunnerTaskState {
296 Finished { child_join_errors: Vec<JoinError> },
297 Cancelled,
298}
299
300impl RunnerTaskState {
301 pub(super) fn finished_no_children() -> Self {
303 Self::Finished {
304 child_join_errors: Vec::new(),
305 }
306 }
307}
308
309#[derive(Clone, Copy, Debug)]
310#[must_use]
311pub(super) enum HandleSignalResult {
312 #[cfg(unix)]
314 JobControl,
315
316 #[cfg_attr(not(windows), expect(dead_code))]
318 Terminated(TerminateChildResult),
319}
320
321#[derive(Clone, Copy, Debug)]
322#[must_use]
323pub(super) enum TerminateChildResult {
324 Exited,
326
327 Killed,
329}