1 // Copyright 2015 The Kyua Authors.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 //   notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 //   notice, this list of conditions and the following disclaimer in the
12 //   documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 //   may be used to endorse or promote products derived from this software
15 //   without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #include "engine/atf_list.hpp"
30 
31 #include <sstream>
32 #include <string>
33 
34 #include <atf-c++.hpp>
35 
36 #include "engine/exceptions.hpp"
37 #include "model/metadata.hpp"
38 #include "model/test_case.hpp"
39 #include "model/types.hpp"
40 #include "utils/datetime.hpp"
41 #include "utils/format/containers.ipp"
42 #include "utils/fs/path.hpp"
43 #include "utils/units.hpp"
44 
45 namespace datetime = utils::datetime;
46 namespace fs = utils::fs;
47 namespace units = utils::units;
48 
49 
50 ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_metadata__defaults)
51 ATF_TEST_CASE_BODY(parse_atf_metadata__defaults)
52 {
53     const model::properties_map properties;
54     const model::metadata md = engine::parse_atf_metadata(properties);
55 
56     const model::metadata exp_md = model::metadata_builder().build();
57     ATF_REQUIRE_EQ(exp_md, md);
58 }
59 
60 
61 ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_metadata__override_all)
62 ATF_TEST_CASE_BODY(parse_atf_metadata__override_all)
63 {
64     model::properties_map properties;
65     properties["descr"] = "Some text";
66     properties["has.cleanup"] = "true";
67     properties["require.arch"] = "i386 x86_64";
68     properties["require.config"] = "var1 var2 var3";
69     properties["require.files"] = "/file1 /dir/file2";
70     properties["require.machine"] = "amd64";
71     properties["require.memory"] = "1m";
72     properties["require.progs"] = "/bin/ls svn";
73     properties["require.user"] = "root";
74     properties["timeout"] = "123";
75     properties["X-foo"] = "value1";
76     properties["X-bar"] = "value2";
77     properties["X-baz-www"] = "value3";
78     const model::metadata md = engine::parse_atf_metadata(properties);
79 
80     const model::metadata exp_md = model::metadata_builder()
81         .add_allowed_architecture("i386")
82         .add_allowed_architecture("x86_64")
83         .add_allowed_platform("amd64")
84         .add_custom("foo", "value1")
85         .add_custom("bar", "value2")
86         .add_custom("baz-www", "value3")
87         .add_required_config("var1")
88         .add_required_config("var2")
89         .add_required_config("var3")
90         .add_required_file(fs::path("/file1"))
91         .add_required_file(fs::path("/dir/file2"))
92         .add_required_program(fs::path("/bin/ls"))
93         .add_required_program(fs::path("svn"))
94         .set_description("Some text")
95         .set_has_cleanup(true)
96         .set_required_memory(units::bytes::parse("1m"))
97         .set_required_user("root")
98         .set_timeout(datetime::delta(123, 0))
99         .build();
100     ATF_REQUIRE_EQ(exp_md, md);
101 }
102 
103 
104 ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_metadata__unknown)
105 ATF_TEST_CASE_BODY(parse_atf_metadata__unknown)
106 {
107     model::properties_map properties;
108     properties["foobar"] = "Some text";
109 
110     ATF_REQUIRE_THROW_RE(engine::format_error, "Unknown.*property.*'foobar'",
111                          engine::parse_atf_metadata(properties));
112 }
113 
114 
115 ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__empty);
116 ATF_TEST_CASE_BODY(parse_atf_list__empty)
117 {
118     const std::string text = "";
119     std::istringstream input(text);
120     ATF_REQUIRE_THROW_RE(engine::format_error, "expecting Content-Type",
121         engine::parse_atf_list(input));
122 }
123 
124 
125 ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__invalid_header);
126 ATF_TEST_CASE_BODY(parse_atf_list__invalid_header)
127 {
128     {
129         const std::string text =
130             "Content-Type: application/X-atf-tp; version=\"1\"\n";
131         std::istringstream input(text);
132         ATF_REQUIRE_THROW_RE(engine::format_error, "expecting.*blank line",
133             engine::parse_atf_list(input));
134     }
135 
136     {
137         const std::string text =
138             "Content-Type: application/X-atf-tp; version=\"1\"\nfoo\n";
139         std::istringstream input(text);
140         ATF_REQUIRE_THROW_RE(engine::format_error, "expecting.*blank line",
141             engine::parse_atf_list(input));
142     }
143 
144     {
145         const std::string text =
146             "Content-Type: application/X-atf-tp; version=\"2\"\n\n";
147         std::istringstream input(text);
148         ATF_REQUIRE_THROW_RE(engine::format_error, "expecting Content-Type",
149             engine::parse_atf_list(input));
150     }
151 }
152 
153 
154 ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__no_test_cases);
155 ATF_TEST_CASE_BODY(parse_atf_list__no_test_cases)
156 {
157     const std::string text =
158         "Content-Type: application/X-atf-tp; version=\"1\"\n\n";
159     std::istringstream input(text);
160     ATF_REQUIRE_THROW_RE(engine::format_error, "No test cases",
161         engine::parse_atf_list(input));
162 }
163 
164 
165 ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__one_test_case_simple);
166 ATF_TEST_CASE_BODY(parse_atf_list__one_test_case_simple)
167 {
168     const std::string text =
169         "Content-Type: application/X-atf-tp; version=\"1\"\n"
170         "\n"
171         "ident: test-case\n";
172     std::istringstream input(text);
173     const model::test_cases_map tests = engine::parse_atf_list(input);
174 
175     const model::test_cases_map exp_tests = model::test_cases_map_builder()
176         .add("test-case").build();
177     ATF_REQUIRE_EQ(exp_tests, tests);
178 }
179 
180 
181 ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__one_test_case_complex);
182 ATF_TEST_CASE_BODY(parse_atf_list__one_test_case_complex)
183 {
184     const std::string text =
185         "Content-Type: application/X-atf-tp; version=\"1\"\n"
186         "\n"
187         "ident: first\n"
188         "descr: This is the description\n"
189         "timeout: 500\n";
190     std::istringstream input(text);
191     const model::test_cases_map tests = engine::parse_atf_list(input);
192 
193     const model::test_cases_map exp_tests = model::test_cases_map_builder()
194         .add("first", model::metadata_builder()
195              .set_description("This is the description")
196              .set_timeout(datetime::delta(500, 0))
197              .build())
198         .build();
199     ATF_REQUIRE_EQ(exp_tests, tests);
200 }
201 
202 
203 ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__one_test_case_invalid_syntax);
204 ATF_TEST_CASE_BODY(parse_atf_list__one_test_case_invalid_syntax)
205 {
206     const std::string text =
207         "Content-Type: application/X-atf-tp; version=\"1\"\n\n"
208         "descr: This is the description\n"
209         "ident: first\n";
210     std::istringstream input(text);
211     ATF_REQUIRE_THROW_RE(engine::format_error, "preceeded.*identifier",
212         engine::parse_atf_list(input));
213 }
214 
215 
216 ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__one_test_case_invalid_properties);
217 ATF_TEST_CASE_BODY(parse_atf_list__one_test_case_invalid_properties)
218 {
219     // Inject a single invalid property that makes test_case::from_properties()
220     // raise a particular error message so that we can validate that such
221     // function was called.  We do intensive testing separately, so it is not
222     // necessary to redo it here.
223     const std::string text =
224         "Content-Type: application/X-atf-tp; version=\"1\"\n\n"
225         "ident: first\n"
226         "require.progs: bin/ls\n";
227     std::istringstream input(text);
228     ATF_REQUIRE_THROW_RE(engine::format_error, "Relative path 'bin/ls'",
229         engine::parse_atf_list(input));
230 }
231 
232 
233 ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__many_test_cases);
234 ATF_TEST_CASE_BODY(parse_atf_list__many_test_cases)
235 {
236     const std::string text =
237         "Content-Type: application/X-atf-tp; version=\"1\"\n"
238         "\n"
239         "ident: first\n"
240         "descr: This is the description\n"
241         "\n"
242         "ident: second\n"
243         "timeout: 500\n"
244         "descr: Some text\n"
245         "\n"
246         "ident: third\n";
247     std::istringstream input(text);
248     const model::test_cases_map tests = engine::parse_atf_list(input);
249 
250     const model::test_cases_map exp_tests = model::test_cases_map_builder()
251         .add("first", model::metadata_builder()
252              .set_description("This is the description")
253              .build())
254         .add("second", model::metadata_builder()
255              .set_description("Some text")
256              .set_timeout(datetime::delta(500, 0))
257              .build())
258         .add("third")
259         .build();
260     ATF_REQUIRE_EQ(exp_tests, tests);
261 }
262 
263 
264 ATF_INIT_TEST_CASES(tcs)
265 {
266     ATF_ADD_TEST_CASE(tcs, parse_atf_metadata__defaults);
267     ATF_ADD_TEST_CASE(tcs, parse_atf_metadata__override_all);
268     ATF_ADD_TEST_CASE(tcs, parse_atf_metadata__unknown);
269 
270     ATF_ADD_TEST_CASE(tcs, parse_atf_list__empty);
271     ATF_ADD_TEST_CASE(tcs, parse_atf_list__invalid_header);
272     ATF_ADD_TEST_CASE(tcs, parse_atf_list__no_test_cases);
273     ATF_ADD_TEST_CASE(tcs, parse_atf_list__one_test_case_simple);
274     ATF_ADD_TEST_CASE(tcs, parse_atf_list__one_test_case_complex);
275     ATF_ADD_TEST_CASE(tcs, parse_atf_list__one_test_case_invalid_syntax);
276     ATF_ADD_TEST_CASE(tcs, parse_atf_list__one_test_case_invalid_properties);
277     ATF_ADD_TEST_CASE(tcs, parse_atf_list__many_test_cases);
278 }
279