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 46namespace datetime = utils::datetime; 47namespace fs = utils::fs; 48namespace scan_action = engine::drivers::scan_action; 49 50using utils::none; 51using utils::optional; 52 53 54namespace { 55 56 57/// Records the callback values for futher investigation. 58class capture_hooks : public scan_action::base_hooks { 59public: 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. 113static int64_t 114populate_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 158ATF_TEST_CASE_WITHOUT_HEAD(latest_action); 159ATF_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 185ATF_TEST_CASE_WITHOUT_HEAD(explicit_action); 186ATF_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 210ATF_TEST_CASE_WITHOUT_HEAD(missing_db); 211ATF_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 219ATF_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