| Index: webkit/dom_storage/dom_storage_database_unittest.cc
|
| diff --git a/webkit/dom_storage/dom_storage_database_unittest.cc b/webkit/dom_storage/dom_storage_database_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9c5bdfb8249ae16b786e504e646137a03431309d
|
| --- /dev/null
|
| +++ b/webkit/dom_storage/dom_storage_database_unittest.cc
|
| @@ -0,0 +1,340 @@
|
| +// Copyright (c) 2012 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 "webkit/dom_storage/dom_storage_database.h"
|
| +
|
| +#include "base/file_path.h"
|
| +#include "base/file_util.h"
|
| +#include "base/path_service.h"
|
| +#include "base/scoped_temp_dir.h"
|
| +#include "base/utf_string_conversions.h"
|
| +#include "sql/statement.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace dom_storage {
|
| +
|
| +DomStorageDatabase::DomStorageDatabase()
|
| + : db_(NULL),
|
| + failed_to_open_(false),
|
| + tried_to_recreate_(false) {
|
| + // This constructor allows us to bypass the DCHECK in the other
|
| + // constructor that verifies a valid file path was passed to
|
| + // back the database. We want to be able to do this in unit
|
| + // tests to use databases in memory.
|
| +}
|
| +
|
| +void CreateV1Table(sql::Connection* db) {
|
| + ASSERT_FALSE(db->is_open());
|
| + ASSERT_TRUE(db->OpenInMemory());
|
| + ASSERT_TRUE(db->Execute("DROP TABLE IF EXISTS ItemTable"));
|
| + ASSERT_TRUE(db->Execute(
|
| + "CREATE TABLE IF NOT EXISTS ItemTable ("
|
| + "key TEXT UNIQUE ON CONFLICT REPLACE, "
|
| + "value TEXT NOT NULL ON CONFLICT FAIL)"));
|
| +}
|
| +
|
| +void CreateInvalidTable(sql::Connection* db) {
|
| + // Create a table with the value type as FLOAT - this is "invalid"
|
| + // as far as the DOM Storage db is concerned.
|
| + ASSERT_FALSE(db->is_open());
|
| + ASSERT_TRUE(db->OpenInMemory());
|
| + ASSERT_TRUE(db->Execute("DROP TABLE IF EXISTS ItemTable"));
|
| + ASSERT_TRUE(db->Execute(
|
| + "CREATE TABLE IF NOT EXISTS ItemTable ("
|
| + "key TEXT UNIQUE ON CONFLICT REPLACE, "
|
| + "value FLOAT NOT NULL ON CONFLICT FAIL)"));
|
| +}
|
| +
|
| +void InsertDataV1(sql::Connection* db,
|
| + const string16& key,
|
| + const string16& value) {
|
| + sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE,
|
| + "INSERT INTO ItemTable VALUES (?,?)"));
|
| + statement.BindString16(0, key);
|
| + statement.BindString16(1, value);
|
| + ASSERT_TRUE(statement.is_valid());
|
| + statement.Run();
|
| +}
|
| +
|
| +void CheckValuesMatch(DomStorageDatabase* db,
|
| + const DomStorageDatabase::ValuesMap& expected) {
|
| + DomStorageDatabase::ValuesMap values_read;
|
| + db->ReadAllValues(&values_read);
|
| + EXPECT_EQ(expected.size(), values_read.size());
|
| +
|
| + DomStorageDatabase::ValuesMap::const_iterator it = values_read.begin();
|
| + for (; it != values_read.end(); ++it) {
|
| + string16 key = it->first;
|
| + NullableString16 value = it->second;
|
| + NullableString16 expected_value = expected.find(key)->second;
|
| + EXPECT_EQ(expected_value.string(), value.string());
|
| + EXPECT_EQ(expected_value.is_null(), value.is_null());
|
| + }
|
| +}
|
| +
|
| +void VerifySchema(sql::Connection* db) {
|
| + sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE,
|
| + "SELECT * from ItemTable LIMIT 1"));
|
| + EXPECT_TRUE(db->DoesTableExist("ItemTable"));
|
| + // Ensure that we've got the colums we expect and they are of the
|
| + // correct type.
|
| + EXPECT_TRUE(db->DoesColumnExist("ItemTable", "key"));
|
| + EXPECT_TRUE(db->DoesColumnExist("ItemTable", "value"));
|
| + EXPECT_EQ(sql::COLUMN_TYPE_TEXT, statement.DeclaredColumnType(0));
|
| + EXPECT_EQ(sql::COLUMN_TYPE_BLOB, statement.DeclaredColumnType(1));
|
| +}
|
| +
|
| +void CreateMapWithValues(DomStorageDatabase::ValuesMap* values) {
|
| + string16 kCannedKeys[] = {
|
| + ASCIIToUTF16("test"),
|
| + ASCIIToUTF16("company"),
|
| + ASCIIToUTF16("date"),
|
| + ASCIIToUTF16("empty")
|
| + };
|
| + NullableString16 kCannedValues[] = {
|
| + NullableString16(ASCIIToUTF16("123"), false),
|
| + NullableString16(ASCIIToUTF16("Google"), false),
|
| + NullableString16(ASCIIToUTF16("18-01-2012"), false),
|
| + NullableString16(ASCIIToUTF16(""), false)
|
| + };
|
| + for (int i = 0; i < 3; i++)
|
| + (*values)[kCannedKeys[i]] = kCannedValues[i];
|
| +}
|
| +
|
| +TEST(DomStorageDatabaseTest, SimpleOpenAndClose) {
|
| + DomStorageDatabase db;
|
| + ASSERT_TRUE(db.LazyOpen(true));
|
| + VerifySchema(db.db_.get());
|
| + db.Close();
|
| + EXPECT_FALSE(db.IsOpen());
|
| +}
|
| +
|
| +TEST(DomStorageDatabaseTest, TestLazyOpenIsLazy) {
|
| + // This test needs to operate with a file on disk to ensure that we will
|
| + // open a file that already exists when only invoking ReadAllValues.
|
| + ScopedTempDir temp_dir;
|
| + ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
|
| + FilePath file_name = temp_dir.path().AppendASCII("TestDomStorageDatabase.db");
|
| +
|
| + DomStorageDatabase db(file_name);
|
| + EXPECT_FALSE(db.IsOpen());
|
| + DomStorageDatabase::ValuesMap values;
|
| + db.ReadAllValues(&values);
|
| + EXPECT_FALSE(db.IsOpen());
|
| + values[ASCIIToUTF16("key")] = NullableString16(ASCIIToUTF16("value"), false);
|
| + db.CommitChanges(false, values);
|
| + EXPECT_TRUE(db.IsOpen());
|
| +
|
| + db.Close();
|
| + ASSERT_FALSE(db.IsOpen());
|
| +
|
| + db.ReadAllValues(&values);
|
| + EXPECT_TRUE(db.IsOpen());
|
| +}
|
| +
|
| +TEST(DomStorageDatabaseTest, TestLazyOpenUpgradesV1TableToV2) {
|
| + DomStorageDatabase db;
|
| + db.db_.reset(new sql::Connection());
|
| + CreateV1Table(db.db_.get());
|
| + db.Close();
|
| +
|
| + EXPECT_TRUE(db.LazyOpen(true));
|
| + VerifySchema(db.db_.get());
|
| +}
|
| +
|
| +TEST(DomStorageDatabaseTest, TestFailedUpgrade) {
|
| + DomStorageDatabase db;
|
| + db.db_.reset(new sql::Connection());
|
| + CreateInvalidTable(db.db_.get());
|
| +
|
| + EXPECT_FALSE(db.UpgradeVersion1To2IfNeeded());
|
| +
|
| + db.Close();
|
| +
|
| + // LazyOpen should be able to deal with the failed upgrade by
|
| + // dropping the table and creating a new one.
|
| + EXPECT_TRUE(db.LazyOpen(true));
|
| + VerifySchema(db.db_.get());
|
| +}
|
| +
|
| +
|
| +TEST(DomStorageDatabaseTest, TestIsOpen) {
|
| + DomStorageDatabase db;
|
| + EXPECT_FALSE(db.IsOpen());
|
| + ASSERT_TRUE(db.LazyOpen(true));
|
| + EXPECT_TRUE(db.IsOpen());
|
| + db.Close();
|
| + EXPECT_FALSE(db.IsOpen());
|
| +}
|
| +
|
| +TEST(DomStorageDatabaseTest, SimpleWriteAndReadBack) {
|
| + DomStorageDatabase db;
|
| +
|
| + DomStorageDatabase::ValuesMap storage;
|
| + CreateMapWithValues(&storage);
|
| +
|
| + // Test write.
|
| + EXPECT_TRUE(db.CommitChanges(false, storage));
|
| +
|
| + // CheckValuesMatch will invoke DomStorageDatabase::ReadAllValues
|
| + // to test reading.
|
| + CheckValuesMatch(&db, storage);
|
| +}
|
| +
|
| +TEST(DomStorageDatabaseTest, WriteWithClear) {
|
| + DomStorageDatabase db;
|
| +
|
| + DomStorageDatabase::ValuesMap storage;
|
| + CreateMapWithValues(&storage);
|
| +
|
| + ASSERT_TRUE(db.CommitChanges(false, storage));
|
| + CheckValuesMatch(&db, storage);
|
| +
|
| + // Insert some values, clearing the database first.
|
| + storage.clear();
|
| + storage[ASCIIToUTF16("another_key")] =
|
| + NullableString16(ASCIIToUTF16("test"), false);
|
| + ASSERT_TRUE(db.CommitChanges(true, storage));
|
| + CheckValuesMatch(&db, storage);
|
| +
|
| + // Now clear the values without inserting any new ones.
|
| + storage.clear();
|
| + ASSERT_TRUE(db.CommitChanges(true, storage));
|
| + CheckValuesMatch(&db, storage);
|
| +}
|
| +
|
| +TEST(DomStorageDatabaseTest, UpgradeFromV1ToV2WithData) {
|
| + const string16 kCannedKey = ASCIIToUTF16("foo");
|
| + const NullableString16 kCannedValue(ASCIIToUTF16("bar"), false);
|
| + DomStorageDatabase::ValuesMap expected;
|
| + expected[kCannedKey] = kCannedValue;
|
| +
|
| + DomStorageDatabase db;
|
| + db.db_.reset(new sql::Connection());
|
| + CreateV1Table(db.db_.get());
|
| + InsertDataV1(db.db_.get(), kCannedKey, kCannedValue.string());
|
| +
|
| + ASSERT_TRUE(db.UpgradeVersion1To2IfNeeded());
|
| +
|
| + VerifySchema(db.db_.get());
|
| +
|
| + CheckValuesMatch(&db, expected);
|
| +}
|
| +
|
| +TEST(DomStorageDatabaseTest, TestOpenCloseDataPreserved) {
|
| + // This test needs to operate on a file on disk.
|
| + ScopedTempDir temp_dir;
|
| + ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
|
| + FilePath file_name = temp_dir.path().AppendASCII("TestDomStorageDatabase.db");
|
| +
|
| + DomStorageDatabase db(file_name);
|
| +
|
| + ASSERT_TRUE(db.LazyOpen(true));
|
| +
|
| + const string16 kCannedKey = ASCIIToUTF16("test");
|
| + const NullableString16 kCannedValue(ASCIIToUTF16("data"), false);
|
| + DomStorageDatabase::ValuesMap expected;
|
| + expected[kCannedKey] = kCannedValue;
|
| + db.CommitChanges(false, expected);
|
| + db.Close();
|
| +
|
| + ASSERT_TRUE(db.LazyOpen(true));
|
| +
|
| + CheckValuesMatch(&db, expected);
|
| +}
|
| +
|
| +TEST(DomStorageDatabaseTest, TestSimpleRemoveOneValue) {
|
| + DomStorageDatabase db;
|
| +
|
| + ASSERT_TRUE(db.LazyOpen(true));
|
| + const string16 kCannedKey = ASCIIToUTF16("test");
|
| + const NullableString16 kCannedValue(ASCIIToUTF16("data"), false);
|
| + DomStorageDatabase::ValuesMap expected;
|
| + expected[kCannedKey] = kCannedValue;
|
| + db.CommitChanges(false, expected);
|
| +
|
| + CheckValuesMatch(&db, expected);
|
| +
|
| + DomStorageDatabase::ValuesMap values;
|
| + // A null string in the map should mean that that key gets
|
| + // removed.
|
| + values[kCannedKey] = NullableString16(true);
|
| + db.CommitChanges(false, values);
|
| +
|
| + expected.clear();
|
| + CheckValuesMatch(&db, expected);
|
| +}
|
| +
|
| +// TODO(benm): Enable this test in follow up patch once the test data has
|
| +// landed. (try-bots don't like adding binary files like test databases :) )
|
| +TEST(DomStorageDatabaseTest, DISABLED_TestCanOpenAndReadWebCoreDatabase) {
|
| + FilePath webcore_database;
|
| + PathService::Get(base::DIR_SOURCE_ROOT, &webcore_database);
|
| + webcore_database = webcore_database.AppendASCII("webkit");
|
| + webcore_database = webcore_database.AppendASCII("data");
|
| + webcore_database = webcore_database.AppendASCII("dom_storage");
|
| + webcore_database =
|
| + webcore_database.AppendASCII("webcore_test_database.localstorage");
|
| +
|
| + ASSERT_TRUE(file_util::PathExists(webcore_database));
|
| +
|
| + DomStorageDatabase db(webcore_database);
|
| + DomStorageDatabase::ValuesMap values;
|
| + db.ReadAllValues(&values);
|
| + EXPECT_TRUE(db.IsOpen());
|
| + EXPECT_EQ(2u, values.size());
|
| +
|
| + DomStorageDatabase::ValuesMap::const_iterator it =
|
| + values.find(ASCIIToUTF16("value"));
|
| + EXPECT_TRUE(it != values.end());
|
| + EXPECT_EQ(ASCIIToUTF16("I am in local storage!"), it->second.string());
|
| +
|
| + it = values.find(ASCIIToUTF16("timestamp"));
|
| + EXPECT_TRUE(it != values.end());
|
| + EXPECT_EQ(ASCIIToUTF16("1326738338841"), it->second.string());
|
| +
|
| + it = values.find(ASCIIToUTF16("not_there"));
|
| + EXPECT_TRUE(it == values.end());
|
| +}
|
| +
|
| +TEST(DomStorageDatabaseTest, TestCanOpenFileThatIsNotADatabase) {
|
| + // Write into the temporary file first.
|
| + ScopedTempDir temp_dir;
|
| + ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
|
| + FilePath file_name = temp_dir.path().AppendASCII("TestDomStorageDatabase.db");
|
| +
|
| + const char kData[] = "I am not a database.";
|
| + file_util::WriteFile(file_name, kData, strlen(kData));
|
| +
|
| + {
|
| + // Try and open the file. We should end up deleting it and creating a new
|
| + // file, so everything should actually succeed.
|
| + DomStorageDatabase db(file_name);
|
| + DomStorageDatabase::ValuesMap values;
|
| + CreateMapWithValues(&values);
|
| + EXPECT_TRUE(db.CommitChanges(true, values));
|
| + EXPECT_TRUE(db.CommitChanges(false, values));
|
| + EXPECT_TRUE(db.IsOpen());
|
| +
|
| + CheckValuesMatch(&db, values);
|
| + }
|
| +
|
| + {
|
| + // Try to open a directory, we should fail gracefully.
|
| + DomStorageDatabase db(temp_dir.path());
|
| + DomStorageDatabase::ValuesMap values;
|
| + CreateMapWithValues(&values);
|
| + EXPECT_FALSE(db.CommitChanges(true, values));
|
| + EXPECT_FALSE(db.CommitChanges(false, values));
|
| + EXPECT_FALSE(db.IsOpen());
|
| +
|
| + values.clear();
|
| +
|
| + db.ReadAllValues(&values);
|
| + EXPECT_EQ(0u, values.size());
|
| + EXPECT_FALSE(db.IsOpen());
|
| + }
|
| +}
|
| +
|
| +} // namespace dom_storage
|
|
|