diff options
Diffstat (limited to 'crates/ra_text_edit/src/test_utils.rs')
-rw-r--r-- | crates/ra_text_edit/src/test_utils.rs | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/crates/ra_text_edit/src/test_utils.rs b/crates/ra_text_edit/src/test_utils.rs new file mode 100644 index 000000000..745f21c93 --- /dev/null +++ b/crates/ra_text_edit/src/test_utils.rs | |||
@@ -0,0 +1,85 @@ | |||
1 | use proptest::prelude::*; | ||
2 | use text_unit::{TextUnit, TextRange}; | ||
3 | use crate::{AtomTextEdit, TextEdit}; | ||
4 | |||
5 | pub fn arb_text() -> proptest::string::RegexGeneratorStrategy<String> { | ||
6 | // generate multiple newlines | ||
7 | proptest::string::string_regex("(.*\n?)*").unwrap() | ||
8 | } | ||
9 | |||
10 | fn text_offsets(text: &str) -> Vec<TextUnit> { | ||
11 | text.char_indices() | ||
12 | .map(|(i, _)| TextUnit::from_usize(i)) | ||
13 | .collect() | ||
14 | } | ||
15 | |||
16 | pub fn arb_offset(text: &str) -> BoxedStrategy<TextUnit> { | ||
17 | let offsets = text_offsets(text); | ||
18 | // this is necessary to avoid "Uniform::new called with `low >= high`" panic | ||
19 | if offsets.is_empty() { | ||
20 | Just(TextUnit::from(0)).boxed() | ||
21 | } else { | ||
22 | prop::sample::select(offsets).boxed() | ||
23 | } | ||
24 | } | ||
25 | |||
26 | pub fn arb_text_edit(text: &str) -> BoxedStrategy<TextEdit> { | ||
27 | if text.is_empty() { | ||
28 | // only valid edits | ||
29 | return Just(vec![]) | ||
30 | .boxed() | ||
31 | .prop_union( | ||
32 | arb_text() | ||
33 | .prop_map(|text| vec![AtomTextEdit::insert(TextUnit::from(0), text)]) | ||
34 | .boxed(), | ||
35 | ) | ||
36 | .prop_map(TextEdit::from_atoms) | ||
37 | .boxed(); | ||
38 | } | ||
39 | |||
40 | let offsets = text_offsets(text); | ||
41 | let max_cuts = 7.min(offsets.len()); | ||
42 | |||
43 | proptest::sample::subsequence(offsets, 0..max_cuts) | ||
44 | .prop_flat_map(|cuts| { | ||
45 | let strategies: Vec<_> = cuts | ||
46 | .chunks(2) | ||
47 | .map(|chunk| match chunk { | ||
48 | &[from, to] => { | ||
49 | let range = TextRange::from_to(from, to); | ||
50 | Just(AtomTextEdit::delete(range)) | ||
51 | .boxed() | ||
52 | .prop_union( | ||
53 | arb_text() | ||
54 | .prop_map(move |text| AtomTextEdit::replace(range, text)) | ||
55 | .boxed(), | ||
56 | ) | ||
57 | .boxed() | ||
58 | } | ||
59 | &[x] => arb_text() | ||
60 | .prop_map(move |text| AtomTextEdit::insert(x, text)) | ||
61 | .boxed(), | ||
62 | _ => unreachable!(), | ||
63 | }) | ||
64 | .collect(); | ||
65 | strategies | ||
66 | }) | ||
67 | .prop_map(TextEdit::from_atoms) | ||
68 | .boxed() | ||
69 | } | ||
70 | |||
71 | #[derive(Debug, Clone)] | ||
72 | pub struct ArbTextWithEdit { | ||
73 | pub text: String, | ||
74 | pub edit: TextEdit, | ||
75 | } | ||
76 | |||
77 | pub fn arb_text_with_edit() -> BoxedStrategy<ArbTextWithEdit> { | ||
78 | let text = arb_text(); | ||
79 | text.prop_flat_map(|s| { | ||
80 | let edit = arb_text_edit(&s); | ||
81 | (Just(s), edit) | ||
82 | }) | ||
83 | .prop_map(|(text, edit)| ArbTextWithEdit { text, edit }) | ||
84 | .boxed() | ||
85 | } | ||