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 "engine/drivers/scan_action.hpp" 30 31 #include <set> 32 33 #include <atf-c++.hpp> 34 35 #include "engine/action.hpp" 36 #include "engine/context.hpp" 37 #include "engine/test_result.hpp" 38 #include "store/backend.hpp" 39 #include "store/exceptions.hpp" 40 #include "store/transaction.hpp" 41 #include "utils/datetime.hpp" 42 #include "utils/format/macros.hpp" 43 #include "utils/optional.ipp" 44 #include "utils/sanity.hpp" 45 46 namespace datetime = utils::datetime; 47 namespace fs = utils::fs; 48 namespace scan_action = engine::drivers::scan_action; 49 50 using utils::none; 51 using utils::optional; 52 53 54 namespace { 55 56 57 /// Records the callback values for futher investigation. 58 class capture_hooks : public scan_action::base_hooks { 59 public: 60 /// The captured action ID, if any. 61 optional< int64_t > _action_id; 62 63 /// The captured action, if any. 64 optional< engine::action > _action; 65 66 /// The captured results, flattened as "program:test_case:result". 67 std::set< std::string > _results; 68 69 /// Callback executed when an action is found. 70 /// 71 /// \param action_id The identifier of the loaded action. 72 /// \param action The action loaded from the database. 73 void got_action(const int64_t action_id, 74 const engine::action& action) 75 { 76 PRE(!_action_id); 77 _action_id = action_id; 78 PRE(!_action); 79 _action = action; 80 } 81 82 /// Callback executed when a test results is found. 83 /// 84 /// \param iter Container for the test result's data. 85 void got_result(store::results_iterator& iter) 86 { 87 const char* type; 88 switch (iter.result().type()) { 89 case engine::test_result::passed: type = "passed"; break; 90 case engine::test_result::skipped: type = "skipped"; break; 91 default: 92 UNREACHABLE_MSG("Formatting unimplemented"); 93 } 94 _results.insert(F("%s:%s:%s:%s:%s:%s") % 95 iter.test_program()->absolute_path() % 96 iter.test_case_name() % type % iter.result().reason() % 97 iter.duration().seconds % iter.duration().useconds); 98 } 99 }; 100 101 102 /// Populates a test database with a new action. 103 /// 104 /// It is OK to call this function multiple times on the same file. Doing this 105 /// will generate a new action every time on the test database. 106 /// 107 /// \param db_name The database to update. 108 /// \param count A number that indicates how many elements to insert in the 109 /// action. Can be used to determine from the caller which particular 110 /// action has been loaded. 111 /// 112 /// \return The identifier of the committed action. 113 static int64_t 114 populate_db(const char* db_name, const int count) 115 { 116 store::backend backend = store::backend::open_rw(fs::path(db_name)); 117 118 store::transaction tx = backend.start(); 119 120 std::map< std::string, std::string > env; 121 for (int i = 0; i < count; i++) 122 env[F("VAR%s") % i] = F("Value %s") % i; 123 const engine::context context(fs::path("/root"), env); 124 const engine::action action(context); 125 const int64_t context_id = tx.put_context(context); 126 const int64_t action_id = tx.put_action(action, context_id); 127 128 for (int i = 0; i < count; i++) { 129 const engine::test_program test_program( 130 "plain", fs::path(F("dir/prog_%s") % i), fs::path("/root"), 131 F("suite_%s") % i, engine::metadata_builder().build()); 132 const int64_t tp_id = tx.put_test_program(test_program, action_id); 133 134 for (int j = 0; j < count; j++) { 135 const engine::test_case test_case( 136 "plain", test_program, "main", 137 engine::metadata_builder().build()); 138 const engine::test_result result(engine::test_result::skipped, 139 F("Count %s") % j); 140 const int64_t tc_id = tx.put_test_case(test_case, tp_id); 141 const datetime::timestamp start = 142 datetime::timestamp::from_microseconds(1000010); 143 const datetime::timestamp end = 144 datetime::timestamp::from_microseconds(5000020 + i + j); 145 tx.put_result(result, tc_id, start, end); 146 } 147 } 148 149 tx.commit(); 150 151 return action_id; 152 } 153 154 155 } // anonymous namespace 156 157 158 ATF_TEST_CASE_WITHOUT_HEAD(latest_action); 159 ATF_TEST_CASE_BODY(latest_action) 160 { 161 (void)populate_db("test.db", 3); 162 const int64_t action_id = populate_db("test.db", 2); 163 164 capture_hooks hooks; 165 scan_action::drive(fs::path("test.db"), none, hooks); 166 167 ATF_REQUIRE_EQ(action_id, hooks._action_id.get()); 168 169 std::map< std::string, std::string > env; 170 env["VAR0"] = "Value 0"; 171 env["VAR1"] = "Value 1"; 172 const engine::context context(fs::path("/root"), env); 173 const engine::action action(context); 174 ATF_REQUIRE(action == hooks._action.get()); 175 176 std::set< std::string > results; 177 results.insert("/root/dir/prog_0:main:skipped:Count 0:4:10"); 178 results.insert("/root/dir/prog_0:main:skipped:Count 1:4:11"); 179 results.insert("/root/dir/prog_1:main:skipped:Count 0:4:11"); 180 results.insert("/root/dir/prog_1:main:skipped:Count 1:4:12"); 181 ATF_REQUIRE(results == hooks._results); 182 } 183 184 185 ATF_TEST_CASE_WITHOUT_HEAD(explicit_action); 186 ATF_TEST_CASE_BODY(explicit_action) 187 { 188 (void)populate_db("test.db", 5); 189 const int64_t action_id = populate_db("test.db", 1); 190 (void)populate_db("test.db", 2); 191 192 capture_hooks hooks; 193 scan_action::drive(fs::path("test.db"), 194 optional< int64_t >(action_id), hooks); 195 196 ATF_REQUIRE_EQ(action_id, hooks._action_id.get()); 197 198 std::map< std::string, std::string > env; 199 env["VAR0"] = "Value 0"; 200 const engine::context context(fs::path("/root"), env); 201 const engine::action action(context); 202 ATF_REQUIRE(action == hooks._action.get()); 203 204 std::set< std::string > results; 205 results.insert("/root/dir/prog_0:main:skipped:Count 0:4:10"); 206 ATF_REQUIRE(results == hooks._results); 207 } 208 209 210 ATF_TEST_CASE_WITHOUT_HEAD(missing_db); 211 ATF_TEST_CASE_BODY(missing_db) 212 { 213 capture_hooks hooks; 214 ATF_REQUIRE_THROW(store::error, 215 scan_action::drive(fs::path("test.db"), none, hooks)); 216 } 217 218 219 ATF_INIT_TEST_CASES(tcs) 220 { 221 ATF_ADD_TEST_CASE(tcs, latest_action); 222 ATF_ADD_TEST_CASE(tcs, explicit_action); 223 ATF_ADD_TEST_CASE(tcs, missing_db); 224 } 225