1 use crate::common
::Config
;
2 use crate::header
::line_directive
;
3 use crate::runtest
::ProcRes
;
7 use std
::io
::{BufRead, BufReader}
;
8 use std
::path
::{Path, PathBuf}
;
10 /// Representation of information to invoke a debugger and check its output
11 pub(super) struct DebuggerCommands
{
12 /// Commands for the debuuger
13 pub commands
: Vec
<String
>,
14 /// Lines to insert breakpoints at
15 pub breakpoint_lines
: Vec
<usize>,
16 /// Contains the source line number to check and the line itself
17 check_lines
: Vec
<(usize, String
)>,
22 impl DebuggerCommands
{
26 debugger_prefixes
: &[&str],
28 ) -> Result
<Self, String
> {
29 let directives
= debugger_prefixes
31 .map(|prefix
| (format
!("{prefix}-command"), format
!("{prefix}-check")))
34 let mut breakpoint_lines
= vec
![];
35 let mut commands
= vec
![];
36 let mut check_lines
= vec
![];
38 let reader
= BufReader
::new(File
::open(file
).unwrap());
39 for (line_no
, line
) in reader
.lines().enumerate() {
41 let line
= line
.map_err(|e
| format
!("Error while parsing debugger commands: {}", e
))?
;
42 let (lnrev
, line
) = line_directive("//", &line
).unwrap_or((None
, &line
));
44 // Skip any revision specific directive that doesn't match the current
45 // revision being tested
46 if lnrev
.is_some() && lnrev
!= rev
{
50 if line
.contains("#break") {
51 breakpoint_lines
.push(counter
);
54 for &(ref command_directive
, ref check_directive
) in &directives
{
56 .parse_name_value_directive(&line
, command_directive
)
57 .map(|cmd
| commands
.push(cmd
));
60 .parse_name_value_directive(&line
, check_directive
)
61 .map(|cmd
| check_lines
.push((line_no
, cmd
)));
65 Ok(Self { commands, breakpoint_lines, check_lines, file: file.to_owned() }
)
68 /// Given debugger output and lines to check, ensure that every line is
69 /// contained in the debugger output. The check lines need to be found in
70 /// order, but there can be extra lines between.
71 pub fn check_output(&self, debugger_run_result
: &ProcRes
) -> Result
<(), String
> {
72 // (src_lineno, ck_line) that we did find
73 let mut found
= vec
![];
74 // (src_lineno, ck_line) that we couldn't find
75 let mut missing
= vec
![];
76 // We can find our any current match anywhere after our last match
78 let dbg_lines
: Vec
<&str> = debugger_run_result
.stdout
.lines().collect();
80 for (src_lineno
, ck_line
) in &self.check_lines
{
81 if let Some(offset
) = dbg_lines
84 .position(|out_line
| check_single_line(out_line
, &ck_line
))
87 found
.push((src_lineno
, dbg_lines
[last_idx
]));
89 missing
.push((src_lineno
, ck_line
));
93 if missing
.is_empty() {
96 let fname
= self.file
.file_name().unwrap().to_string_lossy();
97 let mut msg
= format
!(
98 "check directive(s) from `{}` not found in debugger output. errors:",
102 for (src_lineno
, err_line
) in missing
{
103 write
!(msg
, "\n ({fname}:{num}) `{err_line}`", num
= src_lineno
+ 1).unwrap();
106 if !found
.is_empty() {
107 let init
= "\nthe following subset of check directive(s) was found successfully:";
109 for (src_lineno
, found_line
) in found
{
110 write
!(msg
, "\n ({fname}:{num}) `{found_line}`", num
= src_lineno
+ 1)
120 /// Check that the pattern in `check_line` applies to `line`. Returns `true` if they do match.
121 fn check_single_line(line
: &str, check_line
: &str) -> bool
{
122 // Allow check lines to leave parts unspecified (e.g., uninitialized
123 // bits in the wrong case of an enum) with the notation "[...]".
124 let line
= line
.trim();
125 let check_line
= check_line
.trim();
126 let can_start_anywhere
= check_line
.starts_with("[...]");
127 let can_end_anywhere
= check_line
.ends_with("[...]");
129 let check_fragments
: Vec
<&str> =
130 check_line
.split("[...]").filter(|frag
| !frag
.is_empty()).collect();
131 if check_fragments
.is_empty() {
135 let (mut rest
, first_fragment
) = if can_start_anywhere
{
136 let Some(pos
) = line
.find(check_fragments
[0]) else {
139 (&line
[pos
+ check_fragments
[0].len()..], 1)
144 for current_fragment
in &check_fragments
[first_fragment
..] {
145 let Some(pos
) = rest
.find(current_fragment
) else {
148 rest
= &rest
[pos
+ current_fragment
.len()..];
151 if !can_end_anywhere
&& !rest
.is_empty() { false }
else { true }