Skip to content

Commit cbbb2ee

Browse files
committed
Pretty print failing tests
Signed-off-by: Didier Wenzek <[email protected]>
1 parent 658a727 commit cbbb2ee

File tree

2 files changed

+114
-16
lines changed
  • crates/core/tedge_agent

2 files changed

+114
-16
lines changed

crates/core/tedge_agent/proptest-regressions/entity_manager/tests.txt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@
44
#
55
# It is recommended to check this file in to source control so that
66
# everyone who runs the test benefits from these saved cases.
7-
cc eb284ae3160cf4a071a3ca80175b2c72680b7c8ba19b8d577805ef1317eca2db # shrinks to registrations = [(MQTT, AddService { topic: "ac", props: [] }), (MQTT, AddService { topic: "ac", props: [] }), (MQTT, AddDevice { topic: "ac", props: [] }), (HTTP, AddService { topic: "ac", props: [("x", "4")] }), (HTTP, AddDevice { topic: "a", props: [] })]
8-
cc 6e3a5e19daf6c3a4732a0504822f2164c5d0f49ed5652e1e67c779a23aedbe77 # shrinks to registrations = [(HTTP, AddService { topic: "a", props: [] }), (MQTT, AddDevice { topic: "abc", props: [("z", "0")] }), (MQTT, AddDevice { topic: "abc", props: [("z", "8")] }), (HTTP, AddService { topic: "aa", props: [] }), (MQTT, AddDevice { topic: "abc", props: [("y", "3")] }), (MQTT, AddDevice { topic: "ab", props: [] }), (HTTP, AddService { topic: "a", props: [] }), (HTTP, AddDevice { topic: "abc", props: [("y", "7")] }), (MQTT, AddDevice { topic: "a", props: [] })]
7+
cc eb284ae3160cf4a071a3ca80175b2c72680b7c8ba19b8d577805ef1317eca2db # shrinks to registrations = tedge mqtt pub -r te/device/a/service/c '{"@parent":"device/a//","@type":"service"}' && tedge mqtt pub -r te/device/a/service/c '{"@parent":"device/a//","@type":"service"}' && tedge mqtt pub -r te/device/ac// '{"@parent":"device/a//","@type":"child-device"}' && tedge http post /tedge/entity-store/v1/entities '{"@parent":"device/a//","@topic-id":"device/a/service/c","@type":"service","x":"4"}' && tedge http post /tedge/entity-store/v1/entities '{"@parent":"device/main//","@topic-id":"device/a//","@type":"child-device"}'
8+
cc 6e3a5e19daf6c3a4732a0504822f2164c5d0f49ed5652e1e67c779a23aedbe77 # shrinks to registrations = tedge http post /tedge/entity-store/v1/entities '{"@parent":"device/main//","@topic-id":"device/main/service/a","@type":"service"}' && tedge mqtt pub -r te/device/abc// '{"@parent":"device/ab//","@type":"child-device","z":"0"}' && tedge mqtt pub -r te/device/abc// '{"@parent":"device/ab//","@type":"child-device","z":"8"}' && tedge http post /tedge/entity-store/v1/entities '{"@parent":"device/a//","@topic-id":"device/a/service/a","@type":"service"}' && tedge mqtt pub -r te/device/abc// '{"@parent":"device/ab//","@type":"child-device","y":"3"}' && tedge mqtt pub -r te/device/ab// '{"@parent":"device/a//","@type":"child-device"}' && tedge http post /tedge/entity-store/v1/entities '{"@parent":"device/main//","@topic-id":"device/main/service/a","@type":"service"}' && tedge http post /tedge/entity-store/v1/entities '{"@parent":"device/ab//","@topic-id":"device/abc//","@type":"child-device","y":"7"}' && tedge mqtt pub -r te/device/a// '{"@parent":"device/main//","@type":"child-device"}'
9+
cc aa8e0b76fd3142bf3cd1df1f453a4468defd437111b406887d760f68d84ad5c3 # shrinks to registrations = tedge http delete /tedge/entity-store/v1/entities/device/a// && tedge http delete /tedge/entity-store/v1/entities/device/main/service/a && tedge mqtt pub -r te/device/main/service/a '{"@parent":"device/main//","@type":"service","z":"1"}' && tedge http delete /tedge/entity-store/v1/entities/device/main/service/a && tedge mqtt pub -r te/device/ab// '' && tedge mqtt pub -r te/device/a// '' && tedge http post /tedge/entity-store/v1/entities '{"@parent":"device/main//","@topic-id":"device/a//","@type":"child-device"}' && tedge http post /tedge/entity-store/v1/entities '{"@parent":"device/main//","@topic-id":"device/a//","@type":"child-device"}' && tedge mqtt pub -r te/device/a// '{"@parent":"device/main//","@type":"child-device","z":"4"}'
10+
cc 758d928d082b3dcf1091381c5079eb67097a3128d5185923bd25be490af4980d # shrinks to registrations = tedge mqtt pub -r te/device/main/service/b '{"@parent":"device/main//","@type":"service","z":"6"}' && tedge http delete /tedge/entity-store/v1/entities/device/b// && tedge mqtt pub -r te/device/main/service/b '' && tedge mqtt pub -r te/device/main/service/b '' && tedge mqtt pub -r te/device/b// '{"@parent":"device/main//","@type":"child-device","z":"4"}' && tedge mqtt pub -r te/device/b// '' && tedge mqtt pub -r te/device/b// '' && tedge mqtt pub -r te/device/b// '' && tedge mqtt pub -r te/device/b// '{"@parent":"device/main//","@type":"child-device","y":"7"}' && tedge mqtt pub -r te/device/main/service/b ''
11+
cc 8566571d898d3309d89053873256baa6ca3e459ae65f679c95d5883632531e4b # shrinks to registrations = tedge http post /tedge/entity-store/v1/entities '{"@parent":"device/main//","@topic-id":"device/main/service/a","@type":"service","y":"5"}' && tedge mqtt pub -r te/device/main/service/b '{"@parent":"device/main//","@type":"service"}' && tedge http post /tedge/entity-store/v1/entities '{"@parent":"device/a//","@topic-id":"device/ac//","@type":"child-device"}' && tedge mqtt pub -r te/device/ac// '{"@parent":"device/a//","@type":"child-device"}' && tedge http delete /tedge/entity-store/v1/entities/device/ac// && tedge http post /tedge/entity-store/v1/entities '{"@parent":"device/main//","@topic-id":"device/a//","@type":"child-device","z":"0"}' && tedge http delete /tedge/entity-store/v1/entities/device/a// && tedge http delete /tedge/entity-store/v1/entities/device/main/service/a && tedge http post /tedge/entity-store/v1/entities '{"@parent":"device/main//","@topic-id":"device/a//","@type":"child-device","x":"5"}'

