xref: /netbsd-src/external/bsd/kyua-cli/dist/engine/testers_test.cpp (revision a4ddc2c8fb9af816efe3b1c375a5530aef0e89e9)
1 // Copyright 2012 Google Inc.
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/testers.hpp"
30 
31 extern "C" {
32 #include <sys/stat.h>
33 }
34 
35 #include <cstdlib>
36 
37 #include <atf-c++.hpp>
38 
39 #include "engine/exceptions.hpp"
40 #include "utils/datetime.hpp"
41 #include "utils/env.hpp"
42 #include "utils/format/macros.hpp"
43 #include "utils/fs/path.hpp"
44 #include "utils/fs/operations.hpp"
45 #include "utils/optional.ipp"
46 #include "utils/passwd.hpp"
47 
48 namespace datetime = utils::datetime;
49 namespace fs = utils::fs;
50 namespace passwd = utils::passwd;
51 
52 using utils::none;
53 using utils::optional;
54 
55 
56 namespace {
57 
58 
59 /// Creates a mock tester.
60 ///
61 /// The interface accepted by the tester is 'mock'.  This tester outputs the
62 /// arguments passed to it and then prints a message to both the stdout and the
63 /// stderr.
64 ///
65 /// \param exit_status Code to exit with.
66 static void
67 create_mock_tester(const int exit_status)
68 {
69     atf::utils::create_file(
70         "kyua-mock-tester",
71         F("#! /bin/sh\n"
72           "while [ ${#} -gt 0 ]; do\n"
73           "    echo \"Arg: ${1}\"\n"
74           "    shift\n"
75           "done\n"
76           "echo 'tester output'\n"
77           "echo 'tester error' 1>&2\n"
78           "exit %s\n") % exit_status);
79     ATF_REQUIRE(::chmod("kyua-mock-tester", 0755) != -1);
80 
81     utils::setenv("KYUA_TESTERSDIR", fs::current_path().str());
82 }
83 
84 
85 }  // anonymous namespace
86 
87 
88 ATF_TEST_CASE_WITHOUT_HEAD(tester__list__defaults);
89 ATF_TEST_CASE_BODY(tester__list__defaults)
90 {
91     create_mock_tester(EXIT_SUCCESS);
92     engine::tester tester("mock", none, none);
93     const std::string output = tester.list(fs::path("/foo/bar"));
94 
95     const std::string exp_output =
96         "Arg: list\n"
97         "Arg: /foo/bar\n"
98         "tester output\n"
99         "tester error\n";
100     ATF_REQUIRE_EQ(exp_output, output);
101 }
102 
103 
104 ATF_TEST_CASE_WITHOUT_HEAD(tester__list__explicit_common_args);
105 ATF_TEST_CASE_BODY(tester__list__explicit_common_args)
106 {
107     const passwd::user user("fake", 123, 456);
108     const datetime::delta timeout(15, 0);
109 
110     create_mock_tester(EXIT_SUCCESS);
111     engine::tester tester("mock", utils::make_optional(user),
112                           utils::make_optional(timeout));
113     const std::string output = tester.list(fs::path("/another/program/1"));
114 
115     const std::string exp_output =
116         "Arg: -u123\n"
117         "Arg: -g456\n"
118         "Arg: -t15\n"
119         "Arg: list\n"
120         "Arg: /another/program/1\n"
121         "tester output\n"
122         "tester error\n";
123     ATF_REQUIRE_EQ(exp_output, output);
124 }
125 
126 
127 ATF_TEST_CASE_WITHOUT_HEAD(tester__list__unknown_interface);
128 ATF_TEST_CASE_BODY(tester__list__unknown_interface)
129 {
130     utils::setenv("KYUA_TESTERSDIR", ".");
131     engine::tester tester("non-existent", none, none);
132     ATF_REQUIRE_THROW_RE(engine::error, "Unknown interface non-existent",
133                          tester.list(fs::path("does-not-matter")));
134 }
135 
136 
137 ATF_TEST_CASE_WITHOUT_HEAD(tester__list__tester_fails);
138 ATF_TEST_CASE_BODY(tester__list__tester_fails)
139 {
140     create_mock_tester(EXIT_FAILURE);
141     engine::tester tester("mock", none, none);
142     ATF_REQUIRE_THROW_RE(
143         engine::error,
144         "Tester did not exit cleanly:.*tester output.*tester error",
145         tester.list(fs::path("does-not-matter")));
146 }
147 
148 
149 ATF_TEST_CASE_WITHOUT_HEAD(tester__test__defaults);
150 ATF_TEST_CASE_BODY(tester__test__defaults)
151 {
152     std::map< std::string, std::string > vars;
153 
154     create_mock_tester(EXIT_FAILURE);
155     engine::tester tester("mock", none, none);
156     tester.test(fs::path("/foo/bar"), "test-case", fs::path("/the/result/file"),
157                 fs::path("tester.out"), fs::path("tester.err"), vars);
158 
159     const std::string exp_output =
160         "Arg: test\n"
161         "Arg: /foo/bar\n"
162         "Arg: test-case\n"
163         "Arg: /the/result/file\n"
164         "tester output\n";
165     const std::string exp_error =
166         "tester error\n";
167     ATF_REQUIRE(atf::utils::compare_file("tester.out", exp_output));
168     ATF_REQUIRE(atf::utils::compare_file("tester.err", exp_error));
169 }
170 
171 
172 ATF_TEST_CASE_WITHOUT_HEAD(tester__test__explicit_common_args_and_vars);
173 ATF_TEST_CASE_BODY(tester__test__explicit_common_args_and_vars)
174 {
175     const passwd::user user("fake", 123, 456);
176     const datetime::delta timeout(15, 0);
177 
178     std::map< std::string, std::string > vars;
179     vars["var1"] = "value1";
180     vars["variable-2"] = "value with spaces";
181 
182     create_mock_tester(EXIT_SUCCESS);
183     engine::tester tester("mock", utils::make_optional(user),
184                           utils::make_optional(timeout));
185     tester.test(fs::path("/foo/bar"), "test-case", fs::path("/the/result/file"),
186                 fs::path("tester.out"), fs::path("tester.err"), vars);
187 
188     const std::string exp_output =
189         "Arg: -u123\n"
190         "Arg: -g456\n"
191         "Arg: -t15\n"
192         "Arg: test\n"
193         "Arg: -vvar1=value1\n"
194         "Arg: -vvariable-2=value with spaces\n"
195         "Arg: /foo/bar\n"
196         "Arg: test-case\n"
197         "Arg: /the/result/file\n"
198         "tester output\n";
199     const std::string exp_error =
200         "tester error\n";
201     ATF_REQUIRE(atf::utils::compare_file("tester.out", exp_output));
202     ATF_REQUIRE(atf::utils::compare_file("tester.err", exp_error));
203 }
204 
205 
206 ATF_TEST_CASE_WITHOUT_HEAD(tester__test__unknown_interface);
207 ATF_TEST_CASE_BODY(tester__test__unknown_interface)
208 {
209     const std::map< std::string, std::string > vars;
210 
211     utils::setenv("KYUA_TESTERSDIR", ".");
212     engine::tester tester("non-existent", none, none);
213     ATF_REQUIRE_THROW_RE(engine::error, "Unknown interface non-existent",
214                          tester.test(fs::path("foo"), "bar", fs::path("baz"),
215                                      fs::path("out"), fs::path("err"), vars));
216 }
217 
218 
219 ATF_TEST_CASE_WITHOUT_HEAD(tester__test__tester_fails);
220 ATF_TEST_CASE_BODY(tester__test__tester_fails)
221 {
222     const std::map< std::string, std::string > vars;
223 
224     create_mock_tester(2);
225     engine::tester tester("mock", none, none);
226     ATF_REQUIRE_THROW_RE(
227         engine::error,
228         "Tester failed with code 2; this is a bug",
229         tester.test(fs::path("foo"), "bar", fs::path("baz"),
230                     fs::path("out"), fs::path("err"), vars));
231 }
232 
233 
234 ATF_TEST_CASE_WITHOUT_HEAD(tester_path__default);
235 ATF_TEST_CASE_BODY(tester_path__default)
236 {
237     ATF_REQUIRE(atf::utils::file_exists(engine::tester_path("atf").str()));
238     ATF_REQUIRE(atf::utils::file_exists(engine::tester_path("plain").str()));
239 }
240 
241 
242 ATF_TEST_CASE_WITHOUT_HEAD(tester_path__custom);
243 ATF_TEST_CASE_BODY(tester_path__custom)
244 {
245     fs::mkdir(fs::path("testers"), 0755);
246     atf::utils::create_file("testers/kyua-mock-1-tester", "Not a binary");
247     atf::utils::create_file("testers/kyua-mock-2-tester", "Not a binary");
248     utils::setenv("KYUA_TESTERSDIR", (fs::current_path() / "testers").str());
249 
250     const fs::path mock1 = engine::tester_path("mock-1");
251     ATF_REQUIRE(mock1.is_absolute());
252     ATF_REQUIRE(atf::utils::file_exists(mock1.str()));
253 
254     const fs::path mock2 = engine::tester_path("mock-2");
255     ATF_REQUIRE(mock2.is_absolute());
256     ATF_REQUIRE(atf::utils::file_exists(mock2.str()));
257 
258     ATF_REQUIRE_THROW_RE(engine::error, "Unknown interface mock-3",
259                          engine::tester_path("mock-3"));
260 }
261 
262 
263 ATF_TEST_CASE_WITHOUT_HEAD(tester_path__cached);
264 ATF_TEST_CASE_BODY(tester_path__cached)
265 {
266     fs::mkdir(fs::path("testers"), 0755);
267     atf::utils::create_file("testers/kyua-mock-tester", "Not a binary");
268     utils::setenv("KYUA_TESTERSDIR", (fs::current_path() / "testers").str());
269 
270     const fs::path mock = engine::tester_path("mock");
271     ATF_REQUIRE(atf::utils::file_exists(mock.str()));
272     ATF_REQUIRE(::unlink(mock.c_str()) != -1);
273     ATF_REQUIRE(!atf::utils::file_exists(mock.str()));
274     ATF_REQUIRE_EQ(mock, engine::tester_path("mock"));
275 }
276 
277 
278 ATF_TEST_CASE_WITHOUT_HEAD(tester_path__empty);
279 ATF_TEST_CASE_BODY(tester_path__empty)
280 {
281     fs::mkdir(fs::path("testers"), 0755);
282     atf::utils::create_file("testers/kyua--tester", "Not a binary");
283     utils::setenv("KYUA_TESTERSDIR", (fs::current_path() / "testers").str());
284 
285     ATF_REQUIRE_THROW_RE(engine::error, "Unknown interface ",
286                          engine::tester_path(""));
287 }
288 
289 
290 ATF_TEST_CASE_WITHOUT_HEAD(tester_path__missing);
291 ATF_TEST_CASE_BODY(tester_path__missing)
292 {
293     utils::setenv("KYUA_TESTERSDIR", fs::current_path().str());
294     ATF_REQUIRE_THROW_RE(engine::error, "Unknown interface plain",
295                          engine::tester_path("plain"));
296 }
297 
298 
299 ATF_INIT_TEST_CASES(tcs)
300 {
301     ATF_ADD_TEST_CASE(tcs, tester__list__defaults);
302     ATF_ADD_TEST_CASE(tcs, tester__list__explicit_common_args);
303     ATF_ADD_TEST_CASE(tcs, tester__list__unknown_interface);
304     ATF_ADD_TEST_CASE(tcs, tester__list__tester_fails);
305 
306     ATF_ADD_TEST_CASE(tcs, tester__test__defaults);
307     ATF_ADD_TEST_CASE(tcs, tester__test__explicit_common_args_and_vars);
308     ATF_ADD_TEST_CASE(tcs, tester__test__unknown_interface);
309     ATF_ADD_TEST_CASE(tcs, tester__test__tester_fails);
310 
311     ATF_ADD_TEST_CASE(tcs, tester_path__default);
312     ATF_ADD_TEST_CASE(tcs, tester_path__custom);
313     ATF_ADD_TEST_CASE(tcs, tester_path__cached);
314     ATF_ADD_TEST_CASE(tcs, tester_path__empty);
315     ATF_ADD_TEST_CASE(tcs, tester_path__missing);
316 }
317