run_make_support/assertion_helpers/
mod.rs

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
//! Collection of assertions and assertion-related helpers.

#[cfg(test)]
mod tests;

use std::panic;
use std::path::Path;

use crate::{fs, regex};

/// Assert that `actual` is equal to `expected`.
#[track_caller]
pub fn assert_equals<A: AsRef<str>, E: AsRef<str>>(actual: A, expected: E) {
    let actual = actual.as_ref();
    let expected = expected.as_ref();

    if actual != expected {
        eprintln!("=== ACTUAL TEXT ===");
        eprintln!("{}", actual);
        eprintln!("=== EXPECTED ===");
        eprintln!("{}", expected);
        panic!("expected text does not match actual text");
    }
}

struct SearchDetails<'assertion_name, 'haystack, 'needle> {
    assertion_name: &'assertion_name str,
    haystack: &'haystack str,
    needle: &'needle str,
}

impl<'assertion_name, 'haystack, 'needle> SearchDetails<'assertion_name, 'haystack, 'needle> {
    fn dump(&self) {
        eprintln!("{}:", self.assertion_name);
        eprintln!("=== HAYSTACK ===");
        eprintln!("{}", self.haystack);
        eprintln!("=== NEEDLE ===");
        eprintln!("{}", self.needle);
    }
}

/// Assert that `haystack` contains `needle`.
#[track_caller]
pub fn assert_contains<H: AsRef<str>, N: AsRef<str>>(haystack: H, needle: N) {
    let haystack = haystack.as_ref();
    let needle = needle.as_ref();
    if !haystack.contains(needle) {
        SearchDetails { assertion_name: "assert_contains", haystack, needle }.dump();
        panic!("needle was not found in haystack");
    }
}

/// Assert that `haystack` does not contain `needle`.
#[track_caller]
pub fn assert_not_contains<H: AsRef<str>, N: AsRef<str>>(haystack: H, needle: N) {
    let haystack = haystack.as_ref();
    let needle = needle.as_ref();
    if haystack.contains(needle) {
        SearchDetails { assertion_name: "assert_not_contains", haystack, needle }.dump();
        panic!("needle was unexpectedly found in haystack");
    }
}

/// Assert that `haystack` contains the regex `needle`.
#[track_caller]
pub fn assert_contains_regex<H: AsRef<str>, N: AsRef<str>>(haystack: H, needle: N) {
    let haystack = haystack.as_ref();
    let needle = needle.as_ref();
    let re = regex::Regex::new(needle).unwrap();
    if !re.is_match(haystack) {
        SearchDetails { assertion_name: "assert_contains_regex", haystack, needle }.dump();
        panic!("regex was not found in haystack");
    }
}

/// Assert that `haystack` does not contain the regex `needle`.
#[track_caller]
pub fn assert_not_contains_regex<H: AsRef<str>, N: AsRef<str>>(haystack: H, needle: N) {
    let haystack = haystack.as_ref();
    let needle = needle.as_ref();
    let re = regex::Regex::new(needle).unwrap();
    if re.is_match(haystack) {
        SearchDetails { assertion_name: "assert_not_contains_regex", haystack, needle }.dump();
        panic!("regex was unexpectedly found in haystack");
    }
}

/// Assert that `haystack` contains regex `needle` an `expected_count` number of times.
#[track_caller]
pub fn assert_count_is<H: AsRef<str>, N: AsRef<str>>(
    expected_count: usize,
    haystack: H,
    needle: N,
) {
    let haystack = haystack.as_ref();
    let needle = needle.as_ref();

    let actual_count = haystack.matches(needle).count();
    if expected_count != actual_count {
        let count_fmt = format!(
            "assert_count_is (expected_count = {expected_count}, actual_count = {actual_count})"
        );
        SearchDetails { assertion_name: &count_fmt, haystack, needle }.dump();
        panic!(
            "regex did not appear {expected_count} times in haystack (expected_count = \
            {expected_count}, actual_count = {actual_count})"
        );
    }
}

/// Assert that all files in `dir1` exist and have the same content in `dir2`
// FIXME(#135037): not robust against symlinks, lacks sanity test coverage.
pub fn assert_dirs_are_equal(dir1: impl AsRef<Path>, dir2: impl AsRef<Path>) {
    let dir2 = dir2.as_ref();
    fs::read_dir_entries(dir1, |entry_path| {
        let entry_name = entry_path.file_name().unwrap();
        if entry_path.is_dir() {
            assert_dirs_are_equal(&entry_path, &dir2.join(entry_name));
        } else {
            let path2 = dir2.join(entry_name);
            let file1 = fs::read(&entry_path);
            let file2 = fs::read(&path2);

            // We don't use `assert_eq!` because they are `Vec<u8>`, so not great for display.
            // Why not using String? Because there might be minified files or even potentially
            // binary ones, so that would display useless output.
            assert!(
                file1 == file2,
                "`{}` and `{}` have different content",
                entry_path.display(),
                path2.display(),
            );
        }
    });
}