|
1 |
| -pub mod generator; |
| 1 | +mod generator; |
| 2 | +mod reader; |
2 | 3 | mod schema;
|
3 |
| -pub mod util; |
| 4 | +mod util; |
| 5 | +mod writer; |
4 | 6 |
|
5 |
| -use crate::schema::{ |
| 7 | +use crate::generator::Generator; |
| 8 | +use crate::schema::spec::{ |
6 | 9 | Assertion, Assertions, Namespace, Namespaces, TestCase, TestCases, TestDocument,
|
7 | 10 | };
|
8 | 11 | use crate::util::StringExt;
|
9 |
| -use ion_rs::value::owned::OwnedElement; |
10 |
| -use ion_rs::value::{Element, Sequence, Struct, SymbolToken}; |
11 |
| -use ion_rs::IonType; |
12 |
| -use std::collections::HashSet; |
13 |
| -use std::ops::Add; |
14 | 12 |
|
15 |
| -// TODO: move these test data parsing functions to own file |
16 |
| -/// Converts a vector of Ion data into a `TestDocument`, which can be composed of `Namespace`s |
17 |
| -/// and `TestCase`s. `Namespace`s must be provided as IonLists while `TestCase`s must be provided |
18 |
| -/// as IonStructs. Other Ion types will result in a panic. |
19 |
| -/// |
20 |
| -/// When encountering a duplicate namespace/test case, a namespace/test case will be added with |
21 |
| -/// '_0' suffixed to the end of the name. |
22 |
| -pub fn ion_data_to_test_document(all_ion_data: Vec<OwnedElement>) -> TestDocument { |
23 |
| - let mut namespaces = Vec::new(); |
24 |
| - let mut test_cases = Vec::new(); |
| 13 | +use std::path::Path; |
25 | 14 |
|
26 |
| - let mut encountered_ns_names: HashSet<String> = HashSet::new(); |
27 |
| - let mut encountered_tc_names: HashSet<String> = HashSet::new(); |
| 15 | +use crate::reader::read_schema; |
28 | 16 |
|
29 |
| - for elem in all_ion_data { |
30 |
| - match elem.ion_type() { |
31 |
| - IonType::List => { |
32 |
| - // namespace in document |
33 |
| - let mut ns = test_namespace(&elem); |
34 |
| - if encountered_ns_names.contains(&ns.name) { |
35 |
| - ns.name.push_str("_0") |
36 |
| - } |
37 |
| - encountered_ns_names.insert(ns.name.clone()); |
38 |
| - namespaces.push(ns) |
39 |
| - } |
40 |
| - IonType::Struct => { |
41 |
| - // test case in document |
42 |
| - let mut tc = test_case(&elem); |
43 |
| - if encountered_tc_names.contains(&tc.test_name) { |
44 |
| - tc.test_name.push_str("_0") |
45 |
| - } |
46 |
| - encountered_tc_names.insert(tc.test_name.clone()); |
47 |
| - test_cases.push(tc) |
48 |
| - } |
49 |
| - _ => panic!("Document parsing requires an IonList or IonStruct"), |
50 |
| - } |
51 |
| - } |
52 |
| - TestDocument { |
53 |
| - namespaces, |
54 |
| - test_cases, |
55 |
| - } |
| 17 | +// TODO docs |
| 18 | +#[derive(Debug, Copy, Clone)] |
| 19 | +pub enum OverwriteStrategy { |
| 20 | + Overwrite, |
| 21 | + Backup, |
56 | 22 | }
|
57 | 23 |
|
58 |
| -/// Parses the given `OwnedElement` to a `Namespace`. Requires an annotation to provided that will |
59 |
| -/// be used to create the name. '_namespace' will be suffixed to the first annotation provided. |
60 |
| -/// The namespace can contain sub-namespaces and test cases represented by IonLists and IonStructs |
61 |
| -/// respectively. When provided with something other than an IonList or IonStruct, this function |
62 |
| -/// will panic. |
63 |
| -pub fn test_namespace(element: &OwnedElement) -> Namespace { |
64 |
| - let annot: Vec<_> = element |
65 |
| - .annotations() |
66 |
| - .map(|a| a.text().expect("annotation text")) |
67 |
| - .collect(); |
68 |
| - let name = annot |
69 |
| - .first() |
70 |
| - .expect("expected an annotation for the namespace") |
71 |
| - .escaped_snake_case() |
72 |
| - .add("_namespace"); |
73 |
| - |
74 |
| - let mut namespaces: Namespaces = Vec::new(); |
75 |
| - let mut test_cases: TestCases = Vec::new(); |
76 |
| - |
77 |
| - let mut encountered_ns_names: HashSet<String> = HashSet::new(); |
78 |
| - let mut encountered_tc_names: HashSet<String> = HashSet::new(); |
| 24 | +/// Configuration for the generation of conformance tests. |
| 25 | +#[derive(Debug, Copy, Clone)] |
| 26 | +pub struct Config { |
| 27 | + pub overwrite: OverwriteStrategy, |
| 28 | +} |
79 | 29 |
|
80 |
| - for ns_or_test in element.as_sequence().expect("namespace is list").iter() { |
81 |
| - match ns_or_test.ion_type() { |
82 |
| - // namespace within the namespace |
83 |
| - IonType::List => { |
84 |
| - let mut ns = test_namespace(ns_or_test); |
85 |
| - if encountered_ns_names.contains(&ns.name) { |
86 |
| - ns.name.push_str("_0") |
87 |
| - } |
88 |
| - encountered_ns_names.insert(ns.name.clone()); |
89 |
| - namespaces.push(ns) |
90 |
| - } |
91 |
| - // test case within the namespace |
92 |
| - IonType::Struct => { |
93 |
| - let mut tc = test_case(ns_or_test); |
94 |
| - if encountered_tc_names.contains(&tc.test_name) { |
95 |
| - tc.test_name.push_str("_0") |
96 |
| - } |
97 |
| - encountered_tc_names.insert(tc.test_name.clone()); |
98 |
| - test_cases.push(tc) |
99 |
| - } |
100 |
| - _ => panic!("Namespace parsing requires an IonList or IonStruct"), |
| 30 | +impl Default for Config { |
| 31 | + fn default() -> Self { |
| 32 | + Config { |
| 33 | + overwrite: OverwriteStrategy::Overwrite, |
101 | 34 | }
|
102 | 35 | }
|
103 |
| - Namespace { |
104 |
| - name, |
105 |
| - namespaces, |
106 |
| - test_cases, |
107 |
| - } |
108 | 36 | }
|
109 | 37 |
|
110 |
| -/// Parses the given IonStruct to a `TestCase`. The IonStruct requires two string fields with the |
111 |
| -/// 'name' and 'statement' in addition to an 'assert' field containing one or more `Assertions`. |
112 |
| -/// |
113 |
| -/// For test assertions that are not supported (e.g. StaticAnalysisFail), the assertion of |
114 |
| -/// `NotYetImplemented` will be used. |
115 |
| -fn test_case(element: &OwnedElement) -> TestCase { |
116 |
| - let test_struct = element.as_struct().expect("struct"); |
117 |
| - let test_name = test_struct |
118 |
| - .get("name") |
119 |
| - .expect("name") |
120 |
| - .as_str() |
121 |
| - .expect("as_str()") |
122 |
| - .escaped_snake_case() |
123 |
| - .add("_test"); |
124 |
| - let statement = test_struct |
125 |
| - .get("statement") |
126 |
| - .expect("statement") |
127 |
| - .as_str() |
128 |
| - .expect("as_str()") |
129 |
| - .to_string(); |
130 |
| - |
131 |
| - let assert_field = test_struct.get("assert").expect("assert field missing"); |
132 |
| - let assertions_vec: Vec<_> = match assert_field.ion_type() { |
133 |
| - IonType::Struct => vec![assert_field], |
134 |
| - IonType::List => assert_field |
135 |
| - .as_sequence() |
136 |
| - .expect("as_sequence") |
137 |
| - .iter() |
138 |
| - .collect(), |
139 |
| - _ => panic!("Invalid IonType for the test case assertions"), |
140 |
| - }; |
141 |
| - let assertions = assertions(&assertions_vec); |
142 |
| - TestCase { |
143 |
| - test_name, |
144 |
| - statement, |
145 |
| - assertions, |
| 38 | +impl Config { |
| 39 | + pub fn new() -> Config { |
| 40 | + Config::default() |
146 | 41 | }
|
147 |
| -} |
148 | 42 |
|
149 |
| -/// Converts the vector of Ion values into `Assertions`. Checks that a result field is provided |
150 |
| -/// in the vector and has the symbol 'SyntaxSuccess' or 'SyntaxFail', which correspond to |
151 |
| -/// `Assertion::SyntaxSuccess` and `Assertion::SyntaxFail` respectively. Other assertion symbols |
152 |
| -/// will default to `Assertion::NotYetImplemented` |
153 |
| -fn assertions(assertions: &Vec<&OwnedElement>) -> Assertions { |
154 |
| - let mut test_case_assertions: Assertions = Vec::new(); |
155 |
| - for assertion in assertions { |
156 |
| - let assertion_struct = assertion.as_struct().expect("as_struct()"); |
157 |
| - let parse_result = assertion_struct.get("result"); |
158 |
| - match parse_result { |
159 |
| - Some(r) => { |
160 |
| - let r_as_str = r.as_str().expect("as_str()"); |
161 |
| - let assertion = match r_as_str { |
162 |
| - "SyntaxSuccess" => Assertion::SyntaxSuccess, |
163 |
| - "SyntaxFail" => Assertion::SyntaxFail, |
164 |
| - _ => Assertion::NotYetImplemented, |
165 |
| - }; |
166 |
| - test_case_assertions.push(assertion); |
167 |
| - } |
168 |
| - None => (), |
169 |
| - } |
| 43 | + pub fn process_dir( |
| 44 | + &self, |
| 45 | + test_data: impl AsRef<Path>, |
| 46 | + out_path: impl AsRef<Path>, |
| 47 | + ) -> miette::Result<()> { |
| 48 | + let schema = read_schema(test_data)?; |
| 49 | + let scopes = Generator::new().generate(schema)?; |
| 50 | + |
| 51 | + // TODO implement OverwriteStrategy |
| 52 | + writer::write_scopes(out_path, scopes)?; |
| 53 | + Ok(()) |
170 | 54 | }
|
171 |
| - test_case_assertions |
172 | 55 | }
|
173 | 56 |
|
174 | 57 | #[cfg(test)]
|
|
0 commit comments