execution_run_test_headless/
execution_run_test_headless.rs1use std::time::{Duration, Instant};
37
38use rs_teststand::{
39 AdapterKeyName, Engine, Error, PropertyOptions, Sequence, StepGroup, UIMessageCode,
40 pump_thread_messages,
41};
42
43const TESTS: [(&str, &str, f64, f64); 3] = [
45 ("Supply Voltage", "5.0", 4.75, 5.25),
46 ("Bias Current", "9.0", 1.0, 8.0),
47 ("Reference Clock", "10.0", 9.5, 10.5),
48];
49
50const RUN_DEADLINE: Duration = Duration::from_secs(60);
52
53const fn none() -> i32 {
54 PropertyOptions::NONE.bits()
55}
56
57const fn insert_if_missing() -> i32 {
58 PropertyOptions::INSERT_IF_MISSING.bits()
59}
60
61fn add_numeric_limit_test(
63 engine: &Engine,
64 sequence: &Sequence,
65 name: &str,
66 data_source: &str,
67 low: f64,
68 high: f64,
69) -> Result<(), Error> {
70 let step = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "NumericLimitTest")?;
71 step.set_name(name)?;
72 step.set_record_result(true)?;
73
74 let properties = step.as_property_object()?;
75 properties.set_val_string("DataSource", insert_if_missing(), data_source)?;
76 properties.set_val_number("Limits.Low", insert_if_missing(), low)?;
77 properties.set_val_number("Limits.High", insert_if_missing(), high)?;
78
79 sequence.insert_step(
80 &step,
81 sequence.get_num_steps(StepGroup::Main)?,
82 StepGroup::Main,
83 )
84}
85
86fn wait_for_end(engine: &Engine, deadline: Duration) -> Result<bool, Error> {
91 let started = Instant::now();
92 while started.elapsed() < deadline {
93 if pump_thread_messages() {
94 return Ok(false);
95 }
96 while !engine.is_ui_message_queue_empty()? {
97 let message = engine.get_ui_message()?;
98 let ended = matches!(
99 UIMessageCode::from_bits(message.event()?),
100 Ok(UIMessageCode::EndExecution)
101 );
102 message.acknowledge()?;
105 if ended {
106 return Ok(true);
107 }
108 }
109 }
110 Ok(false)
111}
112
113fn report(results: &rs_teststand::PropertyObject) -> Result<(), Error> {
115 if !results.exists("ResultList", none())? {
116 println!("No results recorded.");
117 return Ok(());
118 }
119 let result_list = results.get_property_object("ResultList", none())?;
120 let count = result_list.get_num_elements()?;
121 println!(
122 "\n{count} of {} defined test(s) recorded a result:",
123 TESTS.len()
124 );
125 if count < i32::try_from(TESTS.len()).unwrap_or(i32::MAX) {
126 println!(" (a failure ended the run before the rest could execute)");
127 }
128
129 for index in 0..count {
130 let entry = result_list.get_property_object_by_offset(index, none())?;
131 let name = entry
132 .get_val_string("TS.StepName", none())
133 .unwrap_or_else(|_| "<unnamed>".to_owned());
134 let status = entry
135 .get_val_string("Status", none())
136 .unwrap_or_else(|_| "<no status>".to_owned());
137 match entry.get_val_number("Numeric", none()) {
139 Ok(measured) => println!(" {name}: {status} (measured {measured})"),
140 Err(_) => println!(" {name}: {status}"),
141 }
142 }
143 Ok(())
144}
145
146fn main() -> Result<(), Box<dyn std::error::Error>> {
147 let engine = Engine::new()?;
148 engine.set_ui_message_polling_enabled(true)?;
151
152 let sequence_file = engine.new_sequence_file()?;
153 let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
154 for (name, data_source, low, high) in TESTS {
155 add_numeric_limit_test(&engine, &main_sequence, name, data_source, low, high)?;
156 }
157
158 let execution = engine.new_execution(&sequence_file, "MainSequence", None, false, 0)?;
159 println!("Running {} headless...", execution.display_name()?);
160
161 if wait_for_end(&engine, RUN_DEADLINE)? {
162 println!("Finished with status: {}", execution.result_status()?);
163 report(&execution.result_object()?)?;
164 } else {
165 println!("The run did not finish within {RUN_DEADLINE:?}; terminating.");
168 execution.terminate()?;
169 }
170
171 engine.release_sequence_file_ex(sequence_file, none())?;
172 Ok(())
173}