blob: 5e9bdcc45eb20bb31af1007c2895ab3d54dae2ab [file] [log] [blame]
// 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 <stdint.h>
#include <memory>
#include <set>
#include "base/macros.h"
#include "base/test/task_environment.h"
#include "base/test/test_simple_task_runner.h"
#include "content/browser/indexed_db/indexed_db_active_blob_registry.h"
#include "content/browser/indexed_db/indexed_db_backing_store.h"
#include "content/browser/indexed_db/indexed_db_fake_backing_store.h"
#include "content/browser/indexed_db/mock_indexed_db_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/origin.h"
namespace content {
namespace {
class RegistryTestMockFactory : public MockIndexedDBFactory {
public:
RegistryTestMockFactory() : duplicate_calls_(false) {}
~RegistryTestMockFactory() override = default;
void ReportOutstandingBlobs(const url::Origin& origin,
bool blobs_outstanding) override {
if (blobs_outstanding) {
if (origins_.count(origin)) {
duplicate_calls_ = true;
} else {
origins_.insert(origin);
}
} else {
if (!origins_.count(origin)) {
duplicate_calls_ = true;
} else {
origins_.erase(origin);
}
}
}
bool CheckNoOriginsInUse() const {
return !duplicate_calls_ && origins_.empty();
}
bool CheckSingleOriginInUse(const url::Origin& origin) const {
return !duplicate_calls_ && origins_.size() == 1 && origins_.count(origin);
}
private:
std::set<url::Origin> origins_;
bool duplicate_calls_;
DISALLOW_COPY_AND_ASSIGN(RegistryTestMockFactory);
};
class MockIDBBackingStore : public IndexedDBFakeBackingStore {
public:
typedef std::pair<int64_t, int64_t> KeyPair;
typedef std::set<KeyPair> KeyPairSet;
MockIDBBackingStore(IndexedDBFactory* factory,
base::SequencedTaskRunner* task_runner)
: IndexedDBFakeBackingStore(factory, task_runner),
duplicate_calls_(false) {}
~MockIDBBackingStore() override = default;
void ReportBlobUnused(int64_t database_id, int64_t blob_key) override {
unused_blobs_.insert({database_id, blob_key});
}
bool CheckUnusedBlobsEmpty() const {
return !duplicate_calls_ && !unused_blobs_.size();
}
bool CheckSingleUnusedBlob(int64_t database_id, int64_t blob_key) const {
return !duplicate_calls_ && unused_blobs_.size() == 1 &&
unused_blobs_.count({database_id, blob_key});
}
const KeyPairSet& unused_blobs() const { return unused_blobs_; }
private:
KeyPairSet unused_blobs_;
bool duplicate_calls_;
DISALLOW_COPY_AND_ASSIGN(MockIDBBackingStore);
};
// Base class for our test fixtures.
class IndexedDBActiveBlobRegistryTest : public testing::Test {
public:
typedef IndexedDBBlobInfo::ReleaseCallback ReleaseCallback;
static const int64_t kDatabaseId0 = 7;
static const int64_t kDatabaseId1 = 12;
static const int64_t kBlobKey0 = 77;
static const int64_t kBlobKey1 = 14;
IndexedDBActiveBlobRegistryTest()
: task_runner_(new base::TestSimpleTaskRunner),
factory_(new RegistryTestMockFactory),
backing_store_(
new MockIDBBackingStore(factory_.get(), task_runner_.get())),
registry_(std::make_unique<IndexedDBActiveBlobRegistry>(
backing_store_.get())) {}
void RunUntilIdle() { task_runner_->RunUntilIdle(); }
RegistryTestMockFactory* factory() const { return factory_.get(); }
MockIDBBackingStore* backing_store() const { return backing_store_.get(); }
IndexedDBActiveBlobRegistry* registry() const { return registry_.get(); }
private:
base::test::TaskEnvironment task_environment_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
std::unique_ptr<RegistryTestMockFactory> factory_;
std::unique_ptr<MockIDBBackingStore> backing_store_;
std::unique_ptr<IndexedDBActiveBlobRegistry> registry_;
DISALLOW_COPY_AND_ASSIGN(IndexedDBActiveBlobRegistryTest);
};
TEST_F(IndexedDBActiveBlobRegistryTest, DeleteUnused) {
EXPECT_TRUE(factory()->CheckNoOriginsInUse());
EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
EXPECT_FALSE(registry()->MarkDeletedCheckIfUsed(kDatabaseId0, kBlobKey0));
RunUntilIdle();
EXPECT_TRUE(factory()->CheckNoOriginsInUse());
EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
}
TEST_F(IndexedDBActiveBlobRegistryTest, SimpleUse) {
EXPECT_TRUE(factory()->CheckNoOriginsInUse());
EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
base::Closure add_ref =
registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey0);
ReleaseCallback release =
registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey0);
std::move(add_ref).Run();
RunUntilIdle();
EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin()));
EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
std::move(release).Run(base::FilePath());
RunUntilIdle();
EXPECT_TRUE(factory()->CheckNoOriginsInUse());
EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
}
TEST_F(IndexedDBActiveBlobRegistryTest, DeleteWhileInUse) {
EXPECT_TRUE(factory()->CheckNoOriginsInUse());
EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
base::Closure add_ref =
registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey0);
ReleaseCallback release =
registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey0);
std::move(add_ref).Run();
RunUntilIdle();
EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin()));
EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
EXPECT_TRUE(registry()->MarkDeletedCheckIfUsed(kDatabaseId0, kBlobKey0));
RunUntilIdle();
EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin()));
EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
std::move(release).Run(base::FilePath());
RunUntilIdle();
EXPECT_TRUE(factory()->CheckNoOriginsInUse());
EXPECT_TRUE(backing_store()->CheckSingleUnusedBlob(kDatabaseId0, kBlobKey0));
}
TEST_F(IndexedDBActiveBlobRegistryTest, MultipleBlobs) {
EXPECT_TRUE(factory()->CheckNoOriginsInUse());
EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
base::Closure add_ref_00 =
registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey0);
ReleaseCallback release_00 =
registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey0);
base::Closure add_ref_01 =
registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey1);
ReleaseCallback release_01 =
registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey1);
base::Closure add_ref_10 =
registry()->GetAddBlobRefCallback(kDatabaseId1, kBlobKey0);
ReleaseCallback release_10 =
registry()->GetFinalReleaseCallback(kDatabaseId1, kBlobKey0);
base::Closure add_ref_11 =
registry()->GetAddBlobRefCallback(kDatabaseId1, kBlobKey1);
ReleaseCallback release_11 =
registry()->GetFinalReleaseCallback(kDatabaseId1, kBlobKey1);
std::move(add_ref_00).Run();
std::move(add_ref_01).Run();
RunUntilIdle();
EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin()));
EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
std::move(release_00).Run(base::FilePath());
std::move(add_ref_10).Run();
std::move(add_ref_11).Run();
RunUntilIdle();
EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin()));
EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
EXPECT_TRUE(registry()->MarkDeletedCheckIfUsed(kDatabaseId0, kBlobKey1));
RunUntilIdle();
EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin()));
EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
std::move(release_01).Run(base::FilePath());
std::move(release_11).Run(base::FilePath());
RunUntilIdle();
EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin()));
EXPECT_TRUE(backing_store()->CheckSingleUnusedBlob(kDatabaseId0, kBlobKey1));
std::move(release_10).Run(base::FilePath());
RunUntilIdle();
EXPECT_TRUE(factory()->CheckNoOriginsInUse());
EXPECT_TRUE(backing_store()->CheckSingleUnusedBlob(kDatabaseId0, kBlobKey1));
}
TEST_F(IndexedDBActiveBlobRegistryTest, ForceShutdown) {
EXPECT_TRUE(factory()->CheckNoOriginsInUse());
EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
base::Closure add_ref_0 =
registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey0);
ReleaseCallback release_0 =
registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey0);
base::Closure add_ref_1 =
registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey1);
ReleaseCallback release_1 =
registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey1);
std::move(add_ref_0).Run();
RunUntilIdle();
EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin()));
EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
registry()->ForceShutdown();
std::move(add_ref_1).Run();
RunUntilIdle();
// Nothing changes.
EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin()));
EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
std::move(release_0).Run(base::FilePath());
std::move(release_1).Run(base::FilePath());
RunUntilIdle();
// Nothing changes.
EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin()));
EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
}
} // namespace
} // namespace content