910e62b5创建于 1月15日历史提交
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "sql/meta_table.h"

#include <stdint.h>

#include <string>

#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "sql/database.h"
#include "sql/test/test_helpers.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace sql {

namespace {

class SQLMetaTableTest : public testing::Test {
 public:
  void SetUp() override {
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    ASSERT_TRUE(
        db_.Open(temp_dir_.GetPath().AppendASCII("meta_table_test.sqlite")));
  }

 protected:
  base::ScopedTempDir temp_dir_;
  Database db_{test::kTestTag};
};

TEST_F(SQLMetaTableTest, DoesTableExist) {
  EXPECT_FALSE(MetaTable::DoesTableExist(&db_));

  {
    MetaTable meta_table;
    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));
  }

  EXPECT_TRUE(MetaTable::DoesTableExist(&db_));
}

TEST_F(SQLMetaTableTest, DeleteTableForTesting) {
  MetaTable meta_table;
  EXPECT_TRUE(meta_table.Init(&db_, 1, 1));

  EXPECT_TRUE(MetaTable::DeleteTableForTesting(&db_));
  EXPECT_FALSE(MetaTable::DoesTableExist(&db_));
}

TEST_F(SQLMetaTableTest, RazeIfIncompatiblePreservesDatabasesWithoutMetadata) {
  EXPECT_TRUE(db_.Execute("CREATE TABLE data(id INTEGER PRIMARY KEY)"));
  ASSERT_TRUE(db_.DoesTableExist("data"));

  // The table should not have been cleared, since the database does not have a
  // metadata table.
  EXPECT_EQ(RazeIfIncompatibleResult::kCompatible,
            MetaTable::RazeIfIncompatible(&db_, 1,
                                          /*current_version=*/1));
  EXPECT_TRUE(db_.DoesTableExist("data"));
}

TEST_F(SQLMetaTableTest, RazeIfIncompatibleRazesIncompatiblyOldTables) {
  constexpr int kWrittenVersion = 1;
  constexpr int kCompatibleVersion = 1;

  // Setup a current database.
  {
    MetaTable meta_table;
    EXPECT_TRUE(meta_table.Init(&db_, kWrittenVersion, kCompatibleVersion));
    EXPECT_TRUE(db_.Execute("CREATE TABLE data(id INTEGER PRIMARY KEY)"));
    ASSERT_TRUE(db_.DoesTableExist("data"));
  }

  // The table should have been cleared, since the least version compatible with
  // the written database is greater than the current version.
  EXPECT_EQ(
      RazeIfIncompatibleResult::kRazedSuccessfully,
      MetaTable::RazeIfIncompatible(&db_, kWrittenVersion + 1,
                                    /*current_version=*/kWrittenVersion + 1));
  EXPECT_FALSE(db_.DoesTableExist("data"));
}

TEST_F(SQLMetaTableTest, RazeIfIncompatibleRazesIncompatiblyNewTables) {
  constexpr int kCompatibleVersion = 2;
  constexpr int kWrittenVersion = 3;

  // Setup a current database.
  {
    MetaTable meta_table;
    EXPECT_TRUE(meta_table.Init(&db_, kWrittenVersion, kCompatibleVersion));
    EXPECT_TRUE(db_.Execute("CREATE TABLE data(id INTEGER PRIMARY KEY)"));
    ASSERT_TRUE(db_.DoesTableExist("data"));
  }

  // The table should have been cleared, since the least version compatible with
  // the written database is greater than the current version.
  EXPECT_EQ(RazeIfIncompatibleResult::kRazedSuccessfully,
            MetaTable::RazeIfIncompatible(
                &db_, MetaTable::kNoLowestSupportedVersion,
                /*current_version=*/kCompatibleVersion - 1));
  EXPECT_FALSE(db_.DoesTableExist("data"));
}

