Index: content/browser/indexed_db/leveldb/leveldb_transaction_unittest.cc |
diff --git a/content/browser/indexed_db/leveldb/leveldb_transaction_unittest.cc b/content/browser/indexed_db/leveldb/leveldb_transaction_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..bbdd65227a671e9fc8c9f6c18e07136ca0202f48 |
--- /dev/null |
+++ b/content/browser/indexed_db/leveldb/leveldb_transaction_unittest.cc |
@@ -0,0 +1,368 @@ |
+// Copyright 2017 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 <algorithm> |
+#include <cstring> |
+#include <string> |
+ |
+#include "base/files/file.h" |
+#include "base/files/file_path.h" |
+#include "base/files/scoped_temp_dir.h" |
+#include "base/strings/string_piece.h" |
+#include "content/browser/indexed_db/leveldb/leveldb_comparator.h" |
+#include "content/browser/indexed_db/leveldb/leveldb_database.h" |
+#include "content/browser/indexed_db/leveldb/leveldb_env.h" |
+#include "content/browser/indexed_db/leveldb/leveldb_iterator.h" |
+#include "content/browser/indexed_db/leveldb/leveldb_transaction.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "third_party/leveldatabase/env_chromium.h" |
+ |
+namespace content { |
+ |
+namespace { |
+ |
+class SimpleComparator : public LevelDBComparator { |
+ public: |
+ int Compare(const base::StringPiece& a, |
+ const base::StringPiece& b) const override { |
+ size_t len = std::min(a.size(), b.size()); |
+ return memcmp(a.begin(), b.begin(), len); |
+ } |
+ const char* Name() const override { return "temp_comparator"; } |
+}; |
+ |
+class LevelDBTransactionTest : public testing::Test { |
+ public: |
+ LevelDBTransactionTest() {} |
+ void SetUp() override { |
+ ASSERT_TRUE(temp_directory_.CreateUniqueTempDir()); |
+ LevelDBDatabase::Open(temp_directory_.GetPath(), &comparator_, &leveldb_); |
+ ASSERT_TRUE(leveldb_); |
+ } |
+ void TearDown() override {} |
+ |
+ protected: |
+ // Convenience methods to access the database outside any |
+ // transaction to cut down on boilerplate around calls. |
+ void Put(const base::StringPiece& key, const std::string& value) { |
+ std::string put_value = value; |
+ leveldb::Status s = leveldb_->Put(key, &put_value); |
+ ASSERT_TRUE(s.ok()); |
+ } |
+ |
+ void Remove(const base::StringPiece& key) { |
+ leveldb::Status s = leveldb_->Remove(key); |
+ ASSERT_TRUE(s.ok()); |
+ } |
+ |
+ void Get(const base::StringPiece& key, std::string* value, bool* found) { |
+ leveldb::Status s = leveldb_->Get(key, value, found); |
+ ASSERT_TRUE(s.ok()); |
+ } |
+ |
+ bool Has(const base::StringPiece& key) { |
+ bool found; |
+ std::string value; |
+ leveldb::Status s = leveldb_->Get(key, &value, &found); |
+ EXPECT_TRUE(s.ok()); |
+ return found; |
+ } |
+ |
+ // Convenience wrappers for LevelDBTransaction operations to |
+ // avoid boilerplate in tests. |
+ bool TransactionHas(LevelDBTransaction* transaction, |
+ const base::StringPiece& key) { |
+ std::string value; |
+ bool found; |
+ leveldb::Status s = transaction->Get(key, &value, &found); |
+ EXPECT_TRUE(s.ok()); |
+ return found; |
+ } |
+ |
+ void TransactionPut(LevelDBTransaction* transaction, |
+ const base::StringPiece& key, |
+ const std::string& value) { |
+ std::string put_value = value; |
+ transaction->Put(key, &put_value); |
+ } |
+ |
+ int Compare(const base::StringPiece& a, const base::StringPiece& b) const { |
+ return comparator_.Compare(a, b); |
+ } |
+ |
+ LevelDBDatabase* db() { return leveldb_.get(); } |
+ |
+ const std::string key_before_range_ = "a"; |
+ const std::string range_start_ = "b"; |
+ const std::string key_in_range1_ = "c"; |
+ const std::string key_in_range2_ = "d"; |
+ const std::string range_end_ = "e"; |
+ const std::string key_after_range_ = "f"; |
+ const std::string value_ = "value"; |
+ |
+ private: |
+ base::ScopedTempDir temp_directory_; |
+ SimpleComparator comparator_; |
+ std::unique_ptr<LevelDBDatabase> leveldb_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(LevelDBTransactionTest); |
+}; |
+ |
+} // namespace |
+ |
+TEST_F(LevelDBTransactionTest, GetAndPut) { |
+ leveldb::Status status; |
+ |
+ const std::string key("key"); |
+ std::string got_value; |
+ |
+ const std::string old_value("value"); |
+ Put(key, old_value); |
+ |
+ scoped_refptr<LevelDBTransaction> transaction = new LevelDBTransaction(db()); |
+ |
+ const std::string new_value("new value"); |
+ Put(key, new_value); |
+ |
+ bool found = false; |
+ status = transaction->Get(key, &got_value, &found); |
+ EXPECT_TRUE(status.ok()); |
+ EXPECT_TRUE(found); |
+ EXPECT_EQ(Compare(got_value, old_value), 0); |
+ |
+ Get(key, &got_value, &found); |
+ EXPECT_TRUE(found); |
+ EXPECT_EQ(Compare(got_value, new_value), 0); |
+ |
+ const std::string added_key("added key"); |
+ const std::string added_value("added value"); |
+ Put(added_key, added_value); |
+ |
+ Get(added_key, &got_value, &found); |
+ EXPECT_TRUE(found); |
+ EXPECT_EQ(Compare(got_value, added_value), 0); |
+ |
+ EXPECT_FALSE(TransactionHas(transaction.get(), added_key)); |
+ |
+ const std::string another_key("another key"); |
+ const std::string another_value("another value"); |
+ TransactionPut(transaction.get(), another_key, another_value); |
+ |
+ status = transaction->Get(another_key, &got_value, &found); |
+ EXPECT_TRUE(status.ok()); |
+ EXPECT_TRUE(found); |
+ EXPECT_EQ(Compare(got_value, another_value), 0); |
+} |
+ |
+TEST_F(LevelDBTransactionTest, Iterator) { |
+ const std::string key1("key1"); |
+ const std::string value1("value1"); |
+ const std::string key2("key2"); |
+ const std::string value2("value2"); |
+ |
+ Put(key1, value1); |
+ Put(key2, value2); |
+ |
+ scoped_refptr<LevelDBTransaction> transaction = new LevelDBTransaction(db()); |
+ |
+ Remove(key2); |
+ |
+ std::unique_ptr<LevelDBIterator> it = transaction->CreateIterator(); |
+ |
+ it->Seek(std::string()); |
+ |
+ EXPECT_TRUE(it->IsValid()); |
+ EXPECT_EQ(Compare(it->Key(), key1), 0); |
+ EXPECT_EQ(Compare(it->Value(), value1), 0); |
+ |
+ it->Next(); |
+ |
+ EXPECT_TRUE(it->IsValid()); |
+ EXPECT_EQ(Compare(it->Key(), key2), 0); |
+ EXPECT_EQ(Compare(it->Value(), value2), 0); |
+ |
+ it->Next(); |
+ |
+ EXPECT_FALSE(it->IsValid()); |
+} |
+ |
+TEST_F(LevelDBTransactionTest, Commit) { |
+ const std::string key1("key1"); |
+ const std::string key2("key2"); |
+ const std::string value1("value1"); |
+ const std::string value2("value2"); |
+ const std::string value3("value3"); |
+ |
+ std::string got_value; |
+ bool found; |
+ |
+ scoped_refptr<LevelDBTransaction> transaction = new LevelDBTransaction(db()); |
+ |
+ TransactionPut(transaction.get(), key1, value1); |
+ TransactionPut(transaction.get(), key2, value2); |
+ TransactionPut(transaction.get(), key2, value3); |
+ |
+ leveldb::Status status = transaction->Commit(); |
+ EXPECT_TRUE(status.ok()); |
+ |
+ Get(key1, &got_value, &found); |
+ EXPECT_TRUE(found); |
+ EXPECT_EQ(value1, got_value); |
+ |
+ Get(key2, &got_value, &found); |
+ EXPECT_TRUE(found); |
+ EXPECT_EQ(value3, got_value); |
+} |
+ |
+// Test removals where the data is in the database, and the range has a closed |
+// upper bound (inclusive). |
+TEST_F(LevelDBTransactionTest, RemoveRangeBackingData) { |
+ std::string got_value; |
+ leveldb::Status status; |
+ |
+ Put(key_before_range_, value_); |
+ Put(range_start_, value_); |
+ Put(key_in_range1_, value_); |
+ Put(key_in_range2_, value_); |
+ Put(range_end_, value_); |
+ Put(key_after_range_, value_); |
+ |
+ scoped_refptr<LevelDBTransaction> transaction = new LevelDBTransaction(db()); |
+ |
+ const bool upper_open = false; |
+ status = transaction->RemoveRange(range_start_, range_end_, upper_open); |
+ EXPECT_TRUE(status.ok()); |
+ |
+ EXPECT_TRUE(TransactionHas(transaction.get(), key_before_range_)); |
+ EXPECT_FALSE(TransactionHas(transaction.get(), range_start_)); |
+ EXPECT_FALSE(TransactionHas(transaction.get(), key_in_range1_)); |
+ EXPECT_FALSE(TransactionHas(transaction.get(), key_in_range2_)); |
+ EXPECT_FALSE(TransactionHas(transaction.get(), range_end_)); |
+ EXPECT_TRUE(TransactionHas(transaction.get(), key_after_range_)); |
+ |
+ status = transaction->Commit(); |
+ EXPECT_TRUE(status.ok()); |
+ |
+ EXPECT_TRUE(Has(key_before_range_)); |
+ EXPECT_FALSE(Has(range_start_)); |
+ EXPECT_FALSE(Has(key_in_range1_)); |
+ EXPECT_FALSE(Has(key_in_range2_)); |
+ EXPECT_FALSE(Has(range_end_)); |
+ EXPECT_TRUE(Has(key_after_range_)); |
+} |
+ |
+// Test removals where the data is in the database, and the range has an open |
+// upper bound (exclusive). |
+TEST_F(LevelDBTransactionTest, RemoveRangeBackingDataUpperOpen) { |
+ std::string got_value; |
+ leveldb::Status status; |
+ |
+ Put(key_before_range_, value_); |
+ Put(range_start_, value_); |
+ Put(key_in_range1_, value_); |
+ Put(key_in_range2_, value_); |
+ Put(range_end_, value_); |
+ Put(key_after_range_, value_); |
+ |
+ scoped_refptr<LevelDBTransaction> transaction = new LevelDBTransaction(db()); |
+ |
+ const bool upper_open = true; |
+ status = transaction->RemoveRange(range_start_, range_end_, upper_open); |
+ EXPECT_TRUE(status.ok()); |
+ |
+ EXPECT_TRUE(TransactionHas(transaction.get(), key_before_range_)); |
+ EXPECT_FALSE(TransactionHas(transaction.get(), range_start_)); |
+ EXPECT_FALSE(TransactionHas(transaction.get(), key_in_range1_)); |
+ EXPECT_FALSE(TransactionHas(transaction.get(), key_in_range2_)); |
+ EXPECT_TRUE(TransactionHas(transaction.get(), range_end_)); |
+ EXPECT_TRUE(TransactionHas(transaction.get(), key_after_range_)); |
+ |
+ status = transaction->Commit(); |
+ EXPECT_TRUE(status.ok()); |
+ |
+ EXPECT_TRUE(Has(key_before_range_)); |
+ EXPECT_FALSE(Has(range_start_)); |
+ EXPECT_FALSE(Has(key_in_range1_)); |
+ EXPECT_FALSE(Has(key_in_range2_)); |
+ EXPECT_TRUE(Has(range_end_)); |
+ EXPECT_TRUE(Has(key_after_range_)); |
+} |
+ |
+// Test removals where the data is in transaction's memory map, and the range |
+// has a closed upper bound (inclusive). |
+TEST_F(LevelDBTransactionTest, RemoveRangeMemoryData) { |
+ std::string got_value; |
+ leveldb::Status status; |
+ |
+ scoped_refptr<LevelDBTransaction> transaction = new LevelDBTransaction(db()); |
+ |
+ TransactionPut(transaction.get(), key_before_range_, value_); |
+ TransactionPut(transaction.get(), range_start_, value_); |
+ TransactionPut(transaction.get(), key_in_range1_, value_); |
+ TransactionPut(transaction.get(), key_in_range2_, value_); |
+ TransactionPut(transaction.get(), range_end_, value_); |
+ TransactionPut(transaction.get(), key_after_range_, value_); |
+ |
+ const bool upper_open = false; |
+ status = transaction->RemoveRange(range_start_, range_end_, upper_open); |
+ EXPECT_TRUE(status.ok()); |
+ |
+ EXPECT_TRUE(TransactionHas(transaction.get(), key_before_range_)); |
+ EXPECT_FALSE(TransactionHas(transaction.get(), range_start_)); |
+ EXPECT_FALSE(TransactionHas(transaction.get(), key_in_range1_)); |
+ EXPECT_FALSE(TransactionHas(transaction.get(), key_in_range2_)); |
+ EXPECT_FALSE(TransactionHas(transaction.get(), range_end_)); |
+ EXPECT_TRUE(TransactionHas(transaction.get(), key_after_range_)); |
+ |
+ status = transaction->Commit(); |
+ EXPECT_TRUE(status.ok()); |
+ |
+ EXPECT_TRUE(Has(key_before_range_)); |
+ EXPECT_FALSE(Has(range_start_)); |
+ EXPECT_FALSE(Has(key_in_range1_)); |
+ EXPECT_FALSE(Has(key_in_range2_)); |
+ EXPECT_FALSE(Has(range_end_)); |
+ EXPECT_TRUE(Has(key_after_range_)); |
+} |
+ |
+// Test removals where the data is in transaction's memory map, and the range |
+// has an open upper bound (exclusive). |
+TEST_F(LevelDBTransactionTest, RemoveRangeMemoryDataUpperOpen) { |
+ std::string got_value; |
+ leveldb::Status status; |
+ |
+ scoped_refptr<LevelDBTransaction> transaction = new LevelDBTransaction(db()); |
+ |
+ TransactionPut(transaction.get(), key_before_range_, value_); |
+ TransactionPut(transaction.get(), range_start_, value_); |
+ TransactionPut(transaction.get(), key_in_range1_, value_); |
+ TransactionPut(transaction.get(), key_in_range2_, value_); |
+ TransactionPut(transaction.get(), range_end_, value_); |
+ TransactionPut(transaction.get(), key_after_range_, value_); |
+ |
+ const bool upper_open = true; |
+ status = transaction->RemoveRange(range_start_, range_end_, upper_open); |
+ EXPECT_TRUE(status.ok()); |
+ |
+ EXPECT_TRUE(TransactionHas(transaction.get(), key_before_range_)); |
+ EXPECT_FALSE(TransactionHas(transaction.get(), range_start_)); |
+ EXPECT_FALSE(TransactionHas(transaction.get(), key_in_range1_)); |
+ EXPECT_FALSE(TransactionHas(transaction.get(), key_in_range2_)); |
+ EXPECT_TRUE(TransactionHas(transaction.get(), range_end_)); |
+ EXPECT_TRUE(TransactionHas(transaction.get(), key_after_range_)); |
+ |
+ status = transaction->Commit(); |
+ EXPECT_TRUE(status.ok()); |
+ |
+ EXPECT_TRUE(Has(key_before_range_)); |
+ EXPECT_FALSE(Has(range_start_)); |
+ EXPECT_FALSE(Has(key_in_range1_)); |
+ EXPECT_FALSE(Has(key_in_range2_)); |
+ EXPECT_TRUE(Has(range_end_)); |
+ EXPECT_TRUE(Has(key_after_range_)); |
+} |
+ |
+} // namespace content |