xref: /netbsd-src/external/bsd/kyua-cli/dist/store/transaction_test.cpp (revision 6b3a42af15b5e090c339512c790dd68f3d11a9d8)
1 // Copyright 2011 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/transaction.hpp"
30 
31 #include <cstring>
32 #include <map>
33 #include <set>
34 #include <string>
35 
36 #include <atf-c++.hpp>
37 
38 #include "engine/action.hpp"
39 #include "engine/context.hpp"
40 #include "engine/test_result.hpp"
41 #include "store/backend.hpp"
42 #include "store/exceptions.hpp"
43 #include "utils/datetime.hpp"
44 #include "utils/fs/path.hpp"
45 #include "utils/logging/operations.hpp"
46 #include "utils/optional.ipp"
47 #include "utils/sqlite/database.hpp"
48 #include "utils/sqlite/exceptions.hpp"
49 #include "utils/sqlite/statement.ipp"
50 #include "utils/units.hpp"
51 
52 namespace datetime = utils::datetime;
53 namespace fs = utils::fs;
54 namespace logging = utils::logging;
55 namespace sqlite = utils::sqlite;
56 namespace units = utils::units;
57 
58 using utils::none;
59 using utils::optional;
60 
61 
62 namespace {
63 
64 
65 /// Performs a test for a working put_result
66 ///
67 /// \param result The result object to put.
68 /// \param result_type The textual name of the result to expect in the
69 ///     database.
70 /// \param exp_reason The reason to expect in the database.  This is separate
71 ///     from the result parameter so that we can handle passed() here as well.
72 ///     Just provide NULL in this case.
73 static void
do_put_result_ok_test(const engine::test_result & result,const char * result_type,const char * exp_reason)74 do_put_result_ok_test(const engine::test_result& result,
75                       const char* result_type, const char* exp_reason)
76 {
77     store::backend backend = store::backend::open_rw(fs::path("test.db"));
78     backend.database().exec("PRAGMA foreign_keys = OFF");
79     store::transaction tx = backend.start();
80     const datetime::timestamp start_time = datetime::timestamp::from_values(
81         2012, 01, 30, 22, 10, 00, 0);
82     const datetime::timestamp end_time = datetime::timestamp::from_values(
83         2012, 01, 30, 22, 15, 30, 123456);
84     tx.put_result(result, 312, start_time, end_time);
85     tx.commit();
86 
87     sqlite::statement stmt = backend.database().create_statement(
88         "SELECT test_case_id, result_type, result_reason "
89         "FROM test_results");
90 
91     ATF_REQUIRE(stmt.step());
92     ATF_REQUIRE_EQ(312, stmt.column_int64(0));
93     ATF_REQUIRE_EQ(result_type, stmt.column_text(1));
94     if (exp_reason != NULL)
95         ATF_REQUIRE_EQ(exp_reason, stmt.column_text(2));
96     else
97         ATF_REQUIRE(stmt.column_type(2) == sqlite::type_null);
98     ATF_REQUIRE(!stmt.step());
99 }
100 
101 
102 }  // anonymous namespace
103 
104 
105 ATF_TEST_CASE(commit__ok);
ATF_TEST_CASE_HEAD(commit__ok)106 ATF_TEST_CASE_HEAD(commit__ok)
107 {
108     logging::set_inmemory();
109     set_md_var("require.files", store::detail::schema_file().c_str());
110 }
ATF_TEST_CASE_BODY(commit__ok)111 ATF_TEST_CASE_BODY(commit__ok)
112 {
113     store::backend backend = store::backend::open_rw(fs::path("test.db"));
114     store::transaction tx = backend.start();
115     backend.database().exec("CREATE TABLE a (b INTEGER PRIMARY KEY)");
116     backend.database().exec("SELECT * FROM a");
117     tx.commit();
118     backend.database().exec("SELECT * FROM a");
119 }
120 
121 
122 ATF_TEST_CASE(commit__fail);
ATF_TEST_CASE_HEAD(commit__fail)123 ATF_TEST_CASE_HEAD(commit__fail)
124 {
125     logging::set_inmemory();
126     set_md_var("require.files", store::detail::schema_file().c_str());
127 }
ATF_TEST_CASE_BODY(commit__fail)128 ATF_TEST_CASE_BODY(commit__fail)
129 {
130     store::backend backend = store::backend::open_rw(fs::path("test.db"));
131     const engine::context context(fs::path("/foo/bar"),
132                                   std::map< std::string, std::string >());
133     {
134         store::transaction tx = backend.start();
135         tx.put_context(context);
136         backend.database().exec(
137             "CREATE TABLE foo ("
138             "a REFERENCES contexts(context_id) DEFERRABLE INITIALLY DEFERRED)");
139         backend.database().exec("INSERT INTO foo VALUES (912378472)");
140         ATF_REQUIRE_THROW(store::error, tx.commit());
141     }
142     // If the code attempts to maintain any state regarding the already-put
143     // objects and the commit does not clean up correctly, this would fail in
144     // some manner.
145     store::transaction tx = backend.start();
146     tx.put_context(context);
147     tx.commit();
148 }
149 
150 
151 ATF_TEST_CASE(rollback__ok);
ATF_TEST_CASE_HEAD(rollback__ok)152 ATF_TEST_CASE_HEAD(rollback__ok)
153 {
154     logging::set_inmemory();
155     set_md_var("require.files", store::detail::schema_file().c_str());
156 }
ATF_TEST_CASE_BODY(rollback__ok)157 ATF_TEST_CASE_BODY(rollback__ok)
158 {
159     store::backend backend = store::backend::open_rw(fs::path("test.db"));
160     store::transaction tx = backend.start();
161     backend.database().exec("CREATE TABLE a_table (b INTEGER PRIMARY KEY)");
162     backend.database().exec("SELECT * FROM a_table");
163     tx.rollback();
164     ATF_REQUIRE_THROW_RE(sqlite::error, "a_table",
165                          backend.database().exec("SELECT * FROM a_table"));
166 }
167 
168 
169 ATF_TEST_CASE(get_put_action__ok);
ATF_TEST_CASE_HEAD(get_put_action__ok)170 ATF_TEST_CASE_HEAD(get_put_action__ok)
171 {
172     logging::set_inmemory();
173     set_md_var("require.files", store::detail::schema_file().c_str());
174 }
ATF_TEST_CASE_BODY(get_put_action__ok)175 ATF_TEST_CASE_BODY(get_put_action__ok)
176 {
177     store::backend backend = store::backend::open_rw(fs::path("test.db"));
178     const engine::context context1(fs::path("/foo/bar"),
179                                    std::map< std::string, std::string >());
180     const engine::context context2(fs::path("/foo/baz"),
181                                    std::map< std::string, std::string >());
182     const engine::action exp_action1(context1);
183     const engine::action exp_action2(context2);
184     const engine::action exp_action3(context1);
185 
186     int64_t id1, id2, id3;
187     {
188         store::transaction tx = backend.start();
189         const int64_t context1_id = tx.put_context(context1);
190         const int64_t context2_id = tx.put_context(context2);
191         id1 = tx.put_action(exp_action1, context1_id);
192         id3 = tx.put_action(exp_action3, context1_id);
193         id2 = tx.put_action(exp_action2, context2_id);
194         tx.commit();
195     }
196     {
197         store::transaction tx = backend.start();
198         const engine::action action1 = tx.get_action(id1);
199         const engine::action action2 = tx.get_action(id2);
200         const engine::action action3 = tx.get_action(id3);
201         tx.rollback();
202 
203         ATF_REQUIRE(exp_action1 == action1);
204         ATF_REQUIRE(exp_action2 == action2);
205         ATF_REQUIRE(exp_action3 == action3);
206     }
207 }
208 
209 
210 ATF_TEST_CASE(get_put_action__get_fail__missing);
ATF_TEST_CASE_HEAD(get_put_action__get_fail__missing)211 ATF_TEST_CASE_HEAD(get_put_action__get_fail__missing)
212 {
213     logging::set_inmemory();
214     set_md_var("require.files", store::detail::schema_file().c_str());
215 }
ATF_TEST_CASE_BODY(get_put_action__get_fail__missing)216 ATF_TEST_CASE_BODY(get_put_action__get_fail__missing)
217 {
218     store::backend backend = store::backend::open_rw(fs::path("test.db"));
219 
220     store::transaction tx = backend.start();
221     ATF_REQUIRE_THROW_RE(store::error, "action 523: does not exist",
222                          tx.get_action(523));
223 }
224 
225 
226 ATF_TEST_CASE(get_put_action__get_fail__invalid_context);
ATF_TEST_CASE_HEAD(get_put_action__get_fail__invalid_context)227 ATF_TEST_CASE_HEAD(get_put_action__get_fail__invalid_context)
228 {
229     logging::set_inmemory();
230     set_md_var("require.files", store::detail::schema_file().c_str());
231 }
ATF_TEST_CASE_BODY(get_put_action__get_fail__invalid_context)232 ATF_TEST_CASE_BODY(get_put_action__get_fail__invalid_context)
233 {
234     store::backend backend = store::backend::open_rw(fs::path("test.db"));
235     backend.database().exec("PRAGMA foreign_keys = OFF");
236     backend.database().exec("INSERT INTO actions (action_id, context_id) "
237                             "VALUES (123, 456)");
238 
239     store::transaction tx = backend.start();
240     ATF_REQUIRE_THROW_RE(store::error, "context 456: does not exist",
241                          tx.get_action(123));
242 }
243 
244 
245 ATF_TEST_CASE(get_put_action__put_fail);
ATF_TEST_CASE_HEAD(get_put_action__put_fail)246 ATF_TEST_CASE_HEAD(get_put_action__put_fail)
247 {
248     logging::set_inmemory();
249     set_md_var("require.files", store::detail::schema_file().c_str());
250 }
ATF_TEST_CASE_BODY(get_put_action__put_fail)251 ATF_TEST_CASE_BODY(get_put_action__put_fail)
252 {
253     store::backend backend = store::backend::open_rw(fs::path("test.db"));
254     store::transaction tx = backend.start();
255     const engine::context context(fs::path("/foo/bar"),
256                                   std::map< std::string, std::string >());
257     const int64_t context_id = tx.put_context(context);
258     const engine::action action(context);
259     backend.database().exec("DROP TABLE actions");
260     ATF_REQUIRE_THROW(store::error, tx.put_action(action, context_id));
261     tx.commit();
262 }
263 
264 
265 ATF_TEST_CASE(get_action_results__none);
ATF_TEST_CASE_HEAD(get_action_results__none)266 ATF_TEST_CASE_HEAD(get_action_results__none)
267 {
268     logging::set_inmemory();
269     set_md_var("require.files", store::detail::schema_file().c_str());
270 }
ATF_TEST_CASE_BODY(get_action_results__none)271 ATF_TEST_CASE_BODY(get_action_results__none)
272 {
273     store::backend backend = store::backend::open_rw(fs::path("test.db"));
274     store::transaction tx = backend.start();
275     store::results_iterator iter = tx.get_action_results(1);
276     ATF_REQUIRE(!iter);
277 }
278 
279 
280 ATF_TEST_CASE(get_action_results__many);
ATF_TEST_CASE_HEAD(get_action_results__many)281 ATF_TEST_CASE_HEAD(get_action_results__many)
282 {
283     logging::set_inmemory();
284     set_md_var("require.files", store::detail::schema_file().c_str());
285 }
ATF_TEST_CASE_BODY(get_action_results__many)286 ATF_TEST_CASE_BODY(get_action_results__many)
287 {
288     store::backend backend = store::backend::open_rw(fs::path("test.db"));
289 
290     store::transaction tx = backend.start();
291 
292     const engine::context context(fs::path("/foo/bar"),
293                                   std::map< std::string, std::string >());
294     const engine::action action(context);
295     const int64_t context_id = tx.put_context(context);
296     const int64_t action_id = tx.put_action(action, context_id);
297     const int64_t action2_id = tx.put_action(action, context_id);
298 
299     const datetime::timestamp start_time1 = datetime::timestamp::from_values(
300         2012, 01, 30, 22, 10, 00, 0);
301     const datetime::timestamp end_time1 = datetime::timestamp::from_values(
302         2012, 01, 30, 22, 15, 30, 1234);
303     const datetime::timestamp start_time2 = datetime::timestamp::from_values(
304         2012, 01, 30, 22, 15, 40, 987);
305     const datetime::timestamp end_time2 = datetime::timestamp::from_values(
306         2012, 01, 30, 22, 16, 0, 0);
307 
308     atf::utils::create_file("unused.txt", "unused file\n");
309 
310     engine::test_program test_program_1(
311         "plain", fs::path("a/prog1"), fs::path("/the/root"), "suite1",
312         engine::metadata_builder().build());
313     engine::test_case_ptr test_case_1(new engine::test_case(
314         "plain", test_program_1, "main", engine::metadata_builder().build()));
315     engine::test_cases_vector test_cases_1;
316     test_cases_1.push_back(test_case_1);
317     test_program_1.set_test_cases(test_cases_1);
318     const engine::test_result result_1(engine::test_result::passed);
319     {
320         const int64_t tp_id = tx.put_test_program(test_program_1, action_id);
321         const int64_t tc_id = tx.put_test_case(*test_case_1, tp_id);
322         atf::utils::create_file("prog1.out", "stdout of prog1\n");
323         tx.put_test_case_file("__STDOUT__", fs::path("prog1.out"), tc_id);
324         tx.put_test_case_file("unused.txt", fs::path("unused.txt"), tc_id);
325         tx.put_result(result_1, tc_id, start_time1, end_time1);
326 
327         const int64_t tp2_id = tx.put_test_program(test_program_1, action2_id);
328         const int64_t tc2_id = tx.put_test_case(*test_case_1, tp2_id);
329         tx.put_test_case_file("__STDOUT__", fs::path("unused.txt"), tc2_id);
330         tx.put_test_case_file("__STDERR__", fs::path("unused.txt"), tc2_id);
331         tx.put_result(result_1, tc2_id, start_time1, end_time1);
332     }
333 
334     engine::test_program test_program_2(
335         "plain", fs::path("b/prog2"), fs::path("/the/root"), "suite2",
336         engine::metadata_builder().build());
337     engine::test_case_ptr test_case_2(new engine::test_case(
338         "plain", test_program_2, "main", engine::metadata_builder().build()));
339     engine::test_cases_vector test_cases_2;
340     test_cases_2.push_back(test_case_2);
341     test_program_2.set_test_cases(test_cases_2);
342     const engine::test_result result_2(engine::test_result::failed,
343                                        "Some text");
344     {
345         const int64_t tp_id = tx.put_test_program(test_program_2, action_id);
346         const int64_t tc_id = tx.put_test_case(*test_case_2, tp_id);
347         atf::utils::create_file("prog2.err", "stderr of prog2\n");
348         tx.put_test_case_file("__STDERR__", fs::path("prog2.err"), tc_id);
349         tx.put_test_case_file("unused.txt", fs::path("unused.txt"), tc_id);
350         tx.put_result(result_2, tc_id, start_time2, end_time2);
351     }
352 
353     tx.commit();
354 
355     store::transaction tx2 = backend.start();
356     store::results_iterator iter = tx2.get_action_results(action_id);
357     ATF_REQUIRE(iter);
358     ATF_REQUIRE(test_program_1 == *iter.test_program());
359     ATF_REQUIRE_EQ("main", iter.test_case_name());
360     ATF_REQUIRE_EQ("stdout of prog1\n", iter.stdout_contents());
361     ATF_REQUIRE(iter.stderr_contents().empty());
362     ATF_REQUIRE(result_1 == iter.result());
363     ATF_REQUIRE(end_time1 - start_time1 == iter.duration());
364     ATF_REQUIRE(++iter);
365     ATF_REQUIRE(test_program_2 == *iter.test_program());
366     ATF_REQUIRE_EQ("main", iter.test_case_name());
367     ATF_REQUIRE(iter.stdout_contents().empty());
368     ATF_REQUIRE_EQ("stderr of prog2\n", iter.stderr_contents());
369     ATF_REQUIRE(result_2 == iter.result());
370     ATF_REQUIRE(end_time2 - start_time2 == iter.duration());
371     ATF_REQUIRE(!++iter);
372 }
373 
374 
375 ATF_TEST_CASE(get_latest_action__ok);
ATF_TEST_CASE_HEAD(get_latest_action__ok)376 ATF_TEST_CASE_HEAD(get_latest_action__ok)
377 {
378     logging::set_inmemory();
379     set_md_var("require.files", store::detail::schema_file().c_str());
380 }
ATF_TEST_CASE_BODY(get_latest_action__ok)381 ATF_TEST_CASE_BODY(get_latest_action__ok)
382 {
383     store::backend backend = store::backend::open_rw(fs::path("test.db"));
384     const engine::context context1(fs::path("/foo/bar"),
385                                    std::map< std::string, std::string >());
386     const engine::context context2(fs::path("/foo/baz"),
387                                    std::map< std::string, std::string >());
388     const engine::action exp_action1(context1);
389     const engine::action exp_action2(context2);
390 
391     int64_t id2;
392     {
393         store::transaction tx = backend.start();
394         const int64_t context1_id = tx.put_context(context1);
395         const int64_t context2_id = tx.put_context(context2);
396         (void)tx.put_action(exp_action1, context1_id);
397         id2 = tx.put_action(exp_action2, context2_id);
398         tx.commit();
399     }
400     {
401         store::transaction tx = backend.start();
402         const std::pair< int64_t, engine::action > latest_action =
403             tx.get_latest_action();
404         tx.rollback();
405 
406         ATF_REQUIRE(id2 == latest_action.first);
407         ATF_REQUIRE(exp_action2 == latest_action.second);
408     }
409 }
410 
411 
412 ATF_TEST_CASE(get_latest_action__none);
ATF_TEST_CASE_HEAD(get_latest_action__none)413 ATF_TEST_CASE_HEAD(get_latest_action__none)
414 {
415     logging::set_inmemory();
416     set_md_var("require.files", store::detail::schema_file().c_str());
417 }
ATF_TEST_CASE_BODY(get_latest_action__none)418 ATF_TEST_CASE_BODY(get_latest_action__none)
419 {
420     store::backend backend = store::backend::open_rw(fs::path("test.db"));
421     store::transaction tx = backend.start();
422     ATF_REQUIRE_THROW_RE(store::error, "No actions", tx.get_latest_action());
423 }
424 
425 
426 ATF_TEST_CASE(get_latest_action__invalid_context);
ATF_TEST_CASE_HEAD(get_latest_action__invalid_context)427 ATF_TEST_CASE_HEAD(get_latest_action__invalid_context)
428 {
429     logging::set_inmemory();
430     set_md_var("require.files", store::detail::schema_file().c_str());
431 }
ATF_TEST_CASE_BODY(get_latest_action__invalid_context)432 ATF_TEST_CASE_BODY(get_latest_action__invalid_context)
433 {
434     store::backend backend = store::backend::open_rw(fs::path("test.db"));
435     backend.database().exec("PRAGMA foreign_keys = OFF");
436     backend.database().exec("INSERT INTO actions (action_id, context_id) "
437                             "VALUES (123, 456)");
438 
439     store::transaction tx = backend.start();
440     ATF_REQUIRE_THROW_RE(store::error, "context 456: does not exist",
441                          tx.get_latest_action());
442 }
443 
444 
445 ATF_TEST_CASE(get_put_context__ok);
ATF_TEST_CASE_HEAD(get_put_context__ok)446 ATF_TEST_CASE_HEAD(get_put_context__ok)
447 {
448     logging::set_inmemory();
449     set_md_var("require.files", store::detail::schema_file().c_str());
450 }
ATF_TEST_CASE_BODY(get_put_context__ok)451 ATF_TEST_CASE_BODY(get_put_context__ok)
452 {
453     store::backend backend = store::backend::open_rw(fs::path("test.db"));
454 
455     std::map< std::string, std::string > env1;
456     env1["A1"] = "foo";
457     env1["A2"] = "bar";
458     std::map< std::string, std::string > env2;
459     const engine::context exp_context1(fs::path("/foo/bar"), env1);
460     const engine::context exp_context2(fs::path("/foo/bar"), env1);
461     const engine::context exp_context3(fs::path("/foo/baz"), env2);
462 
463     int64_t id1, id2, id3;
464     {
465         store::transaction tx = backend.start();
466         id1 = tx.put_context(exp_context1);
467         id3 = tx.put_context(exp_context3);
468         id2 = tx.put_context(exp_context2);
469         tx.commit();
470     }
471     {
472         store::transaction tx = backend.start();
473         const engine::context context1 = tx.get_context(id1);
474         const engine::context context2 = tx.get_context(id2);
475         const engine::context context3 = tx.get_context(id3);
476         tx.rollback();
477 
478         ATF_REQUIRE(exp_context1 == context1);
479         ATF_REQUIRE(exp_context2 == context2);
480         ATF_REQUIRE(exp_context3 == context3);
481     }
482 }
483 
484 
485 ATF_TEST_CASE(get_put_context__get_fail__missing);
ATF_TEST_CASE_HEAD(get_put_context__get_fail__missing)486 ATF_TEST_CASE_HEAD(get_put_context__get_fail__missing)
487 {
488     logging::set_inmemory();
489     set_md_var("require.files", store::detail::schema_file().c_str());
490 }
ATF_TEST_CASE_BODY(get_put_context__get_fail__missing)491 ATF_TEST_CASE_BODY(get_put_context__get_fail__missing)
492 {
493     store::backend backend = store::backend::open_rw(fs::path("test.db"));
494 
495     store::transaction tx = backend.start();
496     ATF_REQUIRE_THROW_RE(store::error, "context 456: does not exist",
497                          tx.get_context(456));
498 }
499 
500 
501 ATF_TEST_CASE(get_put_context__get_fail__invalid_cwd);
ATF_TEST_CASE_HEAD(get_put_context__get_fail__invalid_cwd)502 ATF_TEST_CASE_HEAD(get_put_context__get_fail__invalid_cwd)
503 {
504     logging::set_inmemory();
505     set_md_var("require.files", store::detail::schema_file().c_str());
506 }
ATF_TEST_CASE_BODY(get_put_context__get_fail__invalid_cwd)507 ATF_TEST_CASE_BODY(get_put_context__get_fail__invalid_cwd)
508 {
509     store::backend backend = store::backend::open_rw(fs::path("test.db"));
510 
511     sqlite::statement stmt = backend.database().create_statement(
512         "INSERT INTO contexts (context_id, cwd) VALUES (78, :cwd)");
513     const char buffer[10] = "foo bar";
514     stmt.bind(":cwd", sqlite::blob(buffer, sizeof(buffer)));
515     stmt.step_without_results();
516 
517     store::transaction tx = backend.start();
518     ATF_REQUIRE_THROW_RE(store::error, "context 78: .*cwd.*not a string",
519                          tx.get_context(78));
520 }
521 
522 
523 ATF_TEST_CASE(get_put_context__get_fail__invalid_env_vars);
ATF_TEST_CASE_HEAD(get_put_context__get_fail__invalid_env_vars)524 ATF_TEST_CASE_HEAD(get_put_context__get_fail__invalid_env_vars)
525 {
526     logging::set_inmemory();
527     set_md_var("require.files", store::detail::schema_file().c_str());
528 }
ATF_TEST_CASE_BODY(get_put_context__get_fail__invalid_env_vars)529 ATF_TEST_CASE_BODY(get_put_context__get_fail__invalid_env_vars)
530 {
531     store::backend backend = store::backend::open_rw(fs::path("test.db"));
532 
533     backend.database().exec("INSERT INTO contexts (context_id, cwd) "
534                             "VALUES (10, '/foo/bar')");
535     backend.database().exec("INSERT INTO contexts (context_id, cwd) "
536                             "VALUES (20, '/foo/bar')");
537 
538     const char buffer[10] = "foo bar";
539 
540     {
541         sqlite::statement stmt = backend.database().create_statement(
542             "INSERT INTO env_vars (context_id, var_name, var_value) "
543             "VALUES (10, :var_name, 'abc')");
544         stmt.bind(":var_name", sqlite::blob(buffer, sizeof(buffer)));
545         stmt.step_without_results();
546     }
547 
548     {
549         sqlite::statement stmt = backend.database().create_statement(
550             "INSERT INTO env_vars (context_id, var_name, var_value) "
551             "VALUES (20, 'abc', :var_value)");
552         stmt.bind(":var_value", sqlite::blob(buffer, sizeof(buffer)));
553         stmt.step_without_results();
554     }
555 
556     store::transaction tx = backend.start();
557     ATF_REQUIRE_THROW_RE(store::error, "context 10: .*var_name.*not a string",
558                          tx.get_context(10));
559     ATF_REQUIRE_THROW_RE(store::error, "context 20: .*var_value.*not a string",
560                          tx.get_context(20));
561 }
562 
563 
564 ATF_TEST_CASE(get_put_context__put_fail);
ATF_TEST_CASE_HEAD(get_put_context__put_fail)565 ATF_TEST_CASE_HEAD(get_put_context__put_fail)
566 {
567     logging::set_inmemory();
568     set_md_var("require.files", store::detail::schema_file().c_str());
569 }
ATF_TEST_CASE_BODY(get_put_context__put_fail)570 ATF_TEST_CASE_BODY(get_put_context__put_fail)
571 {
572     (void)store::backend::open_rw(fs::path("test.db"));
573     store::backend backend = store::backend::open_ro(fs::path("test.db"));
574     store::transaction tx = backend.start();
575     const engine::context context(fs::path("/foo/bar"),
576                                   std::map< std::string, std::string >());
577     ATF_REQUIRE_THROW(store::error, tx.put_context(context));
578     tx.commit();
579 }
580 
581 
582 ATF_TEST_CASE(put_test_program__ok);
ATF_TEST_CASE_HEAD(put_test_program__ok)583 ATF_TEST_CASE_HEAD(put_test_program__ok)
584 {
585     logging::set_inmemory();
586     set_md_var("require.files", store::detail::schema_file().c_str());
587 }
ATF_TEST_CASE_BODY(put_test_program__ok)588 ATF_TEST_CASE_BODY(put_test_program__ok)
589 {
590     const engine::metadata md = engine::metadata_builder()
591         .add_custom("var1", "value1")
592         .add_custom("var2", "value2")
593         .build();
594     const engine::test_program test_program(
595         "mock", fs::path("the/binary"), fs::path("/some//root"),
596         "the-suite", md);
597 
598     store::backend backend = store::backend::open_rw(fs::path("test.db"));
599     backend.database().exec("PRAGMA foreign_keys = OFF");
600     store::transaction tx = backend.start();
601     const int64_t test_program_id = tx.put_test_program(test_program, 15);
602     tx.commit();
603 
604     {
605         sqlite::statement stmt = backend.database().create_statement(
606             "SELECT * FROM test_programs");
607 
608         ATF_REQUIRE(stmt.step());
609         ATF_REQUIRE_EQ(test_program_id,
610                        stmt.safe_column_int64("test_program_id"));
611         ATF_REQUIRE_EQ(15, stmt.safe_column_int64("action_id"));
612         ATF_REQUIRE_EQ("/some/root/the/binary",
613                        stmt.safe_column_text("absolute_path"));
614         ATF_REQUIRE_EQ("/some/root", stmt.safe_column_text("root"));
615         ATF_REQUIRE_EQ("the/binary", stmt.safe_column_text("relative_path"));
616         ATF_REQUIRE_EQ("the-suite", stmt.safe_column_text("test_suite_name"));
617         ATF_REQUIRE(!stmt.step());
618     }
619 }
620 
621 
622 ATF_TEST_CASE(put_test_program__fail);
ATF_TEST_CASE_HEAD(put_test_program__fail)623 ATF_TEST_CASE_HEAD(put_test_program__fail)
624 {
625     logging::set_inmemory();
626     set_md_var("require.files", store::detail::schema_file().c_str());
627 }
ATF_TEST_CASE_BODY(put_test_program__fail)628 ATF_TEST_CASE_BODY(put_test_program__fail)
629 {
630     const engine::test_program test_program(
631         "mock", fs::path("the/binary"), fs::path("/some/root"), "the-suite",
632         engine::metadata_builder().build());
633 
634     store::backend backend = store::backend::open_rw(fs::path("test.db"));
635     store::transaction tx = backend.start();
636     ATF_REQUIRE_THROW(store::error, tx.put_test_program(test_program, -1));
637     tx.commit();
638 }
639 
640 
641 ATF_TEST_CASE(put_test_case__ok);
ATF_TEST_CASE_HEAD(put_test_case__ok)642 ATF_TEST_CASE_HEAD(put_test_case__ok)
643 {
644     logging::set_inmemory();
645     set_md_var("require.files", store::detail::schema_file().c_str());
646 }
ATF_TEST_CASE_BODY(put_test_case__ok)647 ATF_TEST_CASE_BODY(put_test_case__ok)
648 {
649     engine::test_program test_program(
650         "atf", fs::path("the/binary"), fs::path("/some/root"), "the-suite",
651         engine::metadata_builder().build());
652 
653     const engine::test_case_ptr test_case1(new engine::test_case(
654         "atf", test_program, "tc1", engine::metadata_builder().build()));
655 
656     const engine::metadata md2 = engine::metadata_builder()
657         .add_allowed_architecture("powerpc")
658         .add_allowed_architecture("x86_64")
659         .add_allowed_platform("amd64")
660         .add_allowed_platform("macppc")
661         .add_custom("X-user1", "value1")
662         .add_custom("X-user2", "value2")
663         .add_required_config("var1")
664         .add_required_config("var2")
665         .add_required_config("var3")
666         .add_required_file(fs::path("/file1/yes"))
667         .add_required_file(fs::path("/file2/foo"))
668         .add_required_program(fs::path("/bin/ls"))
669         .add_required_program(fs::path("cp"))
670         .set_description("The description")
671         .set_has_cleanup(true)
672         .set_required_memory(units::bytes::parse("1k"))
673         .set_required_user("root")
674         .set_timeout(datetime::delta(520, 0))
675         .build();
676     const engine::test_case_ptr test_case2(new engine::test_case(
677         "atf", test_program, "tc2", md2));
678 
679     {
680         engine::test_cases_vector test_cases;
681         test_cases.push_back(test_case1);
682         test_cases.push_back(test_case2);
683         test_program.set_test_cases(test_cases);
684     }
685 
686     store::backend backend = store::backend::open_rw(fs::path("test.db"));
687     backend.database().exec("PRAGMA foreign_keys = OFF");
688     int64_t test_program_id;
689     {
690         store::transaction tx = backend.start();
691         test_program_id = tx.put_test_program(test_program, 15);
692         tx.put_test_case(*test_case1, test_program_id);
693         tx.put_test_case(*test_case2, test_program_id);
694         tx.commit();
695     }
696 
697     store::transaction tx = backend.start();
698     const engine::test_program_ptr loaded_test_program =
699         store::detail::get_test_program(backend, test_program_id);
700     ATF_REQUIRE(test_program == *loaded_test_program);
701 }
702 
703 
704 ATF_TEST_CASE(put_test_case__fail);
ATF_TEST_CASE_HEAD(put_test_case__fail)705 ATF_TEST_CASE_HEAD(put_test_case__fail)
706 {
707     logging::set_inmemory();
708     set_md_var("require.files", store::detail::schema_file().c_str());
709 }
ATF_TEST_CASE_BODY(put_test_case__fail)710 ATF_TEST_CASE_BODY(put_test_case__fail)
711 {
712     // TODO(jmmv): Use a mock test program and test case.
713     const engine::test_program test_program(
714         "plain", fs::path("the/binary"), fs::path("/some/root"), "the-suite",
715         engine::metadata_builder().build());
716     const engine::test_case test_case("plain", test_program, "main",
717                                       engine::metadata_builder().build());
718 
719     store::backend backend = store::backend::open_rw(fs::path("test.db"));
720     store::transaction tx = backend.start();
721     ATF_REQUIRE_THROW(store::error, tx.put_test_case(test_case, -1));
722     tx.commit();
723 }
724 
725 
726 ATF_TEST_CASE(put_test_case_file__empty);
ATF_TEST_CASE_HEAD(put_test_case_file__empty)727 ATF_TEST_CASE_HEAD(put_test_case_file__empty)
728 {
729     logging::set_inmemory();
730     set_md_var("require.files", store::detail::schema_file().c_str());
731 }
ATF_TEST_CASE_BODY(put_test_case_file__empty)732 ATF_TEST_CASE_BODY(put_test_case_file__empty)
733 {
734     atf::utils::create_file("input.txt", "");
735 
736     store::backend backend = store::backend::open_rw(fs::path("test.db"));
737     backend.database().exec("PRAGMA foreign_keys = OFF");
738     store::transaction tx = backend.start();
739     const optional< int64_t > file_id = tx.put_test_case_file(
740         "my-file", fs::path("input.txt"), 123L);
741     tx.commit();
742     ATF_REQUIRE(!file_id);
743 
744     sqlite::statement stmt = backend.database().create_statement(
745         "SELECT * FROM test_case_files NATURAL JOIN files");
746     ATF_REQUIRE(!stmt.step());
747 }
748 
749 
750 ATF_TEST_CASE(put_test_case_file__some);
ATF_TEST_CASE_HEAD(put_test_case_file__some)751 ATF_TEST_CASE_HEAD(put_test_case_file__some)
752 {
753     logging::set_inmemory();
754     set_md_var("require.files", store::detail::schema_file().c_str());
755 }
ATF_TEST_CASE_BODY(put_test_case_file__some)756 ATF_TEST_CASE_BODY(put_test_case_file__some)
757 {
758     const char contents[] = "This is a test!";
759 
760     atf::utils::create_file("input.txt", contents);
761 
762     store::backend backend = store::backend::open_rw(fs::path("test.db"));
763     backend.database().exec("PRAGMA foreign_keys = OFF");
764     store::transaction tx = backend.start();
765     const optional< int64_t > file_id = tx.put_test_case_file(
766         "my-file", fs::path("input.txt"), 123L);
767     tx.commit();
768     ATF_REQUIRE(file_id);
769 
770     sqlite::statement stmt = backend.database().create_statement(
771         "SELECT * FROM test_case_files NATURAL JOIN files");
772 
773     ATF_REQUIRE(stmt.step());
774     ATF_REQUIRE_EQ(123L, stmt.safe_column_int64("test_case_id"));
775     ATF_REQUIRE_EQ("my-file", stmt.safe_column_text("file_name"));
776     const sqlite::blob blob = stmt.safe_column_blob("contents");
777     ATF_REQUIRE(std::strlen(contents) == static_cast< std::size_t >(blob.size));
778     ATF_REQUIRE(std::memcmp(contents, blob.memory, blob.size) == 0);
779     ATF_REQUIRE(!stmt.step());
780 }
781 
782 
783 ATF_TEST_CASE(put_test_case_file__fail);
ATF_TEST_CASE_HEAD(put_test_case_file__fail)784 ATF_TEST_CASE_HEAD(put_test_case_file__fail)
785 {
786     logging::set_inmemory();
787     set_md_var("require.files", store::detail::schema_file().c_str());
788 }
ATF_TEST_CASE_BODY(put_test_case_file__fail)789 ATF_TEST_CASE_BODY(put_test_case_file__fail)
790 {
791     store::backend backend = store::backend::open_rw(fs::path("test.db"));
792     backend.database().exec("PRAGMA foreign_keys = OFF");
793     store::transaction tx = backend.start();
794     ATF_REQUIRE_THROW(store::error,
795                       tx.put_test_case_file("foo", fs::path("missing"), 1L));
796     tx.commit();
797 
798     sqlite::statement stmt = backend.database().create_statement(
799         "SELECT * FROM test_case_files NATURAL JOIN files");
800     ATF_REQUIRE(!stmt.step());
801 }
802 
803 
804 ATF_TEST_CASE(put_result__ok__broken);
ATF_TEST_CASE_HEAD(put_result__ok__broken)805 ATF_TEST_CASE_HEAD(put_result__ok__broken)
806 {
807     logging::set_inmemory();
808     set_md_var("require.files", store::detail::schema_file().c_str());
809 }
ATF_TEST_CASE_BODY(put_result__ok__broken)810 ATF_TEST_CASE_BODY(put_result__ok__broken)
811 {
812     const engine::test_result result(engine::test_result::broken, "a b cd");
813     do_put_result_ok_test(result, "broken", "a b cd");
814 }
815 
816 
817 ATF_TEST_CASE(put_result__ok__expected_failure);
ATF_TEST_CASE_HEAD(put_result__ok__expected_failure)818 ATF_TEST_CASE_HEAD(put_result__ok__expected_failure)
819 {
820     logging::set_inmemory();
821     set_md_var("require.files", store::detail::schema_file().c_str());
822 }
ATF_TEST_CASE_BODY(put_result__ok__expected_failure)823 ATF_TEST_CASE_BODY(put_result__ok__expected_failure)
824 {
825     const engine::test_result result(engine::test_result::expected_failure,
826                                      "a b cd");
827     do_put_result_ok_test(result, "expected_failure", "a b cd");
828 }
829 
830 
831 ATF_TEST_CASE(put_result__ok__failed);
ATF_TEST_CASE_HEAD(put_result__ok__failed)832 ATF_TEST_CASE_HEAD(put_result__ok__failed)
833 {
834     logging::set_inmemory();
835     set_md_var("require.files", store::detail::schema_file().c_str());
836 }
ATF_TEST_CASE_BODY(put_result__ok__failed)837 ATF_TEST_CASE_BODY(put_result__ok__failed)
838 {
839     const engine::test_result result(engine::test_result::failed, "a b cd");
840     do_put_result_ok_test(result, "failed", "a b cd");
841 }
842 
843 
844 ATF_TEST_CASE(put_result__ok__passed);
ATF_TEST_CASE_HEAD(put_result__ok__passed)845 ATF_TEST_CASE_HEAD(put_result__ok__passed)
846 {
847     logging::set_inmemory();
848     set_md_var("require.files", store::detail::schema_file().c_str());
849 }
ATF_TEST_CASE_BODY(put_result__ok__passed)850 ATF_TEST_CASE_BODY(put_result__ok__passed)
851 {
852     const engine::test_result result(engine::test_result::passed);
853     do_put_result_ok_test(result, "passed", NULL);
854 }
855 
856 
857 ATF_TEST_CASE(put_result__ok__skipped);
ATF_TEST_CASE_HEAD(put_result__ok__skipped)858 ATF_TEST_CASE_HEAD(put_result__ok__skipped)
859 {
860     logging::set_inmemory();
861     set_md_var("require.files", store::detail::schema_file().c_str());
862 }
ATF_TEST_CASE_BODY(put_result__ok__skipped)863 ATF_TEST_CASE_BODY(put_result__ok__skipped)
864 {
865     const engine::test_result result(engine::test_result::skipped, "a b cd");
866     do_put_result_ok_test(result, "skipped", "a b cd");
867 }
868 
869 
870 ATF_TEST_CASE(put_result__fail);
ATF_TEST_CASE_HEAD(put_result__fail)871 ATF_TEST_CASE_HEAD(put_result__fail)
872 {
873     logging::set_inmemory();
874     set_md_var("require.files", store::detail::schema_file().c_str());
875 }
ATF_TEST_CASE_BODY(put_result__fail)876 ATF_TEST_CASE_BODY(put_result__fail)
877 {
878     const engine::test_result result(engine::test_result::broken, "foo");
879 
880     store::backend backend = store::backend::open_rw(fs::path("test.db"));
881     store::transaction tx = backend.start();
882     const datetime::timestamp zero = datetime::timestamp::from_microseconds(0);
883     ATF_REQUIRE_THROW(store::error, tx.put_result(result, -1, zero, zero));
884     tx.commit();
885 }
886 
887 
ATF_INIT_TEST_CASES(tcs)888 ATF_INIT_TEST_CASES(tcs)
889 {
890     ATF_ADD_TEST_CASE(tcs, commit__ok);
891     ATF_ADD_TEST_CASE(tcs, commit__fail);
892     ATF_ADD_TEST_CASE(tcs, rollback__ok);
893 
894     ATF_ADD_TEST_CASE(tcs, get_put_action__ok);
895     ATF_ADD_TEST_CASE(tcs, get_put_action__get_fail__missing);
896     ATF_ADD_TEST_CASE(tcs, get_put_action__get_fail__invalid_context);
897     ATF_ADD_TEST_CASE(tcs, get_put_action__put_fail);
898 
899     ATF_ADD_TEST_CASE(tcs, get_action_results__none);
900     ATF_ADD_TEST_CASE(tcs, get_action_results__many);
901 
902     ATF_ADD_TEST_CASE(tcs, get_latest_action__ok);
903     ATF_ADD_TEST_CASE(tcs, get_latest_action__none);
904     ATF_ADD_TEST_CASE(tcs, get_latest_action__invalid_context);
905 
906     ATF_ADD_TEST_CASE(tcs, get_put_context__ok);
907     ATF_ADD_TEST_CASE(tcs, get_put_context__get_fail__missing);
908     ATF_ADD_TEST_CASE(tcs, get_put_context__get_fail__invalid_cwd);
909     ATF_ADD_TEST_CASE(tcs, get_put_context__get_fail__invalid_env_vars);
910     ATF_ADD_TEST_CASE(tcs, get_put_context__put_fail);
911 
912     ATF_ADD_TEST_CASE(tcs, put_test_program__ok);
913     ATF_ADD_TEST_CASE(tcs, put_test_program__fail);
914     ATF_ADD_TEST_CASE(tcs, put_test_case__ok);
915     ATF_ADD_TEST_CASE(tcs, put_test_case__fail);
916     ATF_ADD_TEST_CASE(tcs, put_test_case_file__empty);
917     ATF_ADD_TEST_CASE(tcs, put_test_case_file__some);
918     ATF_ADD_TEST_CASE(tcs, put_test_case_file__fail);
919 
920     ATF_ADD_TEST_CASE(tcs, put_result__ok__broken);
921     ATF_ADD_TEST_CASE(tcs, put_result__ok__expected_failure);
922     ATF_ADD_TEST_CASE(tcs, put_result__ok__failed);
923     ATF_ADD_TEST_CASE(tcs, put_result__ok__passed);
924     ATF_ADD_TEST_CASE(tcs, put_result__ok__skipped);
925     ATF_ADD_TEST_CASE(tcs, put_result__fail);
926 }
927