diff options
author | benm@chromium.org <benm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-10 13:31:59 +0000 |
---|---|---|
committer | benm@chromium.org <benm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-10 13:31:59 +0000 |
commit | 98b6f8b1b8f96602e74d431eff2296c582b01275 (patch) | |
tree | 9074ed279c02247caca0e58bd28f720fa350e484 /webkit/dom_storage/dom_storage_database_unittest.cc | |
parent | 9517c6326e123542fab29f20874dcdd8d48d7f83 (diff) | |
download | chromium_src-98b6f8b1b8f96602e74d431eff2296c582b01275.zip chromium_src-98b6f8b1b8f96602e74d431eff2296c582b01275.tar.gz chromium_src-98b6f8b1b8f96602e74d431eff2296c582b01275.tar.bz2 |
Create a class to represent a Dom Storage Database.
Add a class that can read/write a local storage database in the same format as WebCore's StorageArea.
Also add a method to sql::Statement to query the declared type of a column and a method to return a string16 from a Blob.
BUG=106763
Review URL: http://codereview.chromium.org/9159020
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@121442 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/dom_storage/dom_storage_database_unittest.cc')
-rw-r--r-- | webkit/dom_storage/dom_storage_database_unittest.cc | 329 |
1 files changed, 329 insertions, 0 deletions
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 0000000..7e79b80 --- /dev/null +++ b/webkit/dom_storage/dom_storage_database_unittest.cc @@ -0,0 +1,329 @@ +// 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) { +} + +void CreateV1Table(sql::Connection* db) { + ASSERT_TRUE(db->is_open()); + ASSERT_TRUE(db->Execute("DROP TABLE IF EXISTS ItemTable")); + ASSERT_TRUE(db->Execute( + "CREATE TABLE ItemTable (" + "key TEXT UNIQUE ON CONFLICT REPLACE, " + "value TEXT NOT NULL ON CONFLICT FAIL)")); +} + +void CreateV2Table(sql::Connection* db) { + ASSERT_TRUE(db->is_open()); + ASSERT_TRUE(db->Execute("DROP TABLE IF EXISTS ItemTable")); + ASSERT_TRUE(db->Execute( + "CREATE TABLE ItemTable (" + "key TEXT UNIQUE ON CONFLICT REPLACE, " + "value BLOB NOT NULL ON CONFLICT FAIL)")); +} + +void CreateInvalidKeyColumnTable(sql::Connection* db) { + // Create a table with the key type as FLOAT - this is "invalid" + // as far as the DOM Storage db is concerned. + ASSERT_TRUE(db->is_open()); + ASSERT_TRUE(db->Execute("DROP TABLE IF EXISTS ItemTable")); + ASSERT_TRUE(db->Execute( + "CREATE TABLE IF NOT EXISTS ItemTable (" + "key FLOAT UNIQUE ON CONFLICT REPLACE, " + "value BLOB NOT NULL ON CONFLICT FAIL)")); +} +void CreateInvalidValueColumnTable(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_TRUE(db->is_open()); + 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 ValuesMap& expected) { + ValuesMap values_read; + db->ReadAllValues(&values_read); + EXPECT_EQ(expected.size(), values_read.size()); + + 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 CreateMapWithValues(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 (unsigned i = 0; i < sizeof(kCannedKeys) / sizeof(kCannedKeys[0]); i++) + (*values)[kCannedKeys[i]] = kCannedValues[i]; +} + +TEST(DomStorageDatabaseTest, SimpleOpenAndClose) { + DomStorageDatabase db; + EXPECT_FALSE(db.IsOpen()); + ASSERT_TRUE(db.LazyOpen(true)); + EXPECT_TRUE(db.IsOpen()); + EXPECT_EQ(DomStorageDatabase::V2, db.DetectSchemaVersion()); + 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()); + ValuesMap values; + db.ReadAllValues(&values); + // Reading an empty db should not open the database. + EXPECT_FALSE(db.IsOpen()); + + values[ASCIIToUTF16("key")] = NullableString16(ASCIIToUTF16("value"), false); + db.CommitChanges(false, values); + // Writing content should open the database. + EXPECT_TRUE(db.IsOpen()); + + db.Close(); + ASSERT_FALSE(db.IsOpen()); + + // Reading from an existing database should open the database. + CheckValuesMatch(&db, values); + EXPECT_TRUE(db.IsOpen()); +} + +TEST(DomStorageDatabaseTest, TestDetectSchemaVersion) { + DomStorageDatabase db; + db.db_.reset(new sql::Connection()); + ASSERT_TRUE(db.db_->OpenInMemory()); + + CreateInvalidValueColumnTable(db.db_.get()); + EXPECT_EQ(DomStorageDatabase::INVALID, db.DetectSchemaVersion()); + + CreateInvalidKeyColumnTable(db.db_.get()); + EXPECT_EQ(DomStorageDatabase::INVALID, db.DetectSchemaVersion()); + + CreateV1Table(db.db_.get()); + EXPECT_EQ(DomStorageDatabase::V1, db.DetectSchemaVersion()); + + CreateV2Table(db.db_.get()); + EXPECT_EQ(DomStorageDatabase::V2, db.DetectSchemaVersion()); +} + +TEST(DomStorageDatabaseTest, TestLazyOpenUpgradesDatabase) { + // This test needs to operate with a file on disk so that we + // can create a table at version 1 and then close it again + // so that LazyOpen sees there is work to do (LazyOpen will return + // early if the database is already open). + ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + FilePath file_name = temp_dir.path().AppendASCII("TestDomStorageDatabase.db"); + + DomStorageDatabase db(file_name); + db.db_.reset(new sql::Connection()); + ASSERT_TRUE(db.db_->Open(file_name)); + CreateV1Table(db.db_.get()); + db.Close(); + + EXPECT_TRUE(db.LazyOpen(true)); + EXPECT_EQ(DomStorageDatabase::V2, db.DetectSchemaVersion()); +} + +TEST(DomStorageDatabaseTest, SimpleWriteAndReadBack) { + DomStorageDatabase db; + + ValuesMap storage; + CreateMapWithValues(&storage); + + EXPECT_TRUE(db.CommitChanges(false, storage)); + CheckValuesMatch(&db, storage); +} + +TEST(DomStorageDatabaseTest, WriteWithClear) { + DomStorageDatabase db; + + 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); + ValuesMap expected; + expected[kCannedKey] = kCannedValue; + + DomStorageDatabase db; + db.db_.reset(new sql::Connection()); + ASSERT_TRUE(db.db_->OpenInMemory()); + CreateV1Table(db.db_.get()); + InsertDataV1(db.db_.get(), kCannedKey, kCannedValue.string()); + + ASSERT_TRUE(db.UpgradeVersion1To2()); + + EXPECT_EQ(DomStorageDatabase::V2, db.DetectSchemaVersion()); + + CheckValuesMatch(&db, expected); +} + +TEST(DomStorageDatabaseTest, TestSimpleRemoveOneValue) { + DomStorageDatabase db; + + ASSERT_TRUE(db.LazyOpen(true)); + const string16 kCannedKey = ASCIIToUTF16("test"); + const NullableString16 kCannedValue(ASCIIToUTF16("data"), false); + ValuesMap expected; + expected[kCannedKey] = kCannedValue; + + // First write some data into the database. + ASSERT_TRUE(db.CommitChanges(false, expected)); + CheckValuesMatch(&db, expected); + + ValuesMap values; + // A null string in the map should mean that that key gets + // removed. + values[kCannedKey] = NullableString16(true); + EXPECT_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); + ValuesMap values; + db.ReadAllValues(&values); + EXPECT_TRUE(db.IsOpen()); + EXPECT_EQ(2u, values.size()); + + 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. As it's not a database, we should end up deleting + // it and creating a new, valid file, so everything should actually + // succeed. + DomStorageDatabase db(file_name); + 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 and not attempt + // to delete it. + DomStorageDatabase db(temp_dir.path()); + 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()); + + EXPECT_TRUE(file_util::PathExists(temp_dir.path())); + } +} + +} // namespace dom_storage |