xref: /netbsd-src/external/bsd/kyua-cli/dist/store/schema_inttest.cpp (revision 6b3a42af15b5e090c339512c790dd68f3d11a9d8)
1 // Copyright 2013 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 "store/backend.hpp"
30 
31 #include <fstream>
32 #include <map>
33 
34 #include <atf-c++.hpp>
35 
36 #include "engine/action.hpp"
37 #include "engine/context.hpp"
38 #include "engine/test_case.hpp"
39 #include "engine/test_program.hpp"
40 #include "engine/test_result.hpp"
41 #include "store/backend.hpp"
42 #include "store/transaction.hpp"
43 #include "utils/format/macros.hpp"
44 #include "utils/fs/path.hpp"
45 #include "utils/logging/operations.hpp"
46 #include "utils/sqlite/database.hpp"
47 #include "utils/stream.hpp"
48 #include "utils/units.hpp"
49 
50 namespace datetime = utils::datetime;
51 namespace fs = utils::fs;
52 namespace logging = utils::logging;
53 namespace sqlite = utils::sqlite;
54 namespace units = utils::units;
55 
56 
57 /// Executes an SQL script within a database.
58 ///
59 /// \param db Database in which to run the script.
60 /// \param path Path to the data file.
61 static void
exec_db_file(sqlite::database & db,const fs::path & path)62 exec_db_file(sqlite::database& db, const fs::path& path)
63 {
64     std::ifstream input(path.c_str());
65     if (!input)
66         ATF_FAIL(F("Failed to open %s") % path);
67     db.exec(utils::read_stream(input));
68 }
69 
70 
71 /// Validates the contents of the action with identifier 1.
72 ///
73 /// \param transaction An open read transaction in the backend.
74 static void
check_action_1(store::transaction & transaction)75 check_action_1(store::transaction& transaction)
76 {
77     const fs::path root("/some/root");
78     std::map< std::string, std::string > environment;
79     const engine::context context_1(root, environment);
80 
81     const engine::action action_1(context_1);
82 
83     ATF_REQUIRE_EQ(action_1, transaction.get_action(1));
84 
85     store::results_iterator iter = transaction.get_action_results(1);
86     ATF_REQUIRE(!iter);
87 }
88 
89 
90 /// Validates the contents of the action with identifier 2.
91 ///
92 /// \param transaction An open read transaction in the backend.
93 static void
check_action_2(store::transaction & transaction)94 check_action_2(store::transaction& transaction)
95 {
96     const fs::path root("/test/suite/root");
97     std::map< std::string, std::string > environment;
98     environment["HOME"] = "/home/test";
99     environment["PATH"] = "/bin:/usr/bin";
100     const engine::context context_2(root, environment);
101 
102     const engine::action action_2(context_2);
103 
104     ATF_REQUIRE_EQ(action_2, transaction.get_action(2));
105 
106     engine::test_program test_program_1(
107         "plain", fs::path("foo_test"), fs::path("/test/suite/root"),
108         "suite-name", engine::metadata_builder().build());
109     {
110         const engine::test_case_ptr test_case_1(new engine::test_case(
111             "plain", test_program_1, "main",
112             engine::metadata_builder().build()));
113         engine::test_cases_vector test_cases;
114         test_cases.push_back(test_case_1);
115         test_program_1.set_test_cases(test_cases);
116     }
117     const engine::test_result result_1(engine::test_result::passed);
118 
119     engine::test_program test_program_2(
120         "plain", fs::path("subdir/another_test"), fs::path("/test/suite/root"),
121         "subsuite-name", engine::metadata_builder()
122         .set_timeout(datetime::delta(10, 0)).build());
123     {
124         const engine::test_case_ptr test_case_2(new engine::test_case(
125             "plain", test_program_2, "main", engine::metadata_builder()
126             .set_timeout(datetime::delta(10, 0)).build()));
127         engine::test_cases_vector test_cases;
128         test_cases.push_back(test_case_2);
129         test_program_2.set_test_cases(test_cases);
130     }
131     const engine::test_result result_2(engine::test_result::failed,
132                                        "Exited with code 1");
133 
134     engine::test_program test_program_3(
135         "plain", fs::path("subdir/bar_test"), fs::path("/test/suite/root"),
136         "subsuite-name", engine::metadata_builder().build());
137     {
138         const engine::test_case_ptr test_case_3(new engine::test_case(
139             "plain", test_program_3, "main",
140             engine::metadata_builder().build()));
141         engine::test_cases_vector test_cases;
142         test_cases.push_back(test_case_3);
143         test_program_3.set_test_cases(test_cases);
144     }
145     const engine::test_result result_3(engine::test_result::broken,
146                                        "Received signal 1");
147 
148     engine::test_program test_program_4(
149         "plain", fs::path("top_test"), fs::path("/test/suite/root"),
150         "suite-name", engine::metadata_builder().build());
151     {
152         const engine::test_case_ptr test_case_4(new engine::test_case(
153             "plain", test_program_4, "main",
154             engine::metadata_builder().build()));
155         engine::test_cases_vector test_cases;
156         test_cases.push_back(test_case_4);
157         test_program_4.set_test_cases(test_cases);
158     }
159     const engine::test_result result_4(engine::test_result::expected_failure,
160                                        "Known bug");
161 
162     engine::test_program test_program_5(
163         "plain", fs::path("last_test"), fs::path("/test/suite/root"),
164         "suite-name", engine::metadata_builder().build());
165     {
166         const engine::test_case_ptr test_case_5(new engine::test_case(
167             "plain", test_program_5, "main",
168             engine::metadata_builder().build()));
169         engine::test_cases_vector test_cases;
170         test_cases.push_back(test_case_5);
171         test_program_5.set_test_cases(test_cases);
172     }
173     const engine::test_result result_5(engine::test_result::skipped,
174                                        "Does not apply");
175 
176     store::results_iterator iter = transaction.get_action_results(2);
177     ATF_REQUIRE(iter);
178     ATF_REQUIRE_EQ(test_program_1, *iter.test_program());
179     ATF_REQUIRE_EQ("main", iter.test_case_name());
180     ATF_REQUIRE_EQ(result_1, iter.result());
181     ATF_REQUIRE(iter.stdout_contents().empty());
182     ATF_REQUIRE(iter.stderr_contents().empty());
183     ATF_REQUIRE_EQ(datetime::delta(10, 500), iter.duration());
184 
185     ++iter;
186     ATF_REQUIRE(iter);
187     ATF_REQUIRE_EQ(test_program_5, *iter.test_program());
188     ATF_REQUIRE_EQ("main", iter.test_case_name());
189     ATF_REQUIRE_EQ(result_5, iter.result());
190     ATF_REQUIRE(iter.stdout_contents().empty());
191     ATF_REQUIRE(iter.stderr_contents().empty());
192     ATF_REQUIRE_EQ(datetime::delta(6, 0), iter.duration());
193 
194     ++iter;
195     ATF_REQUIRE(iter);
196     ATF_REQUIRE_EQ(test_program_2, *iter.test_program());
197     ATF_REQUIRE_EQ("main", iter.test_case_name());
198     ATF_REQUIRE_EQ(result_2, iter.result());
199     ATF_REQUIRE_EQ("Test stdout", iter.stdout_contents());
200     ATF_REQUIRE_EQ("Test stderr", iter.stderr_contents());
201     ATF_REQUIRE_EQ(datetime::delta(0, 898821), iter.duration());
202 
203     ++iter;
204     ATF_REQUIRE(iter);
205     ATF_REQUIRE_EQ(test_program_3, *iter.test_program());
206     ATF_REQUIRE_EQ("main", iter.test_case_name());
207     ATF_REQUIRE_EQ(result_3, iter.result());
208     ATF_REQUIRE(iter.stdout_contents().empty());
209     ATF_REQUIRE(iter.stderr_contents().empty());
210     ATF_REQUIRE_EQ(datetime::delta(7, 481932), iter.duration());
211 
212     ++iter;
213     ATF_REQUIRE(iter);
214     ATF_REQUIRE_EQ(test_program_4, *iter.test_program());
215     ATF_REQUIRE_EQ("main", iter.test_case_name());
216     ATF_REQUIRE_EQ(result_4, iter.result());
217     ATF_REQUIRE(iter.stdout_contents().empty());
218     ATF_REQUIRE(iter.stderr_contents().empty());
219     ATF_REQUIRE_EQ(datetime::delta(0, 20000), iter.duration());
220 
221     ++iter;
222     ATF_REQUIRE(!iter);
223 }
224 
225 
226 /// Validates the contents of the action with identifier 3.
227 ///
228 /// \param transaction An open read transaction in the backend.
229 static void
check_action_3(store::transaction & transaction)230 check_action_3(store::transaction& transaction)
231 {
232     const fs::path root("/usr/tests");
233     std::map< std::string, std::string > environment;
234     environment["PATH"] = "/bin:/usr/bin";
235     const engine::context context_3(root, environment);
236 
237     const engine::action action_3(context_3);
238 
239     ATF_REQUIRE_EQ(action_3, transaction.get_action(3));
240 
241     engine::test_program test_program_6(
242         "atf", fs::path("complex_test"), fs::path("/usr/tests"),
243         "suite-name", engine::metadata_builder().build());
244     {
245         const engine::test_case_ptr test_case_6(new engine::test_case(
246             "atf", test_program_6, "this_passes",
247             engine::metadata_builder().build()));
248         const engine::test_case_ptr test_case_7(new engine::test_case(
249             "atf", test_program_6, "this_fails",
250             engine::metadata_builder()
251             .set_description("Test description")
252             .set_has_cleanup(true)
253             .set_required_memory(units::bytes(128))
254             .set_required_user("root").build()));
255         const engine::test_case_ptr test_case_8(new engine::test_case(
256             "atf", test_program_6, "this_skips",
257             engine::metadata_builder()
258             .add_allowed_architecture("powerpc")
259             .add_allowed_architecture("x86_64")
260             .add_allowed_platform("amd64")
261             .add_allowed_platform("macppc")
262             .add_required_config("X-foo")
263             .add_required_config("unprivileged_user")
264             .add_required_file(fs::path("/the/data/file"))
265             .add_required_program(fs::path("/bin/ls"))
266             .add_required_program(fs::path("cp"))
267             .set_description("Test explanation")
268             .set_has_cleanup(true)
269             .set_required_memory(units::bytes(512))
270             .set_required_user("unprivileged")
271             .set_timeout(datetime::delta(600, 0))
272             .build()));
273         engine::test_cases_vector test_cases;
274         test_cases.push_back(test_case_6);
275         test_cases.push_back(test_case_7);
276         test_cases.push_back(test_case_8);
277         test_program_6.set_test_cases(test_cases);
278     }
279     const engine::test_result result_6(engine::test_result::passed);
280     const engine::test_result result_7(engine::test_result::failed,
281                                        "Some reason");
282     const engine::test_result result_8(engine::test_result::skipped,
283                                        "Another reason");
284 
285     engine::test_program test_program_7(
286         "atf", fs::path("simple_test"), fs::path("/usr/tests"),
287         "subsuite-name", engine::metadata_builder().build());
288     {
289         const engine::test_case_ptr test_case_9(new engine::test_case(
290             "atf", test_program_7, "main",
291             engine::metadata_builder()
292             .set_description("More text")
293             .set_has_cleanup(true)
294             .set_required_memory(units::bytes(128))
295             .set_required_user("unprivileged")
296             .build()));
297         engine::test_cases_vector test_cases;
298         test_cases.push_back(test_case_9);
299         test_program_7.set_test_cases(test_cases);
300     }
301     const engine::test_result result_9(engine::test_result::failed,
302                                        "Exited with code 1");
303 
304     store::results_iterator iter = transaction.get_action_results(3);
305     ATF_REQUIRE(iter);
306     ATF_REQUIRE_EQ(test_program_6, *iter.test_program());
307     ATF_REQUIRE_EQ("this_fails", iter.test_case_name());
308     ATF_REQUIRE_EQ(result_7, iter.result());
309     ATF_REQUIRE(iter.stdout_contents().empty());
310     ATF_REQUIRE(iter.stderr_contents().empty());
311     ATF_REQUIRE_EQ(datetime::delta(1, 897182), iter.duration());
312 
313     ++iter;
314     ATF_REQUIRE(iter);
315     ATF_REQUIRE_EQ(test_program_6, *iter.test_program());
316     ATF_REQUIRE_EQ("this_passes", iter.test_case_name());
317     ATF_REQUIRE_EQ(result_6, iter.result());
318     ATF_REQUIRE(iter.stdout_contents().empty());
319     ATF_REQUIRE(iter.stderr_contents().empty());
320     ATF_REQUIRE_EQ(datetime::delta(6, 0), iter.duration());
321 
322     ++iter;
323     ATF_REQUIRE(iter);
324     ATF_REQUIRE_EQ(test_program_6, *iter.test_program());
325     ATF_REQUIRE_EQ("this_skips", iter.test_case_name());
326     ATF_REQUIRE_EQ(result_8, iter.result());
327     ATF_REQUIRE_EQ("Another stdout", iter.stdout_contents());
328     ATF_REQUIRE(iter.stderr_contents().empty());
329     ATF_REQUIRE_EQ(datetime::delta(0, 817987), iter.duration());
330 
331     ++iter;
332     ATF_REQUIRE(iter);
333     ATF_REQUIRE_EQ(test_program_7, *iter.test_program());
334     ATF_REQUIRE_EQ("main", iter.test_case_name());
335     ATF_REQUIRE_EQ(result_9, iter.result());
336     ATF_REQUIRE(iter.stdout_contents().empty());
337     ATF_REQUIRE_EQ("Another stderr", iter.stderr_contents());
338     ATF_REQUIRE_EQ(datetime::delta(9, 961700), iter.duration());
339 
340     ++iter;
341     ATF_REQUIRE(!iter);
342 }
343 
344 
345 /// Validates the contents of the action with identifier 4.
346 ///
347 /// \param transaction An open read transaction in the backend.
348 static void
check_action_4(store::transaction & transaction)349 check_action_4(store::transaction& transaction)
350 {
351     const fs::path root("/usr/tests");
352     std::map< std::string, std::string > environment;
353     environment["LANG"] = "C";
354     environment["PATH"] = "/bin:/usr/bin";
355     environment["TERM"] = "xterm";
356     const engine::context context_4(root, environment);
357 
358     const engine::action action_4(context_4);
359 
360     ATF_REQUIRE_EQ(action_4, transaction.get_action(4));
361 
362     engine::test_program test_program_8(
363         "plain", fs::path("subdir/another_test"), fs::path("/usr/tests"),
364         "subsuite-name", engine::metadata_builder()
365         .set_timeout(datetime::delta(10, 0)).build());
366     {
367         const engine::test_case_ptr test_case_10(new engine::test_case(
368             "plain", test_program_8, "main",
369             engine::metadata_builder()
370             .set_timeout(datetime::delta(10, 0)).build()));
371         engine::test_cases_vector test_cases;
372         test_cases.push_back(test_case_10);
373         test_program_8.set_test_cases(test_cases);
374     }
375     const engine::test_result result_10(engine::test_result::failed,
376                                        "Exit failure");
377 
378     engine::test_program test_program_9(
379         "atf", fs::path("complex_test"), fs::path("/usr/tests"),
380         "suite-name", engine::metadata_builder().build());
381     {
382         const engine::test_case_ptr test_case_11(new engine::test_case(
383             "atf", test_program_9, "this_passes",
384             engine::metadata_builder().build()));
385         const engine::test_case_ptr test_case_12(new engine::test_case(
386             "atf", test_program_9, "this_fails",
387             engine::metadata_builder()
388             .set_description("Test description")
389             .set_required_user("root")
390             .build()));
391         engine::test_cases_vector test_cases;
392         test_cases.push_back(test_case_11);
393         test_cases.push_back(test_case_12);
394         test_program_9.set_test_cases(test_cases);
395     }
396     const engine::test_result result_11(engine::test_result::passed);
397     const engine::test_result result_12(engine::test_result::failed,
398                                         "Some reason");
399 
400     store::results_iterator iter = transaction.get_action_results(4);
401     ATF_REQUIRE(iter);
402     ATF_REQUIRE_EQ(test_program_9, *iter.test_program());
403     ATF_REQUIRE_EQ("this_fails", iter.test_case_name());
404     ATF_REQUIRE_EQ(result_12, iter.result());
405     ATF_REQUIRE(iter.stdout_contents().empty());
406     ATF_REQUIRE(iter.stderr_contents().empty());
407     ATF_REQUIRE_EQ(datetime::delta(1, 905000), iter.duration());
408 
409     ++iter;
410     ATF_REQUIRE(iter);
411     ATF_REQUIRE_EQ(test_program_9, *iter.test_program());
412     ATF_REQUIRE_EQ("this_passes", iter.test_case_name());
413     ATF_REQUIRE_EQ(result_11, iter.result());
414     ATF_REQUIRE(iter.stdout_contents().empty());
415     ATF_REQUIRE(iter.stderr_contents().empty());
416     ATF_REQUIRE_EQ(datetime::delta(0, 500000), iter.duration());
417 
418     ++iter;
419     ATF_REQUIRE(iter);
420     ATF_REQUIRE_EQ(test_program_8, *iter.test_program());
421     ATF_REQUIRE_EQ("main", iter.test_case_name());
422     ATF_REQUIRE_EQ(result_10, iter.result());
423     ATF_REQUIRE_EQ("Test stdout", iter.stdout_contents());
424     ATF_REQUIRE_EQ("Test stderr", iter.stderr_contents());
425     ATF_REQUIRE_EQ(datetime::delta(1, 0), iter.duration());
426 
427     ++iter;
428     ATF_REQUIRE(!iter);
429 }
430 
431 
432 /// Validates the contents of an open database agains good known values.
433 ///
434 /// \param transaction An open read-only backend.
435 static void
check_data(store::backend & backend)436 check_data(store::backend& backend)
437 {
438     store::transaction transaction = backend.start();
439     check_action_1(transaction);
440     check_action_2(transaction);
441     check_action_3(transaction);
442     check_action_4(transaction);
443 }
444 
445 
446 ATF_TEST_CASE(current_schema);
ATF_TEST_CASE_HEAD(current_schema)447 ATF_TEST_CASE_HEAD(current_schema)
448 {
449     logging::set_inmemory();
450     const std::string required_files =
451         store::detail::schema_file().str()
452         + " " + (fs::path(get_config_var("srcdir")) / "testdata_v2.sql").str();
453     set_md_var("require.files", required_files);
454 }
ATF_TEST_CASE_BODY(current_schema)455 ATF_TEST_CASE_BODY(current_schema)
456 {
457     const fs::path testpath("test.db");
458 
459     sqlite::database db = sqlite::database::open(
460         testpath, sqlite::open_readwrite | sqlite::open_create);
461     exec_db_file(db, store::detail::schema_file());
462     exec_db_file(db, fs::path(get_config_var("srcdir")) / "testdata_v2.sql");
463     db.close();
464 
465     store::backend backend = store::backend::open_ro(testpath);
466     check_data(backend);
467 }
468 
469 
470 ATF_TEST_CASE(migrate_schema__v1_to_v2);
ATF_TEST_CASE_HEAD(migrate_schema__v1_to_v2)471 ATF_TEST_CASE_HEAD(migrate_schema__v1_to_v2)
472 {
473     logging::set_inmemory();
474     const std::string required_files =
475         store::detail::migration_file(1, 2).str()
476         + " " + (fs::path(get_config_var("srcdir")) / "schema_v1.sql").str()
477         + " " + (fs::path(get_config_var("srcdir")) / "testdata_v1.sql").str();
478     set_md_var("require.files", required_files);
479 }
ATF_TEST_CASE_BODY(migrate_schema__v1_to_v2)480 ATF_TEST_CASE_BODY(migrate_schema__v1_to_v2)
481 {
482     const fs::path testpath("test.db");
483 
484     sqlite::database db = sqlite::database::open(
485         testpath, sqlite::open_readwrite | sqlite::open_create);
486     exec_db_file(db, fs::path(get_config_var("srcdir")) / "schema_v1.sql");
487     exec_db_file(db, fs::path(get_config_var("srcdir")) / "testdata_v1.sql");
488     db.close();
489 
490     store::migrate_schema(fs::path("test.db"));
491 
492     store::backend backend = store::backend::open_ro(testpath);
493     check_data(backend);
494 }
495 
496 
ATF_INIT_TEST_CASES(tcs)497 ATF_INIT_TEST_CASES(tcs)
498 {
499     ATF_ADD_TEST_CASE(tcs, current_schema);
500 
501     ATF_ADD_TEST_CASE(tcs, migrate_schema__v1_to_v2);
502 }
503