text: // Copyright 2014 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "extensions/browser/api/storage/storage_frontend.h"
  
  #include <memory>
  
  #include "base/bind.h"
  #include "base/files/file_util.h"
  #include "base/files/scoped_temp_dir.h"
  #include "base/strings/string_number_conversions.h"
  #include "content/public/browser/browser_context.h"
  #include "content/public/test/test_browser_context.h"
  #include "content/public/test/test_utils.h"
  #include "extensions/browser/api/extensions_api_client.h"
  #include "extensions/browser/api/storage/settings_namespace.h"
  #include "extensions/browser/api/storage/settings_test_util.h"
  #include "extensions/browser/extensions_test.h"
  #include "extensions/browser/value_store/value_store.h"
  #include "extensions/browser/value_store/value_store_factory_impl.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  namespace extensions {
  
  namespace settings = settings_namespace;
  namespace util = settings_test_util;
  
  namespace {
  
  // To save typing ValueStore::DEFAULTS everywhere.
  const ValueStore::WriteOptions DEFAULTS = ValueStore::DEFAULTS;
  
  }  // namespace
  
  // A better name for this would be StorageFrontendTest, but the historical name
  // has been ExtensionSettingsFrontendTest. In order to preserve crash/failure
  // history, the test names are unchanged.
  class ExtensionSettingsFrontendTest : public ExtensionsTest {
   public:
    void SetUp() override {
      ExtensionsTest::SetUp();
      ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
      storage_factory_ = new ValueStoreFactoryImpl(temp_dir_.GetPath());
      ResetFrontend();
    }
  
    void TearDown() override {
      frontend_.reset();
      // Execute any pending deletion tasks.
      content::RunAllTasksUntilIdle();
      ExtensionsTest::TearDown();
    }
  
   protected:
    void ResetFrontend() {
      frontend_ =
          StorageFrontend::CreateForTesting(storage_factory_, browser_context());
    }
  
    base::ScopedTempDir temp_dir_;
    std::unique_ptr<StorageFrontend> frontend_;
    scoped_refptr<ValueStoreFactoryImpl> storage_factory_;
  
   private:
    ExtensionsAPIClient extensions_api_client_;
  };
  
  // Get a semblance of coverage for both extension and app settings by
  // alternating in each test.
  // TODO(kalman): explicitly test the two interact correctly.
  
  // Tests that the frontend is set up correctly.
  TEST_F(ExtensionSettingsFrontendTest, Basics) {
    // Local storage is always enabled.
    EXPECT_TRUE(frontend_->IsStorageEnabled(settings::LOCAL));
    EXPECT_TRUE(frontend_->GetValueStoreCache(settings::LOCAL));
  
    // Invalid storage areas are not available.
    EXPECT_FALSE(frontend_->IsStorageEnabled(settings::INVALID));
    EXPECT_FALSE(frontend_->GetValueStoreCache(settings::INVALID));
  }
  
  TEST_F(ExtensionSettingsFrontendTest, SettingsPreservedAcrossReconstruction) {
    const std::string id = "ext";
    scoped_refptr<const Extension> extension =
        util::AddExtensionWithId(browser_context(), id, Manifest::TYPE_EXTENSION);
  
    ValueStore* storage =
        util::GetStorage(extension, settings::LOCAL, frontend_.get());
  
    // The correctness of Get/Set/Remove/Clear is tested elsewhere so no need to
    // be too rigorous.
    {
      base::Value bar("bar");
      ValueStore::WriteResult result = storage->Set(DEFAULTS, "foo", bar);
      ASSERT_TRUE(result.status().ok());
    }
  
    {
      ValueStore::ReadResult result = storage->Get();
      ASSERT_TRUE(result.status().ok());
      EXPECT_FALSE(result.settings().empty());
    }
  
    ResetFrontend();
    storage = util::GetStorage(extension, settings::LOCAL, frontend_.get());
  
    {
      ValueStore::ReadResult result = storage->Get();
      ASSERT_TRUE(result.status().ok());
      EXPECT_FALSE(result.settings().empty());
    }
  }
  
  TEST_F(ExtensionSettingsFrontendTest, SettingsClearedOnUninstall) {
    const std::string id = "ext";
    scoped_refptr<const Extension> extension = util::AddExtensionWithId(
        browser_context(), id, Manifest::TYPE_LEGACY_PACKAGED_APP);
  
    ValueStore* storage =
        util::GetStorage(extension, settings::LOCAL, frontend_.get());
  
    {
      base::Value bar("bar");
      ValueStore::WriteResult result = storage->Set(DEFAULTS, "foo", bar);
      ASSERT_TRUE(result.status().ok());
    }
  
    // This would be triggered by extension uninstall via a DataDeleter.
    frontend_->DeleteStorageSoon(id);
    content::RunAllTasksUntilIdle();
  
    // The storage area may no longer be valid post-uninstall, so re-request.
    storage = util::GetStorage(extension, settings::LOCAL, frontend_.get());
    {
      ValueStore::ReadResult result = storage->Get();
      ASSERT_TRUE(result.status().ok());
      EXPECT_TRUE(result.settings().empty());
    }
  }
  
  TEST_F(ExtensionSettingsFrontendTest, LeveldbDatabaseDeletedFromDiskOnClear) {
    const std::string id = "ext";
    scoped_refptr<const Extension> extension =
        util::AddExtensionWithId(browser_context(), id, Manifest::TYPE_EXTENSION);
  
    ValueStore* storage =
        util::GetStorage(extension, settings::LOCAL, frontend_.get());
  
    {
      base::Value bar("bar");
      ValueStore::WriteResult result = storage->Set(DEFAULTS, "foo", bar);
      ASSERT_TRUE(result.status().ok());
      EXPECT_TRUE(base::PathExists(temp_dir_.GetPath()));
    }
  
    // Should need to both clear the database and delete the frontend for the
    // leveldb database to be deleted from disk.
    {
      ValueStore::WriteResult result = storage->Clear();
      ASSERT_TRUE(result.status().ok());
      EXPECT_TRUE(base::PathExists(temp_dir_.GetPath()));
    }
  
    frontend_.reset();
    content::RunAllTasksUntilIdle();
    // TODO(kalman): Figure out why this fails, despite appearing to work.
    // Leaving this commented out rather than disabling the whole test so that the
    // deletion code paths are at least exercised.
    // EXPECT_FALSE(base::PathExists(temp_dir_.GetPath()));
  }
  
  // Disabled (slow), http://crbug.com/322751 .
  TEST_F(ExtensionSettingsFrontendTest,
         DISABLED_QuotaLimitsEnforcedCorrectlyForSyncAndLocal) {
    const std::string id = "ext";
    scoped_refptr<const Extension> extension =
        util::AddExtensionWithId(browser_context(), id, Manifest::TYPE_EXTENSION);
  
    ValueStore* sync_storage =
        util::GetStorage(extension, settings::SYNC, frontend_.get());
    ValueStore* local_storage =
        util::GetStorage(extension, settings::LOCAL, frontend_.get());
  
    // Sync storage should run out after ~100K.
    std::unique_ptr<base::Value> kilobyte = util::CreateKilobyte();
    for (int i = 0; i < 100; ++i) {
      sync_storage->Set(DEFAULTS, base::IntToString(i), *kilobyte);
    }
  
    EXPECT_FALSE(
        sync_storage->Set(DEFAULTS, "WillError", *kilobyte).status().ok());
  
    // Local storage shouldn't run out after ~100K.
    for (int i = 0; i < 100; ++i) {
      local_storage->Set(DEFAULTS, base::IntToString(i), *kilobyte);
    }
  
    EXPECT_TRUE(
        local_storage->Set(DEFAULTS, "WontError", *kilobyte).status().ok());
  
    // Local storage should run out after ~5MB.
    std::unique_ptr<base::Value> megabyte = util::CreateMegabyte();
    for (int i = 0; i < 5; ++i) {
      local_storage->Set(DEFAULTS, base::IntToString(i), *megabyte);
    }
  
    EXPECT_FALSE(
        local_storage->Set(DEFAULTS, "WillError", *megabyte).status().ok());
  }
  
  }  // namespace extensions
  