Crates.io | pest-test |
lib.rs | pest-test |
version | 0.1.6 |
source | src |
created_at | 2023-02-08 21:18:32.723161 |
updated_at | 2023-10-06 03:50:59.125776 |
description | Testing framework for pest parsers |
homepage | |
repository | https://github.com/jdidion/pest-test |
max_upload_size | |
id | 780264 |
size | 58,695 |
****# pest-test
Testing framework for pest parser (similar to tree-sitter test
).
A test case is a text file with three sections:
Here is an example test. Note that the code block delimiter is exactly 7 '=' characters. In this case, the parser ignores implicit whitespace, so the numbers of blank lines before/after the code are arbitrary.
My Test
=======
fn x() int {
return 1;
}
=======
(source_file
(function_definition
(identifier: "x")
(parameter_list)
(primitive_type: "int")
(block
(return_statement
(number: "1")
)
)
)
)
Nodes in the expected output S-expression may be annotated with attributes of the form #[name(args)]
. The currently recognized attributes are:
skip
: For some grammars, there are multiple levels of nesting that are only necessary to work around the limitations of PEG parsers, e.g., mathematical expressions with left-recursion. To simplify test cases involving such grammars, the skip
attribute can be used to ignore a specified number of levels of nesting in the actual parse tree.
(expression
#[skip(depth = 3)]
(sum
(number: 1)
(number: 2)
)
)
The main interface to the test framework is pest_test::PestTester
. By default, tests are assumed to be in the tests/pest
directory of your crate and have a .txt
file extension. The example below shows using the lazy_static
macro to create a single PestTester
instance and then using it to evaluate any number of tests.
#[cfg(test)]
mod tests {
use mycrate::parser::{MyParser, Rule};
use lazy_static::lazy_static;
use pest_test::{Error, PestTester};
lazy_static! {
static ref TESTER: PestTester<Rule, MyParser> =
PestTester::from_defaults(Rule::root_rule);
}
// Loads test from `tests/pest/mytest.txt` and evaluates it. Returns an `Err<pest_test::Error>`
// if there was an error evaluating the test, or if the expected and actual values do not match.
fn test_my_parser -> Result<(), Error> {
(*TESTER).evaluate_strict("mytest")
}
}
If you add pest-test-gen
as a dev dependency, then you can use the pest_tests
attribute macro to generate tests for all your test cases:
// Generate tests for all test cases in tests/pest/foo/ and all subdirectories. Since
// `lazy_static = true`, a single `PestTester` is created and used by all tests; otherwise a new
// `PestTester` would be created for each test.
#[pest_tests(
mycrate::parser::MyParser,
mycrate::parser::Rule,
"root_rule",
subdir = "foo",
recursive = true,
lazy_static = true,
)]
#[cfg(test)]
mod foo_tests {}
To disable colorization of the diff output, run cargo with CARGO_TERM_COLOR=never
.
Note that a test module is only recompiled when its code changes. Thus, if you add or rename a test case in tests/pest
without changing the test module, the test module might not get updated to include the new/renamed tests, so you may need to delete the target
folder to force your tests to be recompiled.
Test files are parsed using pest. The source code is parsed using your pest grammar, and the resulting tree is iterated exhaustively to build up a nested data structure, which is then matched to the same structure built from the expected output. If they don't match, the tree is printed with the differences in-line.