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

#include <memory>

#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "components/services/storage/public/cpp/constants.h"
#include "sql/database.h"
#include "sql/meta_table.h"
#include "sql/statement.h"
#include "sql/test/test_helpers.h"
#include "storage/browser/quota/quota_database.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace storage {

namespace {

const int kCurrentSchemaVersion = 11;
const int kCurrentCompatibleVersion = 11;

std::string RemoveQuotes(std::string input) {
  std::string output;
  base::RemoveChars(input, "\"", &output);
  return output;
}

}  // namespace

class QuotaDatabaseMigrationsTest : public testing::Test {
 public:
  void SetUp() override {
    ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
    histograms_ = std::make_unique<base::HistogramTester>();
  }

  base::FilePath ProfilePath() { return temp_directory_.GetPath(); }

  base::FilePath DbPath() {
    return ProfilePath()
        .Append(kWebStorageDirectory)
        .AppendASCII("QuotaManager");
  }

 protected:
  // The textual contents of |file| are read from
  // "storage/test/data/quota_database/" and returned in the string
  // |contents|. Returns true if the file exists and is read successfully, false
  // otherwise.
  std::string GetDatabaseData(const char* file) {
    base::FilePath source_path;
    base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &source_path);
    source_path = source_path.AppendASCII("storage/test/data/quota_database");
    source_path = source_path.AppendASCII(file);
    EXPECT_TRUE(base::PathExists(source_path));

    std::string contents;
    base::ReadFileToString(source_path, &contents);
    return contents;
  }

  bool LoadDatabase(const char* file, const base::FilePath& db_path) {
    std::string contents = GetDatabaseData(file);
    if (contents.empty())
      return false;

    sql::Database db(sql::test::kTestTag);
    if (!base::CreateDirectory(db_path.DirName()) || !db.Open(db_path) ||
        !db.ExecuteScriptForTesting(contents)) {
      return false;
    }

    return true;
  }

  void MigrateDatabase() {
    QuotaDatabase db(ProfilePath());
    EXPECT_EQ(db.EnsureOpened(), QuotaError::kNone);

    DCHECK_CALLED_ON_VALID_SEQUENCE(db.sequence_checker_);
    EXPECT_TRUE(db.db_.get());
  }

  std::string GetCurrentSchema() {
    base::FilePath current_version_path =
        temp_directory_.GetPath().AppendASCII("current_version.db");
    EXPECT_TRUE(LoadDatabase("version_11.sql", current_version_path));
    sql::Database db(sql::test::kTestTag);
    EXPECT_TRUE(db.Open(current_version_path));
    return db.GetSchema();
  }

  std::string GetQuotaDatabaseSchema() {
    QuotaDatabase db(ProfilePath());
    EXPECT_EQ(db.EnsureOpened(), QuotaError::kNone);
    DCHECK_CALLED_ON_VALID_SEQUENCE(db.sequence_checker_);
    return db.db_->GetSchema();
  }

  size_t GetTotalHistogramCount() {
    return histograms_->GetTotalCountsForPrefix("Quota.DatabaseMigration")
        .size();
  }

  base::ScopedTempDir temp_directory_;
  std::unique_ptr<base::HistogramTester> histograms_;
};

// Verify that the schema created by a new `QuotaDatabase` instance matches the
// current test schema file.
TEST_F(QuotaDatabaseMigrationsTest, QuotaDatabaseSchemaMatchesTestSchema) {
  EXPECT_EQ(GetCurrentSchema(), GetQuotaDatabaseSchema());
}

TEST_F(QuotaDatabaseMigrationsTest, UpgradeSchemaFromV10) {
  ASSERT_TRUE(LoadDatabase("version_10.sql", DbPath()));

  {
    sql::Database db(sql::test::kTestTag);
    ASSERT_TRUE(db.Open(DbPath()));

    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&db));
    sql::MetaTable meta_table;
    ASSERT_TRUE(
        meta_table.Init(&db, kCurrentSchemaVersion, kCurrentCompatibleVersion));
    ASSERT_EQ(meta_table.GetVersionNumber(), 10);
    ASSERT_EQ(meta_table.GetCompatibleVersionNumber(), 10);

    // Check populated data.
    EXPECT_EQ(
        "1|http://a/|http://a/"
        "|0|bucket_a|123|13260644621105493|13242931862595604|0|0|0|0,"
        "2|http://b/|http://b/"
        "|0|bucket_b|111|13250042735631065|13260999511438890|0|1000|0|0,"
        "3|chrome-extension://abc/|chrome-extension://abc/"
        "|1|_default|321|13261163582572088|13261079941303629|0|10000|0|1",
        sql::test::ExecuteWithResults(
            &db, "SELECT * FROM buckets ORDER BY id ASC", "|", ","));
  }

  MigrateDatabase();

  // Verify upgraded schema.
  {
    sql::Database db(sql::test::kTestTag);
    ASSERT_TRUE(db.Open(DbPath()));

    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&db));
    sql::MetaTable meta_table;
    ASSERT_TRUE(
        meta_table.Init(&db, kCurrentSchemaVersion, kCurrentCompatibleVersion));
    EXPECT_EQ(meta_table.GetVersionNumber(), kCurrentSchemaVersion);
    EXPECT_EQ(meta_table.GetCompatibleVersionNumber(), kCurrentSchemaVersion);

    // Check that buckets data is still present.
    EXPECT_EQ(
        "1|http://a/|http://a/"
        "|bucket_a|123|13260644621105493|13242931862595604|0|0|0|0,"
        "2|http://b/|http://b/"
        "|bucket_b|111|13250042735631065|13260999511438890|0|1000|0|0",
        sql::test::ExecuteWithResults(
            &db, "SELECT * FROM buckets ORDER BY id ASC", "|", ","));

    EXPECT_EQ(GetCurrentSchema(), RemoveQuotes(db.GetSchema()));

    EXPECT_EQ(GetTotalHistogramCount(), 1u);
    histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV10ToV11",
                                   /*sample=*/true, /*expected_count=*/1);
  }
}

}  // namespace storage