##// END OF EJS Templates
copies-rust: add a macro-based unit-testing framework...
Simon Sapin -
r47405:b92083ad default draft
parent child Browse files
Show More
@@ -0,0 +1,141 b''
1 use super::*;
2
3 /// Unit tests for:
4 ///
5 /// ```ignore
6 /// fn compare_value(
7 /// current_merge: Revision,
8 /// merge_case_for_dest: impl Fn() -> MergeCase,
9 /// src_minor: &CopySource,
10 /// src_major: &CopySource,
11 /// ) -> (MergePick, /* overwrite: */ bool)
12 /// ```
13 #[test]
14 fn test_compare_value() {
15 // The `compare_value!` macro calls the `compare_value` function with
16 // arguments given in pseudo-syntax:
17 //
18 // * For `merge_case_for_dest` it takes a plain `MergeCase` value instead
19 // of a closure.
20 // * `CopySource` values are represented as `(rev, path, overwritten)`
21 // tuples of type `(Revision, Option<PathToken>, OrdSet<Revision>)`.
22 // * `PathToken` is an integer not read by `compare_value`. It only checks
23 // for `Some(_)` indicating a file copy v.s. `None` for a file deletion.
24 // * `OrdSet<Revision>` is represented as a Python-like set literal.
25
26 use MergeCase::*;
27 use MergePick::*;
28
29 assert_eq!(
30 compare_value!(1, Normal, (1, None, { 1 }), (1, None, { 1 })),
31 (Any, false)
32 );
33 }
34
35 /// Unit tests for:
36 ///
37 /// ```ignore
38 /// fn merge_copies_dict(
39 /// path_map: &TwoWayPathMap, // Not visible in test cases
40 /// current_merge: Revision,
41 /// minor: InternalPathCopies,
42 /// major: InternalPathCopies,
43 /// get_merge_case: impl Fn(&HgPath) -> MergeCase + Copy,
44 /// ) -> InternalPathCopies
45 /// ```
46 #[test]
47 fn test_merge_copies_dict() {
48 // The `merge_copies_dict!` macro calls the `merge_copies_dict` function
49 // with arguments given in pseudo-syntax:
50 //
51 // * `TwoWayPathMap` and path tokenization are implicitly taken care of.
52 // All paths are given as string literals.
53 // * Key-value maps are represented with `{key1 => value1, key2 => value2}`
54 // pseudo-syntax.
55 // * `InternalPathCopies` is a map of copy destination path keys to
56 // `CopySource` values.
57 // - `CopySource` is represented as a `(rev, source_path, overwritten)`
58 // tuple of type `(Revision, Option<Path>, OrdSet<Revision>)`.
59 // - Unlike in `test_compare_value`, source paths are string literals.
60 // - `OrdSet<Revision>` is again represented as a Python-like set
61 // literal.
62 // * `get_merge_case` is represented as a map of copy destination path to
63 // `MergeCase`. The default for paths not in the map is
64 // `MergeCase::Normal`.
65 //
66 // `internal_path_copies!` creates an `InternalPathCopies` value with the
67 // same pseudo-syntax as in `merge_copies_dict!`.
68
69 use MergeCase::*;
70
71 assert_eq!(
72 merge_copies_dict!(
73 1,
74 {"foo" => (1, None, {})},
75 {},
76 {"foo" => Merged}
77 ),
78 internal_path_copies!("foo" => (1, None, {}))
79 );
80 }
81
82 /// Unit tests for:
83 ///
84 /// ```ignore
85 /// impl CombineChangesetCopies {
86 /// fn new(children_count: HashMap<Revision, usize>) -> Self
87 ///
88 /// // Called repeatedly:
89 /// fn add_revision_inner<'a>(
90 /// &mut self,
91 /// rev: Revision,
92 /// p1: Revision,
93 /// p2: Revision,
94 /// copy_actions: impl Iterator<Item = Action<'a>>,
95 /// get_merge_case: impl Fn(&HgPath) -> MergeCase + Copy,
96 /// )
97 ///
98 /// fn finish(mut self, target_rev: Revision) -> PathCopies
99 /// }
100 /// ```
101 #[test]
102 fn test_combine_changeset_copies() {
103 // `combine_changeset_copies!` creates a `CombineChangesetCopies` with
104 // `new`, then calls `add_revision_inner` repeatedly, then calls `finish`
105 // for its return value.
106 //
107 // All paths given as string literals.
108 //
109 // * Key-value maps are represented with `{key1 => value1, key2 => value2}`
110 // pseudo-syntax.
111 // * `children_count` is a map of revision numbers to count of children in
112 // the DAG. It includes all revisions that should be considered by the
113 // algorithm.
114 // * Calls to `add_revision_inner` are represented as an array of anonymous
115 // structs with named fields, one pseudo-struct per call.
116 //
117 // `path_copies!` creates a `PathCopies` value, a map of copy destination
118 // keys to copy source values. Note: the arrows for map literal syntax
119 // point **backwards** compared to the logical direction of copy!
120
121 use crate::NULL_REVISION as NULL;
122 use Action::*;
123 use MergeCase::*;
124
125 assert_eq!(
126 combine_changeset_copies!(
127 { 1 => 1, 2 => 1 },
128 [
129 { rev: 1, p1: NULL, p2: NULL, actions: [], merge_cases: {}, },
130 { rev: 2, p1: NULL, p2: NULL, actions: [], merge_cases: {}, },
131 {
132 rev: 3, p1: 1, p2: 2,
133 actions: [CopiedFromP1("destination.txt", "source.txt")],
134 merge_cases: {"destination.txt" => Merged},
135 },
136 ],
137 3,
138 ),
139 path_copies!("destination.txt" => "source.txt")
140 );
141 }
@@ -0,0 +1,199 b''
1 //! Supporting macros for `tests.rs` in the same directory.
2 //! See comments there for usage.
3
4 /// Python-like set literal
5 macro_rules! set {
6 (
7 $Type: ty {
8 $( $value: expr ),* $(,)?
9 }
10 ) => {{
11 #[allow(unused_mut)]
12 let mut set = <$Type>::new();
13 $( set.insert($value); )*
14 set
15 }}
16 }
17
18 /// `{key => value}` map literal
19 macro_rules! map {
20 (
21 $Type: ty {
22 $( $key: expr => $value: expr ),* $(,)?
23 }
24 ) => {{
25 #[allow(unused_mut)]
26 let mut set = <$Type>::new();
27 $( set.insert($key, $value); )*
28 set
29 }}
30 }
31
32 macro_rules! copy_source {
33 ($rev: expr, $path: expr, $overwritten: tt) => {
34 CopySource {
35 rev: $rev,
36 path: $path,
37 overwritten: set!(OrdSet<Revision> $overwritten),
38 }
39 };
40 }
41
42 macro_rules! compare_value {
43 (
44 $merge_revision: expr,
45 $merge_case_for_dest: ident,
46 ($min_rev: expr, $min_path: expr, $min_overwrite: tt),
47 ($maj_rev: expr, $maj_path: expr, $maj_overwrite: tt) $(,)?
48 ) => {
49 compare_value(
50 $merge_revision,
51 || $merge_case_for_dest,
52 &copy_source!($min_rev, $min_path, $min_overwrite),
53 &copy_source!($maj_rev, $maj_path, $maj_overwrite),
54 )
55 };
56 }
57
58 macro_rules! tokenized_path_copies {
59 (
60 $path_map: ident, {$(
61 $dest: expr => (
62 $src_rev: expr,
63 $src_path: expr,
64 $src_overwrite: tt
65 )
66 ),*}
67 $(,)*
68 ) => {
69 map!(InternalPathCopies {$(
70 $path_map.tokenize(HgPath::new($dest)) =>
71 copy_source!(
72 $src_rev,
73 Option::map($src_path, |p: &str| {
74 $path_map.tokenize(HgPath::new(p))
75 }),
76 $src_overwrite
77 )
78 )*})
79 }
80 }
81
82 macro_rules! merge_case_callback {
83 (
84 $( $merge_path: expr => $merge_case: ident ),*
85 $(,)?
86 ) => {
87 #[allow(unused)]
88 |merge_path| -> MergeCase {
89 $(
90 if (merge_path == HgPath::new($merge_path)) {
91 return $merge_case
92 }
93 )*
94 MergeCase::Normal
95 }
96 };
97 }
98
99 macro_rules! merge_copies_dict {
100 (
101 $current_merge: expr,
102 $minor_copies: tt,
103 $major_copies: tt,
104 $get_merge_case: tt $(,)?
105 ) => {
106 {
107 #[allow(unused_mut)]
108 let mut map = TwoWayPathMap::default();
109 let minor = tokenized_path_copies!(map, $minor_copies);
110 let major = tokenized_path_copies!(map, $major_copies);
111 merge_copies_dict(
112 &map, $current_merge, minor, major,
113 merge_case_callback! $get_merge_case,
114 )
115 .into_iter()
116 .map(|(token, source)| {
117 (
118 map.untokenize(token).to_string(),
119 (
120 source.rev,
121 source.path.map(|t| map.untokenize(t).to_string()),
122 source.overwritten.into_iter().collect(),
123 ),
124 )
125 })
126 .collect::<OrdMap<_, _>>()
127 }
128 };
129 }
130
131 macro_rules! internal_path_copies {
132 (
133 $(
134 $dest: expr => (
135 $src_rev: expr,
136 $src_path: expr,
137 $src_overwrite: tt $(,)?
138 )
139 ),*
140 $(,)*
141 ) => {
142 map!(OrdMap<_, _> {$(
143 String::from($dest) => (
144 $src_rev,
145 $src_path,
146 set!(OrdSet<Revision> $src_overwrite)
147 )
148 ),*})
149 };
150 }
151
152 macro_rules! combine_changeset_copies {
153 (
154 $children_count: tt,
155 [
156 $(
157 {
158 rev: $rev: expr,
159 p1: $p1: expr,
160 p2: $p2: expr,
161 actions: [
162 $(
163 $Action: ident($( $action_path: expr ),+)
164 ),*
165 $(,)?
166 ],
167 merge_cases: $merge: tt
168 $(,)?
169 }
170 ),*
171 $(,)?
172 ],
173 $target_rev: expr $(,)*
174 ) => {{
175 let count = map!(HashMap<Revision, usize> $children_count);
176 let mut combine_changeset_copies = CombineChangesetCopies::new(count);
177 $(
178 let actions = vec![$(
179 $Action($( HgPath::new($action_path) ),*)
180 ),*];
181 combine_changeset_copies.add_revision_inner(
182 $rev, $p1, $p2, actions.into_iter(),
183 merge_case_callback! $merge
184 );
185 )*
186 combine_changeset_copies.finish($target_rev)
187 }};
188 }
189
190 macro_rules! path_copies {
191 (
192 $( $expected_destination: expr => $expected_source: expr ),* $(,)?
193 ) => {
194 map!(PathCopies {$(
195 HgPath::new($expected_destination).to_owned()
196 => HgPath::new($expected_source).to_owned(),
197 ),*})
198 };
199 }
@@ -1,3 +1,10 b''
1 #[cfg(test)]
2 #[macro_use]
3 mod tests_support;
4
5 #[cfg(test)]
6 mod tests;
7
1 8 use crate::utils::hg_path::HgPath;
2 9 use crate::utils::hg_path::HgPathBuf;
3 10 use crate::Revision;
General Comments 0
You need to be logged in to leave comments. Login now