summaryrefslogtreecommitdiffstats
path: root/src/tools/compiletest/src/runtest/debugger.rs
blob: 379ff0bab408a3120bda8803b9c016a3e216377b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use crate::common::Config;
use crate::header::line_directive;
use crate::runtest::ProcRes;

use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;

pub(super) struct DebuggerCommands {
    pub commands: Vec<String>,
    pub check_lines: Vec<String>,
    pub breakpoint_lines: Vec<usize>,
}

impl DebuggerCommands {
    pub(super) fn parse_from(
        file: &Path,
        config: &Config,
        debugger_prefixes: &[&str],
        rev: Option<&str>,
    ) -> Result<Self, String> {
        let directives = debugger_prefixes
            .iter()
            .map(|prefix| (format!("{}-command", prefix), format!("{}-check", prefix)))
            .collect::<Vec<_>>();

        let mut breakpoint_lines = vec![];
        let mut commands = vec![];
        let mut check_lines = vec![];
        let mut counter = 0;
        let reader = BufReader::new(File::open(file).unwrap());
        for line in reader.lines() {
            counter += 1;
            match line {
                Ok(line) => {
                    let (lnrev, line) = line_directive("//", &line).unwrap_or((None, &line));

                    // Skip any revision specific directive that doesn't match the current
                    // revision being tested
                    if lnrev.is_some() && lnrev != rev {
                        continue;
                    }

                    if line.contains("#break") {
                        breakpoint_lines.push(counter);
                    }

                    for &(ref command_directive, ref check_directive) in &directives {
                        config
                            .parse_name_value_directive(&line, command_directive)
                            .map(|cmd| commands.push(cmd));

                        config
                            .parse_name_value_directive(&line, check_directive)
                            .map(|cmd| check_lines.push(cmd));
                    }
                }
                Err(e) => return Err(format!("Error while parsing debugger commands: {}", e)),
            }
        }

        Ok(Self { commands, check_lines, breakpoint_lines })
    }
}

pub(super) fn check_debugger_output(
    debugger_run_result: &ProcRes,
    check_lines: &[String],
) -> Result<(), String> {
    let num_check_lines = check_lines.len();

    let mut check_line_index = 0;
    for line in debugger_run_result.stdout.lines() {
        if check_line_index >= num_check_lines {
            break;
        }

        if check_single_line(line, &(check_lines[check_line_index])[..]) {
            check_line_index += 1;
        }
    }
    if check_line_index != num_check_lines && num_check_lines > 0 {
        Err(format!("line not found in debugger output: {}", check_lines[check_line_index]))
    } else {
        Ok(())
    }
}

fn check_single_line(line: &str, check_line: &str) -> bool {
    // Allow check lines to leave parts unspecified (e.g., uninitialized
    // bits in the  wrong case of an enum) with the notation "[...]".
    let line = line.trim();
    let check_line = check_line.trim();
    let can_start_anywhere = check_line.starts_with("[...]");
    let can_end_anywhere = check_line.ends_with("[...]");

    let check_fragments: Vec<&str> =
        check_line.split("[...]").filter(|frag| !frag.is_empty()).collect();
    if check_fragments.is_empty() {
        return true;
    }

    let (mut rest, first_fragment) = if can_start_anywhere {
        match line.find(check_fragments[0]) {
            Some(pos) => (&line[pos + check_fragments[0].len()..], 1),
            None => return false,
        }
    } else {
        (line, 0)
    };

    for current_fragment in &check_fragments[first_fragment..] {
        match rest.find(current_fragment) {
            Some(pos) => {
                rest = &rest[pos + current_fragment.len()..];
            }
            None => return false,
        }
    }

    if !can_end_anywhere && !rest.is_empty() { false } else { true }
}