diff --git a/hw-model/src/lib.rs b/hw-model/src/lib.rs index e4f06cbf1a..0152a4876d 100644 --- a/hw-model/src/lib.rs +++ b/hw-model/src/lib.rs @@ -557,7 +557,7 @@ pub trait HwModel { } } - /// Execute until the output contains `expected_output`. + /// Execute until the output ends with `expected_output` fn step_until_output(&mut self, expected_output: &str) -> Result<(), Box> { self.step_until(|m| m.output().peek().len() >= expected_output.len()); if &self.output().peek()[..expected_output.len()] != expected_output { @@ -571,6 +571,12 @@ pub trait HwModel { Ok(()) } + // Execute (at least) until the output provided substr is written to the + // output. Additional data may be present in the output after the provided + // substr, which often happens with the fpga_realtime hardware model. + // + // This function will not match any data in the output that was written + // before this function was called. fn step_until_output_contains(&mut self, substr: &str) -> Result<(), Box> { self.output().set_search_term(substr); self.step_until(|m| m.output().search_matched()); diff --git a/hw-model/src/output.rs b/hw-model/src/output.rs index df044fd750..4883f81aad 100644 --- a/hw-model/src/output.rs +++ b/hw-model/src/output.rs @@ -158,7 +158,7 @@ pub struct Output { sink: OutputSink, search_term: Option, - unsearched: usize, // Number of characters that have not been searched yet + search_pos: usize, // Position to start searching from search_matched: bool, } impl Output { @@ -177,7 +177,7 @@ impl Output { next_write_needs_time_prefix: Cell::new(true), })), search_term: None, - unsearched: 0, + search_pos: 0, search_matched: false, } } @@ -210,6 +210,9 @@ impl Output { fn process_new_data(&mut self) { let new_data = self.sink.0.new_uart_output.take(); let new_data_len = new_data.len(); + if new_data_len == 0 { + return; + } if self.output.is_empty() { self.output = new_data; @@ -219,21 +222,19 @@ impl Output { if let Some(term) = &self.search_term { if !self.search_matched { - let to_search = term.len() + self.unsearched; - if self.output.len() >= to_search { - self.search_matched = - self.output[self.output.len() - to_search..].contains(term); - self.unsearched = 0; - } else { - self.unsearched += new_data_len; + self.search_matched = self.output[self.search_pos..].contains(term); + self.search_pos = self.output.len().saturating_sub(term.len()); + if self.search_matched { + self.search_term = None; } } } } pub(crate) fn set_search_term(&mut self, search_term: &str) { + self.process_new_data(); self.search_term = Some(search_term.to_string()); - self.unsearched = 0; + self.search_pos = self.output.len(); self.search_matched = false; } @@ -369,4 +370,37 @@ mod tests { assert_eq!(&out.take(30), ""); assert_eq!(log.into_string(), "Unknown generic load 0xd3\n"); } + + #[test] + fn test_search() { + let mut out = Output::new(Log::new()); + out.set_search_term("foobar"); + assert!(!out.search_matched); + for &ch in b"this is my foobar string!" { + out.sink.push_uart_char(ch); + } + out.process_new_data(); + assert!(out.search_matched); + out.set_search_term("foobar"); + out.process_new_data(); + assert!(!out.search_matched); + + for &ch in b"hello world strin" { + out.sink.push_uart_char(ch); + } + out.set_search_term("string"); + for &ch in b"g no match" { + out.sink.push_uart_char(ch); + } + out.process_new_data(); + assert!(!out.search_matched); + + for &ch in b" matching string" { + out.sink.push_uart_char(ch); + } + out.process_new_data(); + assert!(out.search_matched); + out.set_search_term("string"); + assert!(!out.search_matched); + } }