1 // Copyright 2011 The Kyua Authors. 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/write_transaction.hpp" 30 31 #include <cstring> 32 #include <map> 33 #include <string> 34 35 #include <atf-c++.hpp> 36 37 #include "model/context.hpp" 38 #include "model/metadata.hpp" 39 #include "model/test_case.hpp" 40 #include "model/test_program.hpp" 41 #include "model/test_result.hpp" 42 #include "store/exceptions.hpp" 43 #include "store/write_backend.hpp" 44 #include "utils/datetime.hpp" 45 #include "utils/fs/path.hpp" 46 #include "utils/logging/operations.hpp" 47 #include "utils/optional.ipp" 48 #include "utils/sqlite/database.hpp" 49 #include "utils/sqlite/exceptions.hpp" 50 #include "utils/sqlite/statement.ipp" 51 52 namespace datetime = utils::datetime; 53 namespace fs = utils::fs; 54 namespace logging = utils::logging; 55 namespace sqlite = utils::sqlite; 56 57 using utils::optional; 58 59 60 namespace { 61 62 63 /// Performs a test for a working put_result 64 /// 65 /// \param result The result object to put. 66 /// \param result_type The textual name of the result to expect in the 67 /// database. 68 /// \param exp_reason The reason to expect in the database. This is separate 69 /// from the result parameter so that we can handle passed() here as well. 70 /// Just provide NULL in this case. 71 static void 72 do_put_result_ok_test(const model::test_result& result, 73 const char* result_type, const char* exp_reason) 74 { 75 store::write_backend backend = store::write_backend::open_rw( 76 fs::path("test.db")); 77 backend.database().exec("PRAGMA foreign_keys = OFF"); 78 store::write_transaction tx = backend.start_write(); 79 const datetime::timestamp start_time = datetime::timestamp::from_values( 80 2012, 01, 30, 22, 10, 00, 0); 81 const datetime::timestamp end_time = datetime::timestamp::from_values( 82 2012, 01, 30, 22, 15, 30, 123456); 83 tx.put_result(result, 312, start_time, end_time); 84 tx.commit(); 85 86 sqlite::statement stmt = backend.database().create_statement( 87 "SELECT test_case_id, result_type, result_reason " 88 "FROM test_results"); 89 90 ATF_REQUIRE(stmt.step()); 91 ATF_REQUIRE_EQ(312, stmt.column_int64(0)); 92 ATF_REQUIRE_EQ(result_type, stmt.column_text(1)); 93 if (exp_reason != NULL) 94 ATF_REQUIRE_EQ(exp_reason, stmt.column_text(2)); 95 else 96 ATF_REQUIRE(stmt.column_type(2) == sqlite::type_null); 97 ATF_REQUIRE(!stmt.step()); 98 } 99 100 101 } // anonymous namespace 102 103 104 ATF_TEST_CASE(commit__ok); 105 ATF_TEST_CASE_HEAD(commit__ok) 106 { 107 logging::set_inmemory(); 108 set_md_var("require.files", store::detail::schema_file().c_str()); 109 } 110 ATF_TEST_CASE_BODY(commit__ok) 111 { 112 store::write_backend backend = store::write_backend::open_rw( 113 fs::path("test.db")); 114 store::write_transaction tx = backend.start_write(); 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::write_backend backend = store::write_backend::open_rw( 131 fs::path("test.db")); 132 const model::context context(fs::path("/foo/bar"), 133 std::map< std::string, std::string >()); 134 { 135 store::write_transaction tx = backend.start_write(); 136 tx.put_context(context); 137 backend.database().exec( 138 "CREATE TABLE foo (" 139 "a REFERENCES env_vars(var_name) DEFERRABLE INITIALLY DEFERRED)"); 140 backend.database().exec("INSERT INTO foo VALUES (\"WHAT\")"); 141 ATF_REQUIRE_THROW(store::error, tx.commit()); 142 } 143 // If the code attempts to maintain any state regarding the already-put 144 // objects and the commit does not clean up correctly, this would fail in 145 // some manner. 146 store::write_transaction tx = backend.start_write(); 147 tx.put_context(context); 148 tx.commit(); 149 } 150 151 152 ATF_TEST_CASE(rollback__ok); 153 ATF_TEST_CASE_HEAD(rollback__ok) 154 { 155 logging::set_inmemory(); 156 set_md_var("require.files", store::detail::schema_file().c_str()); 157 } 158 ATF_TEST_CASE_BODY(rollback__ok) 159 { 160 store::write_backend backend = store::write_backend::open_rw( 161 fs::path("test.db")); 162 store::write_transaction tx = backend.start_write(); 163 backend.database().exec("CREATE TABLE a_table (b INTEGER PRIMARY KEY)"); 164 backend.database().exec("SELECT * FROM a_table"); 165 tx.rollback(); 166 ATF_REQUIRE_THROW_RE(sqlite::error, "a_table", 167 backend.database().exec("SELECT * FROM a_table")); 168 } 169 170 171 ATF_TEST_CASE(put_test_program__ok); 172 ATF_TEST_CASE_HEAD(put_test_program__ok) 173 { 174 logging::set_inmemory(); 175 set_md_var("require.files", store::detail::schema_file().c_str()); 176 } 177 ATF_TEST_CASE_BODY(put_test_program__ok) 178 { 179 const model::metadata md = model::metadata_builder() 180 .add_custom("var1", "value1") 181 .add_custom("var2", "value2") 182 .build(); 183 const model::test_program test_program( 184 "mock", fs::path("the/binary"), fs::path("/some//root"), 185 "the-suite", md, model::test_cases_map()); 186 187 store::write_backend backend = store::write_backend::open_rw( 188 fs::path("test.db")); 189 backend.database().exec("PRAGMA foreign_keys = OFF"); 190 store::write_transaction tx = backend.start_write(); 191 const int64_t test_program_id = tx.put_test_program(test_program); 192 tx.commit(); 193 194 { 195 sqlite::statement stmt = backend.database().create_statement( 196 "SELECT * FROM test_programs"); 197 198 ATF_REQUIRE(stmt.step()); 199 ATF_REQUIRE_EQ(test_program_id, 200 stmt.safe_column_int64("test_program_id")); 201 ATF_REQUIRE_EQ("/some/root/the/binary", 202 stmt.safe_column_text("absolute_path")); 203 ATF_REQUIRE_EQ("/some/root", stmt.safe_column_text("root")); 204 ATF_REQUIRE_EQ("the/binary", stmt.safe_column_text("relative_path")); 205 ATF_REQUIRE_EQ("the-suite", stmt.safe_column_text("test_suite_name")); 206 ATF_REQUIRE(!stmt.step()); 207 } 208 } 209 210 211 ATF_TEST_CASE(put_test_case__fail); 212 ATF_TEST_CASE_HEAD(put_test_case__fail) 213 { 214 logging::set_inmemory(); 215 set_md_var("require.files", store::detail::schema_file().c_str()); 216 } 217 ATF_TEST_CASE_BODY(put_test_case__fail) 218 { 219 const model::test_program test_program = model::test_program_builder( 220 "plain", fs::path("the/binary"), fs::path("/some/root"), "the-suite") 221 .add_test_case("main") 222 .build(); 223 224 store::write_backend backend = store::write_backend::open_rw( 225 fs::path("test.db")); 226 store::write_transaction tx = backend.start_write(); 227 ATF_REQUIRE_THROW(store::error, tx.put_test_case(test_program, "main", -1)); 228 tx.commit(); 229 } 230 231 232 ATF_TEST_CASE(put_test_case_file__empty); 233 ATF_TEST_CASE_HEAD(put_test_case_file__empty) 234 { 235 logging::set_inmemory(); 236 set_md_var("require.files", store::detail::schema_file().c_str()); 237 } 238 ATF_TEST_CASE_BODY(put_test_case_file__empty) 239 { 240 atf::utils::create_file("input.txt", ""); 241 242 store::write_backend backend = store::write_backend::open_rw( 243 fs::path("test.db")); 244 backend.database().exec("PRAGMA foreign_keys = OFF"); 245 store::write_transaction tx = backend.start_write(); 246 const optional< int64_t > file_id = tx.put_test_case_file( 247 "my-file", fs::path("input.txt"), 123L); 248 tx.commit(); 249 ATF_REQUIRE(!file_id); 250 251 sqlite::statement stmt = backend.database().create_statement( 252 "SELECT * FROM test_case_files NATURAL JOIN files"); 253 ATF_REQUIRE(!stmt.step()); 254 } 255 256 257 ATF_TEST_CASE(put_test_case_file__some); 258 ATF_TEST_CASE_HEAD(put_test_case_file__some) 259 { 260 logging::set_inmemory(); 261 set_md_var("require.files", store::detail::schema_file().c_str()); 262 } 263 ATF_TEST_CASE_BODY(put_test_case_file__some) 264 { 265 const char contents[] = "This is a test!"; 266 267 atf::utils::create_file("input.txt", contents); 268 269 store::write_backend backend = store::write_backend::open_rw( 270 fs::path("test.db")); 271 backend.database().exec("PRAGMA foreign_keys = OFF"); 272 store::write_transaction tx = backend.start_write(); 273 const optional< int64_t > file_id = tx.put_test_case_file( 274 "my-file", fs::path("input.txt"), 123L); 275 tx.commit(); 276 ATF_REQUIRE(file_id); 277 278 sqlite::statement stmt = backend.database().create_statement( 279 "SELECT * FROM test_case_files NATURAL JOIN files"); 280 281 ATF_REQUIRE(stmt.step()); 282 ATF_REQUIRE_EQ(123L, stmt.safe_column_int64("test_case_id")); 283 ATF_REQUIRE_EQ("my-file", stmt.safe_column_text("file_name")); 284 const sqlite::blob blob = stmt.safe_column_blob("contents"); 285 ATF_REQUIRE(std::strlen(contents) == static_cast< std::size_t >(blob.size)); 286 ATF_REQUIRE(std::memcmp(contents, blob.memory, blob.size) == 0); 287 ATF_REQUIRE(!stmt.step()); 288 } 289 290 291 ATF_TEST_CASE(put_test_case_file__fail); 292 ATF_TEST_CASE_HEAD(put_test_case_file__fail) 293 { 294 logging::set_inmemory(); 295 set_md_var("require.files", store::detail::schema_file().c_str()); 296 } 297 ATF_TEST_CASE_BODY(put_test_case_file__fail) 298 { 299 store::write_backend backend = store::write_backend::open_rw( 300 fs::path("test.db")); 301 backend.database().exec("PRAGMA foreign_keys = OFF"); 302 store::write_transaction tx = backend.start_write(); 303 ATF_REQUIRE_THROW(store::error, 304 tx.put_test_case_file("foo", fs::path("missing"), 1L)); 305 tx.commit(); 306 307 sqlite::statement stmt = backend.database().create_statement( 308 "SELECT * FROM test_case_files NATURAL JOIN files"); 309 ATF_REQUIRE(!stmt.step()); 310 } 311 312 313 ATF_TEST_CASE(put_result__ok__broken); 314 ATF_TEST_CASE_HEAD(put_result__ok__broken) 315 { 316 logging::set_inmemory(); 317 set_md_var("require.files", store::detail::schema_file().c_str()); 318 } 319 ATF_TEST_CASE_BODY(put_result__ok__broken) 320 { 321 const model::test_result result(model::test_result_broken, "a b cd"); 322 do_put_result_ok_test(result, "broken", "a b cd"); 323 } 324 325 326 ATF_TEST_CASE(put_result__ok__expected_failure); 327 ATF_TEST_CASE_HEAD(put_result__ok__expected_failure) 328 { 329 logging::set_inmemory(); 330 set_md_var("require.files", store::detail::schema_file().c_str()); 331 } 332 ATF_TEST_CASE_BODY(put_result__ok__expected_failure) 333 { 334 const model::test_result result(model::test_result_expected_failure, 335 "a b cd"); 336 do_put_result_ok_test(result, "expected_failure", "a b cd"); 337 } 338 339 340 ATF_TEST_CASE(put_result__ok__failed); 341 ATF_TEST_CASE_HEAD(put_result__ok__failed) 342 { 343 logging::set_inmemory(); 344 set_md_var("require.files", store::detail::schema_file().c_str()); 345 } 346 ATF_TEST_CASE_BODY(put_result__ok__failed) 347 { 348 const model::test_result result(model::test_result_failed, "a b cd"); 349 do_put_result_ok_test(result, "failed", "a b cd"); 350 } 351 352 353 ATF_TEST_CASE(put_result__ok__passed); 354 ATF_TEST_CASE_HEAD(put_result__ok__passed) 355 { 356 logging::set_inmemory(); 357 set_md_var("require.files", store::detail::schema_file().c_str()); 358 } 359 ATF_TEST_CASE_BODY(put_result__ok__passed) 360 { 361 const model::test_result result(model::test_result_passed); 362 do_put_result_ok_test(result, "passed", NULL); 363 } 364 365 366 ATF_TEST_CASE(put_result__ok__skipped); 367 ATF_TEST_CASE_HEAD(put_result__ok__skipped) 368 { 369 logging::set_inmemory(); 370 set_md_var("require.files", store::detail::schema_file().c_str()); 371 } 372 ATF_TEST_CASE_BODY(put_result__ok__skipped) 373 { 374 const model::test_result result(model::test_result_skipped, "a b cd"); 375 do_put_result_ok_test(result, "skipped", "a b cd"); 376 } 377 378 379 ATF_TEST_CASE(put_result__fail); 380 ATF_TEST_CASE_HEAD(put_result__fail) 381 { 382 logging::set_inmemory(); 383 set_md_var("require.files", store::detail::schema_file().c_str()); 384 } 385 ATF_TEST_CASE_BODY(put_result__fail) 386 { 387 const model::test_result result(model::test_result_broken, "foo"); 388 389 store::write_backend backend = store::write_backend::open_rw( 390 fs::path("test.db")); 391 store::write_transaction tx = backend.start_write(); 392 const datetime::timestamp zero = datetime::timestamp::from_microseconds(0); 393 ATF_REQUIRE_THROW(store::error, tx.put_result(result, -1, zero, zero)); 394 tx.commit(); 395 } 396 397 398 ATF_INIT_TEST_CASES(tcs) 399 { 400 ATF_ADD_TEST_CASE(tcs, commit__ok); 401 ATF_ADD_TEST_CASE(tcs, commit__fail); 402 ATF_ADD_TEST_CASE(tcs, rollback__ok); 403 404 ATF_ADD_TEST_CASE(tcs, put_test_program__ok); 405 ATF_ADD_TEST_CASE(tcs, put_test_case__fail); 406 ATF_ADD_TEST_CASE(tcs, put_test_case_file__empty); 407 ATF_ADD_TEST_CASE(tcs, put_test_case_file__some); 408 ATF_ADD_TEST_CASE(tcs, put_test_case_file__fail); 409 410 ATF_ADD_TEST_CASE(tcs, put_result__ok__broken); 411 ATF_ADD_TEST_CASE(tcs, put_result__ok__expected_failure); 412 ATF_ADD_TEST_CASE(tcs, put_result__ok__failed); 413 ATF_ADD_TEST_CASE(tcs, put_result__ok__passed); 414 ATF_ADD_TEST_CASE(tcs, put_result__ok__skipped); 415 ATF_ADD_TEST_CASE(tcs, put_result__fail); 416 } 417