Chromium Code Reviews| Index: content/browser/indexed_db/indexed_db_backing_store_unittest.cc |
| diff --git a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc |
| index 8d7307744a4db821ada95e4cade89899fbd45914..a4c44c13193407e345ee1ef931ff6b92b88c715f 100644 |
| --- a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc |
| +++ b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc |
| @@ -4,13 +4,23 @@ |
| #include "content/browser/indexed_db/indexed_db_backing_store.h" |
| +#include "base/callback.h" |
| +#include "base/file_util.h" |
| +#include "base/files/scoped_temp_dir.h" |
| #include "base/logging.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/utf_string_conversions.h" |
| +#include "base/task_runner.h" |
| +#include "base/test/test_simple_task_runner.h" |
| +#include "content/browser/indexed_db/indexed_db_context_impl.h" |
| #include "content/browser/indexed_db/indexed_db_leveldb_coding.h" |
| #include "content/browser/indexed_db/indexed_db_value.h" |
| +#include "content/public/test/mock_special_storage_policy.h" |
| +#include "net/url_request/url_request_test_util.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/WebKit/public/platform/WebIDBTypes.h" |
| +#include "webkit/browser/blob/blob_data_handle.h" |
| +#include "webkit/browser/quota/special_storage_policy.h" |
| using base::ASCIIToUTF16; |
| @@ -18,35 +28,321 @@ namespace content { |
| namespace { |
| +class Comparator : public LevelDBComparator { |
| + public: |
| + virtual int Compare(const base::StringPiece& a, |
| + const base::StringPiece& b) const OVERRIDE { |
| + return content::Compare(a, b, false /*index_keys*/); |
| + } |
| + virtual const char* Name() const OVERRIDE { return "idb_cmp1"; } |
| +}; |
| + |
| +class DefaultLevelDBFactory : public LevelDBFactory { |
| + public: |
| + virtual leveldb::Status OpenLevelDB(const base::FilePath& file_name, |
| + const LevelDBComparator* comparator, |
| + scoped_ptr<LevelDBDatabase>* db, |
| + bool* is_disk_full) OVERRIDE { |
| + return LevelDBDatabase::Open(file_name, comparator, db, is_disk_full); |
| + } |
| + virtual leveldb::Status DestroyLevelDB( |
| + const base::FilePath& file_name) OVERRIDE { |
| + return LevelDBDatabase::Destroy(file_name); |
| + } |
| +}; |
| + |
| +class TestableIndexedDBBackingStore : public IndexedDBBackingStore { |
| + public: |
| + static scoped_refptr<TestableIndexedDBBackingStore> Open( |
| + IndexedDBFactory* indexed_db_factory, |
| + const GURL& origin_url, |
| + const base::FilePath& path_base, |
| + net::URLRequestContext* request_context, |
| + LevelDBFactory* leveldb_factory, |
| + base::TaskRunner* task_runner) { |
| + DCHECK(!path_base.empty()); |
| + |
| + scoped_ptr<LevelDBComparator> comparator(new Comparator()); |
| + |
| + if (!base::CreateDirectory(path_base)) |
| + return scoped_refptr<TestableIndexedDBBackingStore>(); |
| + |
| + const base::FilePath file_path = path_base.AppendASCII("test_db_path"); |
| + const base::FilePath blob_path = path_base.AppendASCII("test_blob_path"); |
| + |
| + scoped_ptr<LevelDBDatabase> db; |
| + bool is_disk_full = false; |
| + leveldb::Status status = leveldb_factory->OpenLevelDB( |
| + file_path, comparator.get(), &db, &is_disk_full); |
| + |
| + if (!db || !status.ok()) |
| + return scoped_refptr<TestableIndexedDBBackingStore>(); |
| + |
| + scoped_refptr<TestableIndexedDBBackingStore> backing_store( |
| + new TestableIndexedDBBackingStore(indexed_db_factory, |
| + origin_url, |
| + blob_path, |
| + request_context, |
| + db.Pass(), |
| + comparator.Pass(), |
| + task_runner)); |
| + |
| + if (!backing_store->SetUpMetadata()) |
| + return scoped_refptr<TestableIndexedDBBackingStore>(); |
| + |
| + return backing_store; |
| + } |
| + |
| + const std::vector<IndexedDBBackingStore::Transaction::WriteDescriptor>& |
| + writes() const { |
| + return writes_; |
| + } |
| + void ClearWrites() { writes_.clear(); } |
| + const std::vector<int64>& removals() const { return removals_; } |
| + void ClearRemovals() { removals_.clear(); } |
| + |
| + protected: |
| + virtual ~TestableIndexedDBBackingStore() {} |
| + |
| + virtual bool WriteBlobFile( |
| + int64 database_id, |
| + const Transaction::WriteDescriptor& descriptor, |
| + Transaction::ChainedBlobWriter* chained_blob_writer) OVERRIDE { |
| + if (KeyPrefix::IsValidDatabaseId(database_id_)) { |
| + if (database_id_ != database_id) { |
| + return false; |
| + } |
| + } else { |
| + database_id_ = database_id; |
| + } |
| + writes_.push_back(descriptor); |
| + task_runner()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion, |
| + chained_blob_writer, |
| + true, |
| + 1)); |
| + return true; |
| + } |
| + |
| + virtual bool RemoveBlobFile(int64 database_id, int64 key) OVERRIDE { |
| + if (database_id_ != database_id || |
| + !KeyPrefix::IsValidDatabaseId(database_id)) { |
| + return false; |
| + } |
| + removals_.push_back(key); |
| + return true; |
| + } |
| + |
| + // Timers don't play nicely with unit tests. |
| + virtual void StartJournalCleaningTimer() OVERRIDE { |
| + CleanPrimaryJournalIgnoreReturn(); |
| + } |
| + |
| + private: |
| + TestableIndexedDBBackingStore(IndexedDBFactory* indexed_db_factory, |
| + const GURL& origin_url, |
| + const base::FilePath& blob_path, |
| + net::URLRequestContext* request_context, |
| + scoped_ptr<LevelDBDatabase> db, |
| + scoped_ptr<LevelDBComparator> comparator, |
| + base::TaskRunner* task_runner) |
| + : IndexedDBBackingStore(indexed_db_factory, |
| + origin_url, |
| + blob_path, |
| + request_context, |
| + db.Pass(), |
| + comparator.Pass(), |
| + task_runner), |
| + database_id_(0) {} |
| + |
| + int64 database_id_; |
| + std::vector<Transaction::WriteDescriptor> writes_; |
| + std::vector<int64> removals_; |
| +}; |
| + |
| +class TestIDBFactory : public IndexedDBFactory { |
| + public: |
| + TestIDBFactory(IndexedDBContextImpl* idb_context) |
| + : IndexedDBFactory(idb_context) {} |
| + |
| + scoped_refptr<TestableIndexedDBBackingStore> OpenBackingStoreForTest( |
| + const GURL& origin, |
| + net::URLRequestContext* url_request_context) { |
| + blink::WebIDBDataLoss data_loss; |
| + std::string data_loss_reason; |
| + bool disk_full; |
| + scoped_refptr<IndexedDBBackingStore> backing_store = |
| + OpenBackingStore(origin, |
| + context()->data_path(), |
| + url_request_context, |
| + &data_loss, |
| + &data_loss_reason, |
| + &disk_full); |
| + scoped_refptr<TestableIndexedDBBackingStore> testable_store = |
| + static_cast<TestableIndexedDBBackingStore*>(backing_store.get()); |
| + return testable_store; |
| + } |
| + |
| + protected: |
| + virtual ~TestIDBFactory() {} |
| + |
| + virtual scoped_refptr<IndexedDBBackingStore> OpenBackingStoreHelper( |
| + const GURL& origin_url, |
| + const base::FilePath& data_directory, |
| + net::URLRequestContext* request_context, |
| + blink::WebIDBDataLoss* data_loss, |
| + std::string* data_loss_message, |
| + bool* disk_full, |
| + bool first_time) OVERRIDE { |
| + DefaultLevelDBFactory leveldb_factory; |
| + return TestableIndexedDBBackingStore::Open(this, |
| + origin_url, |
| + data_directory, |
| + request_context, |
| + &leveldb_factory, |
| + context()->TaskRunner()); |
| + } |
| +}; |
| + |
| class IndexedDBBackingStoreTest : public testing::Test { |
| public: |
| IndexedDBBackingStoreTest() {} |
| virtual void SetUp() { |
| const GURL origin("http://localhost:81"); |
| + task_runner_ = new base::TestSimpleTaskRunner(); |
| + special_storage_policy_ = new MockSpecialStoragePolicy(); |
| + special_storage_policy_->SetAllUnlimited(true); |
| + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| + idb_context_ = new IndexedDBContextImpl( |
| + temp_dir_.path(), special_storage_policy_, NULL, task_runner_); |
| + idb_factory_ = new TestIDBFactory(idb_context_); |
| backing_store_ = |
| - IndexedDBBackingStore::OpenInMemory(origin, NULL /* task_runner */); |
| + idb_factory_->OpenBackingStoreForTest(origin, &url_request_context_); |
| // useful keys and values during tests |
| m_value1 = IndexedDBValue("value1", std::vector<IndexedDBBlobInfo>()); |
| m_value2 = IndexedDBValue("value2", std::vector<IndexedDBBlobInfo>()); |
| + m_blob_info.push_back( |
| + IndexedDBBlobInfo("uuid 3", base::UTF8ToUTF16("blob type"), 1)); |
| + m_blob_info.push_back( |
| + IndexedDBBlobInfo("uuid 4", |
| + base::FilePath(FILE_PATH_LITERAL("path/to/file")), |
| + base::UTF8ToUTF16("file name"), |
| + base::UTF8ToUTF16("file type"))); |
| + m_value3 = IndexedDBValue("value3", m_blob_info); |
| + |
| m_key1 = IndexedDBKey(99, blink::WebIDBKeyTypeNumber); |
| m_key2 = IndexedDBKey(ASCIIToUTF16("key2")); |
| + m_key3 = IndexedDBKey(ASCIIToUTF16("key3")); |
| + } |
| + |
| + // This just checks the data that survive getting stored and recalled, e.g. |
| + // the file path and UUID will change and thus aren't verified. |
| + bool CheckBlobInfoMatches(const std::vector<IndexedDBBlobInfo>& reads) const { |
| + if (m_blob_info.size() != reads.size()) |
| + return false; |
| + for (size_t i = 0; i < m_blob_info.size(); ++i) { |
| + const IndexedDBBlobInfo& a = m_blob_info[i]; |
| + const IndexedDBBlobInfo& b = reads[i]; |
| + if (a.is_file() != b.is_file()) |
|
jsbell
2014/05/28 21:29:15
Maybe add IndexedDBBlobInfo::Compare ?
ericu
2014/05/28 22:50:42
It's not clear to me that there's a general-purpos
jsbell
2014/05/28 23:35:18
I'm fine with leaving this inline, since some fiel
|
| + return false; |
| + if (a.type() != b.type()) |
| + return false; |
| + if (a.is_file()) { |
| + if (a.file_name() != b.file_name()) |
|
cmumford
2014/05/28 20:34:54
Do you also need to check last_modified?
ericu
2014/05/28 22:50:42
That can change too, for files, since we may not k
|
| + return false; |
| + } else { |
| + if (a.size() != b.size()) |
| + return false; |
| + } |
| + } |
| + return true; |
| + } |
| + |
| + bool CheckBlobReadsMatchWrites( |
| + const std::vector<IndexedDBBlobInfo>& reads) const { |
| + if (backing_store_->writes().size() != reads.size()) |
| + return false; |
| + std::set<int64> ids; |
| + for (size_t i = 0; i < backing_store_->writes().size(); ++i) |
| + ids.insert(backing_store_->writes()[i].key()); |
| + if (ids.size() != backing_store_->writes().size()) |
| + return false; |
| + for (size_t i = 0; i < reads.size(); ++i) { |
| + if (ids.count(reads[i].key()) != 1) |
| + return false; |
| + } |
| + return true; |
| + } |
| + |
| + bool CheckBlobWrites() const { |
| + if (backing_store_->writes().size() != m_blob_info.size()) |
| + return false; |
| + for (size_t i = 0; i < backing_store_->writes().size(); ++i) { |
| + const IndexedDBBackingStore::Transaction::WriteDescriptor& desc = |
| + backing_store_->writes()[i]; |
| + const IndexedDBBlobInfo& info = m_blob_info[i]; |
| + if (desc.is_file() != info.is_file()) |
| + return false; |
| + if (desc.is_file()) { |
| + if (desc.file_path() != info.file_path()) |
| + return false; |
| + } else { |
| + if (desc.url() != GURL("blob:uuid/" + info.uuid())) |
| + return false; |
| + } |
| + } |
| + return true; |
| + } |
| + |
| + bool CheckBlobRemovals() const { |
| + if (backing_store_->removals().size() != backing_store_->writes().size()) |
| + return false; |
| + for (size_t i = 0; i < backing_store_->writes().size(); ++i) |
|
jsbell
2014/05/28 21:29:15
Nit: Needs {}
ericu
2014/05/28 22:50:42
Done.
|
| + if (backing_store_->writes()[i].key() != backing_store_->removals()[i]) |
| + return false; |
| + return true; |
| } |
| protected: |
| - scoped_refptr<IndexedDBBackingStore> backing_store_; |
| + base::ScopedTempDir temp_dir_; |
| + scoped_refptr<base::TestSimpleTaskRunner> task_runner_; |
| + scoped_refptr<MockSpecialStoragePolicy> special_storage_policy_; |
| + scoped_refptr<IndexedDBContextImpl> idb_context_; |
| + scoped_refptr<TestIDBFactory> idb_factory_; |
| + net::TestURLRequestContext url_request_context_; |
| + |
| + scoped_refptr<TestableIndexedDBBackingStore> backing_store_; |
| // Sample keys and values that are consistent. |
| IndexedDBKey m_key1; |
| IndexedDBKey m_key2; |
| + IndexedDBKey m_key3; |
| IndexedDBValue m_value1; |
| IndexedDBValue m_value2; |
| + IndexedDBValue m_value3; |
| + std::vector<IndexedDBBlobInfo> m_blob_info; |
| private: |
| DISALLOW_COPY_AND_ASSIGN(IndexedDBBackingStoreTest); |
| }; |
| +class TestCallback : public IndexedDBBackingStore::BlobWriteCallback { |
| + public: |
| + TestCallback() : called(false), succeeded(false) {} |
| + virtual void Run(bool succeeded_in) OVERRIDE { |
| + called = true; |
| + succeeded = succeeded_in; |
| + } |
| + bool called; |
| + bool succeeded; |
| + |
| + protected: |
| + virtual ~TestCallback() {} |
| +}; |
| + |
| TEST_F(IndexedDBBackingStoreTest, PutGetConsistency) { |
| { |
| IndexedDBBackingStore::Transaction transaction1(backing_store_); |
| @@ -56,21 +352,330 @@ TEST_F(IndexedDBBackingStoreTest, PutGetConsistency) { |
| leveldb::Status s = backing_store_->PutRecord( |
| &transaction1, 1, 1, m_key1, m_value1, &handles, &record); |
| EXPECT_TRUE(s.ok()); |
| - transaction1.Commit(); |
| + scoped_refptr<TestCallback> callback(new TestCallback()); |
| + EXPECT_TRUE(transaction1.CommitPhaseOne(callback).ok()); |
| + EXPECT_TRUE(callback->called); |
| + EXPECT_TRUE(callback->succeeded); |
| + EXPECT_TRUE(transaction1.CommitPhaseTwo().ok()); |
| } |
| { |
| IndexedDBBackingStore::Transaction transaction2(backing_store_); |
| transaction2.Begin(); |
| IndexedDBValue result_value; |
| - leveldb::Status s = |
| - backing_store_->GetRecord(&transaction2, 1, 1, m_key1, &result_value); |
| - transaction2.Commit(); |
| - EXPECT_TRUE(s.ok()); |
| + EXPECT_TRUE( |
| + backing_store_->GetRecord(&transaction2, 1, 1, m_key1, &result_value) |
| + .ok()); |
| + scoped_refptr<TestCallback> callback(new TestCallback()); |
| + EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok()); |
| + EXPECT_TRUE(callback->called); |
| + EXPECT_TRUE(callback->succeeded); |
| + EXPECT_TRUE(transaction2.CommitPhaseTwo().ok()); |
| EXPECT_EQ(m_value1.bits, result_value.bits); |
| } |
| } |
| +TEST_F(IndexedDBBackingStoreTest, PutGetConsistencyWithBlobs) { |
| + { |
| + IndexedDBBackingStore::Transaction transaction1(backing_store_); |
| + transaction1.Begin(); |
| + ScopedVector<webkit_blob::BlobDataHandle> handles; |
| + IndexedDBBackingStore::RecordIdentifier record; |
| + EXPECT_TRUE(backing_store_->PutRecord(&transaction1, |
| + 1, |
| + 1, |
| + m_key3, |
| + m_value3, |
| + &handles, |
| + &record).ok()); |
| + scoped_refptr<TestCallback> callback(new TestCallback()); |
| + EXPECT_TRUE(transaction1.CommitPhaseOne(callback).ok()); |
| + task_runner_->RunUntilIdle(); |
| + EXPECT_TRUE(CheckBlobWrites()); |
| + EXPECT_TRUE(callback->called); |
| + EXPECT_TRUE(callback->succeeded); |
| + EXPECT_TRUE(transaction1.CommitPhaseTwo().ok()); |
| + } |
| + |
| + { |
| + IndexedDBBackingStore::Transaction transaction2(backing_store_); |
| + transaction2.Begin(); |
| + IndexedDBValue result_value; |
| + EXPECT_TRUE( |
| + backing_store_->GetRecord(&transaction2, 1, 1, m_key3, &result_value) |
| + .ok()); |
| + scoped_refptr<TestCallback> callback(new TestCallback()); |
| + EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok()); |
| + EXPECT_TRUE(callback->called); |
| + EXPECT_TRUE(callback->succeeded); |
| + EXPECT_TRUE(transaction2.CommitPhaseTwo().ok()); |
| + EXPECT_EQ(m_value3.bits, result_value.bits); |
| + EXPECT_TRUE(CheckBlobInfoMatches(result_value.blob_info)); |
| + EXPECT_TRUE(CheckBlobReadsMatchWrites(result_value.blob_info)); |
| + } |
| + |
| + { |
| + IndexedDBBackingStore::Transaction transaction3(backing_store_); |
| + transaction3.Begin(); |
| + IndexedDBValue result_value; |
| + EXPECT_TRUE(backing_store_->DeleteRange(&transaction3, |
| + 1, |
| + 1, |
| + IndexedDBKeyRange(m_key3)).ok()); |
| + scoped_refptr<TestCallback> callback(new TestCallback()); |
| + EXPECT_TRUE(transaction3.CommitPhaseOne(callback).ok()); |
| + task_runner_->RunUntilIdle(); |
| + EXPECT_TRUE(callback->called); |
| + EXPECT_TRUE(callback->succeeded); |
| + EXPECT_TRUE(transaction3.CommitPhaseTwo().ok()); |
| + EXPECT_TRUE(CheckBlobRemovals()); |
| + } |
| +} |
| + |
| +TEST_F(IndexedDBBackingStoreTest, DeleteRange) { |
| + IndexedDBKey key0 = IndexedDBKey(ASCIIToUTF16("key0")); |
| + IndexedDBKey key1 = IndexedDBKey(ASCIIToUTF16("key1")); |
| + IndexedDBKey key2 = IndexedDBKey(ASCIIToUTF16("key2")); |
| + IndexedDBKey key3 = IndexedDBKey(ASCIIToUTF16("key3")); |
| + IndexedDBBlobInfo blob0("uuid 0", base::UTF8ToUTF16("type 0"), 1); |
| + IndexedDBBlobInfo blob1("uuid 1", base::UTF8ToUTF16("type 1"), 1); |
| + IndexedDBBlobInfo blob2("uuid 2", base::UTF8ToUTF16("type 2"), 1); |
| + IndexedDBBlobInfo blob3("uuid 3", base::UTF8ToUTF16("type 3"), 1); |
| + IndexedDBKeyRange ranges[] = {IndexedDBKeyRange(key1, key2, false, false), |
| + IndexedDBKeyRange(key1, key2, false, false), |
| + IndexedDBKeyRange(key0, key2, true, false), |
| + IndexedDBKeyRange(key1, key3, false, true), |
| + IndexedDBKeyRange(key0, key3, true, true)}; |
| + |
| + for (unsigned i = 0; i < sizeof(ranges) / sizeof(IndexedDBKeyRange); ++i) { |
| + backing_store_->ClearWrites(); |
| + backing_store_->ClearRemovals(); |
| + |
| + { |
| + std::vector<IndexedDBBlobInfo> blob_info0, blob_info1, blob_info2, |
| + blob_info3; |
| + blob_info0.push_back(blob0); |
| + blob_info1.push_back(blob1); |
| + blob_info2.push_back(blob2); |
| + blob_info3.push_back(blob3); |
| + IndexedDBValue value0 = IndexedDBValue("value0", blob_info0); |
| + IndexedDBValue value1 = IndexedDBValue("value1", blob_info1); |
| + IndexedDBValue value2 = IndexedDBValue("value2", blob_info2); |
| + IndexedDBValue value3 = IndexedDBValue("value3", blob_info3); |
| + IndexedDBBackingStore::Transaction transaction1(backing_store_); |
| + transaction1.Begin(); |
| + ScopedVector<webkit_blob::BlobDataHandle> handles; |
| + IndexedDBBackingStore::RecordIdentifier record; |
| + EXPECT_TRUE(backing_store_->PutRecord(&transaction1, |
| + 1, |
| + i + 1, |
| + key0, |
| + value0, |
| + &handles, |
| + &record).ok()); |
| + EXPECT_TRUE(backing_store_->PutRecord(&transaction1, |
| + 1, |
| + i + 1, |
| + key1, |
| + value1, |
| + &handles, |
| + &record).ok()); |
| + EXPECT_TRUE(backing_store_->PutRecord(&transaction1, |
| + 1, |
| + i + 1, |
| + key2, |
| + value2, |
| + &handles, |
| + &record).ok()); |
| + EXPECT_TRUE(backing_store_->PutRecord(&transaction1, |
| + 1, |
| + i + 1, |
| + key3, |
| + value3, |
| + &handles, |
| + &record).ok()); |
| + scoped_refptr<TestCallback> callback(new TestCallback()); |
| + EXPECT_TRUE(transaction1.CommitPhaseOne(callback).ok()); |
| + task_runner_->RunUntilIdle(); |
| + EXPECT_TRUE(callback->called); |
| + EXPECT_TRUE(callback->succeeded); |
| + EXPECT_TRUE(transaction1.CommitPhaseTwo().ok()); |
| + } |
| + |
| + { |
| + IndexedDBBackingStore::Transaction transaction2(backing_store_); |
| + transaction2.Begin(); |
| + IndexedDBValue result_value; |
| + EXPECT_TRUE( |
| + backing_store_->DeleteRange(&transaction2, 1, i + 1, ranges[i]).ok()); |
| + scoped_refptr<TestCallback> callback(new TestCallback()); |
| + EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok()); |
| + task_runner_->RunUntilIdle(); |
| + EXPECT_TRUE(callback->called); |
| + EXPECT_TRUE(callback->succeeded); |
| + EXPECT_TRUE(transaction2.CommitPhaseTwo().ok()); |
| + EXPECT_EQ(2UL, backing_store_->removals().size()); |
| + EXPECT_EQ(backing_store_->writes()[1].key(), |
| + backing_store_->removals()[0]); |
| + EXPECT_EQ(backing_store_->writes()[2].key(), |
| + backing_store_->removals()[1]); |
| + } |
| + } |
| +} |
| + |
| +TEST_F(IndexedDBBackingStoreTest, DeleteRangeEmptyRange) { |
| + IndexedDBKey key0 = IndexedDBKey(ASCIIToUTF16("key0")); |
| + IndexedDBKey key1 = IndexedDBKey(ASCIIToUTF16("key1")); |
| + IndexedDBKey key2 = IndexedDBKey(ASCIIToUTF16("key2")); |
| + IndexedDBKey key3 = IndexedDBKey(ASCIIToUTF16("key3")); |
| + IndexedDBKey key4 = IndexedDBKey(ASCIIToUTF16("key4")); |
| + IndexedDBBlobInfo blob0("uuid 0", base::UTF8ToUTF16("type 0"), 1); |
| + IndexedDBBlobInfo blob1("uuid 1", base::UTF8ToUTF16("type 1"), 1); |
| + IndexedDBBlobInfo blob2("uuid 2", base::UTF8ToUTF16("type 2"), 1); |
| + IndexedDBBlobInfo blob3("uuid 3", base::UTF8ToUTF16("type 3"), 1); |
| + IndexedDBKeyRange ranges[] = {IndexedDBKeyRange(key3, key4, true, false), |
| + IndexedDBKeyRange(key2, key1, false, false), |
| + IndexedDBKeyRange(key2, key1, true, true)}; |
| + |
| + for (unsigned i = 0; i < sizeof(ranges) / sizeof(IndexedDBKeyRange); ++i) { |
|
cmumford
2014/05/28 20:34:54
Nit: Why not just use "arraysize(ranges)"?
ericu
2014/05/28 22:50:42
Done. Thanks; I'd forgotten we had that.
|
| + backing_store_->ClearWrites(); |
| + backing_store_->ClearRemovals(); |
| + |
| + { |
| + std::vector<IndexedDBBlobInfo> blob_info0, blob_info1, blob_info2, |
| + blob_info3; |
| + blob_info0.push_back(blob0); |
| + blob_info1.push_back(blob1); |
| + blob_info2.push_back(blob2); |
| + blob_info3.push_back(blob3); |
| + IndexedDBValue value0 = IndexedDBValue("value0", blob_info0); |
| + IndexedDBValue value1 = IndexedDBValue("value1", blob_info1); |
| + IndexedDBValue value2 = IndexedDBValue("value2", blob_info2); |
| + IndexedDBValue value3 = IndexedDBValue("value3", blob_info3); |
| + IndexedDBBackingStore::Transaction transaction1(backing_store_); |
| + transaction1.Begin(); |
| + ScopedVector<webkit_blob::BlobDataHandle> handles; |
| + IndexedDBBackingStore::RecordIdentifier record; |
| + EXPECT_TRUE(backing_store_->PutRecord(&transaction1, |
| + 1, |
| + i + 1, |
| + key0, |
| + value0, |
| + &handles, |
| + &record).ok()); |
| + EXPECT_TRUE(backing_store_->PutRecord(&transaction1, |
| + 1, |
| + i + 1, |
| + key1, |
| + value1, |
| + &handles, |
| + &record).ok()); |
| + EXPECT_TRUE(backing_store_->PutRecord(&transaction1, |
| + 1, |
| + i + 1, |
| + key2, |
| + value2, |
| + &handles, |
| + &record).ok()); |
| + EXPECT_TRUE(backing_store_->PutRecord(&transaction1, |
| + 1, |
| + i + 1, |
| + key3, |
| + value3, |
| + &handles, |
| + &record).ok()); |
| + scoped_refptr<TestCallback> callback(new TestCallback()); |
| + EXPECT_TRUE(transaction1.CommitPhaseOne(callback).ok()); |
| + task_runner_->RunUntilIdle(); |
| + EXPECT_TRUE(callback->called); |
| + EXPECT_TRUE(callback->succeeded); |
| + EXPECT_TRUE(transaction1.CommitPhaseTwo().ok()); |
| + } |
| + |
| + { |
| + IndexedDBBackingStore::Transaction transaction2(backing_store_); |
| + transaction2.Begin(); |
| + IndexedDBValue result_value; |
| + EXPECT_TRUE( |
| + backing_store_->DeleteRange(&transaction2, 1, i + 1, ranges[i]).ok()); |
| + scoped_refptr<TestCallback> callback(new TestCallback()); |
| + EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok()); |
| + task_runner_->RunUntilIdle(); |
| + EXPECT_TRUE(callback->called); |
| + EXPECT_TRUE(callback->succeeded); |
| + EXPECT_TRUE(transaction2.CommitPhaseTwo().ok()); |
| + EXPECT_EQ(0UL, backing_store_->removals().size()); |
| + } |
| + } |
| +} |
| + |
| +TEST_F(IndexedDBBackingStoreTest, LiveBlobJournal) { |
| + { |
| + IndexedDBBackingStore::Transaction transaction1(backing_store_); |
| + transaction1.Begin(); |
| + ScopedVector<webkit_blob::BlobDataHandle> handles; |
| + IndexedDBBackingStore::RecordIdentifier record; |
| + EXPECT_TRUE(backing_store_->PutRecord(&transaction1, |
| + 1, |
| + 1, |
| + m_key3, |
| + m_value3, |
| + &handles, |
| + &record).ok()); |
| + scoped_refptr<TestCallback> callback(new TestCallback()); |
| + EXPECT_TRUE(transaction1.CommitPhaseOne(callback).ok()); |
| + task_runner_->RunUntilIdle(); |
| + EXPECT_TRUE(CheckBlobWrites()); |
| + EXPECT_TRUE(callback->called); |
| + EXPECT_TRUE(callback->succeeded); |
| + EXPECT_TRUE(transaction1.CommitPhaseTwo().ok()); |
| + } |
| + |
| + IndexedDBValue read_result_value; |
| + { |
| + IndexedDBBackingStore::Transaction transaction2(backing_store_); |
| + transaction2.Begin(); |
| + EXPECT_TRUE( |
| + backing_store_->GetRecord( |
| + &transaction2, 1, 1, m_key3, &read_result_value) |
| + .ok()); |
| + scoped_refptr<TestCallback> callback(new TestCallback()); |
| + EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok()); |
| + EXPECT_TRUE(callback->called); |
| + EXPECT_TRUE(callback->succeeded); |
| + EXPECT_TRUE(transaction2.CommitPhaseTwo().ok()); |
| + EXPECT_EQ(m_value3.bits, read_result_value.bits); |
| + EXPECT_TRUE(CheckBlobInfoMatches(read_result_value.blob_info)); |
| + EXPECT_TRUE(CheckBlobReadsMatchWrites(read_result_value.blob_info)); |
| + for (size_t i = 0; i < read_result_value.blob_info.size(); ++i) { |
| + read_result_value.blob_info[i].mark_used_callback().Run(); |
| + } |
| + } |
| + |
| + { |
| + IndexedDBBackingStore::Transaction transaction3(backing_store_); |
| + transaction3.Begin(); |
| + EXPECT_TRUE(backing_store_->DeleteRange(&transaction3, |
| + 1, |
| + 1, |
| + IndexedDBKeyRange(m_key3)).ok()); |
| + scoped_refptr<TestCallback> callback(new TestCallback()); |
| + EXPECT_TRUE(transaction3.CommitPhaseOne(callback).ok()); |
| + task_runner_->RunUntilIdle(); |
| + EXPECT_TRUE(callback->called); |
| + EXPECT_TRUE(callback->succeeded); |
| + EXPECT_TRUE(transaction3.CommitPhaseTwo().ok()); |
| + EXPECT_EQ(0U, backing_store_->removals().size()); |
| + for (size_t i = 0; i < read_result_value.blob_info.size(); ++i) { |
| + read_result_value.blob_info[i].release_callback().Run( |
| + read_result_value.blob_info[i].file_path()); |
| + } |
| + task_runner_->RunUntilIdle(); |
| + EXPECT_NE(0U, backing_store_->removals().size()); |
| + EXPECT_TRUE(CheckBlobRemovals()); |
| + } |
| +} |
| + |
| // Make sure that using very high ( more than 32 bit ) values for database_id |
| // and object_store_id still work. |
| TEST_F(IndexedDBBackingStoreTest, HighIds) { |
| @@ -114,7 +719,12 @@ TEST_F(IndexedDBBackingStoreTest, HighIds) { |
| record); |
| EXPECT_TRUE(s.ok()); |
| - s = transaction1.Commit(); |
| + scoped_refptr<TestCallback> callback(new TestCallback()); |
| + s = transaction1.CommitPhaseOne(callback); |
| + EXPECT_TRUE(s.ok()); |
| + EXPECT_TRUE(callback->called); |
| + EXPECT_TRUE(callback->succeeded); |
| + s = transaction1.CommitPhaseTwo(); |
| EXPECT_TRUE(s.ok()); |
| } |
| @@ -148,7 +758,12 @@ TEST_F(IndexedDBBackingStoreTest, HighIds) { |
| EXPECT_TRUE(s.ok()); |
| EXPECT_TRUE(new_primary_key->Equals(m_key1)); |
| - s = transaction2.Commit(); |
| + scoped_refptr<TestCallback> callback(new TestCallback()); |
| + s = transaction2.CommitPhaseOne(callback); |
| + EXPECT_TRUE(s.ok()); |
| + EXPECT_TRUE(callback->called); |
| + EXPECT_TRUE(callback->succeeded); |
| + s = transaction2.CommitPhaseTwo(); |
| EXPECT_TRUE(s.ok()); |
| } |
| } |
| @@ -287,7 +902,12 @@ TEST_F(IndexedDBBackingStoreTest, CreateDatabase) { |
| multi_entry); |
| EXPECT_TRUE(s.ok()); |
| - s = transaction.Commit(); |
| + scoped_refptr<TestCallback> callback(new TestCallback()); |
| + s = transaction.CommitPhaseOne(callback); |
| + EXPECT_TRUE(s.ok()); |
| + EXPECT_TRUE(callback->called); |
| + EXPECT_TRUE(callback->succeeded); |
| + s = transaction.CommitPhaseTwo(); |
| EXPECT_TRUE(s.ok()); |
| } |