blob: f638eeb00cefb309ab6ae492919b9ccd09dcdde6 [file] [log] [blame]
// Copyright 2013 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 <stddef.h>
#include <stdint.h>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/browser/dom_storage/dom_storage_area.h"
#include "content/browser/dom_storage/dom_storage_database_adapter.h"
#include "content/browser/dom_storage/dom_storage_task_runner.h"
#include "content/browser/dom_storage/session_storage_database.h"
#include "content/common/dom_storage/dom_storage_types.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/origin.h"
using base::ASCIIToUTF16;
using ::testing::Invoke;
namespace content {
class DOMStorageAreaMock : public DOMStorageArea {
public:
DOMStorageAreaMock(const std::string& namespace_id,
std::vector<std::string> original_namespace_ids,
const url::Origin& origin,
SessionStorageDatabase* session_storage_backing,
DOMStorageTaskRunner* task_runner)
: DOMStorageArea(namespace_id,
std::move(original_namespace_ids),
origin,
session_storage_backing,
task_runner) {}
MOCK_METHOD0(AfterOnCommitTimerCalled, void());
void ReadAllValuesFromBackingStore(DOMStorageValuesMap* result) {
backing_->ReadAllValues(result);
}
private:
~DOMStorageAreaMock() override {}
void OnCommitTimer() override {
DOMStorageArea::OnCommitTimer();
AfterOnCommitTimerCalled();
}
};
class DOMStorageAreaTest : public testing::Test {
public:
DOMStorageAreaTest()
: kOrigin(url::Origin::Create(GURL("http://dom_storage/"))),
kKey(ASCIIToUTF16("key")),
kValue(ASCIIToUTF16("value")),
kKey2(ASCIIToUTF16("key2")),
kValue2(ASCIIToUTF16("value2")) {}
const url::Origin kOrigin;
const base::string16 kKey;
const base::string16 kValue;
const base::string16 kKey2;
const base::string16 kValue2;
protected:
TestBrowserThreadBundle test_browser_thread_bundle_;
};
class DOMStorageAreaParamTest : public DOMStorageAreaTest,
public testing::WithParamInterface<bool> {
public:
DOMStorageAreaParamTest() {}
~DOMStorageAreaParamTest() override {}
};
INSTANTIATE_TEST_SUITE_P(_, DOMStorageAreaParamTest, ::testing::Bool());
TEST_P(DOMStorageAreaParamTest, DOMStorageAreaBasics) {
const std::string kFirstNamespaceId = "id1";
const std::string kSecondNamespaceId = "id2";
scoped_refptr<DOMStorageArea> area(
new DOMStorageArea(kFirstNamespaceId, std::vector<std::string>(), kOrigin,
nullptr, nullptr));
const bool values_cached = GetParam();
area->SetCacheOnlyKeys(!values_cached);
base::string16 old_value;
base::NullableString16 old_nullable_value;
scoped_refptr<DOMStorageArea> copy;
// We don't focus on the underlying DOMStorageMap functionality
// since that's covered by seperate unit tests.
EXPECT_EQ(kOrigin, area->origin());
EXPECT_EQ(kFirstNamespaceId, area->namespace_id());
EXPECT_EQ(0u, area->Length());
EXPECT_TRUE(
area->SetItem(kKey, kValue, old_nullable_value, &old_nullable_value));
EXPECT_TRUE(
area->SetItem(kKey2, kValue2, old_nullable_value, &old_nullable_value));
EXPECT_FALSE(area->HasUncommittedChanges());
// Verify that a copy shares the same map.
copy = area->ShallowCopy(kSecondNamespaceId);
EXPECT_EQ(kOrigin, copy->origin());
EXPECT_EQ(kSecondNamespaceId, copy->namespace_id());
EXPECT_EQ(area->Length(), copy->Length());
if (values_cached)
EXPECT_EQ(area->GetItem(kKey).string(), copy->GetItem(kKey).string());
EXPECT_EQ(area->Key(0).string(), copy->Key(0).string());
EXPECT_EQ(copy->map_.get(), area->map_.get());
copy->ClearShallowCopiedCommitBatches();
// But will deep copy-on-write as needed.
old_nullable_value = base::NullableString16(kValue, false);
EXPECT_TRUE(area->RemoveItem(kKey, old_nullable_value, &old_value));
EXPECT_EQ(kValue, old_value);
EXPECT_NE(copy->map_.get(), area->map_.get());
copy = area->ShallowCopy(kSecondNamespaceId);
EXPECT_EQ(copy->map_.get(), area->map_.get());
EXPECT_TRUE(
area->SetItem(kKey, kValue, old_nullable_value, &old_nullable_value));
EXPECT_NE(copy->map_.get(), area->map_.get());
copy = area->ShallowCopy(kSecondNamespaceId);
EXPECT_EQ(copy->map_.get(), area->map_.get());
EXPECT_NE(0u, area->Length());
EXPECT_TRUE(area->Clear());
EXPECT_EQ(0u, area->Length());
EXPECT_NE(copy->map_.get(), area->map_.get());
// Verify that once Shutdown(), behaves that way.
area->Shutdown();
EXPECT_TRUE(area->is_shutdown_);
EXPECT_FALSE(area->map_.get());
EXPECT_EQ(0u, area->Length());
EXPECT_TRUE(area->Key(0).is_null());
if (values_cached)
EXPECT_TRUE(area->GetItem(kKey).is_null());
EXPECT_FALSE(
area->SetItem(kKey, kValue, old_nullable_value, &old_nullable_value));
EXPECT_FALSE(area->RemoveItem(kKey, old_nullable_value, &old_value));
EXPECT_FALSE(area->Clear());
}
TEST_F(DOMStorageAreaTest, BackingDatabaseOpened) {
const std::string kSessionStorageNamespaceId = "id1";
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
const base::FilePath kExpectedOriginFilePath = temp_dir.GetPath().Append(
DOMStorageArea::DatabaseFileNameFromOrigin(kOrigin));
// Valid directory and origin but no session storage backing. Backing should
// be null.
{
scoped_refptr<DOMStorageArea> area(new DOMStorageArea(
kSessionStorageNamespaceId, std::vector<std::string>(), kOrigin,
nullptr, nullptr));
EXPECT_EQ(nullptr, area->backing_.get());
base::NullableString16 old_value;
EXPECT_TRUE(area->SetItem(kKey, kValue, old_value, &old_value));
ASSERT_TRUE(old_value.is_null());
// Check that saving a value has still left us without a backing database.
EXPECT_EQ(nullptr, area->backing_.get());
EXPECT_FALSE(base::PathExists(kExpectedOriginFilePath));
}
}
TEST_P(DOMStorageAreaParamTest, ShallowCopyWithBacking) {
const std::string kFirstNamespaceId = "id1";
const std::string kSecondNamespaceId = "id2";
const std::string kThirdNamespaceId = "id3";
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
scoped_refptr<SessionStorageDatabase> db = new SessionStorageDatabase(
temp_dir.GetPath(), base::ThreadTaskRunnerHandle::Get());
scoped_refptr<DOMStorageArea> area(new DOMStorageArea(
kFirstNamespaceId, std::vector<std::string>(), kOrigin, db.get(),
new MockDOMStorageTaskRunner(base::ThreadTaskRunnerHandle::Get().get())));
EXPECT_TRUE(area->backing_.get());
EXPECT_TRUE(area->session_storage_backing_);
const bool values_cached = GetParam();
area->SetCacheOnlyKeys(!values_cached);
scoped_refptr<DOMStorageArea> temp_copy;
temp_copy = area->ShallowCopy(kSecondNamespaceId);
EXPECT_TRUE(temp_copy->commit_batches_.empty());
temp_copy->ClearShallowCopiedCommitBatches();
// Check if shallow copy is consistent.
base::string16 old_value;
base::NullableString16 old_nullable_value;
scoped_refptr<DOMStorageArea> copy;
EXPECT_TRUE(
area->SetItem(kKey, kValue, old_nullable_value, &old_nullable_value));
EXPECT_TRUE(area->HasUncommittedChanges());
EXPECT_EQ(DOMStorageArea::CommitBatchHolder::TYPE_CURRENT_BATCH,
area->commit_batches_.front().type);
copy = area->ShallowCopy(kThirdNamespaceId);
EXPECT_EQ(copy->map_.get(), area->map_.get());
EXPECT_EQ(1u, copy->original_namespace_ids_.size());
EXPECT_EQ(kFirstNamespaceId, copy->original_namespace_ids_[0]);
if (!values_cached) {
EXPECT_EQ(area->commit_batches_.front().batch,
copy->commit_batches_.front().batch);
EXPECT_EQ(DOMStorageArea::CommitBatchHolder::TYPE_IN_FLIGHT,
area->commit_batches_.front().type);
EXPECT_EQ(DOMStorageArea::CommitBatchHolder::TYPE_CLONE,
copy->commit_batches_.front().type);
} else {
EXPECT_TRUE(copy->commit_batches_.empty());
}
DOMStorageValuesMap map;
copy->ExtractValues(&map);
EXPECT_EQ(1u, map.size());
EXPECT_EQ(kValue, map[kKey].string());
// Check if copy on write works.
EXPECT_TRUE(
copy->SetItem(kKey2, kValue2, old_nullable_value, &old_nullable_value));
EXPECT_TRUE(copy->GetCurrentCommitBatch());
EXPECT_FALSE(copy->commit_batches_.front().type);
if (!values_cached)
EXPECT_EQ(DOMStorageArea::CommitBatchHolder::TYPE_CLONE,
copy->commit_batches_.back().type);
else
EXPECT_FALSE(copy->HasCommitBatchInFlight());
EXPECT_EQ(1u, area->Length());
// Check clearing of cloned batches.
area->ClearShallowCopiedCommitBatches();
copy->ClearShallowCopiedCommitBatches();
EXPECT_EQ(DOMStorageArea::CommitBatchHolder::TYPE_IN_FLIGHT,
area->commit_batches_.front().type);
EXPECT_FALSE(copy->HasCommitBatchInFlight());
}
TEST_F(DOMStorageAreaTest, SetCacheOnlyKeysWithoutBacking) {
const std::string kFirstNamespaceId = "id1";
scoped_refptr<DOMStorageArea> area(
new DOMStorageArea(kFirstNamespaceId, std::vector<std::string>(), kOrigin,
nullptr, nullptr));
EXPECT_EQ(DOMStorageArea::LOAD_STATE_KEYS_AND_VALUES,
area->desired_load_state_);
EXPECT_FALSE(area->map_->has_only_keys());
base::NullableString16 old_value;
EXPECT_TRUE(area->SetItem(kKey, kValue, old_value, &old_value));
DOMStorageValuesMap map;
area->ExtractValues(&map);
EXPECT_EQ(1u, map.size());
area->SetCacheOnlyKeys(true);
EXPECT_EQ(DOMStorageArea::LOAD_STATE_KEYS_AND_VALUES,
area->desired_load_state_); // cannot be disabled without backing.
area->SetItem(kKey, kValue2, old_value, &old_value);
EXPECT_FALSE(area->map_->has_only_keys());
EXPECT_EQ(kValue, old_value.string());
area->ExtractValues(&map);
EXPECT_EQ(kValue2, map[kKey].string());
EXPECT_EQ(1u, map.size());
}
TEST_F(DOMStorageAreaTest, SetCacheOnlyKeysWithBacking) {
const std::string kNamespaceId = "id1";
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
scoped_refptr<SessionStorageDatabase> db = new SessionStorageDatabase(
temp_dir.GetPath(), base::ThreadTaskRunnerHandle::Get());
scoped_refptr<DOMStorageArea> area(new DOMStorageArea(
kNamespaceId, std::vector<std::string>(), kOrigin, db.get(),
new MockDOMStorageTaskRunner(base::ThreadTaskRunnerHandle::Get().get())));
EXPECT_TRUE(area->backing_.get());
EXPECT_EQ(DOMStorageArea::LOAD_STATE_UNLOADED, area->load_state_);
#if !defined(OS_ANDROID)
EXPECT_EQ(DOMStorageArea::LOAD_STATE_KEYS_AND_VALUES,
area->desired_load_state_);
area->SetCacheOnlyKeys(true);
#endif
EXPECT_EQ(DOMStorageArea::LOAD_STATE_KEYS_ONLY, area->desired_load_state_);
base::NullableString16 old_value;
EXPECT_TRUE(area->SetItem(kKey, kValue, old_value, &old_value));
EXPECT_EQ(DOMStorageArea::LOAD_STATE_KEYS_ONLY, area->load_state_);
EXPECT_TRUE(area->GetCurrentCommitBatch());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(area->GetCurrentCommitBatch());
EXPECT_EQ(1u, area->Length());
// Fill the current batch and in flight batch.
EXPECT_TRUE(area->SetItem(kKey2, kValue, old_value, &old_value));
area->PostCommitTask();
EXPECT_FALSE(area->GetCurrentCommitBatch());
EXPECT_TRUE(area->HasCommitBatchInFlight());
EXPECT_TRUE(area->SetItem(kKey2, kValue2, old_value, &old_value));
EXPECT_TRUE(area->GetCurrentCommitBatch());
// The values must be imported from the backing, and from the commit batches.
area->SetCacheOnlyKeys(false);
EXPECT_EQ(2u, area->Length());
EXPECT_EQ(kValue, area->GetItem(kKey).string());
EXPECT_EQ(kValue2, area->GetItem(kKey2).string());
// Check if disabling cache clears the cache after committing only.
area->SetCacheOnlyKeys(true);
EXPECT_EQ(DOMStorageArea::LOAD_STATE_KEYS_ONLY, area->desired_load_state_);
EXPECT_EQ(DOMStorageArea::LOAD_STATE_KEYS_AND_VALUES, area->load_state_);
ASSERT_FALSE(area->map_->has_only_keys());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(DOMStorageArea::LOAD_STATE_KEYS_ONLY, area->load_state_);
EXPECT_TRUE(area->map_->has_only_keys());
EXPECT_FALSE(area->HasCommitBatchInFlight());
// Check if Clear() works as expected when values are desired.
area->Clear();
EXPECT_TRUE(area->SetItem(kKey2, kValue2, old_value, &old_value));
area->PostCommitTask();
EXPECT_TRUE(area->SetItem(kKey, kValue, old_value, &old_value));
EXPECT_EQ(2u, area->Length());
area->Clear();
EXPECT_TRUE(area->SetItem(kKey2, kValue, old_value, &old_value));
EXPECT_TRUE(area->GetCurrentCommitBatch()->batch->clear_all_first);
EXPECT_TRUE(area->commit_batches_.back().batch->clear_all_first);
area->SetCacheOnlyKeys(false);
EXPECT_EQ(DOMStorageArea::LOAD_STATE_KEYS_ONLY, area->load_state_);
// Unload only after commit.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(DOMStorageArea::LOAD_STATE_UNLOADED, area->load_state_);
EXPECT_EQ(1u, area->Length());
EXPECT_EQ(kValue, area->GetItem(kKey2).string());
EXPECT_EQ(DOMStorageArea::LOAD_STATE_KEYS_AND_VALUES, area->load_state_);
}
TEST_P(DOMStorageAreaParamTest, CommitTasks) {
const std::string kNamespaceId = "id1";
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
scoped_refptr<SessionStorageDatabase> db = new SessionStorageDatabase(
temp_dir.GetPath(), base::ThreadTaskRunnerHandle::Get());
scoped_refptr<DOMStorageArea> area(new DOMStorageArea(
kNamespaceId, std::vector<std::string>(), kOrigin, db.get(),
new MockDOMStorageTaskRunner(base::ThreadTaskRunnerHandle::Get().get())));
area->SetCacheOnlyKeys(GetParam());
// Unrelated to commits, but while we're here, see that querying Length()
// causes the backing database to be opened and presumably read from.
EXPECT_EQ(DOMStorageArea::LOAD_STATE_UNLOADED, area->load_state_);
EXPECT_EQ(0u, area->Length());
EXPECT_EQ(area->desired_load_state_, area->load_state_);
DOMStorageValuesMap values;
base::NullableString16 old_value;
// See that changes are batched up.
EXPECT_FALSE(area->GetCurrentCommitBatch());
EXPECT_TRUE(area->SetItem(kKey, kValue, old_value, &old_value));
EXPECT_TRUE(area->HasUncommittedChanges());
EXPECT_TRUE(area->GetCurrentCommitBatch());
EXPECT_FALSE(area->GetCurrentCommitBatch()->batch->clear_all_first);
EXPECT_EQ(1u, area->GetCurrentCommitBatch()->batch->changed_values.size());
EXPECT_TRUE(area->SetItem(kKey2, kValue2, old_value, &old_value));
EXPECT_FALSE(area->GetCurrentCommitBatch()->batch->clear_all_first);
EXPECT_EQ(2u, area->GetCurrentCommitBatch()->batch->changed_values.size());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(area->HasUncommittedChanges());
EXPECT_FALSE(area->GetCurrentCommitBatch());
EXPECT_FALSE(area->HasCommitBatchInFlight());
// Verify the changes made it to the database.
values.clear();
area->backing_->ReadAllValues(&values);
EXPECT_EQ(2u, values.size());
EXPECT_EQ(kValue, values[kKey].string());
EXPECT_EQ(kValue2, values[kKey2].string());
// See that clear is handled properly.
EXPECT_TRUE(area->Clear());
EXPECT_TRUE(area->GetCurrentCommitBatch());
EXPECT_TRUE(area->GetCurrentCommitBatch()->batch->clear_all_first);
EXPECT_TRUE(area->GetCurrentCommitBatch()->batch->changed_values.empty());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(area->GetCurrentCommitBatch());
EXPECT_FALSE(area->HasCommitBatchInFlight());
// Verify the changes made it to the database.
values.clear();
area->backing_->ReadAllValues(&values);
EXPECT_TRUE(values.empty());
}
TEST_P(DOMStorageAreaParamTest, ChangesDuringInflightCommitGetCommitted) {
const std::string kNamespaceId = "id1";
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
scoped_refptr<SessionStorageDatabase> db = new SessionStorageDatabase(
temp_dir.GetPath(), base::ThreadTaskRunnerHandle::Get());
scoped_refptr<DOMStorageAreaMock> area(new DOMStorageAreaMock(
kNamespaceId, std::vector<std::string>(), kOrigin, db.get(),
new MockDOMStorageTaskRunner(base::ThreadTaskRunnerHandle::Get().get())));
area->SetCacheOnlyKeys(GetParam());
DOMStorageValuesMap values;
base::NullableString16 old_value;
EXPECT_CALL(*area, AfterOnCommitTimerCalled)
.WillOnce(Invoke([&]() {
// At this point the OnCommitTimer has run.
// Verify that it put a commit in flight.
EXPECT_TRUE(area->HasCommitBatchInFlight());
EXPECT_FALSE(area->GetCurrentCommitBatch());
EXPECT_TRUE(area->HasUncommittedChanges());
// Make additional change and verify that a new commit batch
// is created for that change.
base::NullableString16 old_value;
EXPECT_TRUE(area->SetItem(kKey2, kValue2, old_value, &old_value));
EXPECT_TRUE(area->GetCurrentCommitBatch());
EXPECT_TRUE(area->HasCommitBatchInFlight());
EXPECT_TRUE(area->HasUncommittedChanges());
}))
.WillOnce(Invoke([&]() {
// At this point the OnCommitTimer has run.
// Verify that it put a commit in flight.
EXPECT_TRUE(area->HasCommitBatchInFlight());
EXPECT_FALSE(area->GetCurrentCommitBatch());
EXPECT_TRUE(area->HasUncommittedChanges());
}));
// See that if changes accrue while a commit is "in flight"
// those will also get committed.
EXPECT_TRUE(area->SetItem(kKey, kValue, old_value, &old_value));
EXPECT_TRUE(area->HasUncommittedChanges());
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(area->HasOneRef());
EXPECT_FALSE(area->HasUncommittedChanges());
// Verify the changes made it to the database.
values.clear();
area->ReadAllValuesFromBackingStore(&values);
EXPECT_EQ(2u, values.size());
EXPECT_EQ(kValue, values[kKey].string());
EXPECT_EQ(kValue2, values[kKey2].string());
}
TEST_P(DOMStorageAreaParamTest, CommitChangesAtShutdown) {
const std::string kNamespaceId = "id1";
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
scoped_refptr<SessionStorageDatabase> db = new SessionStorageDatabase(
temp_dir.GetPath(), base::ThreadTaskRunnerHandle::Get());
scoped_refptr<DOMStorageArea> area(new DOMStorageArea(
kNamespaceId, std::vector<std::string>(), kOrigin, db.get(),
new MockDOMStorageTaskRunner(base::ThreadTaskRunnerHandle::Get().get())));
area->SetCacheOnlyKeys(GetParam());
DOMStorageValuesMap values;
base::NullableString16 old_value;
EXPECT_TRUE(area->SetItem(kKey, kValue, old_value, &old_value));
EXPECT_TRUE(area->HasUncommittedChanges());
area->backing_->ReadAllValues(&values);
EXPECT_TRUE(values.empty()); // not committed yet
area->Shutdown();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(area->HasOneRef());
EXPECT_FALSE(area->backing_.get());
// Verify changes were committed.
db->ReadAreaValues(kNamespaceId, std::vector<std::string>(), kOrigin,
&values);
EXPECT_EQ(1u, values.size());
EXPECT_EQ(kValue, values[kKey].string());
// A second Shutdown call should be safe.
area->Shutdown();
}
TEST_P(DOMStorageAreaParamTest, PurgeMemory) {
const std::string kNamespaceId = "id1";
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
scoped_refptr<SessionStorageDatabase> db = new SessionStorageDatabase(
temp_dir.GetPath(), base::ThreadTaskRunnerHandle::Get());
scoped_refptr<DOMStorageArea> area(new DOMStorageArea(
kNamespaceId, std::vector<std::string>(), kOrigin, db.get(),
new MockDOMStorageTaskRunner(base::ThreadTaskRunnerHandle::Get().get())));
area->SetCacheOnlyKeys(GetParam());
// Unowned ptr we use to verify that 'purge' has happened.
DOMStorageMap* original_map = area->map_.get();
// Should do no harm when called on a newly constructed object.
EXPECT_EQ(DOMStorageArea::LOAD_STATE_UNLOADED, area->load_state_);
area->PurgeMemory();
EXPECT_EQ(DOMStorageArea::LOAD_STATE_UNLOADED, area->load_state_);
EXPECT_EQ(original_map, area->map_.get());
// Should not do anything when commits are pending.
base::NullableString16 old_value;
area->SetItem(kKey, kValue, old_value, &old_value);
original_map = area->map_.get(); // importing creates new map.
EXPECT_EQ(area->desired_load_state_, area->load_state_);
EXPECT_TRUE(area->HasUncommittedChanges());
area->PurgeMemory();
EXPECT_EQ(area->desired_load_state_, area->load_state_);
EXPECT_TRUE(area->HasUncommittedChanges());
EXPECT_EQ(original_map, area->map_.get());
// Commit the changes from above,
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(area->HasUncommittedChanges());
EXPECT_EQ(original_map, area->map_.get());
// Should drop caches and reset database connections
// when invoked on an area that's loaded up primed.
area->PurgeMemory();
EXPECT_EQ(DOMStorageArea::LOAD_STATE_UNLOADED, area->load_state_);
EXPECT_NE(original_map, area->map_.get());
}
TEST_F(DOMStorageAreaTest, DatabaseFileNames) {
struct {
const char* origin;
const char* file_name;
} kCases[] = {
{"https://www.google.com/", "https_www.google.com_0.localstorage"},
{"http://www.google.com:8080/", "http_www.google.com_8080.localstorage"},
{"file:///", "file__0.localstorage"},
};
for (size_t i = 0; i < base::size(kCases); ++i) {
url::Origin origin =
url::Origin::Create(GURL(kCases[i].origin).GetOrigin());
base::FilePath file_name =
base::FilePath().AppendASCII(kCases[i].file_name);
EXPECT_EQ(file_name,
DOMStorageArea::DatabaseFileNameFromOrigin(origin));
EXPECT_EQ(origin,
DOMStorageArea::OriginFromDatabaseFileName(file_name));
}
}
TEST_F(DOMStorageAreaTest, RateLimiter) {
// Limit to 1000 samples per second
DOMStorageArea::RateLimiter rate_limiter(
1000, base::TimeDelta::FromSeconds(1));
// No samples have been added so no time/delay should be needed.
EXPECT_EQ(base::TimeDelta(),
rate_limiter.ComputeTimeNeeded());
EXPECT_EQ(base::TimeDelta(),
rate_limiter.ComputeDelayNeeded(base::TimeDelta()));
EXPECT_EQ(base::TimeDelta(),
rate_limiter.ComputeDelayNeeded(base::TimeDelta::FromDays(1)));
// Add a seconds worth of samples.
rate_limiter.add_samples(1000);
EXPECT_EQ(base::TimeDelta::FromSeconds(1),
rate_limiter.ComputeTimeNeeded());
EXPECT_EQ(base::TimeDelta::FromSeconds(1),
rate_limiter.ComputeDelayNeeded(base::TimeDelta()));
EXPECT_EQ(base::TimeDelta(),
rate_limiter.ComputeDelayNeeded(base::TimeDelta::FromSeconds(1)));
EXPECT_EQ(base::TimeDelta::FromMilliseconds(250),
rate_limiter.ComputeDelayNeeded(
base::TimeDelta::FromMilliseconds(750)));
EXPECT_EQ(base::TimeDelta(),
rate_limiter.ComputeDelayNeeded(
base::TimeDelta::FromDays(1)));
// And another half seconds worth.
rate_limiter.add_samples(500);
EXPECT_EQ(base::TimeDelta::FromMilliseconds(1500),
rate_limiter.ComputeTimeNeeded());
EXPECT_EQ(base::TimeDelta::FromMilliseconds(1500),
rate_limiter.ComputeDelayNeeded(base::TimeDelta()));
EXPECT_EQ(base::TimeDelta::FromMilliseconds(500),
rate_limiter.ComputeDelayNeeded(base::TimeDelta::FromSeconds(1)));
EXPECT_EQ(base::TimeDelta::FromMilliseconds(750),
rate_limiter.ComputeDelayNeeded(
base::TimeDelta::FromMilliseconds(750)));
EXPECT_EQ(base::TimeDelta(),
rate_limiter.ComputeDelayNeeded(
base::TimeDelta::FromMilliseconds(1500)));
EXPECT_EQ(base::TimeDelta(),
rate_limiter.ComputeDelayNeeded(
base::TimeDelta::FromDays(1)));
}
} // namespace content