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/backend.hpp" 30 31extern "C" { 32#include <sys/stat.h> 33} 34 35#include <atf-c++.hpp> 36 37#include "store/exceptions.hpp" 38#include "store/metadata.hpp" 39#include "utils/datetime.hpp" 40#include "utils/env.hpp" 41#include "utils/fs/operations.hpp" 42#include "utils/fs/path.hpp" 43#include "utils/logging/operations.hpp" 44#include "utils/sqlite/database.hpp" 45#include "utils/sqlite/statement.ipp" 46 47namespace datetime = utils::datetime; 48namespace fs = utils::fs; 49namespace logging = utils::logging; 50namespace sqlite = utils::sqlite; 51 52 53ATF_TEST_CASE_WITHOUT_HEAD(detail__backup_database__ok); 54ATF_TEST_CASE_BODY(detail__backup_database__ok) 55{ 56 atf::utils::create_file("test.db", "The DB\n"); 57 store::detail::backup_database(fs::path("test.db"), 13); 58 ATF_REQUIRE(fs::exists(fs::path("test.db"))); 59 ATF_REQUIRE(fs::exists(fs::path("test.db.v13.backup"))); 60 ATF_REQUIRE(atf::utils::compare_file("test.db.v13.backup", "The DB\n")); 61} 62 63 64ATF_TEST_CASE_WITHOUT_HEAD(detail__backup_database__ok_overwrite); 65ATF_TEST_CASE_BODY(detail__backup_database__ok_overwrite) 66{ 67 atf::utils::create_file("test.db", "Original contents"); 68 atf::utils::create_file("test.db.v1.backup", "Overwrite me"); 69 store::detail::backup_database(fs::path("test.db"), 1); 70 ATF_REQUIRE(fs::exists(fs::path("test.db"))); 71 ATF_REQUIRE(fs::exists(fs::path("test.db.v1.backup"))); 72 ATF_REQUIRE(atf::utils::compare_file("test.db.v1.backup", 73 "Original contents")); 74} 75 76 77ATF_TEST_CASE_WITHOUT_HEAD(detail__backup_database__fail_open); 78ATF_TEST_CASE_BODY(detail__backup_database__fail_open) 79{ 80 ATF_REQUIRE_THROW_RE(store::error, "Cannot open.*foo.db", 81 store::detail::backup_database(fs::path("foo.db"), 5)); 82} 83 84 85ATF_TEST_CASE(detail__backup_database__fail_create); 86ATF_TEST_CASE_HEAD(detail__backup_database__fail_create) 87{ 88 set_md_var("require.user", "unprivileged"); 89} 90ATF_TEST_CASE_BODY(detail__backup_database__fail_create) 91{ 92 ATF_REQUIRE(::mkdir("dir", 0755) != -1); 93 atf::utils::create_file("dir/test.db", "Does not need to be valid"); 94 ATF_REQUIRE(::chmod("dir", 0111) != -1); 95 ATF_REQUIRE_THROW_RE( 96 store::error, "Cannot create.*dir/test.db.v13.backup", 97 store::detail::backup_database(fs::path("dir/test.db"), 13)); 98} 99 100 101ATF_TEST_CASE(detail__initialize__ok); 102ATF_TEST_CASE_HEAD(detail__initialize__ok) 103{ 104 logging::set_inmemory(); 105 set_md_var("require.files", store::detail::schema_file().c_str()); 106} 107ATF_TEST_CASE_BODY(detail__initialize__ok) 108{ 109 sqlite::database db = sqlite::database::in_memory(); 110 const datetime::timestamp before = datetime::timestamp::now(); 111 const store::metadata md = store::detail::initialize(db); 112 const datetime::timestamp after = datetime::timestamp::now(); 113 114 ATF_REQUIRE(md.timestamp() >= before.to_seconds()); 115 ATF_REQUIRE(md.timestamp() <= after.to_microseconds()); 116 ATF_REQUIRE_EQ(store::detail::current_schema_version, md.schema_version()); 117 118 // Query some known tables to ensure they were created. 119 db.exec("SELECT * FROM metadata"); 120 121 // And now query some know values. 122 sqlite::statement stmt = db.create_statement( 123 "SELECT COUNT(*) FROM metadata"); 124 ATF_REQUIRE(stmt.step()); 125 ATF_REQUIRE_EQ(1, stmt.column_int(0)); 126 ATF_REQUIRE(!stmt.step()); 127} 128 129 130ATF_TEST_CASE_WITHOUT_HEAD(detail__initialize__missing_schema); 131ATF_TEST_CASE_BODY(detail__initialize__missing_schema) 132{ 133 utils::setenv("KYUA_STOREDIR", "/non-existent"); 134 store::detail::current_schema_version = 712; 135 136 sqlite::database db = sqlite::database::in_memory(); 137 ATF_REQUIRE_THROW_RE(store::error, 138 "Cannot open.*'/non-existent/schema_v712.sql'", 139 store::detail::initialize(db)); 140} 141 142 143ATF_TEST_CASE_WITHOUT_HEAD(detail__initialize__sqlite_error); 144ATF_TEST_CASE_BODY(detail__initialize__sqlite_error) 145{ 146 utils::setenv("KYUA_STOREDIR", "."); 147 store::detail::current_schema_version = 712; 148 149 atf::utils::create_file("schema_v712.sql", "foo_bar_baz;\n"); 150 151 sqlite::database db = sqlite::database::in_memory(); 152 ATF_REQUIRE_THROW_RE(store::error, "Failed to initialize.*:.*foo_bar_baz", 153 store::detail::initialize(db)); 154} 155 156 157ATF_TEST_CASE_WITHOUT_HEAD(detail__migration_file__builtin); 158ATF_TEST_CASE_BODY(detail__migration_file__builtin) 159{ 160 utils::unsetenv("KYUA_STOREDIR"); 161 ATF_REQUIRE_EQ(fs::path(KYUA_STOREDIR) / "migrate_v5_v9.sql", 162 store::detail::migration_file(5, 9)); 163} 164 165 166ATF_TEST_CASE_WITHOUT_HEAD(detail__migration_file__overriden); 167ATF_TEST_CASE_BODY(detail__migration_file__overriden) 168{ 169 utils::setenv("KYUA_STOREDIR", "/tmp/test"); 170 ATF_REQUIRE_EQ(fs::path("/tmp/test/migrate_v5_v9.sql"), 171 store::detail::migration_file(5, 9)); 172} 173 174 175ATF_TEST_CASE_WITHOUT_HEAD(detail__schema_file__builtin); 176ATF_TEST_CASE_BODY(detail__schema_file__builtin) 177{ 178 utils::unsetenv("KYUA_STOREDIR"); 179 ATF_REQUIRE_EQ(fs::path(KYUA_STOREDIR) / "schema_v2.sql", 180 store::detail::schema_file()); 181} 182 183 184ATF_TEST_CASE_WITHOUT_HEAD(detail__schema_file__overriden); 185ATF_TEST_CASE_BODY(detail__schema_file__overriden) 186{ 187 utils::setenv("KYUA_STOREDIR", "/tmp/test"); 188 store::detail::current_schema_version = 123; 189 ATF_REQUIRE_EQ(fs::path("/tmp/test/schema_v123.sql"), 190 store::detail::schema_file()); 191} 192 193 194ATF_TEST_CASE(backend__open_ro__ok); 195ATF_TEST_CASE_HEAD(backend__open_ro__ok) 196{ 197 logging::set_inmemory(); 198 set_md_var("require.files", store::detail::schema_file().c_str()); 199} 200ATF_TEST_CASE_BODY(backend__open_ro__ok) 201{ 202 { 203 sqlite::database db = sqlite::database::open( 204 fs::path("test.db"), sqlite::open_readwrite | sqlite::open_create); 205 store::detail::initialize(db); 206 } 207 store::backend backend = store::backend::open_ro(fs::path("test.db")); 208 backend.database().exec("SELECT * FROM metadata"); 209} 210 211 212ATF_TEST_CASE_WITHOUT_HEAD(backend__open_ro__missing_file); 213ATF_TEST_CASE_BODY(backend__open_ro__missing_file) 214{ 215 ATF_REQUIRE_THROW_RE(store::error, "Cannot open 'missing.db': ", 216 store::backend::open_ro(fs::path("missing.db"))); 217 ATF_REQUIRE(!fs::exists(fs::path("missing.db"))); 218} 219 220 221ATF_TEST_CASE(backend__open_ro__integrity_error); 222ATF_TEST_CASE_HEAD(backend__open_ro__integrity_error) 223{ 224 logging::set_inmemory(); 225 set_md_var("require.files", store::detail::schema_file().c_str()); 226} 227ATF_TEST_CASE_BODY(backend__open_ro__integrity_error) 228{ 229 { 230 sqlite::database db = sqlite::database::open( 231 fs::path("test.db"), sqlite::open_readwrite | sqlite::open_create); 232 store::detail::initialize(db); 233 db.exec("DELETE FROM metadata"); 234 } 235 ATF_REQUIRE_THROW_RE(store::integrity_error, "metadata.*empty", 236 store::backend::open_ro(fs::path("test.db"))); 237} 238 239 240ATF_TEST_CASE(backend__open_rw__ok); 241ATF_TEST_CASE_HEAD(backend__open_rw__ok) 242{ 243 logging::set_inmemory(); 244 set_md_var("require.files", store::detail::schema_file().c_str()); 245} 246ATF_TEST_CASE_BODY(backend__open_rw__ok) 247{ 248 { 249 sqlite::database db = sqlite::database::open( 250 fs::path("test.db"), sqlite::open_readwrite | sqlite::open_create); 251 store::detail::initialize(db); 252 } 253 store::backend backend = store::backend::open_rw(fs::path("test.db")); 254 backend.database().exec("SELECT * FROM metadata"); 255} 256 257 258ATF_TEST_CASE(backend__open_rw__create_missing); 259ATF_TEST_CASE_HEAD(backend__open_rw__create_missing) 260{ 261 logging::set_inmemory(); 262 set_md_var("require.files", store::detail::schema_file().c_str()); 263} 264ATF_TEST_CASE_BODY(backend__open_rw__create_missing) 265{ 266 store::backend backend = store::backend::open_rw(fs::path("test.db")); 267 backend.database().exec("SELECT * FROM metadata"); 268} 269 270 271ATF_TEST_CASE(backend__open_rw__integrity_error); 272ATF_TEST_CASE_HEAD(backend__open_rw__integrity_error) 273{ 274 logging::set_inmemory(); 275 set_md_var("require.files", store::detail::schema_file().c_str()); 276} 277ATF_TEST_CASE_BODY(backend__open_rw__integrity_error) 278{ 279 { 280 sqlite::database db = sqlite::database::open( 281 fs::path("test.db"), sqlite::open_readwrite | sqlite::open_create); 282 store::detail::initialize(db); 283 db.exec("DELETE FROM metadata"); 284 } 285 ATF_REQUIRE_THROW_RE(store::integrity_error, "metadata.*empty", 286 store::backend::open_rw(fs::path("test.db"))); 287} 288 289 290ATF_INIT_TEST_CASES(tcs) 291{ 292 ATF_ADD_TEST_CASE(tcs, detail__backup_database__ok); 293 ATF_ADD_TEST_CASE(tcs, detail__backup_database__ok_overwrite); 294 ATF_ADD_TEST_CASE(tcs, detail__backup_database__fail_open); 295 ATF_ADD_TEST_CASE(tcs, detail__backup_database__fail_create); 296 297 ATF_ADD_TEST_CASE(tcs, detail__initialize__ok); 298 ATF_ADD_TEST_CASE(tcs, detail__initialize__missing_schema); 299 ATF_ADD_TEST_CASE(tcs, detail__initialize__sqlite_error); 300 301 ATF_ADD_TEST_CASE(tcs, detail__migration_file__builtin); 302 ATF_ADD_TEST_CASE(tcs, detail__migration_file__overriden); 303 304 ATF_ADD_TEST_CASE(tcs, detail__schema_file__builtin); 305 ATF_ADD_TEST_CASE(tcs, detail__schema_file__overriden); 306 307 ATF_ADD_TEST_CASE(tcs, backend__open_ro__ok); 308 ATF_ADD_TEST_CASE(tcs, backend__open_ro__missing_file); 309 ATF_ADD_TEST_CASE(tcs, backend__open_ro__integrity_error); 310 ATF_ADD_TEST_CASE(tcs, backend__open_rw__ok); 311 ATF_ADD_TEST_CASE(tcs, backend__open_rw__create_missing); 312 ATF_ADD_TEST_CASE(tcs, backend__open_rw__integrity_error); 313 314 // Tests for migrate_schema are in schema_inttest. This is because, for 315 // such tests to be meaningful, they need to be integration tests and don't 316 // really fit the goal of this unit-test module. 317} 318