TEST_F(SQLMetaTableTest, RazeIfIncompatibleDoesntRazeWhenItShouldnt) {
  constexpr int kVersion = 2;

  {
    MetaTable meta_table;
    EXPECT_TRUE(
        meta_table.Init(&db_, kVersion, /*compatible_version=*/kVersion - 1));
    EXPECT_TRUE(db_.Execute("CREATE TABLE data(id INTEGER PRIMARY KEY)"));
    EXPECT_TRUE(db_.DoesTableExist("data"));
  }

  EXPECT_EQ(RazeIfIncompatibleResult::kCompatible,
            MetaTable::RazeIfIncompatible(&db_, kVersion,
                                          /*current_version=*/kVersion));
  EXPECT_TRUE(db_.DoesTableExist("data"))
      << "Table should still exist if the database version is exactly right.";

  EXPECT_EQ(RazeIfIncompatibleResult::kCompatible,
            MetaTable::RazeIfIncompatible(&db_, kVersion - 1,
                                          /*current_version=*/kVersion));
  EXPECT_TRUE(db_.DoesTableExist("data"))
      << "... or if the lower bound is less than the actual version";

  EXPECT_EQ(
      RazeIfIncompatibleResult::kCompatible,
      MetaTable::RazeIfIncompatible(&db_, MetaTable::kNoLowestSupportedVersion,
                                    /*current_version=*/kVersion));
  EXPECT_TRUE(db_.DoesTableExist("data"))
      << "... or if the lower bound is not set";

  EXPECT_EQ(
      RazeIfIncompatibleResult::kCompatible,
      MetaTable::RazeIfIncompatible(&db_, MetaTable::kNoLowestSupportedVersion,
                                    /*current_version=*/kVersion - 1));
  EXPECT_TRUE(db_.DoesTableExist("data"))
      << "... even if the current version exactly matches the written "
         "database's least compatible version.";
}

TEST_F(SQLMetaTableTest, VersionNumber) {
  // Compatibility versions one less than the main versions to make
  // sure the values aren't being crossed with each other.
  constexpr int kVersionFirst = 2;
  constexpr int kCompatVersionFirst = kVersionFirst - 1;
  constexpr int kVersionSecond = 4;
  constexpr int kCompatVersionSecond = kVersionSecond - 1;
  constexpr int kVersionThird = 6;
  constexpr int kCompatVersionThird = kVersionThird - 1;

  // First Init() sets the version info as expected.
  {
    MetaTable meta_table;
    EXPECT_TRUE(meta_table.Init(&db_, kVersionFirst, kCompatVersionFirst));
    EXPECT_EQ(kVersionFirst, meta_table.GetVersionNumber());
    EXPECT_EQ(kCompatVersionFirst, meta_table.GetCompatibleVersionNumber());
  }

  // Second Init() does not change the version info.
  {
    MetaTable meta_table;
    EXPECT_TRUE(meta_table.Init(&db_, kVersionSecond, kCompatVersionSecond));
    EXPECT_EQ(kVersionFirst, meta_table.GetVersionNumber());
    EXPECT_EQ(kCompatVersionFirst, meta_table.GetCompatibleVersionNumber());

    EXPECT_TRUE(meta_table.SetVersionNumber(kVersionSecond));
    EXPECT_TRUE(meta_table.SetCompatibleVersionNumber(kCompatVersionSecond));
  }

  // Version info from Set*() calls is seen.
  {
    MetaTable meta_table;
    EXPECT_TRUE(meta_table.Init(&db_, kVersionThird, kCompatVersionThird));
    EXPECT_EQ(kVersionSecond, meta_table.GetVersionNumber());
    EXPECT_EQ(kCompatVersionSecond, meta_table.GetCompatibleVersionNumber());
  }
}

