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
45namespace datetime = utils::datetime;
46namespace fs = utils::fs;
47namespace units = utils::units;
48
49
50ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_metadata__defaults)
51ATF_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
61ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_metadata__override_all)
62ATF_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
104ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_metadata__unknown)
105ATF_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
115ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__empty);
116ATF_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
125ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__invalid_header);
126ATF_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
154ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__no_test_cases);
155ATF_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
165ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__one_test_case_simple);
166ATF_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
181ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__one_test_case_complex);
182ATF_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
203ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__one_test_case_invalid_syntax);
204ATF_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
216ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__one_test_case_invalid_properties);
217ATF_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
233ATF_TEST_CASE_WITHOUT_HEAD(parse_atf_list__many_test_cases);
234ATF_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
264ATF_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