diff options
author | caitkp@chromium.org <caitkp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-05 18:41:22 +0000 |
---|---|---|
committer | caitkp@chromium.org <caitkp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-05 18:41:22 +0000 |
commit | c8e56bd49ed2a92cc51164aa2f06c5d7e1ed0a60 (patch) | |
tree | c6d621de3b1164c0752d76e7cc4f8b1c39c55bd4 /components | |
parent | b1babd99ec7a05a76f0b93f6fa17519b661a07b5 (diff) | |
download | chromium_src-c8e56bd49ed2a92cc51164aa2f06c5d7e1ed0a60.zip chromium_src-c8e56bd49ed2a92cc51164aa2f06c5d7e1ed0a60.tar.gz chromium_src-c8e56bd49ed2a92cc51164aa2f06c5d7e1ed0a60.tar.bz2 |
Move WebData component unittests to //components/webdata.
TBR=ben@chromium.org
BUG=181277
Review URL: https://chromiumcodereview.appspot.com/13650007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@192608 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components')
-rw-r--r-- | components/autofill/browser/autocomplete_history_manager_unittest.cc | 2 | ||||
-rw-r--r-- | components/webdata/DEPS | 22 | ||||
-rw-r--r-- | components/webdata/autofill/autofill_entry_unittest.cc | 78 | ||||
-rw-r--r-- | components/webdata/autofill/autofill_table_unittest.cc | 1328 | ||||
-rw-r--r-- | components/webdata/autofill/web_data_service_unittest.cc | 532 | ||||
-rw-r--r-- | components/webdata/autofill/web_database_migration_unittest.cc | 1992 | ||||
-rw-r--r-- | components/webdata/common/web_data_service_test_util.cc | 38 | ||||
-rw-r--r-- | components/webdata/common/web_data_service_test_util.h | 76 |
8 files changed, 4063 insertions, 5 deletions
diff --git a/components/autofill/browser/autocomplete_history_manager_unittest.cc b/components/autofill/browser/autocomplete_history_manager_unittest.cc index 5d1e2a2..fcee54d 100644 --- a/components/autofill/browser/autocomplete_history_manager_unittest.cc +++ b/components/autofill/browser/autocomplete_history_manager_unittest.cc @@ -9,7 +9,6 @@ #include "base/string16.h" #include "base/utf_string_conversions.h" #include "chrome/browser/webdata/web_data_service_factory.h" -#include "chrome/browser/webdata/web_data_service_test_util.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" @@ -19,6 +18,7 @@ #include "components/autofill/browser/test_autofill_manager_delegate.h" #include "components/autofill/common/form_data.h" #include "components/webdata/autofill/autofill_webdata_service.h" +#include "components/webdata/common/web_data_service_test_util.h" #include "content/public/test/test_browser_thread.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/components/webdata/DEPS b/components/webdata/DEPS index c9730aa..6ccecd5 100644 --- a/components/webdata/DEPS +++ b/components/webdata/DEPS @@ -1,10 +1,24 @@ include_rules = [ - # Webdata is being made into a component (it will end up at - # //components/webdata and not depend on //chrome), so we have these basic - # rules followed by temporary exceptions. Please don't add to the list of - # exceptions! "+components/encryptor", "+content/public/browser", "+sql", "+ui", ] + +specific_include_rules = { + # TODO(caitkp): Extract unit tests from //chrome, at lower priority + # than production code. + r'(.*_unittest|.*_test_util)\.(cc|h)': [ + "+chrome/browser/webdata/keyword_table.h", + "+chrome/browser/webdata/logins_table.h", + "+chrome/browser/webdata/token_service_table.h", + "+chrome/browser/webdata/web_apps_table.h", + "+chrome/browser/webdata/web_data_service.h", + "+chrome/browser/webdata/web_data_service_factory.h", + "+chrome/browser/webdata/web_intents_table.h", + "+chrome/test/base/ui_test_utils.h", + "+content/public/test", + "+testing/gmock/include/gmock/gmock.h", + "+testing/gtest/include/gtest/gtest.h", + ], +} diff --git a/components/webdata/autofill/autofill_entry_unittest.cc b/components/webdata/autofill/autofill_entry_unittest.cc new file mode 100644 index 0000000..481877b --- /dev/null +++ b/components/webdata/autofill/autofill_entry_unittest.cc @@ -0,0 +1,78 @@ +// 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 <algorithm> + +#include "base/time.h" +#include "base/utf_string_conversions.h" +#include "components/webdata/autofill/autofill_entry.h" +#include "testing/gtest/include/gtest/gtest.h" + +const unsigned int kMaxAutofillTimeStamps = 2; + +TEST(AutofillEntryTest, NoCulling) { + std::vector<base::Time> source, result; + base::Time current = base::Time::Now(); + for (size_t i = 0; i < kMaxAutofillTimeStamps; ++i) + source.push_back(current); + + EXPECT_FALSE(AutofillEntry::CullTimeStamps(source, &result)); + EXPECT_EQ(result.size(), kMaxAutofillTimeStamps); + for (std::vector<base::Time>::const_iterator it = result.begin(); + it != result.end(); ++it) { + EXPECT_EQ(*it, current); + } +} + +TEST(AutofillEntryTest, Culling) { + std::vector<base::Time> source, result; + base::Time current = base::Time::Now(); + const int offset = 10000; + + int64 internal_value = current.ToInternalValue(); + for (size_t i = 0; i < kMaxAutofillTimeStamps * 2 ; ++i) { + source.push_back(base::Time::FromInternalValue( + internal_value + i * offset)); + } + std::sort(source.begin(), source.end()); + EXPECT_TRUE(AutofillEntry::CullTimeStamps(source, &result)); + + EXPECT_EQ(result.size(), kMaxAutofillTimeStamps); + EXPECT_EQ(result.front(), base::Time::FromInternalValue(internal_value)); + int last_offset = (kMaxAutofillTimeStamps * 2 - 1) * offset; + EXPECT_EQ(result.back(), + base::Time::FromInternalValue(last_offset + internal_value)); +} + +TEST(AutofillEntryTest, CullByTime) { + base::TimeDelta one_hour = base::TimeDelta::FromHours(1); + + std::vector<base::Time> timestamps; + base::Time cutoff_time = AutofillEntry::ExpirationTime(); + + // Within the time limit. + timestamps.push_back(cutoff_time + one_hour); + + AutofillKey key(UTF8ToUTF16("test_key"), UTF8ToUTF16("test_value")); + + AutofillEntry entry_within_the_limits(key, timestamps); + EXPECT_FALSE(entry_within_the_limits.IsExpired()); + + // One within the time limit, one outside. + timestamps.push_back(cutoff_time - one_hour); + + AutofillEntry entry_partially_within_the_limits(key, timestamps); + EXPECT_TRUE( + entry_partially_within_the_limits.IsExpired()); + + // All outside the time limit. + timestamps.clear(); + timestamps.push_back(cutoff_time - one_hour); + timestamps.push_back(cutoff_time - one_hour * 2); + timestamps.push_back(cutoff_time - one_hour * 3); + + AutofillEntry entry_outside_the_limits(key, timestamps); + EXPECT_TRUE(entry_outside_the_limits.IsExpired()); + EXPECT_TRUE(entry_outside_the_limits.timestamps_culled()); +} diff --git a/components/webdata/autofill/autofill_table_unittest.cc b/components/webdata/autofill/autofill_table_unittest.cc new file mode 100644 index 0000000..6ce6d57 --- /dev/null +++ b/components/webdata/autofill/autofill_table_unittest.cc @@ -0,0 +1,1328 @@ +// 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 <vector> + +#include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/guid.h" +#include "base/path_service.h" +#include "base/strings/string_number_conversions.h" +#include "base/time.h" +#include "base/utf_string_conversions.h" +#include "components/autofill/browser/autofill_profile.h" +#include "components/autofill/browser/autofill_type.h" +#include "components/autofill/browser/credit_card.h" +#include "components/autofill/common/form_field_data.h" +#include "components/webdata/autofill/autofill_change.h" +#include "components/webdata/autofill/autofill_entry.h" +#include "components/webdata/autofill/autofill_table.h" +#include "components/webdata/common/web_database.h" +#include "components/webdata/encryptor/encryptor.h" +#include "sql/statement.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::Time; +using base::TimeDelta; + +// So we can compare AutofillKeys with EXPECT_EQ(). +std::ostream& operator<<(std::ostream& os, const AutofillKey& key) { + return os << UTF16ToASCII(key.name()) << ", " << UTF16ToASCII(key.value()); +} + +// So we can compare AutofillChanges with EXPECT_EQ(). +std::ostream& operator<<(std::ostream& os, const AutofillChange& change) { + switch (change.type()) { + case AutofillChange::ADD: { + os << "ADD"; + break; + } + case AutofillChange::UPDATE: { + os << "UPDATE"; + break; + } + case AutofillChange::REMOVE: { + os << "REMOVE"; + break; + } + } + return os << " " << change.key(); +} + +namespace { + +bool CompareAutofillEntries(const AutofillEntry& a, const AutofillEntry& b) { + std::set<Time> timestamps1(a.timestamps().begin(), a.timestamps().end()); + std::set<Time> timestamps2(b.timestamps().begin(), b.timestamps().end()); + + int compVal = a.key().name().compare(b.key().name()); + if (compVal != 0) { + return compVal < 0; + } + + compVal = a.key().value().compare(b.key().value()); + if (compVal != 0) { + return compVal < 0; + } + + if (timestamps1.size() != timestamps2.size()) { + return timestamps1.size() < timestamps2.size(); + } + + std::set<Time>::iterator it; + for (it = timestamps1.begin(); it != timestamps1.end(); it++) { + timestamps2.erase(*it); + } + + return !timestamps2.empty(); +} + +} // anonymous namespace + +class AutofillTableTest : public testing::Test { + public: + AutofillTableTest() {} + virtual ~AutofillTableTest() {} + + protected: + typedef std::set<AutofillEntry, + bool (*)(const AutofillEntry&, const AutofillEntry&)> AutofillEntrySet; + typedef std::set<AutofillEntry, bool (*)(const AutofillEntry&, + const AutofillEntry&)>::iterator AutofillEntrySetIterator; + + virtual void SetUp() { +#if defined(OS_MACOSX) + Encryptor::UseMockKeychain(true); +#endif + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + file_ = temp_dir_.path().AppendASCII("TestWebDatabase"); + + table_.reset(new AutofillTable); + db_.reset(new WebDatabase); + db_->AddTable(table_.get()); + ASSERT_EQ(sql::INIT_OK, db_->Init(file_)); + } + + static AutofillEntry MakeAutofillEntry(const char* name, + const char* value, + time_t timestamp0, + time_t timestamp1) { + std::vector<Time> timestamps; + if (timestamp0 >= 0) + timestamps.push_back(Time::FromTimeT(timestamp0)); + if (timestamp1 >= 0) + timestamps.push_back(Time::FromTimeT(timestamp1)); + return AutofillEntry( + AutofillKey(ASCIIToUTF16(name), ASCIIToUTF16(value)), timestamps); + } + + base::FilePath file_; + base::ScopedTempDir temp_dir_; + scoped_ptr<AutofillTable> table_; + scoped_ptr<WebDatabase> db_; + + private: + DISALLOW_COPY_AND_ASSIGN(AutofillTableTest); +}; + +TEST_F(AutofillTableTest, Autofill) { + Time t1 = Time::Now(); + + // Simulate the submission of a handful of entries in a field called "Name", + // some more often than others. + AutofillChangeList changes; + FormFieldData field; + field.name = ASCIIToUTF16("Name"); + field.value = ASCIIToUTF16("Superman"); + base::Time now = base::Time::Now(); + base::TimeDelta two_seconds = base::TimeDelta::FromSeconds(2); + EXPECT_TRUE(table_->AddFormFieldValue(field, &changes)); + std::vector<string16> v; + for (int i = 0; i < 5; i++) { + field.value = ASCIIToUTF16("Clark Kent"); + EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, + now + i * two_seconds)); + } + for (int i = 0; i < 3; i++) { + field.value = ASCIIToUTF16("Clark Sutter"); + EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, + now + i * two_seconds)); + } + for (int i = 0; i < 2; i++) { + field.name = ASCIIToUTF16("Favorite Color"); + field.value = ASCIIToUTF16("Green"); + EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, + now + i * two_seconds)); + } + + int count = 0; + int64 pair_id = 0; + + // We have added the name Clark Kent 5 times, so count should be 5 and pair_id + // should be somthing non-zero. + field.name = ASCIIToUTF16("Name"); + field.value = ASCIIToUTF16("Clark Kent"); + EXPECT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count)); + EXPECT_EQ(5, count); + EXPECT_NE(0, pair_id); + + // Storing in the data base should be case sensitive, so there should be no + // database entry for clark kent lowercase. + field.value = ASCIIToUTF16("clark kent"); + EXPECT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count)); + EXPECT_EQ(0, count); + + field.name = ASCIIToUTF16("Favorite Color"); + field.value = ASCIIToUTF16("Green"); + EXPECT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count)); + EXPECT_EQ(2, count); + + // This is meant to get a list of suggestions for Name. The empty prefix + // in the second argument means it should return all suggestions for a name + // no matter what they start with. The order that the names occur in the list + // should be decreasing order by count. + EXPECT_TRUE(table_->GetFormValuesForElementName( + ASCIIToUTF16("Name"), string16(), &v, 6)); + EXPECT_EQ(3U, v.size()); + if (v.size() == 3) { + EXPECT_EQ(ASCIIToUTF16("Clark Kent"), v[0]); + EXPECT_EQ(ASCIIToUTF16("Clark Sutter"), v[1]); + EXPECT_EQ(ASCIIToUTF16("Superman"), v[2]); + } + + // If we query again limiting the list size to 1, we should only get the most + // frequent entry. + EXPECT_TRUE(table_->GetFormValuesForElementName( + ASCIIToUTF16("Name"), string16(), &v, 1)); + EXPECT_EQ(1U, v.size()); + if (v.size() == 1) { + EXPECT_EQ(ASCIIToUTF16("Clark Kent"), v[0]); + } + + // Querying for suggestions given a prefix is case-insensitive, so the prefix + // "cLa" shoud get suggestions for both Clarks. + EXPECT_TRUE(table_->GetFormValuesForElementName( + ASCIIToUTF16("Name"), ASCIIToUTF16("cLa"), &v, 6)); + EXPECT_EQ(2U, v.size()); + if (v.size() == 2) { + EXPECT_EQ(ASCIIToUTF16("Clark Kent"), v[0]); + EXPECT_EQ(ASCIIToUTF16("Clark Sutter"), v[1]); + } + + // Removing all elements since the beginning of this function should remove + // everything from the database. + changes.clear(); + EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(t1, Time(), &changes)); + + const AutofillChange expected_changes[] = { + AutofillChange(AutofillChange::REMOVE, + AutofillKey(ASCIIToUTF16("Name"), + ASCIIToUTF16("Superman"))), + AutofillChange(AutofillChange::REMOVE, + AutofillKey(ASCIIToUTF16("Name"), + ASCIIToUTF16("Clark Kent"))), + AutofillChange(AutofillChange::REMOVE, + AutofillKey(ASCIIToUTF16("Name"), + ASCIIToUTF16("Clark Sutter"))), + AutofillChange(AutofillChange::REMOVE, + AutofillKey(ASCIIToUTF16("Favorite Color"), + ASCIIToUTF16("Green"))), + }; + EXPECT_EQ(arraysize(expected_changes), changes.size()); + for (size_t i = 0; i < arraysize(expected_changes); i++) { + EXPECT_EQ(expected_changes[i], changes[i]); + } + + field.name = ASCIIToUTF16("Name"); + field.value = ASCIIToUTF16("Clark Kent"); + EXPECT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count)); + EXPECT_EQ(0, count); + + EXPECT_TRUE(table_->GetFormValuesForElementName( + ASCIIToUTF16("Name"), string16(), &v, 6)); + EXPECT_EQ(0U, v.size()); + + // Now add some values with empty strings. + const string16 kValue = ASCIIToUTF16(" toto "); + field.name = ASCIIToUTF16("blank"); + field.value = string16(); + EXPECT_TRUE(table_->AddFormFieldValue(field, &changes)); + field.name = ASCIIToUTF16("blank"); + field.value = ASCIIToUTF16(" "); + EXPECT_TRUE(table_->AddFormFieldValue(field, &changes)); + field.name = ASCIIToUTF16("blank"); + field.value = ASCIIToUTF16(" "); + EXPECT_TRUE(table_->AddFormFieldValue(field, &changes)); + field.name = ASCIIToUTF16("blank"); + field.value = kValue; + EXPECT_TRUE(table_->AddFormFieldValue(field, &changes)); + + // They should be stored normally as the DB layer does not check for empty + // values. + v.clear(); + EXPECT_TRUE(table_->GetFormValuesForElementName( + ASCIIToUTF16("blank"), string16(), &v, 10)); + EXPECT_EQ(4U, v.size()); + + // Now we'll check that ClearAutofillEmptyValueElements() works as expected. + table_->ClearAutofillEmptyValueElements(); + + v.clear(); + EXPECT_TRUE(table_->GetFormValuesForElementName( + ASCIIToUTF16("blank"), string16(), &v, 10)); + ASSERT_EQ(1U, v.size()); + + EXPECT_EQ(kValue, v[0]); +} + +TEST_F(AutofillTableTest, Autofill_RemoveBetweenChanges) { + TimeDelta one_day(TimeDelta::FromDays(1)); + Time t1 = Time::Now(); + Time t2 = t1 + one_day; + + AutofillChangeList changes; + FormFieldData field; + field.name = ASCIIToUTF16("Name"); + field.value = ASCIIToUTF16("Superman"); + EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, t1)); + EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, t2)); + + changes.clear(); + EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(t1, t2, &changes)); + ASSERT_EQ(1U, changes.size()); + EXPECT_EQ(AutofillChange(AutofillChange::UPDATE, + AutofillKey(ASCIIToUTF16("Name"), + ASCIIToUTF16("Superman"))), + changes[0]); + changes.clear(); + + EXPECT_TRUE( + table_->RemoveFormElementsAddedBetween(t2, t2 + one_day, &changes)); + ASSERT_EQ(1U, changes.size()); + EXPECT_EQ(AutofillChange(AutofillChange::REMOVE, + AutofillKey(ASCIIToUTF16("Name"), + ASCIIToUTF16("Superman"))), + changes[0]); +} + +TEST_F(AutofillTableTest, Autofill_AddChanges) { + TimeDelta one_day(TimeDelta::FromDays(1)); + Time t1 = Time::Now(); + Time t2 = t1 + one_day; + + AutofillChangeList changes; + FormFieldData field; + field.name = ASCIIToUTF16("Name"); + field.value = ASCIIToUTF16("Superman"); + EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, t1)); + ASSERT_EQ(1U, changes.size()); + EXPECT_EQ(AutofillChange(AutofillChange::ADD, + AutofillKey(ASCIIToUTF16("Name"), + ASCIIToUTF16("Superman"))), + changes[0]); + + changes.clear(); + EXPECT_TRUE( + table_->AddFormFieldValueTime(field, &changes, t2)); + ASSERT_EQ(1U, changes.size()); + EXPECT_EQ(AutofillChange(AutofillChange::UPDATE, + AutofillKey(ASCIIToUTF16("Name"), + ASCIIToUTF16("Superman"))), + changes[0]); +} + +TEST_F(AutofillTableTest, Autofill_UpdateOneWithOneTimestamp) { + AutofillEntry entry(MakeAutofillEntry("foo", "bar", 1, -1)); + std::vector<AutofillEntry> entries; + entries.push_back(entry); + ASSERT_TRUE(table_->UpdateAutofillEntries(entries)); + + FormFieldData field; + field.name = ASCIIToUTF16("foo"); + field.value = ASCIIToUTF16("bar"); + int64 pair_id; + int count; + ASSERT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count)); + EXPECT_LE(0, pair_id); + EXPECT_EQ(1, count); + + std::vector<AutofillEntry> all_entries; + ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries)); + ASSERT_EQ(1U, all_entries.size()); + EXPECT_TRUE(entry == all_entries[0]); +} + +TEST_F(AutofillTableTest, Autofill_UpdateOneWithTwoTimestamps) { + AutofillEntry entry(MakeAutofillEntry("foo", "bar", 1, 2)); + std::vector<AutofillEntry> entries; + entries.push_back(entry); + ASSERT_TRUE(table_->UpdateAutofillEntries(entries)); + + FormFieldData field; + field.name = ASCIIToUTF16("foo"); + field.value = ASCIIToUTF16("bar"); + int64 pair_id; + int count; + ASSERT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count)); + EXPECT_LE(0, pair_id); + EXPECT_EQ(2, count); + + std::vector<AutofillEntry> all_entries; + ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries)); + ASSERT_EQ(1U, all_entries.size()); + EXPECT_TRUE(entry == all_entries[0]); +} + +TEST_F(AutofillTableTest, Autofill_GetAutofillTimestamps) { + AutofillEntry entry(MakeAutofillEntry("foo", "bar", 1, 2)); + std::vector<AutofillEntry> entries; + entries.push_back(entry); + ASSERT_TRUE(table_->UpdateAutofillEntries(entries)); + + std::vector<Time> timestamps; + ASSERT_TRUE(table_->GetAutofillTimestamps(ASCIIToUTF16("foo"), + ASCIIToUTF16("bar"), + ×tamps)); + ASSERT_EQ(2U, timestamps.size()); + EXPECT_TRUE(Time::FromTimeT(1) == timestamps[0]); + EXPECT_TRUE(Time::FromTimeT(2) == timestamps[1]); +} + +TEST_F(AutofillTableTest, Autofill_UpdateTwo) { + AutofillEntry entry0(MakeAutofillEntry("foo", "bar0", 1, -1)); + AutofillEntry entry1(MakeAutofillEntry("foo", "bar1", 2, 3)); + std::vector<AutofillEntry> entries; + entries.push_back(entry0); + entries.push_back(entry1); + ASSERT_TRUE(table_->UpdateAutofillEntries(entries)); + + FormFieldData field0; + field0.name = ASCIIToUTF16("foo"); + field0.value = ASCIIToUTF16("bar0"); + int64 pair_id; + int count; + ASSERT_TRUE(table_->GetIDAndCountOfFormElement(field0, &pair_id, &count)); + EXPECT_LE(0, pair_id); + EXPECT_EQ(1, count); + + FormFieldData field1; + field1.name = ASCIIToUTF16("foo"); + field1.value = ASCIIToUTF16("bar1"); + ASSERT_TRUE(table_->GetIDAndCountOfFormElement(field1, &pair_id, &count)); + EXPECT_LE(0, pair_id); + EXPECT_EQ(2, count); +} + +TEST_F(AutofillTableTest, Autofill_UpdateReplace) { + AutofillChangeList changes; + // Add a form field. This will be replaced. + FormFieldData field; + field.name = ASCIIToUTF16("Name"); + field.value = ASCIIToUTF16("Superman"); + EXPECT_TRUE(table_->AddFormFieldValue(field, &changes)); + + AutofillEntry entry(MakeAutofillEntry("Name", "Superman", 1, 2)); + std::vector<AutofillEntry> entries; + entries.push_back(entry); + ASSERT_TRUE(table_->UpdateAutofillEntries(entries)); + + std::vector<AutofillEntry> all_entries; + ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries)); + ASSERT_EQ(1U, all_entries.size()); + EXPECT_TRUE(entry == all_entries[0]); +} + +TEST_F(AutofillTableTest, Autofill_UpdateDontReplace) { + Time t = Time::Now(); + AutofillEntry existing( + MakeAutofillEntry("Name", "Superman", t.ToTimeT(), -1)); + + AutofillChangeList changes; + // Add a form field. This will NOT be replaced. + FormFieldData field; + field.name = existing.key().name(); + field.value = existing.key().value(); + EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, t)); + AutofillEntry entry(MakeAutofillEntry("Name", "Clark Kent", 1, 2)); + std::vector<AutofillEntry> entries; + entries.push_back(entry); + ASSERT_TRUE(table_->UpdateAutofillEntries(entries)); + + std::vector<AutofillEntry> all_entries; + ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries)); + ASSERT_EQ(2U, all_entries.size()); + AutofillEntrySet expected_entries(all_entries.begin(), + all_entries.end(), + CompareAutofillEntries); + EXPECT_EQ(1U, expected_entries.count(existing)); + EXPECT_EQ(1U, expected_entries.count(entry)); +} + +TEST_F(AutofillTableTest, Autofill_AddFormFieldValues) { + Time t = Time::Now(); + + // Add multiple values for "firstname" and "lastname" names. Test that only + // first value of each gets added. Related to security issue: + // http://crbug.com/51727. + std::vector<FormFieldData> elements; + FormFieldData field; + field.name = ASCIIToUTF16("firstname"); + field.value = ASCIIToUTF16("Joe"); + elements.push_back(field); + + field.name = ASCIIToUTF16("firstname"); + field.value = ASCIIToUTF16("Jane"); + elements.push_back(field); + + field.name = ASCIIToUTF16("lastname"); + field.value = ASCIIToUTF16("Smith"); + elements.push_back(field); + + field.name = ASCIIToUTF16("lastname"); + field.value = ASCIIToUTF16("Jones"); + elements.push_back(field); + + std::vector<AutofillChange> changes; + table_->AddFormFieldValuesTime(elements, &changes, t); + + ASSERT_EQ(2U, changes.size()); + EXPECT_EQ(changes[0], AutofillChange(AutofillChange::ADD, + AutofillKey(ASCIIToUTF16("firstname"), + ASCIIToUTF16("Joe")))); + EXPECT_EQ(changes[1], AutofillChange(AutofillChange::ADD, + AutofillKey(ASCIIToUTF16("lastname"), + ASCIIToUTF16("Smith")))); + + std::vector<AutofillEntry> all_entries; + ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries)); + ASSERT_EQ(2U, all_entries.size()); +} + +TEST_F(AutofillTableTest, AutofillProfile) { + // Add a 'Home' profile. + AutofillProfile home_profile; + home_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + home_profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Q.")); + home_profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Smith")); + home_profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("js@smith.xyz")); + home_profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Google")); + home_profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1234 Apple Way")); + home_profile.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("unit 5")); + home_profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Los Angeles")); + home_profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CA")); + home_profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("90025")); + home_profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US")); + home_profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("18181234567")); + + Time pre_creation_time = Time::Now(); + EXPECT_TRUE(table_->AddAutofillProfile(home_profile)); + Time post_creation_time = Time::Now(); + + // Get the 'Home' profile. + AutofillProfile* db_profile; + ASSERT_TRUE(table_->GetAutofillProfile(home_profile.guid(), &db_profile)); + EXPECT_EQ(home_profile, *db_profile); + sql::Statement s_home(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified " + "FROM autofill_profiles WHERE guid=?")); + s_home.BindString(0, home_profile.guid()); + ASSERT_TRUE(s_home.is_valid()); + ASSERT_TRUE(s_home.Step()); + EXPECT_GE(s_home.ColumnInt64(0), pre_creation_time.ToTimeT()); + EXPECT_LE(s_home.ColumnInt64(0), post_creation_time.ToTimeT()); + EXPECT_FALSE(s_home.Step()); + delete db_profile; + + // Add a 'Billing' profile. + AutofillProfile billing_profile = home_profile; + billing_profile.set_guid(base::GenerateGUID()); + billing_profile.SetRawInfo(ADDRESS_HOME_LINE1, + ASCIIToUTF16("5678 Bottom Street")); + billing_profile.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("suite 3")); + + pre_creation_time = Time::Now(); + EXPECT_TRUE(table_->AddAutofillProfile(billing_profile)); + post_creation_time = Time::Now(); + + // Get the 'Billing' profile. + ASSERT_TRUE(table_->GetAutofillProfile(billing_profile.guid(), &db_profile)); + EXPECT_EQ(billing_profile, *db_profile); + sql::Statement s_billing(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM autofill_profiles WHERE guid=?")); + s_billing.BindString(0, billing_profile.guid()); + ASSERT_TRUE(s_billing.is_valid()); + ASSERT_TRUE(s_billing.Step()); + EXPECT_GE(s_billing.ColumnInt64(0), pre_creation_time.ToTimeT()); + EXPECT_LE(s_billing.ColumnInt64(0), post_creation_time.ToTimeT()); + EXPECT_FALSE(s_billing.Step()); + delete db_profile; + + // Update the 'Billing' profile, name only. + billing_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane")); + Time pre_modification_time = Time::Now(); + EXPECT_TRUE(table_->UpdateAutofillProfileMulti(billing_profile)); + Time post_modification_time = Time::Now(); + ASSERT_TRUE(table_->GetAutofillProfile(billing_profile.guid(), &db_profile)); + EXPECT_EQ(billing_profile, *db_profile); + sql::Statement s_billing_updated(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM autofill_profiles WHERE guid=?")); + s_billing_updated.BindString(0, billing_profile.guid()); + ASSERT_TRUE(s_billing_updated.is_valid()); + ASSERT_TRUE(s_billing_updated.Step()); + EXPECT_GE(s_billing_updated.ColumnInt64(0), + pre_modification_time.ToTimeT()); + EXPECT_LE(s_billing_updated.ColumnInt64(0), + post_modification_time.ToTimeT()); + EXPECT_FALSE(s_billing_updated.Step()); + delete db_profile; + + // Update the 'Billing' profile. + billing_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Janice")); + billing_profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("C.")); + billing_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Joplin")); + billing_profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("jane@singer.com")); + billing_profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Indy")); + billing_profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("Open Road")); + billing_profile.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("Route 66")); + billing_profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("NFA")); + billing_profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("NY")); + billing_profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("10011")); + billing_profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US")); + billing_profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, + ASCIIToUTF16("18181230000")); + Time pre_modification_time_2 = Time::Now(); + EXPECT_TRUE(table_->UpdateAutofillProfileMulti(billing_profile)); + Time post_modification_time_2 = Time::Now(); + ASSERT_TRUE(table_->GetAutofillProfile(billing_profile.guid(), &db_profile)); + EXPECT_EQ(billing_profile, *db_profile); + sql::Statement s_billing_updated_2( + db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM autofill_profiles WHERE guid=?")); + s_billing_updated_2.BindString(0, billing_profile.guid()); + ASSERT_TRUE(s_billing_updated_2.is_valid()); + ASSERT_TRUE(s_billing_updated_2.Step()); + EXPECT_GE(s_billing_updated_2.ColumnInt64(0), + pre_modification_time_2.ToTimeT()); + EXPECT_LE(s_billing_updated_2.ColumnInt64(0), + post_modification_time_2.ToTimeT()); + EXPECT_FALSE(s_billing_updated_2.Step()); + delete db_profile; + + // Remove the 'Billing' profile. + EXPECT_TRUE(table_->RemoveAutofillProfile(billing_profile.guid())); + EXPECT_FALSE(table_->GetAutofillProfile(billing_profile.guid(), &db_profile)); +} + +TEST_F(AutofillTableTest, AutofillProfileMultiValueNames) { + AutofillProfile p; + const string16 kJohnDoe(ASCIIToUTF16("John Doe")); + const string16 kJohnPDoe(ASCIIToUTF16("John P. Doe")); + std::vector<string16> set_values; + set_values.push_back(kJohnDoe); + set_values.push_back(kJohnPDoe); + p.SetRawMultiInfo(NAME_FULL, set_values); + + EXPECT_TRUE(table_->AddAutofillProfile(p)); + + AutofillProfile* db_profile; + ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile)); + EXPECT_EQ(p, *db_profile); + EXPECT_EQ(0, p.Compare(*db_profile)); + delete db_profile; + + // Update the values. + const string16 kNoOne(ASCIIToUTF16("No One")); + set_values[1] = kNoOne; + p.SetRawMultiInfo(NAME_FULL, set_values); + EXPECT_TRUE(table_->UpdateAutofillProfileMulti(p)); + ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile)); + EXPECT_EQ(p, *db_profile); + EXPECT_EQ(0, p.Compare(*db_profile)); + delete db_profile; + + // Delete values. + set_values.clear(); + p.SetRawMultiInfo(NAME_FULL, set_values); + EXPECT_TRUE(table_->UpdateAutofillProfileMulti(p)); + ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile)); + EXPECT_EQ(p, *db_profile); + EXPECT_EQ(0, p.Compare(*db_profile)); + EXPECT_EQ(string16(), db_profile->GetRawInfo(NAME_FULL)); + delete db_profile; +} + +TEST_F(AutofillTableTest, AutofillProfileSingleValue) { + AutofillProfile p; + const string16 kJohnDoe(ASCIIToUTF16("John Doe")); + const string16 kJohnPDoe(ASCIIToUTF16("John P. Doe")); + std::vector<string16> set_values; + set_values.push_back(kJohnDoe); + set_values.push_back(kJohnPDoe); + p.SetRawMultiInfo(NAME_FULL, set_values); + + EXPECT_TRUE(table_->AddAutofillProfile(p)); + + AutofillProfile* db_profile; + ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile)); + EXPECT_EQ(p, *db_profile); + EXPECT_EQ(0, p.Compare(*db_profile)); + delete db_profile; + + const string16 kNoOne(ASCIIToUTF16("No One")); + set_values.resize(1); + set_values[0] = kNoOne; + p.SetRawMultiInfo(NAME_FULL, set_values); + EXPECT_TRUE(table_->UpdateAutofillProfile(p)); + ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile)); + EXPECT_EQ(p.PrimaryValue(), db_profile->PrimaryValue()); + EXPECT_EQ(p.guid(), db_profile->guid()); + EXPECT_NE(0, p.Compare(*db_profile)); + db_profile->GetRawMultiInfo(NAME_FULL, &set_values); + ASSERT_EQ(2UL, set_values.size()); + EXPECT_EQ(kNoOne, set_values[0]); + EXPECT_EQ(kJohnPDoe, set_values[1]); + delete db_profile; +} + +TEST_F(AutofillTableTest, AutofillProfileMultiValueEmails) { + AutofillProfile p; + const string16 kJohnDoe(ASCIIToUTF16("john@doe.com")); + const string16 kJohnPDoe(ASCIIToUTF16("john_p@doe.com")); + std::vector<string16> set_values; + set_values.push_back(kJohnDoe); + set_values.push_back(kJohnPDoe); + p.SetRawMultiInfo(EMAIL_ADDRESS, set_values); + + EXPECT_TRUE(table_->AddAutofillProfile(p)); + + AutofillProfile* db_profile; + ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile)); + EXPECT_EQ(p, *db_profile); + EXPECT_EQ(0, p.Compare(*db_profile)); + delete db_profile; + + // Update the values. + const string16 kNoOne(ASCIIToUTF16("no@one.com")); + set_values[1] = kNoOne; + p.SetRawMultiInfo(EMAIL_ADDRESS, set_values); + EXPECT_TRUE(table_->UpdateAutofillProfileMulti(p)); + ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile)); + EXPECT_EQ(p, *db_profile); + EXPECT_EQ(0, p.Compare(*db_profile)); + delete db_profile; + + // Delete values. + set_values.clear(); + p.SetRawMultiInfo(EMAIL_ADDRESS, set_values); + EXPECT_TRUE(table_->UpdateAutofillProfileMulti(p)); + ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile)); + EXPECT_EQ(p, *db_profile); + EXPECT_EQ(0, p.Compare(*db_profile)); + EXPECT_EQ(string16(), db_profile->GetRawInfo(EMAIL_ADDRESS)); + delete db_profile; +} + +TEST_F(AutofillTableTest, AutofillProfileMultiValuePhone) { + AutofillProfile p; + const string16 kJohnDoe(ASCIIToUTF16("4151112222")); + const string16 kJohnPDoe(ASCIIToUTF16("4151113333")); + std::vector<string16> set_values; + set_values.push_back(kJohnDoe); + set_values.push_back(kJohnPDoe); + p.SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, set_values); + + EXPECT_TRUE(table_->AddAutofillProfile(p)); + + AutofillProfile* db_profile; + ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile)); + EXPECT_EQ(p, *db_profile); + EXPECT_EQ(0, p.Compare(*db_profile)); + delete db_profile; + + // Update the values. + const string16 kNoOne(ASCIIToUTF16("4151110000")); + set_values[1] = kNoOne; + p.SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, set_values); + EXPECT_TRUE(table_->UpdateAutofillProfileMulti(p)); + ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile)); + EXPECT_EQ(p, *db_profile); + EXPECT_EQ(0, p.Compare(*db_profile)); + delete db_profile; + + // Delete values. + set_values.clear(); + p.SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, set_values); + EXPECT_TRUE(table_->UpdateAutofillProfileMulti(p)); + ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile)); + EXPECT_EQ(p, *db_profile); + EXPECT_EQ(0, p.Compare(*db_profile)); + EXPECT_EQ(string16(), db_profile->GetRawInfo(EMAIL_ADDRESS)); + delete db_profile; +} + +TEST_F(AutofillTableTest, AutofillProfileTrash) { + std::vector<std::string> guids; + table_->GetAutofillProfilesInTrash(&guids); + EXPECT_TRUE(guids.empty()); + + ASSERT_TRUE(table_->AddAutofillGUIDToTrash( + "00000000-0000-0000-0000-000000000000")); + ASSERT_TRUE(table_->AddAutofillGUIDToTrash( + "00000000-0000-0000-0000-000000000001")); + ASSERT_TRUE(table_->GetAutofillProfilesInTrash(&guids)); + EXPECT_EQ(2UL, guids.size()); + EXPECT_EQ("00000000-0000-0000-0000-000000000000", guids[0]); + EXPECT_EQ("00000000-0000-0000-0000-000000000001", guids[1]); + + ASSERT_TRUE(table_->EmptyAutofillProfilesTrash()); + ASSERT_TRUE(table_->GetAutofillProfilesInTrash(&guids)); + EXPECT_TRUE(guids.empty()); +} + +TEST_F(AutofillTableTest, AutofillProfileTrashInteraction) { + std::vector<std::string> guids; + table_->GetAutofillProfilesInTrash(&guids); + EXPECT_TRUE(guids.empty()); + + AutofillProfile profile; + profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Q.")); + profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Smith")); + profile.SetRawInfo(EMAIL_ADDRESS,ASCIIToUTF16("js@smith.xyz")); + profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1 Main St")); + profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Los Angeles")); + profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CA")); + profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("90025")); + profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US")); + + // Mark this profile as in the trash. This stops |AddAutofillProfile| from + // adding it. + EXPECT_TRUE(table_->AddAutofillGUIDToTrash(profile.guid())); + EXPECT_TRUE(table_->AddAutofillProfile(profile)); + AutofillProfile* added_profile = NULL; + EXPECT_FALSE(table_->GetAutofillProfile(profile.guid(), &added_profile)); + EXPECT_EQ(static_cast<AutofillProfile*>(NULL), added_profile); + + // Add the profile for real this time. + EXPECT_TRUE(table_->EmptyAutofillProfilesTrash()); + EXPECT_TRUE(table_->GetAutofillProfilesInTrash(&guids)); + EXPECT_TRUE(guids.empty()); + EXPECT_TRUE(table_->AddAutofillProfile(profile)); + EXPECT_TRUE(table_->GetAutofillProfile(profile.guid(), + &added_profile)); + ASSERT_NE(static_cast<AutofillProfile*>(NULL), added_profile); + delete added_profile; + + // Mark this profile as in the trash. This stops |UpdateAutofillProfileMulti| + // from updating it. In normal operation a profile should not be both in the + // trash and in the profiles table simultaneously. + EXPECT_TRUE(table_->AddAutofillGUIDToTrash(profile.guid())); + profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane")); + EXPECT_TRUE(table_->UpdateAutofillProfileMulti(profile)); + AutofillProfile* updated_profile = NULL; + EXPECT_TRUE(table_->GetAutofillProfile(profile.guid(), &updated_profile)); + ASSERT_NE(static_cast<AutofillProfile*>(NULL), added_profile); + EXPECT_EQ(ASCIIToUTF16("John"), updated_profile->GetRawInfo(NAME_FIRST)); + delete updated_profile; + + // Try to delete the trashed profile. This stops |RemoveAutofillProfile| from + // deleting it. In normal operation deletion is done by migration step, and + // removal from trash is done by |WebDataService|. |RemoveAutofillProfile| + // does remove the item from the trash if it is found however, so that if + // other clients remove it (via Sync say) then it is gone and doesn't need to + // be processed further by |WebDataService|. + EXPECT_TRUE(table_->RemoveAutofillProfile(profile.guid())); + AutofillProfile* removed_profile = NULL; + EXPECT_TRUE(table_->GetAutofillProfile(profile.guid(), &removed_profile)); + EXPECT_FALSE(table_->IsAutofillGUIDInTrash(profile.guid())); + ASSERT_NE(static_cast<AutofillProfile*>(NULL), removed_profile); + delete removed_profile; + + // Check that emptying the trash now allows removal to occur. + EXPECT_TRUE(table_->EmptyAutofillProfilesTrash()); + EXPECT_TRUE(table_->RemoveAutofillProfile(profile.guid())); + removed_profile = NULL; + EXPECT_FALSE(table_->GetAutofillProfile(profile.guid(), &removed_profile)); + EXPECT_EQ(static_cast<AutofillProfile*>(NULL), removed_profile); +} + +TEST_F(AutofillTableTest, CreditCard) { + // Add a 'Work' credit card. + CreditCard work_creditcard; + work_creditcard.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Jack Torrance")); + work_creditcard.SetRawInfo(CREDIT_CARD_NUMBER, + ASCIIToUTF16("1234567890123456")); + work_creditcard.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("04")); + work_creditcard.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, + ASCIIToUTF16("2013")); + + Time pre_creation_time = Time::Now(); + EXPECT_TRUE(table_->AddCreditCard(work_creditcard)); + Time post_creation_time = Time::Now(); + + // Get the 'Work' credit card. + CreditCard* db_creditcard; + ASSERT_TRUE(table_->GetCreditCard(work_creditcard.guid(), &db_creditcard)); + EXPECT_EQ(work_creditcard, *db_creditcard); + sql::Statement s_work(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT guid, name_on_card, expiration_month, expiration_year, " + "card_number_encrypted, date_modified " + "FROM credit_cards WHERE guid=?")); + s_work.BindString(0, work_creditcard.guid()); + ASSERT_TRUE(s_work.is_valid()); + ASSERT_TRUE(s_work.Step()); + EXPECT_GE(s_work.ColumnInt64(5), pre_creation_time.ToTimeT()); + EXPECT_LE(s_work.ColumnInt64(5), post_creation_time.ToTimeT()); + EXPECT_FALSE(s_work.Step()); + delete db_creditcard; + + // Add a 'Target' credit card. + CreditCard target_creditcard; + target_creditcard.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Jack Torrance")); + target_creditcard.SetRawInfo(CREDIT_CARD_NUMBER, + ASCIIToUTF16("1111222233334444")); + target_creditcard.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("06")); + target_creditcard.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, + ASCIIToUTF16("2012")); + + pre_creation_time = Time::Now(); + EXPECT_TRUE(table_->AddCreditCard(target_creditcard)); + post_creation_time = Time::Now(); + ASSERT_TRUE(table_->GetCreditCard(target_creditcard.guid(), &db_creditcard)); + EXPECT_EQ(target_creditcard, *db_creditcard); + sql::Statement s_target(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT guid, name_on_card, expiration_month, expiration_year, " + "card_number_encrypted, date_modified " + "FROM credit_cards WHERE guid=?")); + s_target.BindString(0, target_creditcard.guid()); + ASSERT_TRUE(s_target.is_valid()); + ASSERT_TRUE(s_target.Step()); + EXPECT_GE(s_target.ColumnInt64(5), pre_creation_time.ToTimeT()); + EXPECT_LE(s_target.ColumnInt64(5), post_creation_time.ToTimeT()); + EXPECT_FALSE(s_target.Step()); + delete db_creditcard; + + // Update the 'Target' credit card. + target_creditcard.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Charles Grady")); + Time pre_modification_time = Time::Now(); + EXPECT_TRUE(table_->UpdateCreditCard(target_creditcard)); + Time post_modification_time = Time::Now(); + ASSERT_TRUE(table_->GetCreditCard(target_creditcard.guid(), &db_creditcard)); + EXPECT_EQ(target_creditcard, *db_creditcard); + sql::Statement s_target_updated(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT guid, name_on_card, expiration_month, expiration_year, " + "card_number_encrypted, date_modified " + "FROM credit_cards WHERE guid=?")); + s_target_updated.BindString(0, target_creditcard.guid()); + ASSERT_TRUE(s_target_updated.is_valid()); + ASSERT_TRUE(s_target_updated.Step()); + EXPECT_GE(s_target_updated.ColumnInt64(5), pre_modification_time.ToTimeT()); + EXPECT_LE(s_target_updated.ColumnInt64(5), post_modification_time.ToTimeT()); + EXPECT_FALSE(s_target_updated.Step()); + delete db_creditcard; + + // Remove the 'Target' credit card. + EXPECT_TRUE(table_->RemoveCreditCard(target_creditcard.guid())); + EXPECT_FALSE(table_->GetCreditCard(target_creditcard.guid(), &db_creditcard)); +} + +TEST_F(AutofillTableTest, UpdateAutofillProfile) { + // Add a profile to the db. + AutofillProfile profile; + profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); + profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Q.")); + profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Smith")); + profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("js@example.com")); + profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Google")); + profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1234 Apple Way")); + profile.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("unit 5")); + profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Los Angeles")); + profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CA")); + profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("90025")); + profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US")); + profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("18181234567")); + table_->AddAutofillProfile(profile); + + // Set a mocked value for the profile's creation time. + const time_t mock_creation_date = Time::Now().ToTimeT() - 13; + sql::Statement s_mock_creation_date( + db_->GetSQLConnection()->GetUniqueStatement( + "UPDATE autofill_profiles SET date_modified = ?")); + ASSERT_TRUE(s_mock_creation_date.is_valid()); + s_mock_creation_date.BindInt64(0, mock_creation_date); + ASSERT_TRUE(s_mock_creation_date.Run()); + + // Get the profile. + AutofillProfile* tmp_profile; + ASSERT_TRUE(table_->GetAutofillProfile(profile.guid(), &tmp_profile)); + scoped_ptr<AutofillProfile> db_profile(tmp_profile); + EXPECT_EQ(profile, *db_profile); + sql::Statement s_original(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM autofill_profiles")); + ASSERT_TRUE(s_original.is_valid()); + ASSERT_TRUE(s_original.Step()); + EXPECT_EQ(mock_creation_date, s_original.ColumnInt64(0)); + EXPECT_FALSE(s_original.Step()); + + // Now, update the profile and save the update to the database. + // The modification date should change to reflect the update. + profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("js@smith.xyz")); + table_->UpdateAutofillProfileMulti(profile); + + // Get the profile. + ASSERT_TRUE(table_->GetAutofillProfile(profile.guid(), &tmp_profile)); + db_profile.reset(tmp_profile); + EXPECT_EQ(profile, *db_profile); + sql::Statement s_updated(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM autofill_profiles")); + ASSERT_TRUE(s_updated.is_valid()); + ASSERT_TRUE(s_updated.Step()); + EXPECT_LT(mock_creation_date, s_updated.ColumnInt64(0)); + EXPECT_FALSE(s_updated.Step()); + + // Set a mocked value for the profile's modification time. + const time_t mock_modification_date = Time::Now().ToTimeT() - 7; + sql::Statement s_mock_modification_date( + db_->GetSQLConnection()->GetUniqueStatement( + "UPDATE autofill_profiles SET date_modified = ?")); + ASSERT_TRUE(s_mock_modification_date.is_valid()); + s_mock_modification_date.BindInt64(0, mock_modification_date); + ASSERT_TRUE(s_mock_modification_date.Run()); + + // Finally, call into |UpdateAutofillProfileMulti()| without changing the + // profile. The modification date should not change. + table_->UpdateAutofillProfileMulti(profile); + + // Get the profile. + ASSERT_TRUE(table_->GetAutofillProfile(profile.guid(), &tmp_profile)); + db_profile.reset(tmp_profile); + EXPECT_EQ(profile, *db_profile); + sql::Statement s_unchanged(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM autofill_profiles")); + ASSERT_TRUE(s_unchanged.is_valid()); + ASSERT_TRUE(s_unchanged.Step()); + EXPECT_EQ(mock_modification_date, s_unchanged.ColumnInt64(0)); + EXPECT_FALSE(s_unchanged.Step()); +} + +TEST_F(AutofillTableTest, UpdateCreditCard) { + // Add a credit card to the db. + CreditCard credit_card; + credit_card.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Jack Torrance")); + credit_card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("1234567890123456")); + credit_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("04")); + credit_card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2013")); + table_->AddCreditCard(credit_card); + + // Set a mocked value for the credit card's creation time. + const time_t mock_creation_date = Time::Now().ToTimeT() - 13; + sql::Statement s_mock_creation_date( + db_->GetSQLConnection()->GetUniqueStatement( + "UPDATE credit_cards SET date_modified = ?")); + ASSERT_TRUE(s_mock_creation_date.is_valid()); + s_mock_creation_date.BindInt64(0, mock_creation_date); + ASSERT_TRUE(s_mock_creation_date.Run()); + + // Get the credit card. + CreditCard* tmp_credit_card; + ASSERT_TRUE(table_->GetCreditCard(credit_card.guid(), &tmp_credit_card)); + scoped_ptr<CreditCard> db_credit_card(tmp_credit_card); + EXPECT_EQ(credit_card, *db_credit_card); + sql::Statement s_original(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM credit_cards")); + ASSERT_TRUE(s_original.is_valid()); + ASSERT_TRUE(s_original.Step()); + EXPECT_EQ(mock_creation_date, s_original.ColumnInt64(0)); + EXPECT_FALSE(s_original.Step()); + + // Now, update the credit card and save the update to the database. + // The modification date should change to reflect the update. + credit_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("01")); + table_->UpdateCreditCard(credit_card); + + // Get the credit card. + ASSERT_TRUE(table_->GetCreditCard(credit_card.guid(), &tmp_credit_card)); + db_credit_card.reset(tmp_credit_card); + EXPECT_EQ(credit_card, *db_credit_card); + sql::Statement s_updated(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM credit_cards")); + ASSERT_TRUE(s_updated.is_valid()); + ASSERT_TRUE(s_updated.Step()); + EXPECT_LT(mock_creation_date, s_updated.ColumnInt64(0)); + EXPECT_FALSE(s_updated.Step()); + + // Set a mocked value for the credit card's modification time. + const time_t mock_modification_date = Time::Now().ToTimeT() - 7; + sql::Statement s_mock_modification_date( + db_->GetSQLConnection()->GetUniqueStatement( + "UPDATE credit_cards SET date_modified = ?")); + ASSERT_TRUE(s_mock_modification_date.is_valid()); + s_mock_modification_date.BindInt64(0, mock_modification_date); + ASSERT_TRUE(s_mock_modification_date.Run()); + + // Finally, call into |UpdateCreditCard()| without changing the credit card. + // The modification date should not change. + table_->UpdateCreditCard(credit_card); + + // Get the profile. + ASSERT_TRUE(table_->GetCreditCard(credit_card.guid(), &tmp_credit_card)); + db_credit_card.reset(tmp_credit_card); + EXPECT_EQ(credit_card, *db_credit_card); + sql::Statement s_unchanged(db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM credit_cards")); + ASSERT_TRUE(s_unchanged.is_valid()); + ASSERT_TRUE(s_unchanged.Step()); + EXPECT_EQ(mock_modification_date, s_unchanged.ColumnInt64(0)); + EXPECT_FALSE(s_unchanged.Step()); +} + +TEST_F(AutofillTableTest, RemoveAutofillDataModifiedBetween) { + // Populate the autofill_profiles and credit_cards tables. + ASSERT_TRUE(db_->GetSQLConnection()->Execute( + "INSERT INTO autofill_profiles (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000000', 11);" + "INSERT INTO autofill_profiles (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000001', 21);" + "INSERT INTO autofill_profiles (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000002', 31);" + "INSERT INTO autofill_profiles (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000003', 41);" + "INSERT INTO autofill_profiles (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000004', 51);" + "INSERT INTO autofill_profiles (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000005', 61);" + "INSERT INTO credit_cards (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000006', 17);" + "INSERT INTO credit_cards (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000007', 27);" + "INSERT INTO credit_cards (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000008', 37);" + "INSERT INTO credit_cards (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000009', 47);" + "INSERT INTO credit_cards (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000010', 57);" + "INSERT INTO credit_cards (guid, date_modified) " + "VALUES('00000000-0000-0000-0000-000000000011', 67);")); + + // Remove all entries modified in the bounded time range [17,41). + std::vector<std::string> profile_guids; + std::vector<std::string> credit_card_guids; + table_->RemoveAutofillDataModifiedBetween( + Time::FromTimeT(17), Time::FromTimeT(41), + &profile_guids, &credit_card_guids); + ASSERT_EQ(2UL, profile_guids.size()); + EXPECT_EQ("00000000-0000-0000-0000-000000000001", profile_guids[0]); + EXPECT_EQ("00000000-0000-0000-0000-000000000002", profile_guids[1]); + sql::Statement s_autofill_profiles_bounded( + db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM autofill_profiles")); + ASSERT_TRUE(s_autofill_profiles_bounded.is_valid()); + ASSERT_TRUE(s_autofill_profiles_bounded.Step()); + EXPECT_EQ(11, s_autofill_profiles_bounded.ColumnInt64(0)); + ASSERT_TRUE(s_autofill_profiles_bounded.Step()); + EXPECT_EQ(41, s_autofill_profiles_bounded.ColumnInt64(0)); + ASSERT_TRUE(s_autofill_profiles_bounded.Step()); + EXPECT_EQ(51, s_autofill_profiles_bounded.ColumnInt64(0)); + ASSERT_TRUE(s_autofill_profiles_bounded.Step()); + EXPECT_EQ(61, s_autofill_profiles_bounded.ColumnInt64(0)); + EXPECT_FALSE(s_autofill_profiles_bounded.Step()); + ASSERT_EQ(3UL, credit_card_guids.size()); + EXPECT_EQ("00000000-0000-0000-0000-000000000006", credit_card_guids[0]); + EXPECT_EQ("00000000-0000-0000-0000-000000000007", credit_card_guids[1]); + EXPECT_EQ("00000000-0000-0000-0000-000000000008", credit_card_guids[2]); + sql::Statement s_credit_cards_bounded( + db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM credit_cards")); + ASSERT_TRUE(s_credit_cards_bounded.is_valid()); + ASSERT_TRUE(s_credit_cards_bounded.Step()); + EXPECT_EQ(47, s_credit_cards_bounded.ColumnInt64(0)); + ASSERT_TRUE(s_credit_cards_bounded.Step()); + EXPECT_EQ(57, s_credit_cards_bounded.ColumnInt64(0)); + ASSERT_TRUE(s_credit_cards_bounded.Step()); + EXPECT_EQ(67, s_credit_cards_bounded.ColumnInt64(0)); + EXPECT_FALSE(s_credit_cards_bounded.Step()); + + // Remove all entries modified on or after time 51 (unbounded range). + table_->RemoveAutofillDataModifiedBetween( + Time::FromTimeT(51), Time(), + &profile_guids, &credit_card_guids); + ASSERT_EQ(2UL, profile_guids.size()); + EXPECT_EQ("00000000-0000-0000-0000-000000000004", profile_guids[0]); + EXPECT_EQ("00000000-0000-0000-0000-000000000005", profile_guids[1]); + sql::Statement s_autofill_profiles_unbounded( + db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM autofill_profiles")); + ASSERT_TRUE(s_autofill_profiles_unbounded.is_valid()); + ASSERT_TRUE(s_autofill_profiles_unbounded.Step()); + EXPECT_EQ(11, s_autofill_profiles_unbounded.ColumnInt64(0)); + ASSERT_TRUE(s_autofill_profiles_unbounded.Step()); + EXPECT_EQ(41, s_autofill_profiles_unbounded.ColumnInt64(0)); + EXPECT_FALSE(s_autofill_profiles_unbounded.Step()); + ASSERT_EQ(2UL, credit_card_guids.size()); + EXPECT_EQ("00000000-0000-0000-0000-000000000010", credit_card_guids[0]); + EXPECT_EQ("00000000-0000-0000-0000-000000000011", credit_card_guids[1]); + sql::Statement s_credit_cards_unbounded( + db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM credit_cards")); + ASSERT_TRUE(s_credit_cards_unbounded.is_valid()); + ASSERT_TRUE(s_credit_cards_unbounded.Step()); + EXPECT_EQ(47, s_credit_cards_unbounded.ColumnInt64(0)); + EXPECT_FALSE(s_credit_cards_unbounded.Step()); + + // Remove all remaining entries. + table_->RemoveAutofillDataModifiedBetween( + Time(), Time(), + &profile_guids, &credit_card_guids); + ASSERT_EQ(2UL, profile_guids.size()); + EXPECT_EQ("00000000-0000-0000-0000-000000000000", profile_guids[0]); + EXPECT_EQ("00000000-0000-0000-0000-000000000003", profile_guids[1]); + sql::Statement s_autofill_profiles_empty( + db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM autofill_profiles")); + ASSERT_TRUE(s_autofill_profiles_empty.is_valid()); + EXPECT_FALSE(s_autofill_profiles_empty.Step()); + ASSERT_EQ(1UL, credit_card_guids.size()); + EXPECT_EQ("00000000-0000-0000-0000-000000000009", credit_card_guids[0]); + sql::Statement s_credit_cards_empty( + db_->GetSQLConnection()->GetUniqueStatement( + "SELECT date_modified FROM credit_cards")); + ASSERT_TRUE(s_credit_cards_empty.is_valid()); + EXPECT_FALSE(s_credit_cards_empty.Step()); +} + +TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_NoResults) { + std::vector<AutofillEntry> entries; + ASSERT_TRUE(table_->GetAllAutofillEntries(&entries)); + + EXPECT_EQ(0U, entries.size()); +} + +TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_OneResult) { + AutofillChangeList changes; + std::map<std::string, std::vector<Time> > name_value_times_map; + + time_t start = 0; + std::vector<Time> timestamps1; + FormFieldData field; + field.name = ASCIIToUTF16("Name"); + field.value = ASCIIToUTF16("Superman"); + EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, + Time::FromTimeT(start))); + timestamps1.push_back(Time::FromTimeT(start)); + std::string key1("NameSuperman"); + name_value_times_map.insert(std::pair<std::string, + std::vector<Time> > (key1, timestamps1)); + + AutofillEntrySet expected_entries(CompareAutofillEntries); + AutofillKey ak1(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman")); + AutofillEntry ae1(ak1, timestamps1); + + expected_entries.insert(ae1); + + std::vector<AutofillEntry> entries; + ASSERT_TRUE(table_->GetAllAutofillEntries(&entries)); + AutofillEntrySet entry_set(entries.begin(), entries.end(), + CompareAutofillEntries); + + // make sure the lists of entries match + ASSERT_EQ(expected_entries.size(), entry_set.size()); + AutofillEntrySetIterator it; + for (it = entry_set.begin(); it != entry_set.end(); it++) { + expected_entries.erase(*it); + } + + EXPECT_EQ(0U, expected_entries.size()); +} + +TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_TwoDistinct) { + AutofillChangeList changes; + std::map<std::string, std::vector<Time> > name_value_times_map; + time_t start = 0; + + std::vector<Time> timestamps1; + FormFieldData field; + field.name = ASCIIToUTF16("Name"); + field.value = ASCIIToUTF16("Superman"); + EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, + Time::FromTimeT(start))); + timestamps1.push_back(Time::FromTimeT(start)); + std::string key1("NameSuperman"); + name_value_times_map.insert(std::pair<std::string, + std::vector<Time> > (key1, timestamps1)); + + start++; + std::vector<Time> timestamps2; + field.name = ASCIIToUTF16("Name"); + field.value = ASCIIToUTF16("Clark Kent"); + EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, + Time::FromTimeT(start))); + timestamps2.push_back(Time::FromTimeT(start)); + std::string key2("NameClark Kent"); + name_value_times_map.insert(std::pair<std::string, + std::vector<Time> > (key2, timestamps2)); + + AutofillEntrySet expected_entries(CompareAutofillEntries); + AutofillKey ak1(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman")); + AutofillKey ak2(ASCIIToUTF16("Name"), ASCIIToUTF16("Clark Kent")); + AutofillEntry ae1(ak1, timestamps1); + AutofillEntry ae2(ak2, timestamps2); + + expected_entries.insert(ae1); + expected_entries.insert(ae2); + + std::vector<AutofillEntry> entries; + ASSERT_TRUE(table_->GetAllAutofillEntries(&entries)); + AutofillEntrySet entry_set(entries.begin(), entries.end(), + CompareAutofillEntries); + + // make sure the lists of entries match + ASSERT_EQ(expected_entries.size(), entry_set.size()); + AutofillEntrySetIterator it; + for (it = entry_set.begin(); it != entry_set.end(); it++) { + expected_entries.erase(*it); + } + + EXPECT_EQ(0U, expected_entries.size()); +} + +TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_TwoSame) { + AutofillChangeList changes; + std::map<std::string, std::vector<Time> > name_value_times_map; + + time_t start = 0; + std::vector<Time> timestamps; + for (int i = 0; i < 2; i++) { + FormFieldData field; + field.name = ASCIIToUTF16("Name"); + field.value = ASCIIToUTF16("Superman"); + EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, + Time::FromTimeT(start))); + timestamps.push_back(Time::FromTimeT(start)); + start++; + } + + std::string key("NameSuperman"); + name_value_times_map.insert(std::pair<std::string, + std::vector<Time> > (key, timestamps)); + + AutofillEntrySet expected_entries(CompareAutofillEntries); + AutofillKey ak1(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman")); + AutofillEntry ae1(ak1, timestamps); + + expected_entries.insert(ae1); + + std::vector<AutofillEntry> entries; + ASSERT_TRUE(table_->GetAllAutofillEntries(&entries)); + AutofillEntrySet entry_set(entries.begin(), entries.end(), + CompareAutofillEntries); + + // make sure the lists of entries match + ASSERT_EQ(expected_entries.size(), entry_set.size()); + AutofillEntrySetIterator it; + for (it = entry_set.begin(); it != entry_set.end(); it++) { + expected_entries.erase(*it); + } + + EXPECT_EQ(0U, expected_entries.size()); +} diff --git a/components/webdata/autofill/web_data_service_unittest.cc b/components/webdata/autofill/web_data_service_unittest.cc new file mode 100644 index 0000000..b7124ac --- /dev/null +++ b/components/webdata/autofill/web_data_service_unittest.cc @@ -0,0 +1,532 @@ +// 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 <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/files/scoped_temp_dir.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/message_loop.h" +#include "base/stl_util.h" +#include "base/string16.h" +#include "base/string_util.h" +#include "base/synchronization/waitable_event.h" +#include "base/time.h" +#include "base/utf_string_conversions.h" +#include "components/autofill/browser/autofill_country.h" +#include "components/autofill/browser/autofill_profile.h" +#include "components/autofill/browser/credit_card.h" +#include "components/autofill/common/form_field_data.h" +#include "components/webdata/autofill/autofill_change.h" +#include "components/webdata/autofill/autofill_entry.h" +#include "components/webdata/autofill/autofill_table.h" +#include "components/webdata/autofill/autofill_webdata_service.h" +#include "components/webdata/autofill/autofill_webdata_service_observer.h" +#include "components/webdata/common/web_data_service_test_util.h" +#include "components/webdata/common/web_database_service.h" +#include "content/public/test/test_browser_thread.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::Time; +using base::TimeDelta; +using base::WaitableEvent; +using content::BrowserThread; +using testing::_; +using testing::DoDefault; +using testing::ElementsAreArray; +using testing::Pointee; +using testing::Property; + +static const int kWebDataServiceTimeoutSeconds = 8; + +ACTION_P(SignalEvent, event) { + event->Signal(); +} + +class MockAutofillWebDataServiceObserver + : public AutofillWebDataServiceObserverOnDBThread { + public: + MOCK_METHOD1(AutofillEntriesChanged, + void(const AutofillChangeList& changes)); + MOCK_METHOD1(AutofillProfileChanged, + void(const AutofillProfileChange& change)); +}; + +class WebDataServiceTest : public testing::Test { + public: + WebDataServiceTest() + : ui_thread_(BrowserThread::UI, &message_loop_), + db_thread_(BrowserThread::DB) {} + + protected: + virtual void SetUp() { + db_thread_.Start(); + + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + base::FilePath path = temp_dir_.path().AppendASCII("TestWebDB"); + + wdbs_ = new WebDatabaseService(path); + wdbs_->AddTable(scoped_ptr<WebDatabaseTable>(new AutofillTable())); + wdbs_->LoadDatabase(WebDatabaseService::InitCallback()); + + wds_ = new AutofillWebDataService( + wdbs_, WebDataServiceBase::ProfileErrorCallback()); + wds_->Init(); + } + + virtual void TearDown() { + wds_->ShutdownOnUIThread(); + wdbs_->ShutdownDatabase(); + wds_ = NULL; + wdbs_ = NULL; + WaitForDatabaseThread(); + + MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure()); + MessageLoop::current()->Run(); + db_thread_.Stop(); + } + + void WaitForDatabaseThread() { + base::WaitableEvent done(false, false); + BrowserThread::PostTask( + BrowserThread::DB, + FROM_HERE, + base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done))); + done.Wait(); + } + + MessageLoopForUI message_loop_; + content::TestBrowserThread ui_thread_; + content::TestBrowserThread db_thread_; + base::FilePath profile_dir_; + scoped_refptr<AutofillWebDataService> wds_; + scoped_refptr<WebDatabaseService> wdbs_; + base::ScopedTempDir temp_dir_; +}; + +class WebDataServiceAutofillTest : public WebDataServiceTest { + public: + WebDataServiceAutofillTest() + : WebDataServiceTest(), + unique_id1_(1), + unique_id2_(2), + test_timeout_(TimeDelta::FromSeconds(kWebDataServiceTimeoutSeconds)), + done_event_(false, false) {} + + protected: + virtual void SetUp() { + WebDataServiceTest::SetUp(); + name1_ = ASCIIToUTF16("name1"); + name2_ = ASCIIToUTF16("name2"); + value1_ = ASCIIToUTF16("value1"); + value2_ = ASCIIToUTF16("value2"); + + void(AutofillWebDataService::*add_observer_func)( + AutofillWebDataServiceObserverOnDBThread*) = + &AutofillWebDataService::AddObserver; + BrowserThread::PostTask( + BrowserThread::DB, + FROM_HERE, + base::Bind(add_observer_func, wds_, &observer_)); + WaitForDatabaseThread(); + } + + virtual void TearDown() { + void(AutofillWebDataService::*remove_observer_func)( + AutofillWebDataServiceObserverOnDBThread*) = + &AutofillWebDataService::RemoveObserver; + BrowserThread::PostTask( + BrowserThread::DB, + FROM_HERE, + base::Bind(remove_observer_func, wds_, &observer_)); + WaitForDatabaseThread(); + + WebDataServiceTest::TearDown(); + } + + void AppendFormField(const string16& name, + const string16& value, + std::vector<FormFieldData>* form_fields) { + FormFieldData field; + field.name = name; + field.value = value; + form_fields->push_back(field); + } + + string16 name1_; + string16 name2_; + string16 value1_; + string16 value2_; + int unique_id1_, unique_id2_; + const TimeDelta test_timeout_; + testing::NiceMock<MockAutofillWebDataServiceObserver> observer_; + WaitableEvent done_event_; +}; + +// Simple consumer for Keywords data. Stores the result data and quits UI +// message loop when callback is invoked. +class KeywordsConsumer : public WebDataServiceConsumer { + public: + KeywordsConsumer() : load_succeeded(false) {} + + virtual void OnWebDataServiceRequestDone( + WebDataService::Handle h, + const WDTypedResult* result) OVERRIDE { + if (result) { + load_succeeded = true; + DCHECK(result->GetType() == KEYWORDS_RESULT); + keywords_result = + reinterpret_cast<const WDResult<WDKeywordsResult>*>( + result)->GetValue(); + } + + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + MessageLoop::current()->Quit(); + } + + // True if keywords data was loaded successfully. + bool load_succeeded; + // Result data from completion callback. + WDKeywordsResult keywords_result; +}; + +TEST_F(WebDataServiceAutofillTest, FormFillAdd) { + const AutofillChange expected_changes[] = { + AutofillChange(AutofillChange::ADD, AutofillKey(name1_, value1_)), + AutofillChange(AutofillChange::ADD, AutofillKey(name2_, value2_)) + }; + + // This will verify that the correct notification is triggered, + // passing the correct list of autofill keys in the details. + EXPECT_CALL(observer_, + AutofillEntriesChanged(ElementsAreArray(expected_changes))) + .WillOnce(SignalEvent(&done_event_)); + + std::vector<FormFieldData> form_fields; + AppendFormField(name1_, value1_, &form_fields); + AppendFormField(name2_, value2_, &form_fields); + wds_->AddFormFields(form_fields); + + // The event will be signaled when the mock observer is notified. + done_event_.TimedWait(test_timeout_); + + AutofillWebDataServiceConsumer<std::vector<string16> > consumer; + WebDataService::Handle handle; + static const int limit = 10; + handle = wds_->GetFormValuesForElementName( + name1_, string16(), limit, &consumer); + + // The message loop will exit when the consumer is called. + MessageLoop::current()->Run(); + + EXPECT_EQ(handle, consumer.handle()); + ASSERT_EQ(1U, consumer.result().size()); + EXPECT_EQ(value1_, consumer.result()[0]); +} + +TEST_F(WebDataServiceAutofillTest, FormFillRemoveOne) { + // First add some values to autofill. + EXPECT_CALL(observer_, AutofillEntriesChanged(_)) + .WillOnce(SignalEvent(&done_event_)); + std::vector<FormFieldData> form_fields; + AppendFormField(name1_, value1_, &form_fields); + wds_->AddFormFields(form_fields); + + // The event will be signaled when the mock observer is notified. + done_event_.TimedWait(test_timeout_); + + // This will verify that the correct notification is triggered, + // passing the correct list of autofill keys in the details. + const AutofillChange expected_changes[] = { + AutofillChange(AutofillChange::REMOVE, AutofillKey(name1_, value1_)) + }; + EXPECT_CALL(observer_, + AutofillEntriesChanged(ElementsAreArray(expected_changes))) + .WillOnce(SignalEvent(&done_event_)); + wds_->RemoveFormValueForElementName(name1_, value1_); + + // The event will be signaled when the mock observer is notified. + done_event_.TimedWait(test_timeout_); +} + +TEST_F(WebDataServiceAutofillTest, FormFillRemoveMany) { + TimeDelta one_day(TimeDelta::FromDays(1)); + Time t = Time::Now(); + + EXPECT_CALL(observer_, AutofillEntriesChanged(_)) + .WillOnce(SignalEvent(&done_event_)); + + std::vector<FormFieldData> form_fields; + AppendFormField(name1_, value1_, &form_fields); + AppendFormField(name2_, value2_, &form_fields); + wds_->AddFormFields(form_fields); + + // The event will be signaled when the mock observer is notified. + done_event_.TimedWait(test_timeout_); + + // This will verify that the correct notification is triggered, + // passing the correct list of autofill keys in the details. + const AutofillChange expected_changes[] = { + AutofillChange(AutofillChange::REMOVE, AutofillKey(name1_, value1_)), + AutofillChange(AutofillChange::REMOVE, AutofillKey(name2_, value2_)) + }; + EXPECT_CALL(observer_, + AutofillEntriesChanged(ElementsAreArray(expected_changes))) + .WillOnce(SignalEvent(&done_event_)); + wds_->RemoveFormElementsAddedBetween(t, t + one_day); + + // The event will be signaled when the mock observer is notified. + done_event_.TimedWait(test_timeout_); +} + +TEST_F(WebDataServiceAutofillTest, ProfileAdd) { + AutofillProfile profile; + + // Check that GUID-based notification was sent. + const AutofillProfileChange expected_change( + AutofillProfileChange::ADD, profile.guid(), &profile); + EXPECT_CALL(observer_, AutofillProfileChanged(expected_change)) + .WillOnce(SignalEvent(&done_event_)); + + wds_->AddAutofillProfile(profile); + done_event_.TimedWait(test_timeout_); + + // Check that it was added. + AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > consumer; + WebDataService::Handle handle = wds_->GetAutofillProfiles(&consumer); + MessageLoop::current()->Run(); + EXPECT_EQ(handle, consumer.handle()); + ASSERT_EQ(1U, consumer.result().size()); + EXPECT_EQ(profile, *consumer.result()[0]); + STLDeleteElements(&consumer.result()); +} + +TEST_F(WebDataServiceAutofillTest, ProfileRemove) { + AutofillProfile profile; + + // Add a profile. + EXPECT_CALL(observer_, AutofillProfileChanged(_)) + .WillOnce(SignalEvent(&done_event_)); + wds_->AddAutofillProfile(profile); + done_event_.TimedWait(test_timeout_); + + // Check that it was added. + AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > consumer; + WebDataService::Handle handle = wds_->GetAutofillProfiles(&consumer); + MessageLoop::current()->Run(); + EXPECT_EQ(handle, consumer.handle()); + ASSERT_EQ(1U, consumer.result().size()); + EXPECT_EQ(profile, *consumer.result()[0]); + STLDeleteElements(&consumer.result()); + + // Check that GUID-based notification was sent. + const AutofillProfileChange expected_change( + AutofillProfileChange::REMOVE, profile.guid(), NULL); + EXPECT_CALL(observer_, AutofillProfileChanged(expected_change)) + .WillOnce(SignalEvent(&done_event_)); + + // Remove the profile. + wds_->RemoveAutofillProfile(profile.guid()); + done_event_.TimedWait(test_timeout_); + + // Check that it was removed. + AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > consumer2; + WebDataService::Handle handle2 = wds_->GetAutofillProfiles(&consumer2); + MessageLoop::current()->Run(); + EXPECT_EQ(handle2, consumer2.handle()); + ASSERT_EQ(0U, consumer2.result().size()); +} + +TEST_F(WebDataServiceAutofillTest, ProfileUpdate) { + AutofillProfile profile1; + profile1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Abe")); + AutofillProfile profile2; + profile2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Alice")); + + EXPECT_CALL(observer_, AutofillProfileChanged(_)) + .WillOnce(DoDefault()) + .WillOnce(SignalEvent(&done_event_)); + + wds_->AddAutofillProfile(profile1); + wds_->AddAutofillProfile(profile2); + done_event_.TimedWait(test_timeout_); + + // Check that they were added. + AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > consumer; + WebDataService::Handle handle = wds_->GetAutofillProfiles(&consumer); + MessageLoop::current()->Run(); + EXPECT_EQ(handle, consumer.handle()); + ASSERT_EQ(2U, consumer.result().size()); + EXPECT_EQ(profile1, *consumer.result()[0]); + EXPECT_EQ(profile2, *consumer.result()[1]); + STLDeleteElements(&consumer.result()); + + AutofillProfile profile1_changed(profile1); + profile1_changed.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Bill")); + const AutofillProfileChange expected_change( + AutofillProfileChange::UPDATE, profile1.guid(), &profile1_changed); + + EXPECT_CALL(observer_, AutofillProfileChanged(expected_change)) + .WillOnce(SignalEvent(&done_event_)); + + // Update the profile. + wds_->UpdateAutofillProfile(profile1_changed); + done_event_.TimedWait(test_timeout_); + + // Check that the updates were made. + AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > consumer2; + WebDataService::Handle handle2 = wds_->GetAutofillProfiles(&consumer2); + MessageLoop::current()->Run(); + EXPECT_EQ(handle2, consumer2.handle()); + ASSERT_EQ(2U, consumer2.result().size()); + EXPECT_NE(profile1, *consumer2.result()[0]); + EXPECT_EQ(profile1_changed, *consumer2.result()[0]); + EXPECT_EQ(profile2, *consumer2.result()[1]); + STLDeleteElements(&consumer2.result()); +} + +TEST_F(WebDataServiceAutofillTest, CreditAdd) { + CreditCard card; + wds_->AddCreditCard(card); + WaitForDatabaseThread(); + + // Check that it was added. + AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer; + WebDataService::Handle handle = wds_->GetCreditCards(&consumer); + MessageLoop::current()->Run(); + EXPECT_EQ(handle, consumer.handle()); + ASSERT_EQ(1U, consumer.result().size()); + EXPECT_EQ(card, *consumer.result()[0]); + STLDeleteElements(&consumer.result()); +} + +TEST_F(WebDataServiceAutofillTest, CreditCardRemove) { + CreditCard credit_card; + + // Add a credit card. + wds_->AddCreditCard(credit_card); + WaitForDatabaseThread(); + + // Check that it was added. + AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer; + WebDataService::Handle handle = wds_->GetCreditCards(&consumer); + MessageLoop::current()->Run(); + EXPECT_EQ(handle, consumer.handle()); + ASSERT_EQ(1U, consumer.result().size()); + EXPECT_EQ(credit_card, *consumer.result()[0]); + STLDeleteElements(&consumer.result()); + + // Remove the credit card. + wds_->RemoveCreditCard(credit_card.guid()); + WaitForDatabaseThread(); + + // Check that it was removed. + AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer2; + WebDataService::Handle handle2 = wds_->GetCreditCards(&consumer2); + MessageLoop::current()->Run(); + EXPECT_EQ(handle2, consumer2.handle()); + ASSERT_EQ(0U, consumer2.result().size()); +} + +TEST_F(WebDataServiceAutofillTest, CreditUpdate) { + CreditCard card1; + card1.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Abe")); + CreditCard card2; + card2.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Alice")); + + wds_->AddCreditCard(card1); + wds_->AddCreditCard(card2); + WaitForDatabaseThread(); + + // Check that they got added. + AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer; + WebDataService::Handle handle = wds_->GetCreditCards(&consumer); + MessageLoop::current()->Run(); + EXPECT_EQ(handle, consumer.handle()); + ASSERT_EQ(2U, consumer.result().size()); + EXPECT_EQ(card1, *consumer.result()[0]); + EXPECT_EQ(card2, *consumer.result()[1]); + STLDeleteElements(&consumer.result()); + + CreditCard card1_changed(card1); + card1_changed.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Bill")); + + wds_->UpdateCreditCard(card1_changed); + WaitForDatabaseThread(); + + // Check that the updates were made. + AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer2; + WebDataService::Handle handle2 = wds_->GetCreditCards(&consumer2); + MessageLoop::current()->Run(); + EXPECT_EQ(handle2, consumer2.handle()); + ASSERT_EQ(2U, consumer2.result().size()); + EXPECT_NE(card1, *consumer2.result()[0]); + EXPECT_EQ(card1_changed, *consumer2.result()[0]); + EXPECT_EQ(card2, *consumer2.result()[1]); + STLDeleteElements(&consumer2.result()); +} + +TEST_F(WebDataServiceAutofillTest, AutofillRemoveModifiedBetween) { + // Add a profile. + EXPECT_CALL(observer_, AutofillProfileChanged(_)) + .WillOnce(SignalEvent(&done_event_)); + AutofillProfile profile; + wds_->AddAutofillProfile(profile); + done_event_.TimedWait(test_timeout_); + + // Check that it was added. + AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > + profile_consumer; + WebDataService::Handle handle = wds_->GetAutofillProfiles(&profile_consumer); + MessageLoop::current()->Run(); + EXPECT_EQ(handle, profile_consumer.handle()); + ASSERT_EQ(1U, profile_consumer.result().size()); + EXPECT_EQ(profile, *profile_consumer.result()[0]); + STLDeleteElements(&profile_consumer.result()); + + // Add a credit card. + CreditCard credit_card; + wds_->AddCreditCard(credit_card); + WaitForDatabaseThread(); + + // Check that it was added. + AutofillWebDataServiceConsumer<std::vector<CreditCard*> > card_consumer; + handle = wds_->GetCreditCards(&card_consumer); + MessageLoop::current()->Run(); + EXPECT_EQ(handle, card_consumer.handle()); + ASSERT_EQ(1U, card_consumer.result().size()); + EXPECT_EQ(credit_card, *card_consumer.result()[0]); + STLDeleteElements(&card_consumer.result()); + + // Check that GUID-based notification was sent for the profile. + const AutofillProfileChange expected_profile_change( + AutofillProfileChange::REMOVE, profile.guid(), NULL); + EXPECT_CALL(observer_, AutofillProfileChanged(expected_profile_change)) + .WillOnce(SignalEvent(&done_event_)); + + // Remove the profile using time range of "all time". + wds_->RemoveAutofillDataModifiedBetween(Time(), Time()); + done_event_.TimedWait(test_timeout_); + WaitForDatabaseThread(); + + // Check that the profile was removed. + AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > + profile_consumer2; + WebDataService::Handle handle2 = + wds_->GetAutofillProfiles(&profile_consumer2); + MessageLoop::current()->Run(); + EXPECT_EQ(handle2, profile_consumer2.handle()); + ASSERT_EQ(0U, profile_consumer2.result().size()); + + // Check that the credit card was removed. + AutofillWebDataServiceConsumer<std::vector<CreditCard*> > card_consumer2; + handle2 = wds_->GetCreditCards(&card_consumer2); + MessageLoop::current()->Run(); + EXPECT_EQ(handle2, card_consumer2.handle()); + ASSERT_EQ(0U, card_consumer2.result().size()); +} diff --git a/components/webdata/autofill/web_database_migration_unittest.cc b/components/webdata/autofill/web_database_migration_unittest.cc new file mode 100644 index 0000000..8a715a5 --- /dev/null +++ b/components/webdata/autofill/web_database_migration_unittest.cc @@ -0,0 +1,1992 @@ +// 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 <string> + +#include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/guid.h" +#include "base/message_loop.h" +#include "base/stl_util.h" +#include "base/string16.h" +#include "base/strings/string_number_conversions.h" +#include "base/time.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/webdata/keyword_table.h" +#include "chrome/browser/webdata/logins_table.h" +#include "chrome/browser/webdata/token_service_table.h" +#include "chrome/browser/webdata/web_apps_table.h" +#include "chrome/browser/webdata/web_intents_table.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/autofill/browser/autofill_country.h" +#include "components/autofill/browser/autofill_profile.h" +#include "components/autofill/browser/autofill_type.h" +#include "components/autofill/browser/credit_card.h" +#include "components/webdata/autofill/autofill_change.h" +#include "components/webdata/autofill/autofill_entry.h" +#include "components/webdata/autofill/autofill_table.h" +#include "components/webdata/common/web_database.h" +#include "content/public/test/test_browser_thread.h" +#include "sql/statement.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::Time; +using content::BrowserThread; + +namespace { + +void AutofillProfile31FromStatement(const sql::Statement& s, + AutofillProfile* profile, + string16* label, + int* unique_id, + int64* date_modified) { + DCHECK(profile); + DCHECK(label); + DCHECK(unique_id); + DCHECK(date_modified); + *label = s.ColumnString16(0); + *unique_id = s.ColumnInt(1); + profile->SetRawInfo(NAME_FIRST, s.ColumnString16(2)); + profile->SetRawInfo(NAME_MIDDLE, s.ColumnString16(3)); + profile->SetRawInfo(NAME_LAST, s.ColumnString16(4)); + profile->SetRawInfo(EMAIL_ADDRESS, s.ColumnString16(5)); + profile->SetRawInfo(COMPANY_NAME, s.ColumnString16(6)); + profile->SetRawInfo(ADDRESS_HOME_LINE1, s.ColumnString16(7)); + profile->SetRawInfo(ADDRESS_HOME_LINE2, s.ColumnString16(8)); + profile->SetRawInfo(ADDRESS_HOME_CITY, s.ColumnString16(9)); + profile->SetRawInfo(ADDRESS_HOME_STATE, s.ColumnString16(10)); + profile->SetRawInfo(ADDRESS_HOME_ZIP, s.ColumnString16(11)); + profile->SetInfo(ADDRESS_HOME_COUNTRY, s.ColumnString16(12), "en-US"); + profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, s.ColumnString16(13)); + *date_modified = s.ColumnInt64(15); + profile->set_guid(s.ColumnString(16)); + EXPECT_TRUE(base::IsValidGUID(profile->guid())); +} + +void AutofillProfile33FromStatement(const sql::Statement& s, + AutofillProfile* profile, + int64* date_modified) { + DCHECK(profile); + DCHECK(date_modified); + profile->set_guid(s.ColumnString(0)); + EXPECT_TRUE(base::IsValidGUID(profile->guid())); + profile->SetRawInfo(COMPANY_NAME, s.ColumnString16(1)); + profile->SetRawInfo(ADDRESS_HOME_LINE1, s.ColumnString16(2)); + profile->SetRawInfo(ADDRESS_HOME_LINE2, s.ColumnString16(3)); + profile->SetRawInfo(ADDRESS_HOME_CITY, s.ColumnString16(4)); + profile->SetRawInfo(ADDRESS_HOME_STATE, s.ColumnString16(5)); + profile->SetRawInfo(ADDRESS_HOME_ZIP, s.ColumnString16(6)); + profile->SetInfo(ADDRESS_HOME_COUNTRY, s.ColumnString16(7), "en-US"); + *date_modified = s.ColumnInt64(8); +} + +void CreditCard31FromStatement(const sql::Statement& s, + CreditCard* credit_card, + string16* label, + int* unique_id, + std::string* encrypted_number, + int64* date_modified) { + DCHECK(credit_card); + DCHECK(label); + DCHECK(unique_id); + DCHECK(encrypted_number); + DCHECK(date_modified); + *label = s.ColumnString16(0); + *unique_id = s.ColumnInt(1); + credit_card->SetRawInfo(CREDIT_CARD_NAME, s.ColumnString16(2)); + credit_card->SetRawInfo(CREDIT_CARD_TYPE, s.ColumnString16(3)); + credit_card->SetRawInfo(CREDIT_CARD_EXP_MONTH, s.ColumnString16(5)); + credit_card->SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, s.ColumnString16(6)); + int encrypted_number_len = s.ColumnByteLength(10); + if (encrypted_number_len) { + encrypted_number->resize(encrypted_number_len); + memcpy(&(*encrypted_number)[0], s.ColumnBlob(10), encrypted_number_len); + } + *date_modified = s.ColumnInt64(12); + credit_card->set_guid(s.ColumnString(13)); + EXPECT_TRUE(base::IsValidGUID(credit_card->guid())); +} + +void CreditCard32FromStatement(const sql::Statement& s, + CreditCard* credit_card, + std::string* encrypted_number, + int64* date_modified) { + DCHECK(credit_card); + DCHECK(encrypted_number); + DCHECK(date_modified); + credit_card->set_guid(s.ColumnString(0)); + EXPECT_TRUE(base::IsValidGUID(credit_card->guid())); + credit_card->SetRawInfo(CREDIT_CARD_NAME, s.ColumnString16(1)); + credit_card->SetRawInfo(CREDIT_CARD_EXP_MONTH, s.ColumnString16(2)); + credit_card->SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, s.ColumnString16(3)); + int encrypted_number_len = s.ColumnByteLength(4); + if (encrypted_number_len) { + encrypted_number->resize(encrypted_number_len); + memcpy(&(*encrypted_number)[0], s.ColumnBlob(4), encrypted_number_len); + } + *date_modified = s.ColumnInt64(5); +} + +void CheckHasBackupData(sql::MetaTable* meta_table) { + std::string value; + EXPECT_TRUE(meta_table->GetValue( + "Default Search Provider ID Backup", &value)); + EXPECT_TRUE(meta_table->GetValue( + "Default Search Provider ID Backup Signature", &value)); +} + +void CheckNoBackupData(const sql::Connection& connection, + sql::MetaTable* meta_table) { + std::string value; + EXPECT_FALSE(meta_table->GetValue( + "Default Search Provider ID Backup", &value)); + EXPECT_FALSE(meta_table->GetValue( + "Default Search Provider ID Backup Signature", &value)); + EXPECT_FALSE(connection.DoesTableExist("keywords_backup")); +} + +} // anonymous namespace + +// The WebDatabaseMigrationTest encapsulates testing of database migrations. +// Specifically, these tests are intended to exercise any schema changes in +// the WebDatabase and data migrations that occur in +// |WebDatabase::MigrateOldVersionsAsNeeded()|. +class WebDatabaseMigrationTest : public testing::Test { + public: + // In order to access the application locale -- which the tested functions do + // internally -- this test must run on the UI thread. + // TODO(isherman): The WebDatabase code should probably verify that it is + // running on the DB thread. Once that verification is added, this code will + // need to be updated to create both threads. + WebDatabaseMigrationTest() + : ui_thread_(BrowserThread::UI, &message_loop_for_ui_) {} + virtual ~WebDatabaseMigrationTest() {} + + virtual void SetUp() { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + } + + // Load the database via the WebDatabase class and migrate the database to + // the current version. + void DoMigration() { + // TODO(joi): This whole unit test file needs to stay in //chrome + // for now, as it needs to know about all the different table + // types. Once all webdata datatypes have been componentized, this + // could move to components_unittests. + AutofillTable autofill_table; + KeywordTable keyword_table; + LoginsTable logins_table; + TokenServiceTable token_service_table; + WebAppsTable web_apps_table; + WebIntentsTable web_intents_table; + + WebDatabase db; + db.AddTable(&autofill_table); + db.AddTable(&keyword_table); + db.AddTable(&logins_table); + db.AddTable(&token_service_table); + db.AddTable(&web_apps_table); + db.AddTable(&web_intents_table); + + // This causes the migration to occur. + ASSERT_EQ(sql::INIT_OK, db.Init(GetDatabasePath())); + } + + protected: + // Current tested version number. When adding a migration in + // |WebDatabase::MigrateOldVersionsAsNeeded()| and changing the version number + // |kCurrentVersionNumber| this value should change to reflect the new version + // number and a new migration test added below. + static const int kCurrentTestedVersionNumber; + + base::FilePath GetDatabasePath() { + const base::FilePath::CharType kWebDatabaseFilename[] = + FILE_PATH_LITERAL("TestWebDatabase.sqlite3"); + return temp_dir_.path().Append(base::FilePath(kWebDatabaseFilename)); + } + + // The textual contents of |file| are read from + // "chrome/test/data/web_database" and returned in the string |contents|. + // Returns true if the file exists and is read successfully, false otherwise. + bool GetWebDatabaseData(const base::FilePath& file, std::string* contents) { + base::FilePath path = ui_test_utils::GetTestFilePath( + base::FilePath(FILE_PATH_LITERAL("web_database")), file); + return file_util::PathExists(path) && + file_util::ReadFileToString(path, contents); + } + + static int VersionFromConnection(sql::Connection* connection) { + // Get version. + sql::Statement s(connection->GetUniqueStatement( + "SELECT value FROM meta WHERE key='version'")); + if (!s.Step()) + return 0; + return s.ColumnInt(0); + } + + // The sql files located in "chrome/test/data/web_database" were generated by + // launching the Chromium application prior to schema change, then using the + // sqlite3 command-line application to dump the contents of the "Web Data" + // database. + // Like this: + // > .output version_nn.sql + // > .dump + void LoadDatabase(const base::FilePath::StringType& file); + + private: + MessageLoopForUI message_loop_for_ui_; + content::TestBrowserThread ui_thread_; + base::ScopedTempDir temp_dir_; + + DISALLOW_COPY_AND_ASSIGN(WebDatabaseMigrationTest); +}; + +const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 49; + +void WebDatabaseMigrationTest::LoadDatabase( + const base::FilePath::StringType& file) { + std::string contents; + ASSERT_TRUE(GetWebDatabaseData(base::FilePath(file), &contents)); + + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(connection.Execute(contents.data())); +} + +// Tests that the all migrations from an empty database succeed. +TEST_F(WebDatabaseMigrationTest, MigrateEmptyToCurrent) { + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + // Check that expected tables are present. + EXPECT_TRUE(connection.DoesTableExist("autofill")); + EXPECT_TRUE(connection.DoesTableExist("autofill_dates")); + EXPECT_TRUE(connection.DoesTableExist("autofill_profiles")); + EXPECT_TRUE(connection.DoesTableExist("credit_cards")); + EXPECT_TRUE(connection.DoesTableExist("keywords")); + // The logins table is obsolete. (We used to store saved passwords here.) + EXPECT_FALSE(connection.DoesTableExist("logins")); + EXPECT_TRUE(connection.DoesTableExist("meta")); + EXPECT_TRUE(connection.DoesTableExist("token_service")); + EXPECT_TRUE(connection.DoesTableExist("web_app_icons")); + EXPECT_TRUE(connection.DoesTableExist("web_apps")); + EXPECT_TRUE(connection.DoesTableExist("web_intents")); + EXPECT_TRUE(connection.DoesTableExist("web_intents_defaults")); + } +} + +// Tests that the |credit_card| table gets added to the schema for a version 22 +// database. +TEST_F(WebDatabaseMigrationTest, MigrateVersion22ToCurrent) { + // This schema is taken from a build prior to the addition of the + // |credit_card| table. Version 22 of the schema. Contrast this with the + // corrupt version below. + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_22.sql"))); + + // Verify pre-conditions. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // No |credit_card| table prior to version 23. + ASSERT_FALSE(connection.DoesColumnExist("credit_cards", "guid")); + ASSERT_FALSE( + connection.DoesColumnExist("credit_cards", "card_number_encrypted")); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + // |credit_card| table now exists. + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "guid")); + EXPECT_TRUE( + connection.DoesColumnExist("credit_cards", "card_number_encrypted")); + } +} + +// Tests that the |credit_card| table gets added to the schema for a corrupt +// version 22 database. The corruption is that the |credit_cards| table exists +// but the schema version number was not set correctly to 23 or later. This +// test exercises code introduced to fix bug http://crbug.com/50699 that +// resulted from the corruption. +TEST_F(WebDatabaseMigrationTest, MigrateVersion22CorruptedToCurrent) { + // This schema is taken from a build after the addition of the |credit_card| + // table. Due to a bug in the migration logic the version is set incorrectly + // to 22 (it should have been updated to 23 at least). + ASSERT_NO_FATAL_FAILURE( + LoadDatabase(FILE_PATH_LITERAL("version_22_corrupt.sql"))); + + // Verify pre-conditions. These are expectations for corrupt version 22 of + // the database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Columns existing and not existing before current version. + ASSERT_TRUE(connection.DoesColumnExist("credit_cards", "unique_id")); + ASSERT_TRUE( + connection.DoesColumnExist("credit_cards", "card_number_encrypted")); + ASSERT_TRUE(connection.DoesColumnExist("keywords", "id")); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + + // Columns existing and not existing before version 25. + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "unique_id")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "guid")); + EXPECT_TRUE( + connection.DoesColumnExist("credit_cards", "card_number_encrypted")); + EXPECT_TRUE(connection.DoesColumnExist("keywords", "id")); + } +} + +// Tests that the |keywords| |created_by_policy| column gets added to the schema +// for a version 25 database. +TEST_F(WebDatabaseMigrationTest, MigrateVersion25ToCurrent) { + // This schema is taken from a build prior to the addition of the |keywords| + // |created_by_policy| column. + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_25.sql"))); + + // Verify pre-conditions. These are expectations for version 25 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + // |keywords| |created_by_policy| column should have been added. + EXPECT_TRUE(connection.DoesColumnExist("keywords", "id")); + EXPECT_TRUE(connection.DoesColumnExist("keywords", "created_by_policy")); + } +} + +// Tests that the credit_cards.billing_address column is changed from a string +// to an int whilst preserving the associated billing address. This version of +// the test makes sure a stored label is converted to an ID. +TEST_F(WebDatabaseMigrationTest, MigrateVersion26ToCurrentStringLabels) { + // This schema is taken from a build prior to the change of column type for + // credit_cards.billing_address from string to int. + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_26.sql"))); + + // Verify pre-conditions. These are expectations for version 26 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Columns existing and not existing before current version. + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "billing_address")); + + std::string stmt = "INSERT INTO autofill_profiles" + "(label, unique_id, first_name, middle_name, last_name, email," + " company_name, address_line_1, address_line_2, city, state, zipcode," + " country, phone, fax)" + "VALUES ('Home',1,'','','','','','','','','','','','','')"; + sql::Statement s(connection.GetUniqueStatement(stmt.c_str())); + ASSERT_TRUE(s.Run()); + + // Insert a CC linked to an existing address. + std::string stmt2 = "INSERT INTO credit_cards" + "(label, unique_id, name_on_card, type, card_number," + " expiration_month, expiration_year, verification_code, billing_address," + " shipping_address, card_number_encrypted, verification_code_encrypted)" + "VALUES ('label',2,'Jack','Visa','1234',2,2012,'','Home','','','')"; + sql::Statement s2(connection.GetUniqueStatement(stmt2.c_str())); + ASSERT_TRUE(s2.Run()); + + // |billing_address| is a string. + std::string stmt3 = "SELECT billing_address FROM credit_cards"; + sql::Statement s3(connection.GetUniqueStatement(stmt3.c_str())); + ASSERT_TRUE(s3.Step()); + EXPECT_EQ(s3.ColumnType(0), sql::COLUMN_TYPE_TEXT); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "billing_address")); + + // Verify the credit card data is converted. + sql::Statement s(connection.GetUniqueStatement( + "SELECT guid, name_on_card, expiration_month, expiration_year, " + "card_number_encrypted, date_modified " + "FROM credit_cards")); + ASSERT_TRUE(s.Step()); + EXPECT_EQ("Jack", s.ColumnString(1)); + EXPECT_EQ(2, s.ColumnInt(2)); + EXPECT_EQ(2012, s.ColumnInt(3)); + // Column 5 is encrypted number blob. + // Column 6 is date_modified. + } +} + +// Tests that the credit_cards.billing_address column is changed from a string +// to an int whilst preserving the associated billing address. This version of +// the test makes sure a stored string ID is converted to an integer ID. +TEST_F(WebDatabaseMigrationTest, MigrateVersion26ToCurrentStringIDs) { + // This schema is taken from a build prior to the change of column type for + // credit_cards.billing_address from string to int. + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_26.sql"))); + + // Verify pre-conditions. These are expectations for version 26 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "billing_address")); + + std::string stmt = "INSERT INTO autofill_profiles" + "(label, unique_id, first_name, middle_name, last_name, email," + " company_name, address_line_1, address_line_2, city, state, zipcode," + " country, phone, fax)" + "VALUES ('Home',1,'','','','','','','','','','','','','')"; + sql::Statement s(connection.GetUniqueStatement(stmt.c_str())); + ASSERT_TRUE(s.Run()); + + // Insert a CC linked to an existing address. + std::string stmt2 = "INSERT INTO credit_cards" + "(label, unique_id, name_on_card, type, card_number," + " expiration_month, expiration_year, verification_code, billing_address," + " shipping_address, card_number_encrypted, verification_code_encrypted)" + "VALUES ('label',2,'Jack','Visa','1234',2,2012,'','1','','','')"; + sql::Statement s2(connection.GetUniqueStatement(stmt2.c_str())); + ASSERT_TRUE(s2.Run()); + + // |billing_address| is a string. + std::string stmt3 = "SELECT billing_address FROM credit_cards"; + sql::Statement s3(connection.GetUniqueStatement(stmt3.c_str())); + ASSERT_TRUE(s3.Step()); + EXPECT_EQ(s3.ColumnType(0), sql::COLUMN_TYPE_TEXT); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + // |keywords| |created_by_policy| column should have been added. + EXPECT_TRUE(connection.DoesColumnExist("keywords", "id")); + EXPECT_TRUE(connection.DoesColumnExist("keywords", "created_by_policy")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "billing_address")); + + // Verify the credit card data is converted. + sql::Statement s(connection.GetUniqueStatement( + "SELECT guid, name_on_card, expiration_month, expiration_year, " + "card_number_encrypted, date_modified " + "FROM credit_cards")); + ASSERT_TRUE(s.Step()); + EXPECT_EQ("Jack", s.ColumnString(1)); + EXPECT_EQ(2, s.ColumnInt(2)); + EXPECT_EQ(2012, s.ColumnInt(3)); + // Column 5 is encrypted credit card number blo b. + // Column 6 is date_modified. + } +} + +// Makes sure instant_url is added correctly to keywords. +TEST_F(WebDatabaseMigrationTest, MigrateVersion27ToCurrent) { + // Initialize the database. + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_27.sql"))); + + // Verify pre-conditions. These are expectations for version 27 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + ASSERT_FALSE(connection.DoesColumnExist("keywords", "instant_url")); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + // Make sure supports_instant (added in Version 28) was ultimately dropped + // again and instant_url was added. + EXPECT_FALSE(connection.DoesColumnExist("keywords", "supports_instant")); + EXPECT_TRUE(connection.DoesColumnExist("keywords", "instant_url")); + + // Check that instant_url is empty. + std::string stmt = "SELECT instant_url FROM keywords"; + sql::Statement s(connection.GetUniqueStatement(stmt.c_str())); + ASSERT_TRUE(s.Step()); + EXPECT_EQ(std::string(), s.ColumnString(0)); + + // Verify the data made it over. + stmt = "SELECT " + KeywordTable::GetKeywordColumns() + " FROM keywords"; + sql::Statement s2(connection.GetUniqueStatement(stmt.c_str())); + ASSERT_TRUE(s2.Step()); + EXPECT_EQ(2, s2.ColumnInt(0)); + EXPECT_EQ("Google", s2.ColumnString(1)); + EXPECT_EQ("google.com", s2.ColumnString(2)); + EXPECT_EQ("http://www.google.com/favicon.ico", s2.ColumnString(3)); + EXPECT_EQ("{google:baseURL}search?{google:RLZ}{google:acceptedSuggestion}"\ + "{google:originalQueryForSuggestion}sourceid=chrome&ie={inputEncoding}"\ + "&q={searchTerms}", + s2.ColumnString(4)); + EXPECT_TRUE(s2.ColumnBool(5)); + EXPECT_EQ(std::string(), s2.ColumnString(6)); + EXPECT_EQ(0, s2.ColumnInt(7)); + EXPECT_EQ(0, s2.ColumnInt(8)); + EXPECT_EQ(std::string("UTF-8"), s2.ColumnString(9)); + EXPECT_TRUE(s2.ColumnBool(10)); + EXPECT_EQ(std::string("{google:baseSuggestURL}search?client=chrome&hl=" + "{language}&q={searchTerms}"), s2.ColumnString(11)); + EXPECT_EQ(1, s2.ColumnInt(12)); + //EXPECT_EQ(false, s2.ColumnBool(13)); + EXPECT_EQ(std::string(), s2.ColumnString(14)); + EXPECT_EQ(0, s2.ColumnInt(15)); + EXPECT_EQ(std::string(), s2.ColumnString(16)); + } +} + +// Makes sure date_modified is added correctly to autofill_profiles and +// credit_cards. +TEST_F(WebDatabaseMigrationTest, MigrateVersion29ToCurrent) { + // Initialize the database. + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_29.sql"))); + + // Verify pre-conditions. These are expectations for version 29 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", + "date_modified")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", + "date_modified")); + } + + Time pre_creation_time = Time::Now(); + DoMigration(); + Time post_creation_time = Time::Now(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + // Check that the columns were created. + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", + "date_modified")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", + "date_modified")); + + sql::Statement s_profiles(connection.GetUniqueStatement( + "SELECT date_modified FROM autofill_profiles ")); + ASSERT_TRUE(s_profiles.is_valid()); + while (s_profiles.Step()) { + EXPECT_GE(s_profiles.ColumnInt64(0), + pre_creation_time.ToTimeT()); + EXPECT_LE(s_profiles.ColumnInt64(0), + post_creation_time.ToTimeT()); + } + EXPECT_TRUE(s_profiles.Succeeded()); + + sql::Statement s_credit_cards(connection.GetUniqueStatement( + "SELECT date_modified FROM credit_cards ")); + ASSERT_TRUE(s_credit_cards.is_valid()); + while (s_credit_cards.Step()) { + EXPECT_GE(s_credit_cards.ColumnInt64(0), + pre_creation_time.ToTimeT()); + EXPECT_LE(s_credit_cards.ColumnInt64(0), + post_creation_time.ToTimeT()); + } + EXPECT_TRUE(s_credit_cards.Succeeded()); + } +} + +// Makes sure guids are added to autofill_profiles and credit_cards tables. +TEST_F(WebDatabaseMigrationTest, MigrateVersion30ToCurrent) { + // Initialize the database. + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_30.sql"))); + + // Verify pre-conditions. These are expectations for version 29 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "guid")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "guid")); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + ASSERT_TRUE(connection.DoesColumnExist("autofill_profiles", "guid")); + ASSERT_TRUE(connection.DoesColumnExist("credit_cards", "guid")); + + // Check that guids are non-null, non-empty, conforms to guid format, and + // are different. + sql::Statement s( + connection.GetUniqueStatement("SELECT guid FROM autofill_profiles")); + + ASSERT_TRUE(s.Step()); + std::string guid1 = s.ColumnString(0); + EXPECT_TRUE(base::IsValidGUID(guid1)); + + ASSERT_TRUE(s.Step()); + std::string guid2 = s.ColumnString(0); + EXPECT_TRUE(base::IsValidGUID(guid2)); + + EXPECT_NE(guid1, guid2); + } +} + +// Removes unique IDs and make GUIDs the primary key. Also removes unused +// columns. +TEST_F(WebDatabaseMigrationTest, MigrateVersion31ToCurrent) { + // Initialize the database. + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_31.sql"))); + + // Verify pre-conditions. These are expectations for version 30 of the + // database. + AutofillProfile profile; + string16 profile_label; + int profile_unique_id = 0; + int64 profile_date_modified = 0; + CreditCard credit_card; + string16 cc_label; + int cc_unique_id = 0; + std::string cc_number_encrypted; + int64 cc_date_modified = 0; + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Verify existence of columns we'll be changing. + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "guid")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "unique_id")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "guid")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "unique_id")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "type")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "card_number")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", + "verification_code")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "billing_address")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "shipping_address")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", + "verification_code_encrypted")); + + // Fetch data in the database prior to migration. + sql::Statement s1( + connection.GetUniqueStatement( + "SELECT label, unique_id, first_name, middle_name, last_name, " + "email, company_name, address_line_1, address_line_2, city, state, " + "zipcode, country, phone, fax, date_modified, guid " + "FROM autofill_profiles")); + ASSERT_TRUE(s1.Step()); + EXPECT_NO_FATAL_FAILURE(AutofillProfile31FromStatement( + s1, &profile, &profile_label, &profile_unique_id, + &profile_date_modified)); + + sql::Statement s2( + connection.GetUniqueStatement( + "SELECT label, unique_id, name_on_card, type, card_number, " + "expiration_month, expiration_year, verification_code, " + "billing_address, shipping_address, card_number_encrypted, " + "verification_code_encrypted, date_modified, guid " + "FROM credit_cards")); + ASSERT_TRUE(s2.Step()); + EXPECT_NO_FATAL_FAILURE(CreditCard31FromStatement(s2, + &credit_card, + &cc_label, + &cc_unique_id, + &cc_number_encrypted, + &cc_date_modified)); + + EXPECT_NE(profile_unique_id, cc_unique_id); + EXPECT_NE(profile.guid(), credit_card.guid()); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + // Verify existence of columns we'll be changing. + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "guid")); + EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "unique_id")); + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "guid")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "unique_id")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "type")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "card_number")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", + "verification_code")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "billing_address")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", + "shipping_address")); + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", + "verification_code_encrypted")); + + // Verify data in the database after the migration. + sql::Statement s1( + connection.GetUniqueStatement( + "SELECT guid, company_name, address_line_1, address_line_2, " + "city, state, zipcode, country, date_modified " + "FROM autofill_profiles")); + ASSERT_TRUE(s1.Step()); + + AutofillProfile profile_a; + int64 profile_date_modified_a = 0; + EXPECT_NO_FATAL_FAILURE(AutofillProfile33FromStatement( + s1, &profile_a, &profile_date_modified_a)); + EXPECT_EQ(profile.guid(), profile_a.guid()); + EXPECT_EQ(profile.GetRawInfo(COMPANY_NAME), + profile_a.GetRawInfo(COMPANY_NAME)); + EXPECT_EQ(profile.GetRawInfo(ADDRESS_HOME_LINE1), + profile_a.GetRawInfo(ADDRESS_HOME_LINE1)); + EXPECT_EQ(profile.GetRawInfo(ADDRESS_HOME_LINE2), + profile_a.GetRawInfo(ADDRESS_HOME_LINE2)); + EXPECT_EQ(profile.GetRawInfo(ADDRESS_HOME_CITY), + profile_a.GetRawInfo(ADDRESS_HOME_CITY)); + EXPECT_EQ(profile.GetRawInfo(ADDRESS_HOME_STATE), + profile_a.GetRawInfo(ADDRESS_HOME_STATE)); + EXPECT_EQ(profile.GetRawInfo(ADDRESS_HOME_ZIP), + profile_a.GetRawInfo(ADDRESS_HOME_ZIP)); + EXPECT_EQ(profile.GetRawInfo(ADDRESS_HOME_COUNTRY), + profile_a.GetRawInfo(ADDRESS_HOME_COUNTRY)); + EXPECT_EQ(profile_date_modified, profile_date_modified_a); + + sql::Statement s2( + connection.GetUniqueStatement( + "SELECT guid, name_on_card, expiration_month, " + "expiration_year, card_number_encrypted, date_modified " + "FROM credit_cards")); + ASSERT_TRUE(s2.Step()); + + CreditCard credit_card_a; + string16 cc_label_a; + std::string cc_number_encrypted_a; + int64 cc_date_modified_a = 0; + EXPECT_NO_FATAL_FAILURE(CreditCard32FromStatement(s2, + &credit_card_a, + &cc_number_encrypted_a, + &cc_date_modified_a)); + EXPECT_EQ(credit_card, credit_card_a); + EXPECT_EQ(cc_label, cc_label_a); + EXPECT_EQ(cc_number_encrypted, cc_number_encrypted_a); + EXPECT_EQ(cc_date_modified, cc_date_modified_a); + } +} + +// Factor |autofill_profiles| address information separately from name, email, +// and phone. +TEST_F(WebDatabaseMigrationTest, MigrateVersion32ToCurrent) { + // Initialize the database. + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_32.sql"))); + + // Verify pre-conditions. These are expectations for version 32 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Verify existence of columns we'll be changing. + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "guid")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "label")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "first_name")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "middle_name")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "last_name")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "email")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", + "company_name")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", + "address_line_1")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", + "address_line_2")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "city")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "state")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "zipcode")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "country")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "phone")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "fax")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", + "date_modified")); + + EXPECT_FALSE(connection.DoesTableExist("autofill_profile_names")); + EXPECT_FALSE(connection.DoesTableExist("autofill_profile_emails")); + EXPECT_FALSE(connection.DoesTableExist("autofill_profile_phones")); + + EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "label")); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + // Verify changes to columns. + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "guid")); + EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "label")); + EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "first_name")); + EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", + "middle_name")); + EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "last_name")); + EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "email")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", + "company_name")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", + "address_line_1")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", + "address_line_2")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "city")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "state")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "zipcode")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "country")); + EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "phone")); + EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "fax")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", + "date_modified")); + + // New "names" table. + EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_names", "guid")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_names", + "first_name")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_names", + "middle_name")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_names", + "last_name")); + + // New "emails" table. + EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_emails", "guid")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_emails", "email")); + + // New "phones" table. + EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_phones", "guid")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_phones", "type")); + EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_phones", + "number")); + + EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "label")); + + // Verify data in the database after the migration. + sql::Statement s1( + connection.GetUniqueStatement( + "SELECT guid, company_name, address_line_1, address_line_2, " + "city, state, zipcode, country, date_modified " + "FROM autofill_profiles")); + + // John Doe. + ASSERT_TRUE(s1.Step()); + EXPECT_EQ("00580526-FF81-EE2A-0546-1AC593A32E2F", s1.ColumnString(0)); + EXPECT_EQ(ASCIIToUTF16("Doe Enterprises"), s1.ColumnString16(1)); + EXPECT_EQ(ASCIIToUTF16("1 Main St"), s1.ColumnString16(2)); + EXPECT_EQ(ASCIIToUTF16("Apt 1"), s1.ColumnString16(3)); + EXPECT_EQ(ASCIIToUTF16("Los Altos"), s1.ColumnString16(4)); + EXPECT_EQ(ASCIIToUTF16("CA"), s1.ColumnString16(5)); + EXPECT_EQ(ASCIIToUTF16("94022"), s1.ColumnString16(6)); + EXPECT_EQ(ASCIIToUTF16("United States"), s1.ColumnString16(7)); + EXPECT_EQ(1297882100L, s1.ColumnInt64(8)); + + // John P. Doe. + // Gets merged during migration from 35 to 37 due to multi-valued fields. + + // Dave Smith. + ASSERT_TRUE(s1.Step()); + EXPECT_EQ("4C74A9D8-7EEE-423E-F9C2-E7FA70ED1396", s1.ColumnString(0)); + EXPECT_EQ(string16(), s1.ColumnString16(1)); + EXPECT_EQ(ASCIIToUTF16("2 Main Street"), s1.ColumnString16(2)); + EXPECT_EQ(string16(), s1.ColumnString16(3)); + EXPECT_EQ(ASCIIToUTF16("Los Altos"), s1.ColumnString16(4)); + EXPECT_EQ(ASCIIToUTF16("CA"), s1.ColumnString16(5)); + EXPECT_EQ(ASCIIToUTF16("94022"), s1.ColumnString16(6)); + EXPECT_EQ(ASCIIToUTF16("United States"), s1.ColumnString16(7)); + EXPECT_EQ(1297882100L, s1.ColumnInt64(8)); + + // Dave Smith (Part 2). + ASSERT_TRUE(s1.Step()); + EXPECT_EQ("722DF5C4-F74A-294A-46F0-31FFDED0D635", s1.ColumnString(0)); + EXPECT_EQ(string16(), s1.ColumnString16(1)); + EXPECT_EQ(ASCIIToUTF16("2 Main St"), s1.ColumnString16(2)); + EXPECT_EQ(string16(), s1.ColumnString16(3)); + EXPECT_EQ(ASCIIToUTF16("Los Altos"), s1.ColumnString16(4)); + EXPECT_EQ(ASCIIToUTF16("CA"), s1.ColumnString16(5)); + EXPECT_EQ(ASCIIToUTF16("94022"), s1.ColumnString16(6)); + EXPECT_EQ(ASCIIToUTF16("United States"), s1.ColumnString16(7)); + EXPECT_EQ(1297882100L, s1.ColumnInt64(8)); + + // Alfred E Newman. + // Gets culled during migration from 35 to 36 due to incomplete address. + + // 3 Main St. + ASSERT_TRUE(s1.Step()); + EXPECT_EQ("9E5FE298-62C7-83DF-6293-381BC589183F", s1.ColumnString(0)); + EXPECT_EQ(string16(), s1.ColumnString16(1)); + EXPECT_EQ(ASCIIToUTF16("3 Main St"), s1.ColumnString16(2)); + EXPECT_EQ(string16(), s1.ColumnString16(3)); + EXPECT_EQ(ASCIIToUTF16("Los Altos"), s1.ColumnString16(4)); + EXPECT_EQ(ASCIIToUTF16("CA"), s1.ColumnString16(5)); + EXPECT_EQ(ASCIIToUTF16("94022"), s1.ColumnString16(6)); + EXPECT_EQ(ASCIIToUTF16("United States"), s1.ColumnString16(7)); + EXPECT_EQ(1297882100L, s1.ColumnInt64(8)); + + // That should be all. + EXPECT_FALSE(s1.Step()); + + sql::Statement s2( + connection.GetUniqueStatement( + "SELECT guid, first_name, middle_name, last_name " + "FROM autofill_profile_names")); + + // John Doe. + ASSERT_TRUE(s2.Step()); + EXPECT_EQ("00580526-FF81-EE2A-0546-1AC593A32E2F", s2.ColumnString(0)); + EXPECT_EQ(ASCIIToUTF16("John"), s2.ColumnString16(1)); + EXPECT_EQ(string16(), s2.ColumnString16(2)); + EXPECT_EQ(ASCIIToUTF16("Doe"), s2.ColumnString16(3)); + + // John P. Doe. Note same guid as above due to merging of multi-valued + // fields. + ASSERT_TRUE(s2.Step()); + EXPECT_EQ("00580526-FF81-EE2A-0546-1AC593A32E2F", s2.ColumnString(0)); + EXPECT_EQ(ASCIIToUTF16("John"), s2.ColumnString16(1)); + EXPECT_EQ(ASCIIToUTF16("P."), s2.ColumnString16(2)); + EXPECT_EQ(ASCIIToUTF16("Doe"), s2.ColumnString16(3)); + + // Dave Smith. + ASSERT_TRUE(s2.Step()); + EXPECT_EQ("4C74A9D8-7EEE-423E-F9C2-E7FA70ED1396", s2.ColumnString(0)); + EXPECT_EQ(ASCIIToUTF16("Dave"), s2.ColumnString16(1)); + EXPECT_EQ(string16(), s2.ColumnString16(2)); + EXPECT_EQ(ASCIIToUTF16("Smith"), s2.ColumnString16(3)); + + // Dave Smith (Part 2). + ASSERT_TRUE(s2.Step()); + EXPECT_EQ("722DF5C4-F74A-294A-46F0-31FFDED0D635", s2.ColumnString(0)); + EXPECT_EQ(ASCIIToUTF16("Dave"), s2.ColumnString16(1)); + EXPECT_EQ(string16(), s2.ColumnString16(2)); + EXPECT_EQ(ASCIIToUTF16("Smith"), s2.ColumnString16(3)); + + // Alfred E Newman. + // Gets culled during migration from 35 to 36 due to incomplete address. + + // 3 Main St. + ASSERT_TRUE(s2.Step()); + EXPECT_EQ("9E5FE298-62C7-83DF-6293-381BC589183F", s2.ColumnString(0)); + EXPECT_EQ(string16(), s2.ColumnString16(1)); + EXPECT_EQ(string16(), s2.ColumnString16(2)); + EXPECT_EQ(string16(), s2.ColumnString16(3)); + + // Should be all. + EXPECT_FALSE(s2.Step()); + + sql::Statement s3( + connection.GetUniqueStatement( + "SELECT guid, email " + "FROM autofill_profile_emails")); + + // John Doe. + ASSERT_TRUE(s3.Step()); + EXPECT_EQ("00580526-FF81-EE2A-0546-1AC593A32E2F", s3.ColumnString(0)); + EXPECT_EQ(ASCIIToUTF16("john@doe.com"), s3.ColumnString16(1)); + + // John P. Doe. + // Gets culled during migration from 35 to 37 due to merging of John Doe and + // John P. Doe addresses. + + // 2 Main Street. + ASSERT_TRUE(s3.Step()); + EXPECT_EQ("4C74A9D8-7EEE-423E-F9C2-E7FA70ED1396", s3.ColumnString(0)); + EXPECT_EQ(string16(), s3.ColumnString16(1)); + + // 2 Main St. + ASSERT_TRUE(s3.Step()); + EXPECT_EQ("722DF5C4-F74A-294A-46F0-31FFDED0D635", s3.ColumnString(0)); + EXPECT_EQ(string16(), s3.ColumnString16(1)); + + // Alfred E Newman. + // Gets culled during migration from 35 to 36 due to incomplete address. + + // 3 Main St. + ASSERT_TRUE(s3.Step()); + EXPECT_EQ("9E5FE298-62C7-83DF-6293-381BC589183F", s3.ColumnString(0)); + EXPECT_EQ(string16(), s3.ColumnString16(1)); + + // Should be all. + EXPECT_FALSE(s3.Step()); + + sql::Statement s4( + connection.GetUniqueStatement( + "SELECT guid, type, number " + "FROM autofill_profile_phones")); + + // John Doe phone. + ASSERT_TRUE(s4.Step()); + EXPECT_EQ("00580526-FF81-EE2A-0546-1AC593A32E2F", s4.ColumnString(0)); + EXPECT_EQ(0, s4.ColumnInt(1)); // 0 means phone. + EXPECT_EQ(ASCIIToUTF16("4151112222"), s4.ColumnString16(2)); + + // John Doe fax. + // Gets culled after fax type removed. + + // John P. Doe phone. + // Gets culled during migration from 35 to 37 due to merging of John Doe and + // John P. Doe addresses. + + // John P. Doe fax. + // Gets culled during migration from 35 to 37 due to merging of John Doe and + // John P. Doe addresses. + + // 2 Main Street phone. + ASSERT_TRUE(s4.Step()); + EXPECT_EQ("4C74A9D8-7EEE-423E-F9C2-E7FA70ED1396", s4.ColumnString(0)); + EXPECT_EQ(0, s4.ColumnInt(1)); // 0 means phone. + EXPECT_EQ(string16(), s4.ColumnString16(2)); + + // 2 Main Street fax. + // Gets culled after fax type removed. + + // 2 Main St phone. + ASSERT_TRUE(s4.Step()); + EXPECT_EQ("722DF5C4-F74A-294A-46F0-31FFDED0D635", s4.ColumnString(0)); + EXPECT_EQ(0, s4.ColumnInt(1)); // 0 means phone. + EXPECT_EQ(string16(), s4.ColumnString16(2)); + + // 2 Main St fax. + // Gets culled after fax type removed. + + // Note no phone or fax for Alfred E Newman. + + // 3 Main St phone. + ASSERT_TRUE(s4.Step()); + EXPECT_EQ("9E5FE298-62C7-83DF-6293-381BC589183F", s4.ColumnString(0)); + EXPECT_EQ(0, s4.ColumnInt(1)); // 0 means phone. + EXPECT_EQ(string16(), s4.ColumnString16(2)); + + // 2 Main St fax. + // Gets culled after fax type removed. + + // Should be all. + EXPECT_FALSE(s4.Step()); + } +} + +// Adds a column for the autofill profile's country code. +TEST_F(WebDatabaseMigrationTest, MigrateVersion33ToCurrent) { + // Initialize the database. + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_33.sql"))); + + // Verify pre-conditions. These are expectations for version 33 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", + "country_code")); + + // Check that the country value is the one we expect. + sql::Statement s( + connection.GetUniqueStatement("SELECT country FROM autofill_profiles")); + + ASSERT_TRUE(s.Step()); + std::string country = s.ColumnString(0); + EXPECT_EQ("United States", country); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + ASSERT_TRUE(connection.DoesColumnExist("autofill_profiles", + "country_code")); + + // Check that the country code is properly converted. + sql::Statement s(connection.GetUniqueStatement( + "SELECT country_code FROM autofill_profiles")); + + ASSERT_TRUE(s.Step()); + std::string country_code = s.ColumnString(0); + EXPECT_EQ("US", country_code); + } +} + +// Cleans up bad country code "UK" in favor of good country code "GB". +TEST_F(WebDatabaseMigrationTest, MigrateVersion34ToCurrent) { + // Initialize the database. + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_34.sql"))); + + // Verify pre-conditions. These are expectations for version 34 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", + "country_code")); + + // Check that the country_code value is the one we expect. + sql::Statement s( + connection.GetUniqueStatement("SELECT country_code " + "FROM autofill_profiles")); + + ASSERT_TRUE(s.Step()); + std::string country_code = s.ColumnString(0); + EXPECT_EQ("UK", country_code); + + // Should have only one. + ASSERT_FALSE(s.Step()); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + ASSERT_TRUE(connection.DoesColumnExist("autofill_profiles", + "country_code")); + + // Check that the country_code code is properly converted. + sql::Statement s(connection.GetUniqueStatement( + "SELECT country_code FROM autofill_profiles")); + + ASSERT_TRUE(s.Step()); + std::string country_code = s.ColumnString(0); + EXPECT_EQ("GB", country_code); + + // Should have only one. + ASSERT_FALSE(s.Step()); + } +} + +// Cleans up invalid profiles based on more agressive merging. Filters out +// profiles that are subsets of other profiles, and profiles with invalid email, +// state, and incomplete address. +TEST_F(WebDatabaseMigrationTest, MigrateVersion35ToCurrent) { + // Initialize the database. + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_35.sql"))); + + // Verify pre-conditions. These are expectations for version 34 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + EXPECT_FALSE(connection.DoesTableExist("autofill_profiles_trash")); + ASSERT_TRUE(connection.DoesColumnExist("autofill_profiles", "guid")); + + // Check that there are 6 profiles prior to merge. + sql::Statement s( + connection.GetUniqueStatement("SELECT guid FROM autofill_profiles")); + int i = 0; + while (s.Step()) + ++i; + EXPECT_EQ(6, i); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + ASSERT_TRUE(connection.DoesTableExist("autofill_profiles_trash")); + ASSERT_TRUE(connection.DoesColumnExist("autofill_profiles_trash", "guid")); + ASSERT_TRUE(connection.DoesColumnExist("autofill_profiles", "guid")); + + // Verify data in the database after the migration. + sql::Statement s1( + connection.GetUniqueStatement( + "SELECT guid, company_name, address_line_1, address_line_2, " + "city, state, zipcode, country, date_modified " + "FROM autofill_profiles")); + + // John Doe. + ASSERT_TRUE(s1.Step()); + EXPECT_EQ("00000000-0000-0000-0000-000000000001", s1.ColumnString(0)); + EXPECT_EQ(ASCIIToUTF16("Acme Inc."), s1.ColumnString16(1)); + EXPECT_EQ(ASCIIToUTF16("1 Main Street"), s1.ColumnString16(2)); + EXPECT_EQ(ASCIIToUTF16("Apt 2"), s1.ColumnString16(3)); + EXPECT_EQ(ASCIIToUTF16("San Francisco"), s1.ColumnString16(4)); + EXPECT_EQ(ASCIIToUTF16("CA"), s1.ColumnString16(5)); + EXPECT_EQ(ASCIIToUTF16("94102"), s1.ColumnString16(6)); + EXPECT_EQ(ASCIIToUTF16("United States"), s1.ColumnString16(7)); + EXPECT_EQ(1300131704, s1.ColumnInt64(8)); + + // That should be it. + ASSERT_FALSE(s1.Step()); + + // Check that there 5 trashed profile after the merge. + sql::Statement s2( + connection.GetUniqueStatement("SELECT guid " + "FROM autofill_profiles_trash")); + ASSERT_TRUE(s2.Step()); + EXPECT_EQ("00000000-0000-0000-0000-000000000002", s2.ColumnString(0)); + + ASSERT_TRUE(s2.Step()); + EXPECT_EQ("00000000-0000-0000-0000-000000000003", s2.ColumnString(0)); + + ASSERT_TRUE(s2.Step()); + EXPECT_EQ("00000000-0000-0000-0000-000000000004", s2.ColumnString(0)); + + ASSERT_TRUE(s2.Step()); + EXPECT_EQ("00000000-0000-0000-0000-000000000005", s2.ColumnString(0)); + + ASSERT_TRUE(s2.Step()); + EXPECT_EQ("00000000-0000-0000-0000-000000000006", s2.ColumnString(0)); + + // That should be it. + ASSERT_FALSE(s2.Step()); + } +} + +// Tests that the |keywords| |last_modified| column gets added to the schema for +// a version 37 database. +TEST_F(WebDatabaseMigrationTest, MigrateVersion37ToCurrent) { + // This schema is taken from a build prior to the addition of the |keywords| + // |last_modified| column. + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_37.sql"))); + + // Verify pre-conditions. These are expectations for version 37 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Columns existing and not existing before current version. + ASSERT_TRUE(connection.DoesColumnExist("keywords", "id")); + ASSERT_FALSE(connection.DoesColumnExist("keywords", "last_modified")); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + // |keywords| |last_modified| column should have been added. + EXPECT_TRUE(connection.DoesColumnExist("keywords", "id")); + EXPECT_TRUE(connection.DoesColumnExist("keywords", "last_modified")); + } +} + +// Tests that the |keywords| |sync_guid| column gets added to the schema for +// a version 38 database. +TEST_F(WebDatabaseMigrationTest, MigrateVersion38ToCurrent) { + // This schema is taken from a build prior to the addition of the |keywords| + // |sync_guid| column. + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_38.sql"))); + + // Verify pre-conditions. These are expectations for version 38 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Columns existing and not existing before current version. + ASSERT_TRUE(connection.DoesColumnExist("keywords", "id")); + ASSERT_FALSE(connection.DoesColumnExist("keywords", "sync_guid")); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + // |keywords| |sync_guid| column should have been added. + EXPECT_TRUE(connection.DoesColumnExist("keywords", "id")); + EXPECT_TRUE(connection.DoesColumnExist("keywords", "sync_guid")); + } +} + +// Tests that no backup data is added to a version 39 database. +TEST_F(WebDatabaseMigrationTest, MigrateVersion39ToCurrent) { + // This schema is taken from a build prior to the addition of the default + // search provider backup field to the meta table. + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_39.sql"))); + + // Verify pre-conditions. These are expectations for version 39 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, 39, 39)); + + int64 default_search_provider_id = 0; + EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey, + &default_search_provider_id)); + + EXPECT_NO_FATAL_FAILURE(CheckNoBackupData(connection, &meta_table)); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, kCurrentTestedVersionNumber, + kCurrentTestedVersionNumber)); + + int64 default_search_provider_id = 0; + EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey, + &default_search_provider_id)); + EXPECT_NE(0, default_search_provider_id); + + EXPECT_NO_FATAL_FAILURE(CheckNoBackupData(connection, &meta_table)); + } +} + +// Tests that the backup data is removed from the database. +TEST_F(WebDatabaseMigrationTest, MigrateVersion40ToCurrent) { + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_40.sql"))); + + // Verify pre-conditions. These are expectations for version 40 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, 40, 40)); + + int64 default_search_provider_id = 0; + EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey, + &default_search_provider_id)); + + EXPECT_NO_FATAL_FAILURE(CheckHasBackupData(&meta_table)); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, kCurrentTestedVersionNumber, + kCurrentTestedVersionNumber)); + + int64 default_search_provider_id = 0; + EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey, + &default_search_provider_id)); + EXPECT_NE(0, default_search_provider_id); + + EXPECT_NO_FATAL_FAILURE(CheckNoBackupData(connection, &meta_table)); + } +} + +// Tests that the backup data is removed from the database. +TEST_F(WebDatabaseMigrationTest, MigrateVersion41ToCurrent) { + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_41.sql"))); + + // Verify pre-conditions. These are expectations for version 41 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, 41, 41)); + + int64 default_search_provider_id = 0; + EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey, + &default_search_provider_id)); + + EXPECT_NO_FATAL_FAILURE(CheckHasBackupData(&meta_table)); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, kCurrentTestedVersionNumber, + kCurrentTestedVersionNumber)); + + int64 default_search_provider_id = 0; + EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey, + &default_search_provider_id)); + EXPECT_NE(0, default_search_provider_id); + + EXPECT_NO_FATAL_FAILURE(CheckNoBackupData(connection, &meta_table)); + } +} + +// Tests that the backup data is removed from the database. +TEST_F(WebDatabaseMigrationTest, MigrateVersion42ToCurrent) { + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_42.sql"))); + + // Verify pre-conditions. These are expectations for version 42 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, 42, 42)); + + int64 default_search_provider_id = 0; + EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey, + &default_search_provider_id)); + + EXPECT_NO_FATAL_FAILURE(CheckHasBackupData(&meta_table)); + + EXPECT_FALSE(connection.DoesTableExist("keywords_backup")); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, kCurrentTestedVersionNumber, + kCurrentTestedVersionNumber)); + + int64 default_search_provider_id = 0; + EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey, + &default_search_provider_id)); + EXPECT_NE(0, default_search_provider_id); + + EXPECT_NO_FATAL_FAILURE(CheckNoBackupData(connection, &meta_table)); + } +} + +// Tests that the backup data is removed from the database. +TEST_F(WebDatabaseMigrationTest, MigrateVersion43ToCurrent) { + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_43.sql"))); + + int64 previous_default_search_provider_id; + + // Verify pre-conditions. These are expectations for version 43 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, 43, 43)); + + int64 default_search_provider_id = 0; + EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey, + &default_search_provider_id)); + EXPECT_NE(default_search_provider_id, 0); + previous_default_search_provider_id = default_search_provider_id; + + EXPECT_NO_FATAL_FAILURE(CheckHasBackupData(&meta_table)); + EXPECT_TRUE(connection.DoesTableExist("keywords_backup")); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init( + &connection, + kCurrentTestedVersionNumber, + kCurrentTestedVersionNumber)); + + int64 default_search_provider_id = 0; + EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey, + &default_search_provider_id)); + // Default search provider ID should not change. + EXPECT_EQ(previous_default_search_provider_id, default_search_provider_id); + + EXPECT_NO_FATAL_FAILURE(CheckNoBackupData(connection, &meta_table)); + } +} + +// Tests that the |autogenerate_keyword| and |logo_id| columns get removed from +// the keyword table schema for a version 45 database. +TEST_F(WebDatabaseMigrationTest, MigrateVersion44ToCurrent) { + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_44.sql"))); + + // Verify pre-conditions. These are expectations for version 44 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, 44, 44)); + + ASSERT_TRUE(connection.DoesColumnExist("keywords", "autogenerate_keyword")); + ASSERT_TRUE(connection.DoesColumnExist("keywords", "logo_id")); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, kCurrentTestedVersionNumber, + kCurrentTestedVersionNumber)); + + // We should have removed this obsolete key. + std::string default_search_provider_backup; + EXPECT_FALSE(meta_table.GetValue("Default Search Provider Backup", + &default_search_provider_backup)); + + // Two columns should have been removed. + EXPECT_FALSE(connection.DoesColumnExist("keywords", + "autogenerate_keyword")); + EXPECT_FALSE(connection.DoesColumnExist("keywords", "logo_id")); + + // Backup data should have been removed. + EXPECT_NO_FATAL_FAILURE(CheckNoBackupData(connection, &meta_table)); + } +} + +// Tests that the web_intents and web_intents_defaults tables are +// modified to include "scheme" columns. +TEST_F(WebDatabaseMigrationTest, MigrateVersion45ToCurrent) { + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_45.sql"))); + + // Verify pre-conditions. These are expectations for version 45 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, 45, 45)); + + ASSERT_FALSE(connection.DoesColumnExist("scheme", "web_intents")); + ASSERT_FALSE(connection.DoesColumnExist( + "scheme", "web_intents_defaults")); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init( + &connection, + kCurrentTestedVersionNumber, + kCurrentTestedVersionNumber)); + + // A new "scheme" column should have been added to each web_intents table. + EXPECT_TRUE(connection.DoesColumnExist("web_intents", "scheme")); + EXPECT_TRUE(connection.DoesColumnExist("web_intents_defaults", "scheme")); + + // Verify existing user data was copied. + sql::Statement s1( + connection.GetUniqueStatement("SELECT * FROM web_intents")); + + ASSERT_TRUE(s1.Step()); + EXPECT_EQ("http://poodles.com/fuzzer", s1.ColumnString(0)); + EXPECT_EQ(ASCIIToUTF16("fuzz"), s1.ColumnString16(1)); + EXPECT_EQ(ASCIIToUTF16("poodle/*"), s1.ColumnString16(2)); + EXPECT_EQ(ASCIIToUTF16("Poodle Fuzzer"), s1.ColumnString16(3)); + EXPECT_EQ(ASCIIToUTF16("window"), s1.ColumnString16(4)); + EXPECT_EQ(ASCIIToUTF16(""), s1.ColumnString16(5)); + ASSERT_FALSE(s1.Step()); + + // Now we want to verify existing user data was copied + sql::Statement s2( + connection.GetUniqueStatement("SELECT * FROM web_intents_defaults")); + + ASSERT_TRUE(s2.Step()); + EXPECT_EQ("fuzz", s2.ColumnString(0)); + EXPECT_EQ(ASCIIToUTF16("poodle/*"), s2.ColumnString16(1)); + EXPECT_EQ(ASCIIToUTF16(""), s2.ColumnString16(2)); + EXPECT_EQ(0, s2.ColumnInt(3)); + EXPECT_EQ(0, s2.ColumnInt(4)); + EXPECT_EQ(ASCIIToUTF16("http://poodles.com/fuzzer"), s2.ColumnString16(5)); + EXPECT_EQ(ASCIIToUTF16(""), s2.ColumnString16(6)); + ASSERT_FALSE(s2.Step()); + + // finally ensure the migration code cleaned up after itself + EXPECT_FALSE(connection.DoesTableExist("old_web_intents")); + EXPECT_FALSE(connection.DoesTableExist("old_web_intents_defaults")); + } +} + +// Tests that the web_intents and web_intents_defaults tables are +// modified to include "scheme" columns. +TEST_F(WebDatabaseMigrationTest, MigrateVersion45InvalidToCurrent) { + ASSERT_NO_FATAL_FAILURE( + LoadDatabase(FILE_PATH_LITERAL("version_45_invalid.sql"))); + + // Verify pre-conditions. These are expectations for version 45 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, 45, 45)); + + ASSERT_FALSE(connection.DoesColumnExist("scheme", "web_intents")); + ASSERT_FALSE(connection.DoesColumnExist( + "scheme", "web_intents_defaults")); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init( + &connection, + kCurrentTestedVersionNumber, + kCurrentTestedVersionNumber)); + + // A new "scheme" column should have been added to each web_intents table. + EXPECT_TRUE(connection.DoesColumnExist("web_intents", "scheme")); + EXPECT_TRUE(connection.DoesColumnExist("web_intents_defaults", "scheme")); + + // Verify existing user data was copied. + sql::Statement s1( + connection.GetUniqueStatement("SELECT * FROM web_intents")); + + ASSERT_FALSE(s1.Step()); // Basically should be empty at this point. + + // Now we want to verify existing user data was copied + sql::Statement s2( + connection.GetUniqueStatement("SELECT * FROM web_intents_defaults")); + + // We were able to create the new tables, but unable to copy any data + // Given the initial bad state of the tables. + ASSERT_FALSE(s2.Step()); + + // Finally ensure the migration code cleaned up after itself. + EXPECT_FALSE(connection.DoesTableExist("old_web_intents")); + EXPECT_FALSE(connection.DoesTableExist("old_web_intents_defaults")); + } +} + +// Check that current version is forced to compatible version before migration, +// if the former is smaller. +TEST_F(WebDatabaseMigrationTest, MigrateVersion45CompatibleToCurrent) { + ASSERT_NO_FATAL_FAILURE( + LoadDatabase(FILE_PATH_LITERAL("version_45_compatible.sql"))); + + // Verify pre-conditions. These are expectations for version 45 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + sql::MetaTable meta_table; + // Database is actually version 45 but the version field states 40. + ASSERT_TRUE(meta_table.Init(&connection, 40, 45)); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + EXPECT_LE(45, VersionFromConnection(&connection)); + } +} + +// Tests that the |alternate_urls| column is added to the keyword table schema +// for a version 47 database. +TEST_F(WebDatabaseMigrationTest, MigrateVersion46ToCurrent) { + ASSERT_NO_FATAL_FAILURE( + LoadDatabase(FILE_PATH_LITERAL("version_46.sql"))); + + // Verify pre-conditions. These are expectations for version 46 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, 46, 46)); + + ASSERT_FALSE(connection.DoesColumnExist("keywords", "alternate_urls")); + ASSERT_FALSE(connection.DoesColumnExist("keywords_backup", + "alternate_urls")); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + // A new column should have been created. + EXPECT_TRUE(connection.DoesColumnExist("keywords", "alternate_urls")); + } +} + +// Tests that the backup data is removed from the database. +TEST_F(WebDatabaseMigrationTest, MigrateVersion47ToCurrent) { + ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_47.sql"))); + + // Verify pre-conditions. These are expectations for version 47 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, 47, 47)); + + int64 default_search_provider_id = 0; + EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey, + &default_search_provider_id)); + EXPECT_NE(0, default_search_provider_id); + + EXPECT_NO_FATAL_FAILURE(CheckHasBackupData(&meta_table)); + EXPECT_TRUE(connection.DoesTableExist("keywords_backup")); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, kCurrentTestedVersionNumber, + kCurrentTestedVersionNumber)); + + int64 default_search_provider_id = 0; + EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey, + &default_search_provider_id)); + EXPECT_NE(0, default_search_provider_id); + + EXPECT_NO_FATAL_FAILURE(CheckNoBackupData(connection, &meta_table)); + } +} + +// Tests that the |search_terms_replacement_key| column is added to the keyword +// table schema for a version 49 database. +TEST_F(WebDatabaseMigrationTest, MigrateVersion48ToCurrent) { + ASSERT_NO_FATAL_FAILURE( + LoadDatabase(FILE_PATH_LITERAL("version_48.sql"))); + + // Verify pre-conditions. These are expectations for version 48 of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + sql::MetaTable meta_table; + ASSERT_TRUE(meta_table.Init(&connection, 48, 48)); + + ASSERT_FALSE(connection.DoesColumnExist("keywords", + "search_terms_replacement_key")); + } + + DoMigration(); + + // Verify post-conditions. These are expectations for current version of the + // database. + { + sql::Connection connection; + ASSERT_TRUE(connection.Open(GetDatabasePath())); + ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); + + // Check version. + EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); + + // A new column should have been created. + EXPECT_TRUE(connection.DoesColumnExist("keywords", + "search_terms_replacement_key")); + } +} diff --git a/components/webdata/common/web_data_service_test_util.cc b/components/webdata/common/web_data_service_test_util.cc new file mode 100644 index 0000000..13cc591 --- /dev/null +++ b/components/webdata/common/web_data_service_test_util.cc @@ -0,0 +1,38 @@ +// Copyright 2013 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 "components/webdata/common/web_data_service_test_util.h" + +#include "components/webdata/autofill/autofill_webdata_service.h" + +MockWebDataServiceWrapperBase::MockWebDataServiceWrapperBase() { +} + +MockWebDataServiceWrapperBase::~MockWebDataServiceWrapperBase() { +} + +void MockWebDataServiceWrapperBase::Shutdown() { +} + +// TODO(caitkp): This won't scale well. As we get more WebData subclasses, we +// will probably need a better way to create these mocks rather than passing +// all the webdatas in. +MockWebDataServiceWrapper::MockWebDataServiceWrapper( + scoped_refptr<WebDataService> fake_service, + scoped_refptr<AutofillWebDataService> fake_autofill) + : fake_autofill_web_data_(fake_autofill), + fake_web_data_(fake_service) { +} + +MockWebDataServiceWrapper::~MockWebDataServiceWrapper() { +} + +scoped_refptr<AutofillWebDataService> + MockWebDataServiceWrapper::GetAutofillWebData() { + return fake_autofill_web_data_; +} + +scoped_refptr<WebDataService> MockWebDataServiceWrapper::GetWebData() { + return fake_web_data_; +} diff --git a/components/webdata/common/web_data_service_test_util.h b/components/webdata/common/web_data_service_test_util.h new file mode 100644 index 0000000..e037a15 --- /dev/null +++ b/components/webdata/common/web_data_service_test_util.h @@ -0,0 +1,76 @@ +// Copyright (c) 2011 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. + +#ifndef COMPONENTS_WEBDATA_COMMON_WEB_DATA_SERVICE_TEST_UTIL_H__ +#define COMPONENTS_WEBDATA_COMMON_WEB_DATA_SERVICE_TEST_UTIL_H__ + +#include "base/basictypes.h" +#include "base/message_loop.h" +#include "chrome/browser/webdata/web_data_service.h" +#include "chrome/browser/webdata/web_data_service_factory.h" +#include "content/public/browser/browser_thread.h" + +template <class T> +class AutofillWebDataServiceConsumer: public WebDataServiceConsumer { + public: + AutofillWebDataServiceConsumer() : handle_(0) {} + virtual ~AutofillWebDataServiceConsumer() {} + + virtual void OnWebDataServiceRequestDone(WebDataService::Handle handle, + const WDTypedResult* result) { + using content::BrowserThread; + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + handle_ = handle; + const WDResult<T>* wrapped_result = + static_cast<const WDResult<T>*>(result); + result_ = wrapped_result->GetValue(); + + MessageLoop::current()->Quit(); + } + + WebDataService::Handle handle() { return handle_; } + T& result() { return result_; } + + private: + WebDataService::Handle handle_; + T result_; + DISALLOW_COPY_AND_ASSIGN(AutofillWebDataServiceConsumer); +}; + +// Base class for mocks of WebDataService, that does nothing in +// Shutdown(). +class MockWebDataServiceWrapperBase : public WebDataServiceWrapper { + public: + MockWebDataServiceWrapperBase(); + virtual ~MockWebDataServiceWrapperBase(); + + virtual void Shutdown() OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(MockWebDataServiceWrapperBase); +}; + +// Pass your fake WebDataService in the constructor and this will +// serve it up via GetWebData(). +class MockWebDataServiceWrapper : public MockWebDataServiceWrapperBase { + public: + MockWebDataServiceWrapper( + scoped_refptr<WebDataService> fake_service, + scoped_refptr<AutofillWebDataService> fake_autofill); + + virtual ~MockWebDataServiceWrapper(); + + virtual scoped_refptr<AutofillWebDataService> GetAutofillWebData() OVERRIDE; + + virtual scoped_refptr<WebDataService> GetWebData() OVERRIDE; + + protected: + scoped_refptr<AutofillWebDataService> fake_autofill_web_data_; + scoped_refptr<WebDataService> fake_web_data_; + + private: + DISALLOW_COPY_AND_ASSIGN(MockWebDataServiceWrapper); +}; + +#endif // COMPONENTS_WEBDATA_COMMON_WEB_DATA_SERVICE_TEST_UTIL_H__ |