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 52namespace datetime = utils::datetime; 53namespace fs = utils::fs; 54namespace logging = utils::logging; 55namespace sqlite = utils::sqlite; 56 57using utils::optional; 58 59 60namespace { 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. 71static void 72do_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 104ATF_TEST_CASE(commit__ok); 105ATF_TEST_CASE_HEAD(commit__ok) 106{ 107 logging::set_inmemory(); 108 set_md_var("require.files", store::detail::schema_file().c_str()); 109} 110ATF_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 122ATF_TEST_CASE(commit__fail); 123ATF_TEST_CASE_HEAD(commit__fail) 124{ 125 logging::set_inmemory(); 126 set_md_var("require.files", store::detail::schema_file().c_str()); 127} 128ATF_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 152ATF_TEST_CASE(rollback__ok); 153ATF_TEST_CASE_HEAD(rollback__ok) 154{ 155 logging::set_inmemory(); 156 set_md_var("require.files", store::detail::schema_file().c_str()); 157} 158ATF_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 171ATF_TEST_CASE(put_test_program__ok); 172ATF_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} 177ATF_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 211ATF_TEST_CASE(put_test_case__fail); 212ATF_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} 217ATF_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 232ATF_TEST_CASE(put_test_case_file__empty); 233ATF_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} 238ATF_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 257ATF_TEST_CASE(put_test_case_file__some); 258ATF_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} 263ATF_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 291ATF_TEST_CASE(put_test_case_file__fail); 292ATF_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} 297ATF_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 313ATF_TEST_CASE(put_result__ok__broken); 314ATF_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} 319ATF_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 326ATF_TEST_CASE(put_result__ok__expected_failure); 327ATF_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} 332ATF_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 340ATF_TEST_CASE(put_result__ok__failed); 341ATF_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} 346ATF_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 353ATF_TEST_CASE(put_result__ok__passed); 354ATF_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} 359ATF_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 366ATF_TEST_CASE(put_result__ok__skipped); 367ATF_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} 372ATF_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 379ATF_TEST_CASE(put_result__fail); 380ATF_TEST_CASE_HEAD(put_result__fail) 381{ 382 logging::set_inmemory(); 383 set_md_var("require.files", store::detail::schema_file().c_str()); 384} 385ATF_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 398ATF_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