TEST_F(SQLMetaTableTest, StringValue) {
  static const char kKey[] = "String Key";
  const std::string kFirstValue("First Value");
  const std::string kSecondValue("Second Value");

  // Initially, the value isn't there until set.
  {
    MetaTable meta_table;
    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));

    std::string value;
    EXPECT_FALSE(meta_table.GetValue(kKey, &value));

    EXPECT_TRUE(meta_table.SetValue(kKey, kFirstValue));
    EXPECT_TRUE(meta_table.GetValue(kKey, &value));
    EXPECT_EQ(kFirstValue, value);
  }

  // Value is persistent across different instances.
  {
    MetaTable meta_table;
    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));

    std::string value;
    EXPECT_TRUE(meta_table.GetValue(kKey, &value));
    EXPECT_EQ(kFirstValue, value);

    EXPECT_TRUE(meta_table.SetValue(kKey, kSecondValue));
  }

  // Existing value was successfully changed.
  {
    MetaTable meta_table;
    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));

    std::string value;
    EXPECT_TRUE(meta_table.GetValue(kKey, &value));
    EXPECT_EQ(kSecondValue, value);
  }
}

TEST_F(SQLMetaTableTest, IntValue) {
  static const char kKey[] = "Int Key";
  constexpr int kFirstValue = 17;
  constexpr int kSecondValue = 23;

  // Initially, the value isn't there until set.
  {
    MetaTable meta_table;
    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));

    int value;
    EXPECT_FALSE(meta_table.GetValue(kKey, &value));

    EXPECT_TRUE(meta_table.SetValue(kKey, kFirstValue));
    EXPECT_TRUE(meta_table.GetValue(kKey, &value));
    EXPECT_EQ(kFirstValue, value);
  }

  // Value is persistent across different instances.
  {
    MetaTable meta_table;
    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));

    int value;
    EXPECT_TRUE(meta_table.GetValue(kKey, &value));
    EXPECT_EQ(kFirstValue, value);

    EXPECT_TRUE(meta_table.SetValue(kKey, kSecondValue));
  }

  // Existing value was successfully changed.
  {
    MetaTable meta_table;
    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));

    int value;
    EXPECT_TRUE(meta_table.GetValue(kKey, &value));
    EXPECT_EQ(kSecondValue, value);
  }
}

TEST_F(SQLMetaTableTest, Int64Value) {
  static const char kKey[] = "Int Key";
  const int64_t kFirstValue = 5000000017LL;
  const int64_t kSecondValue = 5000000023LL;

  // Initially, the value isn't there until set.
  {
    MetaTable meta_table;
    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));

    int64_t value;
    EXPECT_FALSE(meta_table.GetValue(kKey, &value));

    EXPECT_TRUE(meta_table.SetValue(kKey, kFirstValue));
    EXPECT_TRUE(meta_table.GetValue(kKey, &value));
    EXPECT_EQ(kFirstValue, value);
  }

  // Value is persistent across different instances.
  {
    MetaTable meta_table;
    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));

    int64_t value;
    EXPECT_TRUE(meta_table.GetValue(kKey, &value));
    EXPECT_EQ(kFirstValue, value);

    EXPECT_TRUE(meta_table.SetValue(kKey, kSecondValue));
  }

  // Existing value was successfully changed.
  {
    MetaTable meta_table;
    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));

    int64_t value;
    EXPECT_TRUE(meta_table.GetValue(kKey, &value));
    EXPECT_EQ(kSecondValue, value);
  }
}

TEST_F(SQLMetaTableTest, DeleteKey) {
  static const char kKey[] = "String Key";
  const std::string kValue("String Value");

  MetaTable meta_table;
  EXPECT_TRUE(meta_table.Init(&db_, 1, 1));

  // Value isn't present.
  std::string value;
  EXPECT_FALSE(meta_table.GetValue(kKey, &value));

  // Now value is present.
  EXPECT_TRUE(meta_table.SetValue(kKey, kValue));
  EXPECT_TRUE(meta_table.GetValue(kKey, &value));
  EXPECT_EQ(kValue, value);

  // After delete value isn't present.
  EXPECT_TRUE(meta_table.DeleteKey(kKey));
  EXPECT_FALSE(meta_table.GetValue(kKey, &value));
}

}  // namespace

}  // namespace sql