crates/core/tedge_agent/src/entity_manager/tests.rs

Lines changed: 109 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ async fn new_entity_store() {
1515
}
1616

1717
proptest! {
18-
#![proptest_config(proptest::prelude::ProptestConfig::with_cases(1000))]
18+
//#![proptest_config(proptest::prelude::ProptestConfig::with_cases(1000))]
1919
#[test]
2020
fn it_works_for_any_registration_order(registrations in model::walk(10)) {
2121
tokio::runtime::Builder::new_current_thread()
@@ -26,7 +26,7 @@ proptest! {
2626
let (mut entity_store, _mqtt_output) = entity::server("device-under-test");
2727
let mut state = model::State::new();
2828

29-
for (protocol,action) in registrations {
29+
for model::Command{protocol,action} in registrations.0 {
3030
let expected_updates = state.apply(protocol, action.clone());
3131
let actual_updates = match entity_store.handle((protocol,action).into()).await {
3232
EntityStoreResponse::Create(Ok(registered_entities)) => {
@@ -128,11 +128,91 @@ mod model {
128128
use proptest::prelude::*;
129129
use std::collections::HashMap;
130130
use std::collections::HashSet;
131+
use std::fmt::Debug;
132+
use std::fmt::Display;
133+
use std::fmt::Formatter;
131134
use tedge_api::entity::EntityType;
132135
use tedge_api::entity_store::EntityRegistrationMessage;
133136
use tedge_api::mqtt_topics::EntityTopicId;
134137
use tedge_api::mqtt_topics::MqttSchema;
135138

139+
#[derive(Clone)]
140+
pub struct Commands(pub Vec<Command>);
141+
142+
impl Debug for Commands {
143+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
144+
// mimicking a sequence of cli commands, with no extra quotes
145+
// e.g:
146+
// tedge mqtt pub -r device/main/service/a '{"@parent":"device/main//","@type":"service","x":"9"}' \
147+
// && tedge http post /tedge/entity-store/v1/entities '{"@parent":"device/main//","@topic-id":"device/c//","@type":"child-device","z":"5"}'
148+
let mut sep = if f.alternate() {
149+
"\n " // On test unit output, print each command on a new line
150+
} else {
151+
"" // On proptest log, print all the commands on a single line
152+
};
153+
154+
for command in &self.0 {
155+
f.write_str(sep)?;
156+
f.write_str(format!("{command}").as_str())?;
157+
if f.alternate() {
158+
sep = "\n "; // On test unit output, print each command on a new line
159+
} else {
160+
sep = " && "; // On proptest log, print all the commands on a single line
161+
}
162+
}
163+
f.write_str("\n")?;
164+
Ok(())
165+
}
166+
}
167+
168+
#[derive(Clone)]
169+
pub struct Command {
170+
pub protocol: Protocol,
171+
pub action: Action,
172+
}
173+
174+
impl Debug for Command {
175+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
176+
// Print the command line with no extra quotes
177+
f.write_str(format!("{self}").as_str())
178+
}
179+
}
180+
181+
impl Display for Command {
182+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
183+
let topic = self.action.topic_id().to_string();
184+
185+
let cmd = match self.action {
186+
Action::AddDevice { .. } | Action::AddService { .. } => {
187+
let payload = self.action.payload();
188+
match self.protocol {
189+
Protocol::HTTP => {
190+
let mut payload = payload;
191+
payload.insert("@topic-id".to_string(), topic.into());
192+
let payload = serde_json::Value::Object(payload).to_string();
193+
format!("tedge http post /tedge/entity-store/v1/entities '{payload}'")
194+
}
195+
Protocol::MQTT => {
196+
let payload = serde_json::Value::Object(payload).to_string();
197+
format!("tedge mqtt pub -r te/{topic} '{payload}'")
198+
}
199+
}
200+
}
201+
Action::RemDevice { .. } | Action::RemService { .. } => match self.protocol {
202+
Protocol::HTTP => {
203+
format!("tedge http delete /tedge/entity-store/v1/entities/{topic}")
204+
}
205+
Protocol::MQTT => {
206+
format!("tedge mqtt pub -r te/{topic} ''")
207+
}
208+
},
209+
};
210+
211+
// Print the command line with no extra quotes
212+
f.write_str(cmd.as_str())
213+
}
214+
}
215+
136216
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
137217
#[allow(clippy::upper_case_acronyms)]
138218
pub enum Protocol {
@@ -228,6 +308,15 @@ mod model {
228308
Action::RemDevice { .. } | Action::RemService { .. } => serde_json::Map::new(),
229309
}
230310
}
311+
312+
pub fn payload(&self) -> serde_json::Map<String, serde_json::Value> {
313+
let mut props = self.properties();
314+
if let Some(parent) = self.parent_topic_id() {
315+
props.insert("@parent".to_string(), parent.to_string().into());
316+
}
317+
props.insert("@type".to_string(), self.target_type().to_string().into());
318+
props
319+
}
231320
}
232321

233322
impl From<Action> for EntityRegistrationMessage {
@@ -499,42 +588,48 @@ mod model {
499588
}
500589

501590
prop_compose! {
502-
pub fn random_action_on(topic: String)(
591+
pub fn random_command_on(topic: String)(
503592
protocol in random_protocol(),
504593
action in 1..5,
505594
props in random_props(2)
506-
) -> (Protocol,Action) {
595+
) -> Command {
507596
let topic = topic.to_owned();
508597
let action = match action {
509598
1 => Action::AddDevice{ topic, props },
510599
2 => Action::AddService{ topic, props },
511600
3 => Action::RemService{ topic },
512601
_ => Action::RemDevice{ topic },
513602
};
514-
(protocol, action)
603+
Command { protocol, action }
515604
}
516605
}
517606

518-
pub fn random_action() -> impl Strategy<Value = (Protocol, Action)> {
519-
random_name().prop_flat_map(random_action_on)
607+
pub fn random_command() -> impl Strategy<Value = Command> {
608+
random_name().prop_flat_map(random_command_on)
520609
}
521610

522-
fn step(actions: Vec<(Protocol, Action)>) -> impl Strategy<Value = Vec<(Protocol, Action)>> {
523-
let nodes = actions.iter().map(|(_, a)| a.target().to_owned()).collect();
611+
fn step(actions: Commands) -> impl Strategy<Value = Commands> {
612+
let nodes = actions
613+
.0
614+
.iter()
615+
.map(|c| c.action.target().to_owned())
616+
.collect();
524617
pick_random_or_new(nodes)
525-
.prop_flat_map(random_action_on)
618+
.prop_flat_map(random_command_on)
526619
.prop_flat_map(move |action| {
527620
let mut actions = actions.clone();
528-
actions.push(action);
621+
actions.0.push(action);
529622
Just(actions)
530623
})
531624
}
532625

533-
pub fn walk(max_length: u32) -> impl Strategy<Value = Vec<(Protocol, Action)>> {
626+
pub fn walk(max_length: u32) -> impl Strategy<Value = Commands> {
534627
if max_length == 0 {
535-
Just(vec![]).boxed()
628+
Just(Commands(vec![])).boxed()
536629
} else if max_length == 1 {
537-
prop::collection::vec(random_action(), 0..=1).boxed()
630+
prop::collection::vec(random_command(), 0..=1)
631+
.prop_flat_map(|cmds| Just(Commands(cmds)))
632+
.boxed()
538633
} else {
539634
walk(max_length - 1).prop_flat_map(step).boxed()
540635
}

0 commit comments

Comments
 (0)