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