Home | History | Annotate | Line # | Download | only in store
transaction_test.cpp revision 1.1
      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
     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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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);
    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 }
    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 
    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