Path: blob/main/contrib/kyua/store/schema_inttest.cpp
39478 views
// Copyright 2013 The Kyua Authors.1// All rights reserved.2//3// Redistribution and use in source and binary forms, with or without4// modification, are permitted provided that the following conditions are5// met:6//7// * Redistributions of source code must retain the above copyright8// notice, this list of conditions and the following disclaimer.9// * Redistributions in binary form must reproduce the above copyright10// notice, this list of conditions and the following disclaimer in the11// documentation and/or other materials provided with the distribution.12// * Neither the name of Google Inc. nor the names of its contributors13// may be used to endorse or promote products derived from this software14// without specific prior written permission.15//16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.2728#include <map>2930#include <atf-c++.hpp>3132#include "model/context.hpp"33#include "model/metadata.hpp"34#include "model/test_program.hpp"35#include "model/test_result.hpp"36#include "store/migrate.hpp"37#include "store/read_backend.hpp"38#include "store/read_transaction.hpp"39#include "store/write_backend.hpp"40#include "utils/datetime.hpp"41#include "utils/env.hpp"42#include "utils/format/macros.hpp"43#include "utils/fs/path.hpp"44#include "utils/logging/operations.hpp"45#include "utils/sqlite/database.hpp"46#include "utils/stream.hpp"47#include "utils/units.hpp"4849namespace datetime = utils::datetime;50namespace fs = utils::fs;51namespace logging = utils::logging;52namespace sqlite = utils::sqlite;53namespace units = utils::units;545556namespace {575859/// Gets a data file from the tests directory.60///61/// We cannot use the srcdir property because the required files are not there62/// when building with an object directory. In those cases, the data files63/// remainin the source directory while the resulting test program is in the64/// object directory, thus having the wrong value for its srcdir property.65///66/// \param name Basename of the test data file to query.67///68/// \return The actual path to the requested data file.69static fs::path70testdata_file(const std::string& name)71{72const fs::path testdatadir(utils::getenv_with_default(73"KYUA_STORETESTDATADIR", KYUA_STORETESTDATADIR));74return testdatadir / name;75}767778/// Validates the contents of the action with identifier 1.79///80/// \param dbpath Path to the database in which to check the action contents.81static void82check_action_1(const fs::path& dbpath)83{84store::read_backend backend = store::read_backend::open_ro(dbpath);85store::read_transaction transaction = backend.start_read();8687const fs::path root("/some/root");88std::map< std::string, std::string > environment;89const model::context context(root, environment);9091ATF_REQUIRE_EQ(context, transaction.get_context());9293store::results_iterator iter = transaction.get_results();94ATF_REQUIRE(!iter);95}969798/// Validates the contents of the action with identifier 2.99///100/// \param dbpath Path to the database in which to check the action contents.101static void102check_action_2(const fs::path& dbpath)103{104store::read_backend backend = store::read_backend::open_ro(dbpath);105store::read_transaction transaction = backend.start_read();106107const fs::path root("/test/suite/root");108std::map< std::string, std::string > environment;109environment["HOME"] = "/home/test";110environment["PATH"] = "/bin:/usr/bin";111const model::context context(root, environment);112113ATF_REQUIRE_EQ(context, transaction.get_context());114115const model::test_program test_program_1 = model::test_program_builder(116"plain", fs::path("foo_test"), fs::path("/test/suite/root"),117"suite-name")118.add_test_case("main")119.build();120const model::test_result result_1(model::test_result_passed);121122const model::test_program test_program_2 = model::test_program_builder(123"plain", fs::path("subdir/another_test"), fs::path("/test/suite/root"),124"subsuite-name")125.add_test_case("main",126model::metadata_builder()127.set_timeout(datetime::delta(10, 0))128.build())129.set_metadata(model::metadata_builder()130.set_timeout(datetime::delta(10, 0))131.build())132.build();133const model::test_result result_2(model::test_result_failed,134"Exited with code 1");135136const model::test_program test_program_3 = model::test_program_builder(137"plain", fs::path("subdir/bar_test"), fs::path("/test/suite/root"),138"subsuite-name")139.add_test_case("main")140.build();141const model::test_result result_3(model::test_result_broken,142"Received signal 1");143144const model::test_program test_program_4 = model::test_program_builder(145"plain", fs::path("top_test"), fs::path("/test/suite/root"),146"suite-name")147.add_test_case("main")148.build();149const model::test_result result_4(model::test_result_expected_failure,150"Known bug");151152const model::test_program test_program_5 = model::test_program_builder(153"plain", fs::path("last_test"), fs::path("/test/suite/root"),154"suite-name")155.add_test_case("main")156.build();157const model::test_result result_5(model::test_result_skipped,158"Does not apply");159160store::results_iterator iter = transaction.get_results();161ATF_REQUIRE(iter);162ATF_REQUIRE_EQ(test_program_1, *iter.test_program());163ATF_REQUIRE_EQ("main", iter.test_case_name());164ATF_REQUIRE_EQ(result_1, iter.result());165ATF_REQUIRE(iter.stdout_contents().empty());166ATF_REQUIRE(iter.stderr_contents().empty());167ATF_REQUIRE_EQ(1357643611000000LL, iter.start_time().to_microseconds());168ATF_REQUIRE_EQ(1357643621000500LL, iter.end_time().to_microseconds());169170++iter;171ATF_REQUIRE(iter);172ATF_REQUIRE_EQ(test_program_5, *iter.test_program());173ATF_REQUIRE_EQ("main", iter.test_case_name());174ATF_REQUIRE_EQ(result_5, iter.result());175ATF_REQUIRE(iter.stdout_contents().empty());176ATF_REQUIRE(iter.stderr_contents().empty());177ATF_REQUIRE_EQ(1357643632000000LL, iter.start_time().to_microseconds());178ATF_REQUIRE_EQ(1357643638000000LL, iter.end_time().to_microseconds());179180++iter;181ATF_REQUIRE(iter);182ATF_REQUIRE_EQ(test_program_2, *iter.test_program());183ATF_REQUIRE_EQ("main", iter.test_case_name());184ATF_REQUIRE_EQ(result_2, iter.result());185ATF_REQUIRE_EQ("Test stdout", iter.stdout_contents());186ATF_REQUIRE_EQ("Test stderr", iter.stderr_contents());187ATF_REQUIRE_EQ(1357643622001200LL, iter.start_time().to_microseconds());188ATF_REQUIRE_EQ(1357643622900021LL, iter.end_time().to_microseconds());189190++iter;191ATF_REQUIRE(iter);192ATF_REQUIRE_EQ(test_program_3, *iter.test_program());193ATF_REQUIRE_EQ("main", iter.test_case_name());194ATF_REQUIRE_EQ(result_3, iter.result());195ATF_REQUIRE(iter.stdout_contents().empty());196ATF_REQUIRE(iter.stderr_contents().empty());197ATF_REQUIRE_EQ(1357643623500000LL, iter.start_time().to_microseconds());198ATF_REQUIRE_EQ(1357643630981932LL, iter.end_time().to_microseconds());199200++iter;201ATF_REQUIRE(iter);202ATF_REQUIRE_EQ(test_program_4, *iter.test_program());203ATF_REQUIRE_EQ("main", iter.test_case_name());204ATF_REQUIRE_EQ(result_4, iter.result());205ATF_REQUIRE(iter.stdout_contents().empty());206ATF_REQUIRE(iter.stderr_contents().empty());207ATF_REQUIRE_EQ(1357643631000000LL, iter.start_time().to_microseconds());208ATF_REQUIRE_EQ(1357643631020000LL, iter.end_time().to_microseconds());209210++iter;211ATF_REQUIRE(!iter);212}213214215/// Validates the contents of the action with identifier 3.216///217/// \param dbpath Path to the database in which to check the action contents.218static void219check_action_3(const fs::path& dbpath)220{221store::read_backend backend = store::read_backend::open_ro(dbpath);222store::read_transaction transaction = backend.start_read();223224const fs::path root("/usr/tests");225std::map< std::string, std::string > environment;226environment["PATH"] = "/bin:/usr/bin";227const model::context context(root, environment);228229ATF_REQUIRE_EQ(context, transaction.get_context());230231const model::test_program test_program_6 = model::test_program_builder(232"atf", fs::path("complex_test"), fs::path("/usr/tests"),233"suite-name")234.add_test_case("this_passes")235.add_test_case("this_fails",236model::metadata_builder()237.set_description("Test description")238.set_has_cleanup(true)239.set_required_memory(units::bytes(128))240.set_required_user("root")241.build())242.add_test_case("this_skips",243model::metadata_builder()244.add_allowed_architecture("powerpc")245.add_allowed_architecture("x86_64")246.add_allowed_platform("amd64")247.add_allowed_platform("macppc")248.add_required_config("X-foo")249.add_required_config("unprivileged_user")250.add_required_file(fs::path("/the/data/file"))251.add_required_program(fs::path("/bin/ls"))252.add_required_program(fs::path("cp"))253.set_description("Test explanation")254.set_has_cleanup(true)255.set_required_memory(units::bytes(512))256.set_required_user("unprivileged")257.set_timeout(datetime::delta(600, 0))258.build())259.build();260const model::test_result result_6(model::test_result_passed);261const model::test_result result_7(model::test_result_failed,262"Some reason");263const model::test_result result_8(model::test_result_skipped,264"Another reason");265266const model::test_program test_program_7 = model::test_program_builder(267"atf", fs::path("simple_test"), fs::path("/usr/tests"),268"subsuite-name")269.add_test_case("main",270model::metadata_builder()271.set_description("More text")272.set_has_cleanup(true)273.set_required_memory(units::bytes(128))274.set_required_user("unprivileged")275.build())276.build();277const model::test_result result_9(model::test_result_failed,278"Exited with code 1");279280store::results_iterator iter = transaction.get_results();281ATF_REQUIRE(iter);282ATF_REQUIRE_EQ(test_program_6, *iter.test_program());283ATF_REQUIRE_EQ("this_fails", iter.test_case_name());284ATF_REQUIRE_EQ(result_7, iter.result());285ATF_REQUIRE(iter.stdout_contents().empty());286ATF_REQUIRE(iter.stderr_contents().empty());287ATF_REQUIRE_EQ(1357648719000000LL, iter.start_time().to_microseconds());288ATF_REQUIRE_EQ(1357648720897182LL, iter.end_time().to_microseconds());289290++iter;291ATF_REQUIRE(iter);292ATF_REQUIRE_EQ(test_program_6, *iter.test_program());293ATF_REQUIRE_EQ("this_passes", iter.test_case_name());294ATF_REQUIRE_EQ(result_6, iter.result());295ATF_REQUIRE(iter.stdout_contents().empty());296ATF_REQUIRE(iter.stderr_contents().empty());297ATF_REQUIRE_EQ(1357648712000000LL, iter.start_time().to_microseconds());298ATF_REQUIRE_EQ(1357648718000000LL, iter.end_time().to_microseconds());299300++iter;301ATF_REQUIRE(iter);302ATF_REQUIRE_EQ(test_program_6, *iter.test_program());303ATF_REQUIRE_EQ("this_skips", iter.test_case_name());304ATF_REQUIRE_EQ(result_8, iter.result());305ATF_REQUIRE_EQ("Another stdout", iter.stdout_contents());306ATF_REQUIRE(iter.stderr_contents().empty());307ATF_REQUIRE_EQ(1357648729182013LL, iter.start_time().to_microseconds());308ATF_REQUIRE_EQ(1357648730000000LL, iter.end_time().to_microseconds());309310++iter;311ATF_REQUIRE(iter);312ATF_REQUIRE_EQ(test_program_7, *iter.test_program());313ATF_REQUIRE_EQ("main", iter.test_case_name());314ATF_REQUIRE_EQ(result_9, iter.result());315ATF_REQUIRE(iter.stdout_contents().empty());316ATF_REQUIRE_EQ("Another stderr", iter.stderr_contents());317ATF_REQUIRE_EQ(1357648740120000LL, iter.start_time().to_microseconds());318ATF_REQUIRE_EQ(1357648750081700LL, iter.end_time().to_microseconds());319320++iter;321ATF_REQUIRE(!iter);322}323324325/// Validates the contents of the action with identifier 4.326///327/// \param dbpath Path to the database in which to check the action contents.328static void329check_action_4(const fs::path& dbpath)330{331store::read_backend backend = store::read_backend::open_ro(dbpath);332store::read_transaction transaction = backend.start_read();333334const fs::path root("/usr/tests");335std::map< std::string, std::string > environment;336environment["LANG"] = "C";337environment["PATH"] = "/bin:/usr/bin";338environment["TERM"] = "xterm";339const model::context context(root, environment);340341ATF_REQUIRE_EQ(context, transaction.get_context());342343const model::test_program test_program_8 = model::test_program_builder(344"plain", fs::path("subdir/another_test"), fs::path("/usr/tests"),345"subsuite-name")346.add_test_case("main",347model::metadata_builder()348.set_timeout(datetime::delta(10, 0))349.build())350.set_metadata(model::metadata_builder()351.set_timeout(datetime::delta(10, 0))352.build())353.build();354const model::test_result result_10(model::test_result_failed,355"Exit failure");356357const model::test_program test_program_9 = model::test_program_builder(358"atf", fs::path("complex_test"), fs::path("/usr/tests"),359"suite-name")360.add_test_case("this_passes")361.add_test_case("this_fails",362model::metadata_builder()363.set_description("Test description")364.set_required_user("root")365.build())366.build();367const model::test_result result_11(model::test_result_passed);368const model::test_result result_12(model::test_result_failed,369"Some reason");370371store::results_iterator iter = transaction.get_results();372ATF_REQUIRE(iter);373ATF_REQUIRE_EQ(test_program_9, *iter.test_program());374ATF_REQUIRE_EQ("this_fails", iter.test_case_name());375ATF_REQUIRE_EQ(result_12, iter.result());376ATF_REQUIRE(iter.stdout_contents().empty());377ATF_REQUIRE(iter.stderr_contents().empty());378ATF_REQUIRE_EQ(1357644397100000LL, iter.start_time().to_microseconds());379ATF_REQUIRE_EQ(1357644399005000LL, iter.end_time().to_microseconds());380381++iter;382ATF_REQUIRE(iter);383ATF_REQUIRE_EQ(test_program_9, *iter.test_program());384ATF_REQUIRE_EQ("this_passes", iter.test_case_name());385ATF_REQUIRE_EQ(result_11, iter.result());386ATF_REQUIRE(iter.stdout_contents().empty());387ATF_REQUIRE(iter.stderr_contents().empty());388ATF_REQUIRE_EQ(1357644396500000LL, iter.start_time().to_microseconds());389ATF_REQUIRE_EQ(1357644397000000LL, iter.end_time().to_microseconds());390391++iter;392ATF_REQUIRE(iter);393ATF_REQUIRE_EQ(test_program_8, *iter.test_program());394ATF_REQUIRE_EQ("main", iter.test_case_name());395ATF_REQUIRE_EQ(result_10, iter.result());396ATF_REQUIRE_EQ("Test stdout", iter.stdout_contents());397ATF_REQUIRE_EQ("Test stderr", iter.stderr_contents());398ATF_REQUIRE_EQ(1357644395000000LL, iter.start_time().to_microseconds());399ATF_REQUIRE_EQ(1357644396000000LL, iter.end_time().to_microseconds());400401++iter;402ATF_REQUIRE(!iter);403}404405406} // anonymous namespace407408409#define CURRENT_SCHEMA_TEST(dataset) \410ATF_TEST_CASE(current_schema_ ##dataset); \411ATF_TEST_CASE_HEAD(current_schema_ ##dataset) \412{ \413logging::set_inmemory(); \414const std::string required_files = \415store::detail::schema_file().str() + " " + \416testdata_file("testdata_v3_" #dataset ".sql").str(); \417set_md_var("require.files", required_files); \418} \419ATF_TEST_CASE_BODY(current_schema_ ##dataset) \420{ \421const fs::path testpath("test.db"); \422\423sqlite::database db = sqlite::database::open( \424testpath, sqlite::open_readwrite | sqlite::open_create); \425db.exec(utils::read_file(store::detail::schema_file())); \426db.exec(utils::read_file(testdata_file(\427"testdata_v3_" #dataset ".sql"))); \428db.close(); \429\430check_action_ ## dataset (testpath); \431}432CURRENT_SCHEMA_TEST(1);433CURRENT_SCHEMA_TEST(2);434CURRENT_SCHEMA_TEST(3);435CURRENT_SCHEMA_TEST(4);436437438#define MIGRATE_SCHEMA_TEST(from_version) \439ATF_TEST_CASE(migrate_schema__from_v ##from_version); \440ATF_TEST_CASE_HEAD(migrate_schema__from_v ##from_version) \441{ \442logging::set_inmemory(); \443\444const char* schema = "schema_v" #from_version ".sql"; \445const char* testdata = "testdata_v" #from_version ".sql"; \446\447std::string required_files = \448testdata_file(schema).str() + " " + testdata_file(testdata).str(); \449for (int i = from_version; i < store::detail::current_schema_version; \450++i) \451required_files += " " + store::detail::migration_file( \452i, i + 1).str(); \453\454set_md_var("require.files", required_files); \455} \456ATF_TEST_CASE_BODY(migrate_schema__from_v ##from_version) \457{ \458const char* schema = "schema_v" #from_version ".sql"; \459const char* testdata = "testdata_v" #from_version ".sql"; \460\461const fs::path testpath("test.db"); \462\463sqlite::database db = sqlite::database::open( \464testpath, sqlite::open_readwrite | sqlite::open_create); \465db.exec(utils::read_file(testdata_file(schema))); \466db.exec(utils::read_file(testdata_file(testdata))); \467db.close(); \468\469store::migrate_schema(fs::path("test.db")); \470\471check_action_2(fs::path(".kyua/store/" \472"results.test_suite_root.20130108-111331-000000.db")); \473check_action_3(fs::path(".kyua/store/" \474"results.usr_tests.20130108-123832-000000.db")); \475check_action_4(fs::path(".kyua/store/" \476"results.usr_tests.20130108-112635-000000.db")); \477}478MIGRATE_SCHEMA_TEST(1);479MIGRATE_SCHEMA_TEST(2);480481482ATF_INIT_TEST_CASES(tcs)483{484ATF_ADD_TEST_CASE(tcs, current_schema_1);485ATF_ADD_TEST_CASE(tcs, current_schema_2);486ATF_ADD_TEST_CASE(tcs, current_schema_3);487ATF_ADD_TEST_CASE(tcs, current_schema_4);488489ATF_ADD_TEST_CASE(tcs, migrate_schema__from_v1);490ATF_ADD_TEST_CASE(tcs, migrate_schema__from_v2);491}492493494