pub struct Execution { /* private fields */ }Expand description
A running sequence (Execution).
Created by Engine::new_execution, which
starts it immediately, there is no separate “run” step.
Implementations§
Source§impl Execution
impl Execution
Sourcepub fn id(&self) -> Result<i32, Error>
pub fn id(&self) -> Result<i32, Error>
The execution’s identifier (Execution.Id).
Messages posted by a sequence carry the execution that posted them, so this is how a host attributes a message to the run that produced it.
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
40fn main() -> Result<(), Box<dyn std::error::Error>> {
41 let engine = Engine::new()?;
42
43 // Nothing reaches the queue until polling is switched on.
44 engine.set_ui_message_polling_enabled(true)?;
45
46 let sequence_file = engine.new_sequence_file()?;
47 let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
48
49 // Posted through the engine and tagged with the execution. Synchronous, so
50 // the sequence waits until the host acknowledges it.
51 add_statement(
52 &engine,
53 &main_sequence,
54 "Report Stage",
55 &format!(
56 "RunState.Engine.PostUIMessage(RunState.Execution, RunState.Thread, {STAGE_MESSAGE}, \
57 1, \"stage: configuring instruments\", Nothing, True)"
58 ),
59 )?;
60 // Posted through the thread. Asynchronous, so the sequence carries on.
61 add_statement(
62 &engine,
63 &main_sequence,
64 "Report Progress",
65 &format!(
66 "RunState.Thread.PostUIMessageEx({PROGRESS_MESSAGE}, 50, \"progress: halfway\", \
67 Nothing, False)"
68 ),
69 )?;
70
71 let execution = engine.new_execution(&sequence_file, "MainSequence", None, false, 0)?;
72 println!(
73 "Execution {} started; polling for messages...",
74 execution.id()?
75 );
76
77 // The end is detected from the message stream, not by waiting on the
78 // execution: a wait call does not pump the queue, so a synchronous message
79 // would sit unacknowledged and both sides would stop.
80 let started = Instant::now();
81 let mut ended = false;
82 while started.elapsed() < Duration::from_secs(60) && !ended {
83 while !engine.is_ui_message_queue_empty()? {
84 let message = engine.get_ui_message()?;
85 let code = message.event()?;
86 if matches!(
87 UIMessageCode::from_bits(code),
88 Ok(UIMessageCode::EndExecution)
89 ) {
90 ended = true;
91 }
92 let origin = if UIMessageCode::is_user_message(code) {
93 "sequence".to_owned()
94 } else {
95 format!("engine {:?}", UIMessageCode::from_bits(code))
96 };
97 println!(
98 " [{origin}] code={code} numeric={} string={:?} synchronous={}",
99 message.numeric_data()?,
100 message.string_data()?,
101 message.is_synchronous()?
102 );
103 // Required: releases a synchronous poster and asks for the next.
104 message.acknowledge()?;
105 }
106 }
107
108 println!("\nExecution ended: {ended}");
109 engine.release_sequence_file_ex(sequence_file, NO_OPTIONS)?;
110 Ok(())
111}Sourcepub fn wait_for_end_ex(
&self,
milliseconds: i32,
process_windows_messages: bool,
) -> Result<bool, Error>
pub fn wait_for_end_ex( &self, milliseconds: i32, process_windows_messages: bool, ) -> Result<bool, Error>
Waits for the execution to finish (Execution.WaitForEndEx).
Returns true when it ended, false when the timeout came first. Pass
-1 for no timeout.
Not for a host that polls for messages. This does not pump the
message queue while it waits, so a synchronous message posted by the
sequence would never be acknowledged and both sides would stop. A
polling host should watch for
UIMessageCode::EndExecution on
the queue instead, which is how a front end knows an execution finished.
This is for synchronising between executions, waiting from a step for another execution to finish.
§Errors
Error if the COM call fails or returns an unexpected type.
Sourcepub fn wait_for_end(
&self,
milliseconds: i32,
process_windows_messages: bool,
) -> Result<bool, Error>
pub fn wait_for_end( &self, milliseconds: i32, process_windows_messages: bool, ) -> Result<bool, Error>
Waits for the execution to finish (Execution.WaitForEnd).
Superseded by wait_for_end_ex; kept because
it is the member available on engines from TestStand 2016. The same
warning applies: it does not pump the message queue.
§Errors
Error if the COM call fails or returns an unexpected type.
Sourcepub fn terminate(&self) -> Result<(), Error>
pub fn terminate(&self) -> Result<(), Error>
Asks the execution to stop (Execution.Terminate).
Termination is requested, not immediate: cleanup still runs.
§Errors
Error if the COM call fails.
Examples found in repository?
108fn run(engine: &Engine, sequence_file: &SequenceFile, entry_point: &str) -> Result<(), Error> {
109 let execution = engine.new_execution(sequence_file, entry_point, None, false, 0)?;
110 println!("\nRunning {entry_point}...");
111
112 if !wait_for_end(engine, RUN_DEADLINE)? {
113 println!(" did not finish within {RUN_DEADLINE:?}; terminating");
114 execution.terminate()?;
115 return Ok(());
116 }
117 println!(" status: {}", execution.result_status()?);
118
119 let results = execution.result_object()?;
120 if !results.exists("ResultList", none())? {
121 println!(" no results recorded");
122 return Ok(());
123 }
124 let result_list = results.get_property_object("ResultList", none())?;
125 for index in 0..result_list.get_num_elements()? {
126 let entry = result_list.get_property_object_by_offset(index, none())?;
127 println!(
128 " {}: {}",
129 entry
130 .get_val_string("TS.StepName", none())
131 .unwrap_or_else(|_| "<unnamed>".to_owned()),
132 entry
133 .get_val_string("Status", none())
134 .unwrap_or_else(|_| "<no status>".to_owned())
135 );
136 }
137 Ok(())
138}More examples
146fn main() -> Result<(), Box<dyn std::error::Error>> {
147 let engine = Engine::new()?;
148 // Nothing reaches the queue until this is on, and without the queue there
149 // is no way to know the run ended.
150 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 // Reported rather than ignored: a host that assumes success here would
166 // publish results from a run that never finished.
167 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}109fn main() -> Result<(), Box<dyn std::error::Error>> {
110 let engine = Engine::new()?;
111 engine.set_ui_message_polling_enabled(true)?;
112
113 let sequence_file = engine.new_sequence_file()?;
114 let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
115
116 add_pass_fail(&engine, &main_sequence, "Verify Power Rail", "True")?;
117 add_unrecorded_filler(&engine, &main_sequence, "Filler (not recorded)")?;
118 add_numeric(
119 &engine,
120 &main_sequence,
121 "Verify Current Draw",
122 "1.5",
123 1.0,
124 2.0,
125 )?;
126 add_pass_fail(&engine, &main_sequence, "Verify Ground Bond", "True")?;
127
128 let step_count = main_sequence.get_num_steps(StepGroup::Main)?;
129 println!("Running {step_count} steps...");
130 let execution = engine.new_execution(&sequence_file, "MainSequence", None, false, 0)?;
131
132 if !wait_for_end(&engine, RUN_DEADLINE)? {
133 println!("The run did not finish within {RUN_DEADLINE:?}; terminating.");
134 execution.terminate()?;
135 return Ok(());
136 }
137 println!("Finished with status: {}\n", execution.result_status()?);
138
139 let results = execution.result_list()?;
140 let parsed = results.parse()?;
141
142 println!("{} of {step_count} steps recorded a result:", parsed.len());
143 for (index, result) in parsed.iter().enumerate() {
144 let measurement = match &result.value {
145 Some(ResultValue::Number(number)) => format!("measured {number}"),
146 Some(ResultValue::Text(text)) => format!("measured {text:?}"),
147 None => "no measurement".to_owned(),
148 };
149 println!(
150 " [{index}] {} ({}) -> {}, {measurement}",
151 result.name, result.step_type, result.status
152 );
153 }
154
155 // The step whose recording was switched off is absent, by design.
156 println!(
157 "\nThe unrecorded step left no entry, which is why {} < {step_count}.",
158 parsed.len()
159 );
160
161 // The parsed results are plain data, so they outlive the run and can be
162 // handed to anything: a report writer, a database, a serde format.
163 let failures: Vec<&str> = parsed
164 .iter()
165 .filter(|result| result.status == "Failed")
166 .map(|result| result.name.as_str())
167 .collect();
168 if failures.is_empty() {
169 println!("No failures.");
170 } else {
171 println!("Failed steps: {}", failures.join(", "));
172 }
173
174 engine.release_sequence_file_ex(sequence_file, none())?;
175 Ok(())
176}Sourcepub fn display_name(&self) -> Result<String, Error>
pub fn display_name(&self) -> Result<String, Error>
The name a front end shows for this execution (Execution.DisplayName).
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
146fn main() -> Result<(), Box<dyn std::error::Error>> {
147 let engine = Engine::new()?;
148 // Nothing reaches the queue until this is on, and without the queue there
149 // is no way to know the run ended.
150 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 // Reported rather than ignored: a host that assumes success here would
166 // publish results from a run that never finished.
167 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}Sourcepub fn result_status(&self) -> Result<String, Error>
pub fn result_status(&self) -> Result<String, Error>
The overall result so far (Execution.ResultStatus).
A string the engine owns, "Passed", "Failed", "Terminated",
"Error", "Running" and others. Deliberately not narrowed to an enum:
a sequence may set a status of its own, and folding an unknown one into
a fixed set would lose it.
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
108fn run(engine: &Engine, sequence_file: &SequenceFile, entry_point: &str) -> Result<(), Error> {
109 let execution = engine.new_execution(sequence_file, entry_point, None, false, 0)?;
110 println!("\nRunning {entry_point}...");
111
112 if !wait_for_end(engine, RUN_DEADLINE)? {
113 println!(" did not finish within {RUN_DEADLINE:?}; terminating");
114 execution.terminate()?;
115 return Ok(());
116 }
117 println!(" status: {}", execution.result_status()?);
118
119 let results = execution.result_object()?;
120 if !results.exists("ResultList", none())? {
121 println!(" no results recorded");
122 return Ok(());
123 }
124 let result_list = results.get_property_object("ResultList", none())?;
125 for index in 0..result_list.get_num_elements()? {
126 let entry = result_list.get_property_object_by_offset(index, none())?;
127 println!(
128 " {}: {}",
129 entry
130 .get_val_string("TS.StepName", none())
131 .unwrap_or_else(|_| "<unnamed>".to_owned()),
132 entry
133 .get_val_string("Status", none())
134 .unwrap_or_else(|_| "<no status>".to_owned())
135 );
136 }
137 Ok(())
138}More examples
146fn main() -> Result<(), Box<dyn std::error::Error>> {
147 let engine = Engine::new()?;
148 // Nothing reaches the queue until this is on, and without the queue there
149 // is no way to know the run ended.
150 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 // Reported rather than ignored: a host that assumes success here would
166 // publish results from a run that never finished.
167 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}109fn main() -> Result<(), Box<dyn std::error::Error>> {
110 let engine = Engine::new()?;
111 engine.set_ui_message_polling_enabled(true)?;
112
113 let sequence_file = engine.new_sequence_file()?;
114 let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
115
116 add_pass_fail(&engine, &main_sequence, "Verify Power Rail", "True")?;
117 add_unrecorded_filler(&engine, &main_sequence, "Filler (not recorded)")?;
118 add_numeric(
119 &engine,
120 &main_sequence,
121 "Verify Current Draw",
122 "1.5",
123 1.0,
124 2.0,
125 )?;
126 add_pass_fail(&engine, &main_sequence, "Verify Ground Bond", "True")?;
127
128 let step_count = main_sequence.get_num_steps(StepGroup::Main)?;
129 println!("Running {step_count} steps...");
130 let execution = engine.new_execution(&sequence_file, "MainSequence", None, false, 0)?;
131
132 if !wait_for_end(&engine, RUN_DEADLINE)? {
133 println!("The run did not finish within {RUN_DEADLINE:?}; terminating.");
134 execution.terminate()?;
135 return Ok(());
136 }
137 println!("Finished with status: {}\n", execution.result_status()?);
138
139 let results = execution.result_list()?;
140 let parsed = results.parse()?;
141
142 println!("{} of {step_count} steps recorded a result:", parsed.len());
143 for (index, result) in parsed.iter().enumerate() {
144 let measurement = match &result.value {
145 Some(ResultValue::Number(number)) => format!("measured {number}"),
146 Some(ResultValue::Text(text)) => format!("measured {text:?}"),
147 None => "no measurement".to_owned(),
148 };
149 println!(
150 " [{index}] {} ({}) -> {}, {measurement}",
151 result.name, result.step_type, result.status
152 );
153 }
154
155 // The step whose recording was switched off is absent, by design.
156 println!(
157 "\nThe unrecorded step left no entry, which is why {} < {step_count}.",
158 parsed.len()
159 );
160
161 // The parsed results are plain data, so they outlive the run and can be
162 // handed to anything: a report writer, a database, a serde format.
163 let failures: Vec<&str> = parsed
164 .iter()
165 .filter(|result| result.status == "Failed")
166 .map(|result| result.name.as_str())
167 .collect();
168 if failures.is_empty() {
169 println!("No failures.");
170 } else {
171 println!("Failed steps: {}", failures.join(", "));
172 }
173
174 engine.release_sequence_file_ex(sequence_file, none())?;
175 Ok(())
176}Sourcepub fn sequence_file_path(&self) -> Result<String, Error>
pub fn sequence_file_path(&self) -> Result<String, Error>
Sourcepub fn num_threads(&self) -> Result<i32, Error>
pub fn num_threads(&self) -> Result<i32, Error>
Sourcepub fn seconds_executing(&self) -> Result<f64, Error>
pub fn seconds_executing(&self) -> Result<f64, Error>
Sourcepub fn seconds_suspended(&self) -> Result<f64, Error>
pub fn seconds_suspended(&self) -> Result<f64, Error>
Sourcepub fn cancel_termination(&self) -> Result<(), Error>
pub fn cancel_termination(&self) -> Result<(), Error>
Calls off a termination already under way (Execution.CancelTermination).
§Deadlock
Call this only from inside a running step. The reference is explicit that calling it from an application’s main thread, or from a step type’s edit substep, deadlocks, and it does: measured from a test thread, the call never returns and the process has to be killed, which then leaves sequence files unreleased.
A host driving an engine from its own thread is in exactly the position the reference warns about, so this is not a member a host calls to stop a termination it started. It exists for code running as part of the execution itself.
§Errors
Error if the COM call fails.
Sourcepub fn foreground_thread(&self) -> Result<Thread, Error>
pub fn foreground_thread(&self) -> Result<Thread, Error>
Sourcepub fn as_property_object(&self) -> Result<PropertyObject, Error>
pub fn as_property_object(&self) -> Result<PropertyObject, Error>
Sourcepub fn get_sequence_file(&self) -> Result<SequenceFile, Error>
pub fn get_sequence_file(&self) -> Result<SequenceFile, Error>
Sourcepub fn result_object(&self) -> Result<PropertyObject, Error>
pub fn result_object(&self) -> Result<PropertyObject, Error>
What the run recorded (Execution.ResultObject).
The root of the results tree. ResultList beneath it holds one entry per
step that recorded a result, which is what a headless caller reads
instead of a report file.
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
108fn run(engine: &Engine, sequence_file: &SequenceFile, entry_point: &str) -> Result<(), Error> {
109 let execution = engine.new_execution(sequence_file, entry_point, None, false, 0)?;
110 println!("\nRunning {entry_point}...");
111
112 if !wait_for_end(engine, RUN_DEADLINE)? {
113 println!(" did not finish within {RUN_DEADLINE:?}; terminating");
114 execution.terminate()?;
115 return Ok(());
116 }
117 println!(" status: {}", execution.result_status()?);
118
119 let results = execution.result_object()?;
120 if !results.exists("ResultList", none())? {
121 println!(" no results recorded");
122 return Ok(());
123 }
124 let result_list = results.get_property_object("ResultList", none())?;
125 for index in 0..result_list.get_num_elements()? {
126 let entry = result_list.get_property_object_by_offset(index, none())?;
127 println!(
128 " {}: {}",
129 entry
130 .get_val_string("TS.StepName", none())
131 .unwrap_or_else(|_| "<unnamed>".to_owned()),
132 entry
133 .get_val_string("Status", none())
134 .unwrap_or_else(|_| "<no status>".to_owned())
135 );
136 }
137 Ok(())
138}More examples
146fn main() -> Result<(), Box<dyn std::error::Error>> {
147 let engine = Engine::new()?;
148 // Nothing reaches the queue until this is on, and without the queue there
149 // is no way to know the run ended.
150 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 // Reported rather than ignored: a host that assumes success here would
166 // publish results from a run that never finished.
167 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}Sourcepub fn result_list(&self) -> Result<ResultList, Error>
pub fn result_list(&self) -> Result<ResultList, Error>
The results this run recorded, ready to read.
Composed from result_object, which is where the
ResultList array lives. This is the short path a headless caller wants:
run a sequence, then read what it produced without walking the tree by
hand.
§Errors
Error if the run recorded no result list, or a COM call fails.
Examples found in repository?
109fn main() -> Result<(), Box<dyn std::error::Error>> {
110 let engine = Engine::new()?;
111 engine.set_ui_message_polling_enabled(true)?;
112
113 let sequence_file = engine.new_sequence_file()?;
114 let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
115
116 add_pass_fail(&engine, &main_sequence, "Verify Power Rail", "True")?;
117 add_unrecorded_filler(&engine, &main_sequence, "Filler (not recorded)")?;
118 add_numeric(
119 &engine,
120 &main_sequence,
121 "Verify Current Draw",
122 "1.5",
123 1.0,
124 2.0,
125 )?;
126 add_pass_fail(&engine, &main_sequence, "Verify Ground Bond", "True")?;
127
128 let step_count = main_sequence.get_num_steps(StepGroup::Main)?;
129 println!("Running {step_count} steps...");
130 let execution = engine.new_execution(&sequence_file, "MainSequence", None, false, 0)?;
131
132 if !wait_for_end(&engine, RUN_DEADLINE)? {
133 println!("The run did not finish within {RUN_DEADLINE:?}; terminating.");
134 execution.terminate()?;
135 return Ok(());
136 }
137 println!("Finished with status: {}\n", execution.result_status()?);
138
139 let results = execution.result_list()?;
140 let parsed = results.parse()?;
141
142 println!("{} of {step_count} steps recorded a result:", parsed.len());
143 for (index, result) in parsed.iter().enumerate() {
144 let measurement = match &result.value {
145 Some(ResultValue::Number(number)) => format!("measured {number}"),
146 Some(ResultValue::Text(text)) => format!("measured {text:?}"),
147 None => "no measurement".to_owned(),
148 };
149 println!(
150 " [{index}] {} ({}) -> {}, {measurement}",
151 result.name, result.step_type, result.status
152 );
153 }
154
155 // The step whose recording was switched off is absent, by design.
156 println!(
157 "\nThe unrecorded step left no entry, which is why {} < {step_count}.",
158 parsed.len()
159 );
160
161 // The parsed results are plain data, so they outlive the run and can be
162 // handed to anything: a report writer, a database, a serde format.
163 let failures: Vec<&str> = parsed
164 .iter()
165 .filter(|result| result.status == "Failed")
166 .map(|result| result.name.as_str())
167 .collect();
168 if failures.is_empty() {
169 println!("No failures.");
170 } else {
171 println!("Failed steps: {}", failures.join(", "));
172 }
173
174 engine.release_sequence_file_ex(sequence_file, none())?;
175 Ok(())
176}