inspect_validator/
runner.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use super::data::Data;
6use super::trials::{self, Step};
7use super::{PUPPET_MONIKER, puppet, results};
8use anyhow::{Error, bail};
9use diagnostics_reader::{ArchiveReader, ComponentSelector};
10use fidl_diagnostics_validate as validate;
11
12pub async fn run_all_trials() -> results::Results {
13    let trial_set = trials::real_trials();
14    let mut results = results::Results::new();
15    for mut trial in trial_set {
16        match puppet::Puppet::connect().await {
17            Ok(mut puppet) => {
18                results.diff_type = puppet.config.diff_type;
19                if puppet.config.test_archive {
20                    match puppet.publish().await {
21                        Ok(validate::TestResult::Ok) => {}
22                        Ok(result) => {
23                            results.error(format!("Publish reported {result:?}"));
24                            return results;
25                        }
26                        Err(e) => {
27                            results.error(format!("Publish error: {e:?}"));
28                            return results;
29                        }
30                    }
31                }
32                let mut data = Data::new();
33                if let Err(e) = run_trial(&mut puppet, &mut data, &mut trial, &mut results).await {
34                    results.error(format!("Running trial {}, got failure:\n{}", trial.name, e));
35                } else {
36                    results.log(format!("Trial {} succeeds", trial.name));
37                }
38
39                // The puppet has to be shut down (it will restart) so the VMO fetch will succeed
40                // on the next trial. This also makes sure the puppet is in a clean state for
41                // each trial.
42                puppet.shutdown().await;
43            }
44            Err(e) => {
45                results.error(format!("Failed to form Puppet - error {e:?}."));
46            }
47        }
48    }
49    results
50}
51
52#[derive(PartialEq)]
53enum StepResult {
54    // Signals that we should continue with the next step.
55    Continue,
56    // Signals that the state after executing this step is inconsistent, we should stop running
57    // further steps.
58    Stop,
59}
60
61async fn run_trial(
62    puppet: &mut puppet::Puppet,
63    data: &mut Data,
64    trial: &mut trials::Trial,
65    results: &mut results::Results,
66) -> Result<(), Error> {
67    let trial_name = format!("{}:{}", puppet.printable_name(), trial.name);
68    // We have to give explicit type here because compiler can't deduce it from None option value.
69    try_compare::<validate::Action>(data, puppet, &trial_name, -1, None, -1, results).await?;
70    for (step_index, step) in trial.steps.iter_mut().enumerate() {
71        let step_result = match step {
72            Step::Actions(actions) => {
73                run_actions(actions, data, puppet, &trial.name, step_index, results).await?
74            }
75            Step::WithMetrics(actions, step_name) => {
76                let r =
77                    run_actions(actions, data, puppet, &trial.name, step_index, results).await?;
78                results.remember_metrics(puppet.metrics()?, &trial.name, step_index, step_name);
79                r
80            }
81            Step::LazyActions(actions) => {
82                run_lazy_actions(actions, data, puppet, &trial.name, step_index, results).await?
83            }
84            Step::ActLazyThreadLocalActions(actions) => {
85                run_act_lazy_thread_local_actions(
86                    actions,
87                    data,
88                    puppet,
89                    &trial.name,
90                    step_index,
91                    results,
92                )
93                .await?
94            }
95        };
96        if step_result == StepResult::Stop {
97            break;
98        }
99    }
100    Ok(())
101}
102
103async fn run_actions(
104    actions: &mut [validate::Action],
105    data: &mut Data,
106    puppet: &mut puppet::Puppet,
107    trial_name: &str,
108    step_index: usize,
109    results: &mut results::Results,
110) -> Result<StepResult, Error> {
111    for (action_number, action) in actions.iter_mut().enumerate() {
112        if let Err(e) = data.apply(action) {
113            bail!(
114                "Local-apply error in trial {}, step {}, action {}: {:?} ",
115                trial_name,
116                step_index,
117                action_number,
118                e
119            );
120        }
121        match puppet.apply(action).await {
122            Err(e) => {
123                bail!(
124                    "Puppet-apply error in trial {}, step {}, action {}: {:?} ",
125                    trial_name,
126                    step_index,
127                    action_number,
128                    e
129                );
130            }
131            Ok(validate::TestResult::Ok) => {}
132            Ok(validate::TestResult::Unimplemented) => {
133                results.unimplemented(puppet.printable_name(), action);
134                return Ok(StepResult::Stop);
135            }
136            Ok(bad_result) => {
137                bail!(
138                    "In trial {}, puppet {} reported action {:?} was {:?}",
139                    trial_name,
140                    puppet.printable_name(),
141                    action,
142                    bad_result
143                );
144            }
145        }
146        try_compare(
147            data,
148            puppet,
149            trial_name,
150            step_index as i32,
151            Some(action),
152            action_number as i32,
153            results,
154        )
155        .await?;
156    }
157    Ok(StepResult::Continue)
158}
159
160async fn run_lazy_actions(
161    actions: &mut [validate::LazyAction],
162    data: &mut Data,
163    puppet: &mut puppet::Puppet,
164    trial_name: &str,
165    step_index: usize,
166    results: &mut results::Results,
167) -> Result<StepResult, Error> {
168    for (action_number, action) in actions.iter_mut().enumerate() {
169        if let Err(e) = data.apply_lazy(action) {
170            bail!(
171                "Local-apply_lazy error in trial {}, step {}, action {}: {:?} ",
172                trial_name,
173                step_index,
174                action_number,
175                e
176            );
177        }
178        match puppet.apply_lazy(action).await {
179            Err(e) => {
180                bail!(
181                    "Puppet-apply_lazy error in trial {}, step {}, action {}: {:?} ",
182                    trial_name,
183                    step_index,
184                    action_number,
185                    e
186                );
187            }
188            Ok(validate::TestResult::Ok) => {}
189            Ok(validate::TestResult::Unimplemented) => {
190                results.unimplemented(puppet.printable_name(), action);
191                return Ok(StepResult::Stop);
192            }
193            Ok(bad_result) => {
194                bail!(
195                    "In trial {}, puppet {} reported action {:?} was {:?}",
196                    trial_name,
197                    puppet.printable_name(),
198                    action,
199                    bad_result
200                );
201            }
202        }
203        try_compare(
204            data,
205            puppet,
206            trial_name,
207            step_index as i32,
208            Some(action),
209            action_number as i32,
210            results,
211        )
212        .await?;
213    }
214    Ok(StepResult::Continue)
215}
216
217async fn run_act_lazy_thread_local_actions(
218    actions: &mut [validate::LazyAction],
219    data: &mut Data,
220    puppet: &mut puppet::Puppet,
221    trial_name: &str,
222    step_index: usize,
223    results: &mut results::Results,
224) -> Result<StepResult, Error> {
225    for (action_number, action) in actions.iter_mut().enumerate() {
226        if let Err(e) = data.apply_lazy(action) {
227            bail!(
228                "Local-apply_lazy error in trial {}, step {}, action {}: {:?} ",
229                trial_name,
230                step_index,
231                action_number,
232                e
233            );
234        }
235        match puppet.act_lazy_thread_local(action).await {
236            Err(e) => {
237                bail!(
238                    "Puppet-apply_lazy error in trial {}, step {}, action {}: {:?} ",
239                    trial_name,
240                    step_index,
241                    action_number,
242                    e
243                );
244            }
245            Ok(validate::TestResult::Ok) => {}
246            Ok(validate::TestResult::Unimplemented) => {
247                results.unimplemented(puppet.printable_name(), action);
248                return Ok(StepResult::Stop);
249            }
250            Ok(bad_result) => {
251                bail!(
252                    "In trial {}, puppet {} reported action {:?} was {:?}",
253                    trial_name,
254                    puppet.printable_name(),
255                    action,
256                    bad_result
257                );
258            }
259        }
260        try_compare(
261            data,
262            puppet,
263            trial_name,
264            step_index as i32,
265            Some(action),
266            action_number as i32,
267            results,
268        )
269        .await?;
270    }
271    Ok(StepResult::Continue)
272}
273
274async fn try_compare<ActionType: std::fmt::Debug>(
275    data: &mut Data,
276    puppet: &puppet::Puppet,
277    trial_name: &str,
278    step_index: i32,
279    action: Option<&ActionType>,
280    action_number: i32,
281    results: &results::Results,
282) -> Result<(), Error> {
283    if !data.is_empty() {
284        match puppet.read_data().await {
285            Err(e) => {
286                bail!(
287                    "Puppet-read error in trial {}, step {}, action {} {:?}: {:?} ",
288                    trial_name,
289                    step_index,
290                    action_number,
291                    action,
292                    e
293                );
294            }
295            Ok(mut puppet_data) => {
296                if puppet.config.has_runner_node {
297                    puppet_data.remove_tree("runner");
298                }
299                if let Err(e) = data.compare(&puppet_data, results.diff_type) {
300                    bail!(
301                        "Compare error in trial {}, step {}, action {}:\n{:?}:\n{} ",
302                        trial_name,
303                        step_index,
304                        action_number,
305                        action,
306                        e
307                    );
308                }
309            }
310        }
311        if puppet.config.test_archive {
312            let archive_data = match ArchiveReader::inspect()
313                .add_selector(ComponentSelector::new(vec![PUPPET_MONIKER.to_string()]))
314                .snapshot()
315                .await
316            {
317                Ok(archive_data) => archive_data,
318                Err(e) => {
319                    bail!(
320                        "Archive read error in trial {}, step {}, action {}:\n{:?}:\n{} ",
321                        trial_name,
322                        step_index,
323                        action_number,
324                        action,
325                        e
326                    );
327                }
328            };
329            if archive_data.len() != 1 {
330                bail!(
331                    "Expected 1 component in trial {}, step {}, action {}:\n{:?}:\nfound {} ",
332                    trial_name,
333                    step_index,
334                    action_number,
335                    action,
336                    archive_data.len()
337                );
338            }
339
340            let mut hierarchy_data: Data = archive_data[0].payload.as_ref().unwrap().clone().into();
341            if puppet.config.has_runner_node {
342                hierarchy_data.remove_tree("runner");
343            }
344            if let Err(e) = data.compare_to_json(&hierarchy_data, results.diff_type) {
345                bail!(
346                    "Archive compare error in trial {}, step {}, action {}:\n{:?}:\n{} ",
347                    trial_name,
348                    step_index,
349                    action_number,
350                    action,
351                    e
352                );
353            }
354        }
355    }
356    Ok(())
357}
358
359#[cfg(test)]
360mod tests {
361    use super::*;
362    use crate::trials::Trial;
363    use crate::trials::tests::trial_with_action;
364    use crate::*;
365    use fidl_diagnostics_validate::*;
366
367    #[fuchsia::test]
368    async fn unimplemented_works() {
369        let mut int_maker = trial_with_action(
370            "foo",
371            create_numeric_property!(
372            parent: ROOT_ID, id: 1, name: "int", value: Value::IntT(0)),
373        );
374        let mut uint_maker = trial_with_action(
375            "foo",
376            create_numeric_property!(
377            parent: ROOT_ID, id: 2, name: "uint", value: Value::UintT(0)),
378        );
379        let mut uint_create_delete = Trial {
380            name: "foo".to_string(),
381            steps: vec![
382                Step::Actions(vec![
383                    create_numeric_property!(parent: ROOT_ID, id: 2, name: "uint", value: Value::UintT(0)),
384                ]),
385                Step::Actions(vec![delete_property!(id: 2)]),
386            ],
387        };
388        let mut results = results::Results::new();
389        let mut puppet = puppet::tests::local_incomplete_puppet().await.unwrap();
390        // results contains a list of the _un_implemented actions. local_incomplete_puppet()
391        // implements Int creation, but not Uint. So results should not include Uint but should
392        // include Int.
393        {
394            let mut data = Data::new();
395            run_trial(&mut puppet, &mut data, &mut int_maker, &mut results).await.unwrap();
396        }
397        {
398            let mut data = Data::new();
399            run_trial(&mut puppet, &mut data, &mut uint_maker, &mut results).await.unwrap();
400        }
401        {
402            let mut data = Data::new();
403            run_trial(&mut puppet, &mut data, &mut uint_create_delete, &mut results).await.unwrap();
404        }
405        assert!(
406            !results
407                .to_json()
408                .contains(&format!("{}: CreateProperty(Int)", puppet.printable_name()))
409        );
410        assert!(
411            results
412                .to_json()
413                .contains(&format!("{}: CreateProperty(Uint)", puppet.printable_name()))
414        );
415    }
416}