aboutsummaryrefslogtreecommitdiff
path: root/src/testkit/display.rs
blob: 3cef59c0b37eb27a01d091a8eb8d55e5258ce79b (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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
//! tools to test the correctness of `yaxpeax-arch` trait implementations.

use core::fmt;
use core::fmt::Write;

use crate::display::DisplaySink;

/// `DisplaySinkValidator` is a `DisplaySink` that panics if invariants required of
/// `DisplaySink`-writing functions are not upheld.
///
/// there are two categories of invariants that `DisplaySinkValidator` validates.
///
/// first, this panics if spans are not `span_end_*`-ed in first-in-last-out order with
/// corresponding `span_start_*. second, this panics if `write_lt_*` functions are ever provided
/// inputs longer than the corresponding maximum length.
///
/// functions that write to a `DisplaySink` are strongly encouraged to come with fuzzing that for
/// all inputs `DisplaySinkValidator` does not panic.
pub struct DisplaySinkValidator {
    spans: alloc::vec::Vec<&'static str>,
}

impl DisplaySinkValidator {
    pub fn new() -> Self {
        Self { spans: alloc::vec::Vec::new() }
    }
}

impl core::ops::Drop for DisplaySinkValidator {
    fn drop(&mut self) {
        if self.spans.len() != 0 {
            panic!("DisplaySinkValidator dropped with open spans");
        }
    }
}

impl fmt::Write for DisplaySinkValidator {
    fn write_str(&mut self, _s: &str) -> Result<(), fmt::Error> {
        Ok(())
    }
    fn write_char(&mut self, _c: char) -> Result<(), fmt::Error> {
        Ok(())
    }
}

impl DisplaySink for DisplaySinkValidator {
    unsafe fn write_lt_32(&mut self, s: &str) -> Result<(), fmt::Error> {
        if s.len() >= 32 {
            panic!("DisplaySinkValidator::write_lt_32 was given a string longer than the maximum permitted length");
        }

        self.write_str(s)
    }
    unsafe fn write_lt_16(&mut self, s: &str) -> Result<(), fmt::Error> {
        if s.len() >= 16 {
            panic!("DisplaySinkValidator::write_lt_16 was given a string longer than the maximum permitted length");
        }

        self.write_str(s)
    }
    unsafe fn write_lt_8(&mut self, s: &str) -> Result<(), fmt::Error> {
        if s.len() >= 8 {
            panic!("DisplaySinkValidator::write_lt_8 was given a string longer than the maximum permitted length");
        }

        self.write_str(s)
    }

    fn span_start_immediate(&mut self) {
        self.spans.push("immediate");
    }

    fn span_end_immediate(&mut self) {
        let last = self.spans.pop().expect("item to pop");
        assert_eq!(last, "immediate");
    }

    fn span_start_register(&mut self) {
        self.spans.push("register");
    }

    fn span_end_register(&mut self) {
        let last = self.spans.pop().expect("item to pop");
        assert_eq!(last, "register");
    }

    fn span_start_opcode(&mut self) {
        self.spans.push("opcode");
    }

    fn span_end_opcode(&mut self) {
        let last = self.spans.pop().expect("item to pop");
        assert_eq!(last, "opcode");
    }

    fn span_start_program_counter(&mut self) {
        self.spans.push("program counter");
    }

    fn span_end_program_counter(&mut self) {
        let last = self.spans.pop().expect("item to pop");
        assert_eq!(last, "program counter");
    }

    fn span_start_number(&mut self) {
        self.spans.push("number");
    }

    fn span_end_number(&mut self) {
        let last = self.spans.pop().expect("item to pop");
        assert_eq!(last, "number");
    }

    fn span_start_address(&mut self) {
        self.spans.push("address");
    }

    fn span_end_address(&mut self) {
        let last = self.spans.pop().expect("item to pop");
        assert_eq!(last, "address");
    }

    fn span_start_function_expr(&mut self) {
        self.spans.push("function expr");
    }

    fn span_end_function_expr(&mut self) {
        let last = self.spans.pop().expect("item to pop");
        assert_eq!(last, "function expr");
    }
}

/// `DisplaySinkWriteComparator` helps test that two `DisplaySink` implementations which should
/// produce the same output actually do.
///
/// this is most useful for cases like testing specialized `write_lt_*` functions, which ought to
/// behave the same as if `write_str()` were called instead and so can be used as a very simple
/// oracle.
///
/// this is somewhat less useful when the sinks are expected to produce unequal text, such as when
/// one sink writes ANSI color sequences and the other does not.
pub struct DisplaySinkWriteComparator<'sinks, T: DisplaySink, U: DisplaySink> {
    sink1: &'sinks mut T,
    sink1_check: fn(&T) -> &str,
    sink2: &'sinks mut U,
    sink2_check: fn(&U) -> &str,
}

impl<'sinks, T: DisplaySink, U: DisplaySink> DisplaySinkWriteComparator<'sinks, T, U> {
    pub fn new(
        t: &'sinks mut T, t_check: fn(&T) -> &str,
        u: &'sinks mut U, u_check: fn(&U) -> &str
    ) -> Self {
        Self {
            sink1: t,
            sink1_check: t_check,
            sink2: u,
            sink2_check: u_check,
        }
    }

    fn compare_sinks(&self) {
        let sink1_text = (self.sink1_check)(self.sink1);
        let sink2_text = (self.sink2_check)(self.sink2);

        if sink1_text != sink2_text {
            panic!("sinks produced different output: {} != {}", sink1_text, sink2_text);
        }
    }
}

impl<'sinks, T: DisplaySink, U: DisplaySink> DisplaySink for DisplaySinkWriteComparator<'sinks, T, U> {
    fn write_u8(&mut self, v: u8) -> Result<(), fmt::Error> {
        self.sink1.write_u8(v).expect("write to sink1 succeeds");
        self.sink2.write_u8(v).expect("write to sink2 succeeds");
        self.compare_sinks();
        Ok(())
    }
}

impl<'sinks, T: DisplaySink, U: DisplaySink> fmt::Write for DisplaySinkWriteComparator<'sinks, T, U> {
    fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
        self.sink1.write_str(s).expect("write to sink1 succeeds");
        self.sink2.write_str(s).expect("write to sink2 succeeds");
        Ok(())
    }
    fn write_char(&mut self, c: char) -> Result<(), fmt::Error> {
        self.sink1.write_char(c).expect("write to sink1 succeeds");
        self.sink2.write_char(c).expect("write to sink2 succeeds");
        Ok(())
    }
}