integra8/decorations/
hierarchy.rs1use std::collections::HashMap;
2
3use crate::decorations::{BookEndDecoration, ComponentDecoration, SuiteAttributesDecoration, TestDecoration};
4
5use crate::components::{
6 ComponentDescription, ComponentGeneratorId, Suite, SuiteAttributes, TestParameters,
7};
8
9#[derive(Debug)]
10pub struct ComponentGroup<TParameters> {
11 pub suite: Option<SuiteAttributesDecoration>,
12 pub tests: Vec<TestDecoration<TParameters>>,
13 pub setups: Vec<BookEndDecoration<TParameters>>,
14 pub tear_downs: Vec<BookEndDecoration<TParameters>>,
15 pub sub_groups: Vec<ComponentGroup<TParameters>>,
16}
17
18impl<TParameters: TestParameters> ComponentGroup<TParameters> {
19 pub fn into_root_component<ComponentsIterator>(
20 components: ComponentsIterator,
21 parameters: &TParameters,
22 ) -> Suite<TParameters>
23 where
24 ComponentsIterator: IntoIterator<Item = ComponentDecoration<TParameters>>,
25 {
26 ComponentHierarchy::from_decorated_components(components)
27 .into_component_groups()
28 .into_component(&mut ComponentGeneratorId::new(), None, parameters)
29 }
30
31 fn into_component(
32 self,
33 id_gen: &mut ComponentGeneratorId,
34 parent: Option<(&SuiteAttributes, &ComponentDescription)>,
35 parameters: &TParameters,
36 ) -> Suite<TParameters> {
37 let parent_suite_attributes = self
38 .suite
39 .unwrap_or_else(|| SuiteAttributesDecoration::root(parameters.root_namespace()));
40
41 let mut suite = parent_suite_attributes.into_component(id_gen.next(), parent, parameters);
42
43 suite.setups = self
47 .setups
48 .into_iter()
49 .map(|x| x.into_setup_component(id_gen.next(), &suite.description, &suite.attributes))
50 .collect();
51
52 suite.tests = self
54 .tests
55 .into_iter()
56 .map(|x| {
57 x.into_component(
58 id_gen.next(),
59 &suite.description,
60 &suite.attributes,
61 parameters,
62 )
63 })
64 .collect();
65
66 suite.suites = self
68 .sub_groups
69 .into_iter()
70 .map(|x| {
71 x.into_component(
72 id_gen,
73 Some((&suite.attributes, &suite.description)),
74 parameters,
75 )
76 })
77 .collect();
78
79 suite.tear_downs = self
81 .tear_downs
82 .into_iter()
83 .map(|x| {
84 x.into_tear_down_component(id_gen.next(), &suite.description, &suite.attributes)
85 })
86 .collect();
87
88 suite
89 }
90}
91
92#[derive(Debug)]
93pub struct ComponentHierarchy<TParameters> {
94 root: HierarchyNode<TParameters>,
95}
96
97impl<TParameters> ComponentHierarchy<TParameters> {
98 pub fn new() -> Self {
99 Self {
100 root: HierarchyNode::new_node(),
101 }
102 }
103
104 pub fn from_decorated_components<ComponentsIterator>(components: ComponentsIterator) -> Self
105 where
106 ComponentsIterator: IntoIterator<Item = ComponentDecoration<TParameters>>,
107 {
108 Self {
109 root: components
110 .into_iter()
111 .fold(HierarchyNode::new_node(), |mut root, c| {
112 root.insert_component(c);
113 root
114 }),
115 }
116 }
117
118 pub fn into_component_groups(self) -> ComponentGroup<TParameters> {
119 self.root.into_component_groups()
120 }
121}
122
123#[derive(Debug)]
124pub struct HierarchyNode<TParameters> {
125 suite: Option<SuiteAttributesDecoration>,
126 tests: Vec<TestDecoration<TParameters>>,
127 setups: Vec<BookEndDecoration<TParameters>>,
128 tear_downs: Vec<BookEndDecoration<TParameters>>,
129 nodes: HashMap<String, HierarchyNode<TParameters>>,
130}
131
132impl<TParameters> HierarchyNode<TParameters> {
133 pub fn new_node() -> Self {
134 Self {
135 suite: None,
136 tests: Vec::<TestDecoration<TParameters>>::new(),
137 setups: Vec::<BookEndDecoration<TParameters>>::new(),
138 tear_downs: Vec::<BookEndDecoration<TParameters>>::new(),
139 nodes: HashMap::new(),
140 }
141 }
142
143 pub fn insert_component(&mut self, component: ComponentDecoration<TParameters>) {
144 match component {
145 ComponentDecoration::IntegrationTest(test) => {
146 self.insert_test(test);
147 }
148 ComponentDecoration::Suite(suite_description) => {
149 self.insert_suite(suite_description);
150 }
151 ComponentDecoration::TearDown(bookend) => {
152 self.insert_teardown(bookend);
153 }
154 ComponentDecoration::Setup(bookend) => {
155 self.insert_setup(bookend);
156 }
157 }
158 }
159
160 pub fn insert_suite(&mut self, suite: SuiteAttributesDecoration) {
161 let mut node = self.find_namespace_entry(&suite.location.path.as_str());
162 node.suite = Some(suite);
163 }
164
165 pub fn insert_test(&mut self, test: TestDecoration<TParameters>) {
166 let node = self.find_method_entry(&test.desc.location.path.as_str());
167 node.tests.push(test);
168 }
169
170 pub fn insert_setup(&mut self, setup: BookEndDecoration<TParameters>) {
171 let node = self.find_method_entry(&setup.desc.location.path.as_str());
172 node.setups.push(setup);
173 }
174
175 pub fn insert_teardown(&mut self, teardown: BookEndDecoration<TParameters>) {
176 let node = self.find_method_entry(&teardown.desc.location.path.as_str());
177 node.tear_downs.push(teardown);
178 }
179
180 fn find_namespace_entry<'a>(&'a mut self, path: &str) -> &'a mut Self {
181 let v: Vec<&str> = path.split("::").collect();
182 self.find_entry_from_path_elements(&v)
183 }
184
185 fn find_method_entry<'a>(&'a mut self, path: &str) -> &'a mut Self {
186 let v: Vec<&str> = path.split("::").collect();
187 match v.split_last() {
189 None => self,
190 Some((_, path)) => self.find_entry_from_path_elements(path),
191 }
192 }
193
194 fn find_entry_from_path_elements<'a>(&'a mut self, path: &[&str]) -> &'a mut Self {
195 match path.split_first() {
196 None => self,
197 Some((cur, rest)) => {
198 let next = self
199 .nodes
200 .entry(cur.to_string())
201 .or_insert(Self::new_node());
202 next.find_entry_from_path_elements(rest)
203 }
204 }
205 }
206
207 pub fn into_component_groups(mut self) -> ComponentGroup<TParameters> {
208 let mut sub_groups = vec![];
209 let suite = std::mem::take(&mut self.suite);
210 let mut tests = std::mem::take(&mut self.tests);
211 let mut setups = std::mem::take(&mut self.setups);
212 let mut tear_downs = std::mem::take(&mut self.tear_downs);
213
214 for (_, node) in self.nodes {
215 let mut group = node.into_component_groups();
216
217 if group.suite.is_some() {
218 sub_groups.push(group);
219 } else {
220 tests.append(&mut group.tests);
221 tear_downs.append(&mut group.tear_downs);
222 setups.append(&mut group.setups);
223 sub_groups.append(&mut group.sub_groups);
224 }
225 }
226
227 sub_groups.sort_unstable_by(|a, b| {
229 let a = a.suite.as_ref().map(|x| &x.location);
230 let b = b.suite.as_ref().map(|x| &x.location);
231 a.cmp(&b)
232 });
233 tests.sort_unstable_by(|a, b| a.desc.location.cmp(&b.desc.location));
234 setups.sort_unstable_by(|a, b| a.desc.location.cmp(&b.desc.location));
235 tear_downs.sort_unstable_by(|a, b| a.desc.location.cmp(&b.desc.location));
236
237 return ComponentGroup {
238 suite,
239 tests,
240 setups,
241 tear_downs,
242 sub_groups,
243 };
244 }
245}