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
#![feature(crate_visibility_modifier)]
#![feature(specialization)]

use lark_collections::seq;
use lark_intern::Intern;
use lark_parser::{ParserDatabase, ParserDatabaseExt};
use lark_query_system::ls_ops::{Cancelled, LsDatabase, RangedDiagnostic};
use lark_query_system::LarkDatabase;
use lark_span::FileName;
use lark_string::Text;
use salsa::Database;
use std::fmt::Debug;

mod harness;
pub use harness::run_test_harness;
pub use harness::search_files;
pub use harness::TestPath;

pub use lark_debug_with::DebugWith;
pub use lark_span::IntoFileName;

pub trait ErrorSpec {
    fn check_errors(&self, errors: &[RangedDiagnostic]);
}

pub struct NoErrors;

impl ErrorSpec for NoErrors {
    fn check_errors(&self, errors: &[RangedDiagnostic]) {
        if errors.is_empty() {
            return;
        }

        for error in errors {
            eprintln!("{:?}", error);
        }

        assert_eq!(0, errors.len());
    }
}

impl ErrorSpec for &str {
    fn check_errors(&self, errors: &[RangedDiagnostic]) {
        assert_eq!(
            errors.len(),
            1,
            "expected exactly one error, got {:#?}",
            errors
        );

        for error in errors {
            let range = error.range;

            let expected = format!("0:{}..0:{}", self.find('~').unwrap(), self.len());
            let actual = format!(
                "{}:{}..{}:{}",
                range.start.line, range.start.character, range.end.line, range.end.character
            );

            if expected != actual {
                eprintln!("expected error on {}", expected);
                eprintln!("found error on {}", actual);
                eprintln!("error = {:#?}", error);
            }

            assert_eq!(expected, actual);
        }
    }
}

pub fn db_with_test(file_name: impl IntoFileName, text: &str) -> LarkDatabase {
    let mut db = LarkDatabase::default();
    db.add_file(file_name, text);
    db
}

pub fn run_test(text: &str, error_spec: impl ErrorSpec) {
    let file_name_str = "input.lark";
    let db = db_with_test(file_name_str, text);
    let parsed = db.parsed_file(file_name_str.into_file_name(&db));
    assert!(parsed.value.entities().len() >= 1, "input with no items");

    match db.errors_for_project() {
        Ok(errors) => {
            let flat_errors: Vec<_> = errors
                .into_iter()
                .flat_map(|(file_name, errors)| {
                    assert_eq!(file_name, file_name_str);
                    errors
                })
                .collect();
            error_spec.check_errors(&flat_errors);
        }

        Err(Cancelled) => {
            panic!("cancelled?!");
        }
    }
}

/// Creates a lark database with a single file containing the given
/// test. Intended for tests targeting the `lark_parser` crate, which
/// is not yet fully wired into everything else.
pub fn lark_parser_db(text: impl AsRef<str>) -> (FileName, LarkDatabase) {
    let text: &str = text.as_ref();
    let mut db = LarkDatabase::default();

    // Setup the input:
    let path1 = FileName {
        id: "path1".intern(&db),
    };
    let text = Text::from(text);
    db.query_mut(lark_parser::FileNamesQuery)
        .set((), seq![path1]);
    db.query_mut(lark_parser::FileTextQuery).set(path1, text);

    (path1, db)
}

/// Test that two values are equal, with a better error than `assert_eq`
pub fn assert_equal<Cx, A>(cx: &Cx, expected_value: &A, actual_value: &A)
where
    A: ?Sized + Debug + DebugWith + Eq,
{
    // First check that they have the same debug text. This produces a better error.
    let expected_text = format!("{:#?}", expected_value.debug_with(cx));
    assert_expected_debug(cx, &expected_text, actual_value);

    // Then check that they are `eq` too, for good measure.
    assert_eq!(expected_value, actual_value);
}

/// Test that the debug output of `actual_value` is as expected. Gives
/// a nice diff if things fail.
pub fn assert_expected_debug<Cx, A>(cx: &Cx, expected_text: &str, actual_value: &A)
where
    A: ?Sized + DebugWith,
{
    let actual_text = format!("{:#?}", actual_value.debug_with(cx));

    if expected_text == actual_text {
        return;
    }

    println!("# expected_text");
    println!("{}", expected_text);

    println!("# actual_text");
    println!("{}", actual_text);

    println!("# diff");
    for diff in diff::lines(&expected_text, &actual_text) {
        match diff {
            diff::Result::Left(l) => println!("-{}", l),
            diff::Result::Both(l, _) => println!(" {}", l),
            diff::Result::Right(r) => println!("+{}", r),
        }
    }

    panic!("debug comparison failed");
}