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 extern "C" { 32 #include <stdint.h> 33 } 34 35 #include <fstream> 36 #include <map> 37 #include <utility> 38 39 #include "engine/action.hpp" 40 #include "engine/context.hpp" 41 #include "engine/test_result.hpp" 42 #include "store/backend.hpp" 43 #include "store/dbtypes.hpp" 44 #include "store/exceptions.hpp" 45 #include "utils/datetime.hpp" 46 #include "utils/defs.hpp" 47 #include "utils/format/macros.hpp" 48 #include "utils/logging/macros.hpp" 49 #include "utils/optional.ipp" 50 #include "utils/sanity.hpp" 51 #include "utils/stream.hpp" 52 #include "utils/sqlite/database.hpp" 53 #include "utils/sqlite/exceptions.hpp" 54 #include "utils/sqlite/statement.ipp" 55 #include "utils/sqlite/transaction.hpp" 56 #include "utils/units.hpp" 57 58 namespace datetime = utils::datetime; 59 namespace fs = utils::fs; 60 namespace sqlite = utils::sqlite; 61 namespace units = utils::units; 62 63 using utils::none; 64 using utils::optional; 65 66 67 namespace { 68 69 70 /// Retrieves the environment variables of a context. 71 /// 72 /// \param db The SQLite database. 73 /// \param context_id The identifier of the context. 74 /// 75 /// \return The environment variables of the specified context. 76 /// 77 /// \throw sqlite::error If there is a problem storing the variables. 78 static std::map< std::string, std::string > 79 get_env_vars(sqlite::database& db, const int64_t context_id) 80 { 81 std::map< std::string, std::string > env; 82 83 sqlite::statement stmt = db.create_statement( 84 "SELECT var_name, var_value FROM env_vars " 85 "WHERE context_id == :context_id"); 86 stmt.bind(":context_id", context_id); 87 88 while (stmt.step()) { 89 const std::string name = stmt.safe_column_text("var_name"); 90 const std::string value = stmt.safe_column_text("var_value"); 91 env[name] = value; 92 } 93 94 return env; 95 } 96 97 98 /// Retrieves a metadata object. 99 /// 100 /// \param db The SQLite database. 101 /// \param metadata_id The identifier of the metadata. 102 /// 103 /// \return A new metadata object. 104 static engine::metadata 105 get_metadata(sqlite::database& db, const int64_t metadata_id) 106 { 107 engine::metadata_builder builder; 108 109 sqlite::statement stmt = db.create_statement( 110 "SELECT * FROM metadatas WHERE metadata_id == :metadata_id"); 111 stmt.bind(":metadata_id", metadata_id); 112 while (stmt.step()) { 113 const std::string name = stmt.safe_column_text("property_name"); 114 const std::string value = stmt.safe_column_text("property_value"); 115 builder.set_string(name, value); 116 } 117 118 return builder.build(); 119 } 120 121 122 /// Gets a file from the database. 123 /// 124 /// \param db The database to query the file from. 125 /// \param file_id The identifier of the file to be queried. 126 /// 127 /// \return A textual representation of the file contents. 128 /// 129 /// \throw integrity_error If there is any problem in the loaded data or if the 130 /// file cannot be found. 131 static std::string 132 get_file(sqlite::database& db, const int64_t file_id) 133 { 134 sqlite::statement stmt = db.create_statement( 135 "SELECT contents FROM files WHERE file_id == :file_id"); 136 stmt.bind(":file_id", file_id); 137 if (!stmt.step()) 138 throw store::integrity_error(F("Cannot find referenced file %s") % 139 file_id); 140 141 try { 142 const sqlite::blob raw_contents = stmt.safe_column_blob("contents"); 143 const std::string contents( 144 static_cast< const char *>(raw_contents.memory), raw_contents.size); 145 146 const bool more = stmt.step(); 147 INV(!more); 148 149 return contents; 150 } catch (const sqlite::error& e) { 151 throw store::integrity_error(e.what()); 152 } 153 } 154 155 156 /// Gets all the test cases within a particular test program. 157 /// 158 /// \param db The database to query the information from. 159 /// \param test_program_id The identifier of the test program whose test cases 160 /// to query. 161 /// \param test_program The test program itself, needed to establish a binding 162 /// between the loaded test cases and the test program. 163 /// \param interface The interface type of the test cases to be loaded. This 164 /// assumes that all test cases within a test program share the same 165 /// interface, which is a pretty reasonable assumption. 166 /// 167 /// \return The collection of loaded test cases. 168 /// 169 /// \throw integrity_error If there is any problem in the loaded data. 170 static engine::test_cases_vector 171 get_test_cases(sqlite::database& db, const int64_t test_program_id, 172 const engine::test_program& test_program, 173 const std::string& interface) 174 { 175 engine::test_cases_vector test_cases; 176 177 sqlite::statement stmt = db.create_statement( 178 "SELECT name, metadata_id " 179 "FROM test_cases WHERE test_program_id == :test_program_id"); 180 stmt.bind(":test_program_id", test_program_id); 181 while (stmt.step()) { 182 const std::string name = stmt.safe_column_text("name"); 183 const int64_t metadata_id = stmt.safe_column_int64("metadata_id"); 184 185 const engine::metadata metadata = get_metadata(db, metadata_id); 186 engine::test_case_ptr test_case( 187 new engine::test_case(interface, test_program, name, metadata)); 188 LD(F("Loaded test case '%s'") % test_case->name()); 189 test_cases.push_back(test_case); 190 } 191 192 return test_cases; 193 } 194 195 196 /// Retrieves a result from the database. 197 /// 198 /// \param stmt The statement with the data for the result to load. 199 /// \param type_column The name of the column containing the type of the result. 200 /// \param reason_column The name of the column containing the reason for the 201 /// result, if any. 202 /// 203 /// \return The loaded result. 204 /// 205 /// \throw integrity_error If the data in the database is invalid. 206 static engine::test_result 207 parse_result(sqlite::statement& stmt, const char* type_column, 208 const char* reason_column) 209 { 210 using engine::test_result; 211 212 try { 213 const std::string type = stmt.safe_column_text(type_column); 214 if (type == "passed") { 215 if (stmt.column_type(stmt.column_id(reason_column)) != 216 sqlite::type_null) 217 throw store::integrity_error("Result of type 'passed' has a " 218 "non-NULL reason"); 219 return test_result(test_result::passed); 220 } else if (type == "broken") { 221 return test_result(test_result::broken, 222 stmt.safe_column_text(reason_column)); 223 } else if (type == "expected_failure") { 224 return test_result(test_result::expected_failure, 225 stmt.safe_column_text(reason_column)); 226 } else if (type == "failed") { 227 return test_result(test_result::failed, 228 stmt.safe_column_text(reason_column)); 229 } else if (type == "skipped") { 230 return test_result(test_result::skipped, 231 stmt.safe_column_text(reason_column)); 232 } else { 233 throw store::integrity_error(F("Unknown test result type %s") % 234 type); 235 } 236 } catch (const sqlite::error& e) { 237 throw store::integrity_error(e.what()); 238 } 239 } 240 241 242 /// Stores the environment variables of a context. 243 /// 244 /// \param db The SQLite database. 245 /// \param context_id The identifier of the context. 246 /// \param env The environment variables to store. 247 /// 248 /// \throw sqlite::error If there is a problem storing the variables. 249 static void 250 put_env_vars(sqlite::database& db, const int64_t context_id, 251 const std::map< std::string, std::string >& env) 252 { 253 sqlite::statement stmt = db.create_statement( 254 "INSERT INTO env_vars (context_id, var_name, var_value) " 255 "VALUES (:context_id, :var_name, :var_value)"); 256 stmt.bind(":context_id", context_id); 257 for (std::map< std::string, std::string >::const_iterator iter = 258 env.begin(); iter != env.end(); iter++) { 259 stmt.bind(":var_name", (*iter).first); 260 stmt.bind(":var_value", (*iter).second); 261 stmt.step_without_results(); 262 stmt.reset(); 263 } 264 } 265 266 267 /// Calculates the last rowid of a table. 268 /// 269 /// \param db The SQLite database. 270 /// \param table Name of the table. 271 /// 272 /// \return The last rowid; 0 if the table is empty. 273 static int64_t 274 last_rowid(sqlite::database& db, const std::string& table) 275 { 276 sqlite::statement stmt = db.create_statement( 277 F("SELECT MAX(ROWID) AS max_rowid FROM %s") % table); 278 stmt.step(); 279 if (stmt.column_type(0) == sqlite::type_null) { 280 return 0; 281 } else { 282 INV(stmt.column_type(0) == sqlite::type_integer); 283 return stmt.column_int64(0); 284 } 285 } 286 287 288 /// Stores a metadata object. 289 /// 290 /// \param db The database into which to store the information. 291 /// \param md The metadata to store. 292 /// 293 /// \return The identifier of the new metadata object. 294 static int64_t 295 put_metadata(sqlite::database& db, const engine::metadata& md) 296 { 297 const engine::properties_map props = md.to_properties(); 298 299 const int64_t metadata_id = last_rowid(db, "metadatas"); 300 301 sqlite::statement stmt = db.create_statement( 302 "INSERT INTO metadatas (metadata_id, property_name, property_value) " 303 "VALUES (:metadata_id, :property_name, :property_value)"); 304 stmt.bind(":metadata_id", metadata_id); 305 306 for (engine::properties_map::const_iterator iter = props.begin(); 307 iter != props.end(); ++iter) { 308 stmt.bind(":property_name", (*iter).first); 309 stmt.bind(":property_value", (*iter).second); 310 stmt.step_without_results(); 311 stmt.reset(); 312 } 313 314 return metadata_id; 315 } 316 317 318 /// Stores an arbitrary file into the database as a BLOB. 319 /// 320 /// \param db The database into which to store the file. 321 /// \param path Path to the file to be stored. 322 /// 323 /// \return The identifier of the stored file, or none if the file was empty. 324 /// 325 /// \throw sqlite::error If there are problems writing to the database. 326 static optional< int64_t > 327 put_file(sqlite::database& db, const fs::path& path) 328 { 329 std::ifstream input(path.c_str()); 330 if (!input) 331 throw store::error(F("Cannot open file %s") % path); 332 333 try { 334 if (utils::stream_length(input) == 0) 335 return none; 336 } catch (const std::runtime_error& e) { 337 // Skipping empty files is an optimization. If we fail to calculate the 338 // size of the file, just ignore the problem. If there are real issues 339 // with the file, the read below will fail anyway. 340 LD(F("Cannot determine if file is empty: %s") % e.what()); 341 } 342 343 // TODO(jmmv): This will probably cause an unreasonable amount of memory 344 // consumption if we decide to store arbitrary files in the database (other 345 // than stdout or stderr). Should this happen, we need to investigate a 346 // better way to feel blobs into SQLite. 347 const std::string contents = utils::read_stream(input); 348 349 sqlite::statement stmt = db.create_statement( 350 "INSERT INTO files (contents) VALUES (:contents)"); 351 stmt.bind(":contents", sqlite::blob(contents.c_str(), contents.length())); 352 stmt.step_without_results(); 353 354 return optional< int64_t >(db.last_insert_rowid()); 355 } 356 357 358 } // anonymous namespace 359 360 361 /// Loads a specific test program from the database. 362 /// 363 /// \param backend_ The store backend we are dealing with. 364 /// \param id The identifier of the test program to load. 365 /// 366 /// \return The instantiated test program. 367 /// 368 /// \throw integrity_error If the data read from the database cannot be properly 369 /// interpreted. 370 engine::test_program_ptr 371 store::detail::get_test_program(backend& backend_, const int64_t id) 372 { 373 sqlite::database& db = backend_.database(); 374 375 engine::test_program_ptr test_program; 376 sqlite::statement stmt = db.create_statement( 377 "SELECT * FROM test_programs WHERE test_program_id == :id"); 378 stmt.bind(":id", id); 379 stmt.step(); 380 const std::string interface = stmt.safe_column_text("interface"); 381 test_program.reset(new engine::test_program( 382 interface, 383 fs::path(stmt.safe_column_text("relative_path")), 384 fs::path(stmt.safe_column_text("root")), 385 stmt.safe_column_text("test_suite_name"), 386 get_metadata(db, stmt.safe_column_int64("metadata_id")))); 387 const bool more = stmt.step(); 388 INV(!more); 389 390 LD(F("Loaded test program '%s'; getting test cases") % 391 test_program->relative_path()); 392 test_program->set_test_cases(get_test_cases(db, id, *test_program, 393 interface)); 394 return test_program; 395 } 396 397 398 /// Internal implementation for a results iterator. 399 struct store::results_iterator::impl { 400 /// The store backend we are dealing with. 401 store::backend _backend; 402 403 /// The statement to iterate on. 404 sqlite::statement _stmt; 405 406 /// A cache for the last loaded test program. 407 optional< std::pair< int64_t, engine::test_program_ptr > > 408 _last_test_program; 409 410 /// Whether the iterator is still valid or not. 411 bool _valid; 412 413 /// Constructor. 414 impl(store::backend& backend_, const int64_t action_id_) : 415 _backend(backend_), 416 _stmt(backend_.database().create_statement( 417 "SELECT test_programs.test_program_id, " 418 " test_programs.interface, " 419 " test_cases.test_case_id, test_cases.name, " 420 " test_results.result_type, test_results.result_reason, " 421 " test_results.start_time, test_results.end_time " 422 "FROM test_programs " 423 " JOIN test_cases " 424 " ON test_programs.test_program_id = test_cases.test_program_id " 425 " JOIN test_results " 426 " ON test_cases.test_case_id = test_results.test_case_id " 427 "WHERE test_programs.action_id == :action_id " 428 "ORDER BY test_programs.absolute_path, test_cases.name")) 429 { 430 _stmt.bind(":action_id", action_id_); 431 _valid = _stmt.step(); 432 } 433 }; 434 435 436 /// Constructor. 437 /// 438 /// \param pimpl_ The internal implementation details of the iterator. 439 store::results_iterator::results_iterator( 440 std::shared_ptr< impl > pimpl_) : 441 _pimpl(pimpl_) 442 { 443 } 444 445 446 /// Destructor. 447 store::results_iterator::~results_iterator(void) 448 { 449 } 450 451 452 /// Moves the iterator forward by one result. 453 /// 454 /// \return The iterator itself. 455 store::results_iterator& 456 store::results_iterator::operator++(void) 457 { 458 _pimpl->_valid = _pimpl->_stmt.step(); 459 return *this; 460 } 461 462 463 /// Checks whether the iterator is still valid. 464 /// 465 /// \return True if there is more elements to iterate on, false otherwise. 466 store::results_iterator::operator bool(void) const 467 { 468 return _pimpl->_valid; 469 } 470 471 472 /// Gets the test program this result belongs to. 473 /// 474 /// \return The representation of a test program. 475 const engine::test_program_ptr 476 store::results_iterator::test_program(void) const 477 { 478 const int64_t id = _pimpl->_stmt.safe_column_int64("test_program_id"); 479 if (!_pimpl->_last_test_program || 480 _pimpl->_last_test_program.get().first != id) 481 { 482 const engine::test_program_ptr tp = detail::get_test_program( 483 _pimpl->_backend, id); 484 _pimpl->_last_test_program = std::make_pair(id, tp); 485 } 486 return _pimpl->_last_test_program.get().second; 487 } 488 489 490 /// Gets the name of the test case pointed by the iterator. 491 /// 492 /// The caller can look up the test case data by using the find() method on the 493 /// test program returned by test_program(). 494 /// 495 /// \return A test case name, unique within the test program. 496 std::string 497 store::results_iterator::test_case_name(void) const 498 { 499 return _pimpl->_stmt.safe_column_text("name"); 500 } 501 502 503 /// Gets the result of the test case pointed by the iterator. 504 /// 505 /// \return A test case result. 506 engine::test_result 507 store::results_iterator::result(void) const 508 { 509 return parse_result(_pimpl->_stmt, "result_type", "result_reason"); 510 } 511 512 513 /// Gets the duration of the test case execution. 514 /// 515 /// \return A time delta representing the run time of the test case. 516 datetime::delta 517 store::results_iterator::duration(void) const 518 { 519 const datetime::timestamp start_time = column_timestamp( 520 _pimpl->_stmt, "start_time"); 521 const datetime::timestamp end_time = column_timestamp( 522 _pimpl->_stmt, "end_time"); 523 return end_time - start_time; 524 } 525 526 527 /// Gets a file from a test case. 528 /// 529 /// \param db The database to query the file from. 530 /// \param test_case_id The identifier of the test case. 531 /// \param filename The name of the file to be retrieved. 532 /// 533 /// \return A textual representation of the file contents. 534 /// 535 /// \throw integrity_error If there is any problem in the loaded data or if the 536 /// file cannot be found. 537 static std::string 538 get_test_case_file(sqlite::database& db, const int64_t test_case_id, 539 const char* filename) 540 { 541 sqlite::statement stmt = db.create_statement( 542 "SELECT file_id FROM test_case_files " 543 "WHERE test_case_id == :test_case_id AND file_name == :file_name"); 544 stmt.bind(":test_case_id", test_case_id); 545 stmt.bind(":file_name", filename); 546 if (stmt.step()) 547 return get_file(db, stmt.safe_column_int64("file_id")); 548 else 549 return ""; 550 } 551 552 553 /// Gets the contents of stdout of a test case. 554 /// 555 /// \return A textual representation of the stdout contents of the test case. 556 /// This may of course be empty if the test case didn't print anything. 557 std::string 558 store::results_iterator::stdout_contents(void) const 559 { 560 return get_test_case_file(_pimpl->_backend.database(), 561 _pimpl->_stmt.safe_column_int64("test_case_id"), 562 "__STDOUT__"); 563 } 564 565 566 /// Gets the contents of stderr of a test case. 567 /// 568 /// \return A textual representation of the stderr contents of the test case. 569 /// This may of course be empty if the test case didn't print anything. 570 std::string 571 store::results_iterator::stderr_contents(void) const 572 { 573 return get_test_case_file(_pimpl->_backend.database(), 574 _pimpl->_stmt.safe_column_int64("test_case_id"), 575 "__STDERR__"); 576 } 577 578 579 /// Internal implementation for a store transaction. 580 struct store::transaction::impl { 581 /// The backend instance. 582 store::backend& _backend; 583 584 585 /// The SQLite database this transaction deals with. 586 sqlite::database _db; 587 588 /// The backing SQLite transaction. 589 sqlite::transaction _tx; 590 591 /// Opens a transaction. 592 /// 593 /// \param backend_ The backend this transaction is connected to. 594 impl(backend& backend_) : 595 _backend(backend_), 596 _db(backend_.database()), 597 _tx(backend_.database().begin_transaction()) 598 { 599 } 600 }; 601 602 603 /// Creates a new transaction. 604 /// 605 /// \param backend_ The backend this transaction belongs to. 606 store::transaction::transaction(backend& backend_) : 607 _pimpl(new impl(backend_)) 608 { 609 } 610 611 612 /// Destructor. 613 store::transaction::~transaction(void) 614 { 615 } 616 617 618 /// Commits the transaction. 619 /// 620 /// \throw error If there is any problem when talking to the database. 621 void 622 store::transaction::commit(void) 623 { 624 try { 625 _pimpl->_tx.commit(); 626 } catch (const sqlite::error& e) { 627 throw error(e.what()); 628 } 629 } 630 631 632 /// Rolls the transaction back. 633 /// 634 /// \throw error If there is any problem when talking to the database. 635 void 636 store::transaction::rollback(void) 637 { 638 try { 639 _pimpl->_tx.rollback(); 640 } catch (const sqlite::error& e) { 641 throw error(e.what()); 642 } 643 } 644 645 646 /// Retrieves an action from the database. 647 /// 648 /// \param action_id The identifier of the action to retrieve. 649 /// 650 /// \return The retrieved action. 651 /// 652 /// \throw error If there is a problem loading the action. 653 engine::action 654 store::transaction::get_action(const int64_t action_id) 655 { 656 try { 657 sqlite::statement stmt = _pimpl->_db.create_statement( 658 "SELECT context_id FROM actions WHERE action_id == :action_id"); 659 stmt.bind(":action_id", action_id); 660 if (!stmt.step()) 661 throw error(F("Error loading action %s: does not exist") % 662 action_id); 663 664 return engine::action( 665 get_context(stmt.safe_column_int64("context_id"))); 666 } catch (const sqlite::error& e) { 667 throw error(F("Error loading action %s: %s") % action_id % e.what()); 668 } 669 } 670 671 672 /// Creates a new iterator to scan the test results of an action. 673 /// 674 /// \param action_id The identifier of the action for which to get the results. 675 /// 676 /// \return The constructed iterator. 677 /// 678 /// \throw error If there is any problem constructing the iterator. 679 store::results_iterator 680 store::transaction::get_action_results(const int64_t action_id) 681 { 682 try { 683 return results_iterator(std::shared_ptr< results_iterator::impl >( 684 new results_iterator::impl(_pimpl->_backend, action_id))); 685 } catch (const sqlite::error& e) { 686 throw error(e.what()); 687 } 688 } 689 690 691 /// Retrieves the latest action from the database. 692 /// 693 /// \return The retrieved action. 694 /// 695 /// \throw error If there is a problem loading the action. 696 std::pair< int64_t, engine::action > 697 store::transaction::get_latest_action(void) 698 { 699 try { 700 sqlite::statement stmt = _pimpl->_db.create_statement( 701 "SELECT action_id, context_id FROM actions WHERE " 702 "action_id == (SELECT max(action_id) FROM actions)"); 703 if (!stmt.step()) 704 throw error("No actions in the database"); 705 706 const int64_t action_id = stmt.safe_column_int64("action_id"); 707 const engine::context context = get_context( 708 stmt.safe_column_int64("context_id")); 709 710 return std::pair< int64_t, engine::action >( 711 action_id, engine::action(context)); 712 } catch (const sqlite::error& e) { 713 throw error(F("Error loading latest action: %s") % e.what()); 714 } 715 } 716 717 718 /// Retrieves an context from the database. 719 /// 720 /// \param context_id The identifier of the context to retrieve. 721 /// 722 /// \return The retrieved context. 723 /// 724 /// \throw error If there is a problem loading the context. 725 engine::context 726 store::transaction::get_context(const int64_t context_id) 727 { 728 try { 729 sqlite::statement stmt = _pimpl->_db.create_statement( 730 "SELECT cwd FROM contexts WHERE context_id == :context_id"); 731 stmt.bind(":context_id", context_id); 732 if (!stmt.step()) 733 throw error(F("Error loading context %s: does not exist") % 734 context_id); 735 736 return engine::context(fs::path(stmt.safe_column_text("cwd")), 737 get_env_vars(_pimpl->_db, context_id)); 738 } catch (const sqlite::error& e) { 739 throw error(F("Error loading context %s: %s") % context_id % e.what()); 740 } 741 } 742 743 744 /// Puts an action into the database. 745 /// 746 /// \pre The action has not been put yet. 747 /// \pre The dependent objects have already been put. 748 /// \post The action is stored into the database with a new identifier. 749 /// 750 /// \param unused_action The action to put. 751 /// \param context_id The identifier for the action's context. 752 /// 753 /// \return The identifier of the inserted action. 754 /// 755 /// \throw error If there is any problem when talking to the database. 756 int64_t 757 store::transaction::put_action(const engine::action& UTILS_UNUSED_PARAM(action), 758 const int64_t context_id) 759 { 760 try { 761 sqlite::statement stmt = _pimpl->_db.create_statement( 762 "INSERT INTO actions (context_id) VALUES (:context_id)"); 763 stmt.bind(":context_id", context_id); 764 stmt.step_without_results(); 765 const int64_t action_id = _pimpl->_db.last_insert_rowid(); 766 767 return action_id; 768 } catch (const sqlite::error& e) { 769 throw error(e.what()); 770 } 771 } 772 773 774 /// Puts a context into the database. 775 /// 776 /// \pre The context has not been put yet. 777 /// \post The context is stored into the database with a new identifier. 778 /// 779 /// \param context The context to put. 780 /// 781 /// \return The identifier of the inserted context. 782 /// 783 /// \throw error If there is any problem when talking to the database. 784 int64_t 785 store::transaction::put_context(const engine::context& context) 786 { 787 try { 788 sqlite::statement stmt = _pimpl->_db.create_statement( 789 "INSERT INTO contexts (cwd) VALUES (:cwd)"); 790 stmt.bind(":cwd", context.cwd().str()); 791 stmt.step_without_results(); 792 const int64_t context_id = _pimpl->_db.last_insert_rowid(); 793 794 put_env_vars(_pimpl->_db, context_id, context.env()); 795 796 return context_id; 797 } catch (const sqlite::error& e) { 798 throw error(e.what()); 799 } 800 } 801 802 803 /// Puts a test program into the database. 804 /// 805 /// \pre The test program has not been put yet. 806 /// \post The test program is stored into the database with a new identifier. 807 /// 808 /// \param test_program The test program to put. 809 /// \param action_id The action this test program belongs to. 810 /// 811 /// \return The identifier of the inserted test program. 812 /// 813 /// \throw error If there is any problem when talking to the database. 814 int64_t 815 store::transaction::put_test_program(const engine::test_program& test_program, 816 const int64_t action_id) 817 { 818 try { 819 const int64_t metadata_id = put_metadata( 820 _pimpl->_db, test_program.get_metadata()); 821 822 sqlite::statement stmt = _pimpl->_db.create_statement( 823 "INSERT INTO test_programs (action_id, absolute_path, " 824 " root, relative_path, test_suite_name, " 825 " metadata_id, interface) " 826 "VALUES (:action_id, :absolute_path, :root, :relative_path, " 827 " :test_suite_name, :metadata_id, :interface)"); 828 stmt.bind(":action_id", action_id); 829 stmt.bind(":absolute_path", test_program.absolute_path().str()); 830 // TODO(jmmv): The root is not necessarily absolute. We need to ensure 831 // that we can recover the absolute path of the test program. Maybe we 832 // need to change the test_program to always ensure root is absolute? 833 stmt.bind(":root", test_program.root().str()); 834 stmt.bind(":relative_path", test_program.relative_path().str()); 835 stmt.bind(":test_suite_name", test_program.test_suite_name()); 836 stmt.bind(":metadata_id", metadata_id); 837 stmt.bind(":interface", test_program.interface_name()); 838 stmt.step_without_results(); 839 return _pimpl->_db.last_insert_rowid(); 840 } catch (const sqlite::error& e) { 841 throw error(e.what()); 842 } 843 } 844 845 846 /// Puts a test case into the database. 847 /// 848 /// \pre The test case has not been put yet. 849 /// \post The test case is stored into the database with a new identifier. 850 /// 851 /// \param test_case The test case to put. 852 /// \param test_program_id The test program this test case belongs to. 853 /// 854 /// \return The identifier of the inserted test case. 855 /// 856 /// \throw error If there is any problem when talking to the database. 857 int64_t 858 store::transaction::put_test_case(const engine::test_case& test_case, 859 const int64_t test_program_id) 860 { 861 try { 862 const int64_t metadata_id = put_metadata( 863 _pimpl->_db, test_case.get_metadata()); 864 865 sqlite::statement stmt = _pimpl->_db.create_statement( 866 "INSERT INTO test_cases (test_program_id, name, metadata_id) " 867 "VALUES (:test_program_id, :name, :metadata_id)"); 868 stmt.bind(":test_program_id", test_program_id); 869 stmt.bind(":name", test_case.name()); 870 stmt.bind(":metadata_id", metadata_id); 871 stmt.step_without_results(); 872 return _pimpl->_db.last_insert_rowid(); 873 } catch (const sqlite::error& e) { 874 throw error(e.what()); 875 } 876 } 877 878 879 /// Stores a file generated by a test case into the database as a BLOB. 880 /// 881 /// \param name The name of the file to store in the database. This needs to be 882 /// unique per test case. The caller is free to decide what names to use 883 /// for which files. For example, it might make sense to always call 884 /// __STDOUT__ the stdout of the test case so that it is easy to locate. 885 /// \param path The path to the file to be stored. 886 /// \param test_case_id The identifier of the test case this file belongs to. 887 /// 888 /// \return The identifier of the stored file, or none if the file was empty. 889 /// 890 /// \throw store::error If there are problems writing to the database. 891 optional< int64_t > 892 store::transaction::put_test_case_file(const std::string& name, 893 const fs::path& path, 894 const int64_t test_case_id) 895 { 896 LD(F("Storing %s (%s) of test case %s") % name % path % test_case_id); 897 try { 898 const optional< int64_t > file_id = put_file(_pimpl->_db, path); 899 if (!file_id) { 900 LD("Not storing empty file"); 901 return none; 902 } 903 904 sqlite::statement stmt = _pimpl->_db.create_statement( 905 "INSERT INTO test_case_files (test_case_id, file_name, file_id) " 906 "VALUES (:test_case_id, :file_name, :file_id)"); 907 stmt.bind(":test_case_id", test_case_id); 908 stmt.bind(":file_name", name); 909 stmt.bind(":file_id", file_id.get()); 910 stmt.step_without_results(); 911 912 return optional< int64_t >(_pimpl->_db.last_insert_rowid()); 913 } catch (const sqlite::error& e) { 914 throw error(e.what()); 915 } 916 } 917 918 919 /// Puts a result into the database. 920 /// 921 /// \pre The result has not been put yet. 922 /// \post The result is stored into the database with a new identifier. 923 /// 924 /// \param result The result to put. 925 /// \param test_case_id The test case this result corresponds to. 926 /// \param start_time The time when the test started to run. 927 /// \param end_time The time when the test finished running. 928 /// 929 /// \return The identifier of the inserted result. 930 /// 931 /// \throw error If there is any problem when talking to the database. 932 int64_t 933 store::transaction::put_result(const engine::test_result& result, 934 const int64_t test_case_id, 935 const datetime::timestamp& start_time, 936 const datetime::timestamp& end_time) 937 { 938 try { 939 sqlite::statement stmt = _pimpl->_db.create_statement( 940 "INSERT INTO test_results (test_case_id, result_type, " 941 " result_reason, start_time, " 942 " end_time) " 943 "VALUES (:test_case_id, :result_type, :result_reason, " 944 " :start_time, :end_time)"); 945 stmt.bind(":test_case_id", test_case_id); 946 947 switch (result.type()) { 948 case engine::test_result::broken: 949 stmt.bind(":result_type", "broken"); 950 break; 951 952 case engine::test_result::expected_failure: 953 stmt.bind(":result_type", "expected_failure"); 954 break; 955 956 case engine::test_result::failed: 957 stmt.bind(":result_type", "failed"); 958 break; 959 960 case engine::test_result::passed: 961 stmt.bind(":result_type", "passed"); 962 break; 963 964 case engine::test_result::skipped: 965 stmt.bind(":result_type", "skipped"); 966 break; 967 968 default: 969 UNREACHABLE; 970 } 971 972 if (result.reason().empty()) 973 stmt.bind(":result_reason", sqlite::null()); 974 else 975 stmt.bind(":result_reason", result.reason()); 976 977 store::bind_timestamp(stmt, ":start_time", start_time); 978 store::bind_timestamp(stmt, ":end_time", end_time); 979 980 stmt.step_without_results(); 981 const int64_t result_id = _pimpl->_db.last_insert_rowid(); 982 983 return result_id; 984 } catch (const sqlite::error& e) { 985 throw error(e.what()); 986 } 987 } 988