Home | History | Annotate | Download | only in appcache
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/bind.h"
      6 #include "base/file_util.h"
      7 #include "base/files/scoped_temp_dir.h"
      8 #include "base/strings/stringprintf.h"
      9 #include "sql/connection.h"
     10 #include "sql/meta_table.h"
     11 #include "sql/statement.h"
     12 #include "sql/test/scoped_error_ignorer.h"
     13 #include "sql/test/test_helpers.h"
     14 #include "sql/transaction.h"
     15 #include "testing/gtest/include/gtest/gtest.h"
     16 #include "third_party/sqlite/sqlite3.h"
     17 #include "webkit/browser/appcache/appcache_database.h"
     18 #include "webkit/browser/appcache/appcache_entry.h"
     19 
     20 namespace {
     21 
     22 const base::Time kZeroTime;
     23 
     24 }  // namespace
     25 
     26 namespace appcache {
     27 
     28 class AppCacheDatabaseTest {};
     29 
     30 TEST(AppCacheDatabaseTest, LazyOpen) {
     31   // Use an empty file path to use an in-memory sqlite database.
     32   const base::FilePath kEmptyPath;
     33   AppCacheDatabase db(kEmptyPath);
     34 
     35   EXPECT_FALSE(db.LazyOpen(false));
     36   EXPECT_TRUE(db.LazyOpen(true));
     37 
     38   int64 group_id, cache_id, response_id, deleteable_response_rowid;
     39   group_id = cache_id = response_id = deleteable_response_rowid = 0;
     40   EXPECT_TRUE(db.FindLastStorageIds(&group_id, &cache_id, &response_id,
     41                                     &deleteable_response_rowid));
     42   EXPECT_EQ(0, group_id);
     43   EXPECT_EQ(0, cache_id);
     44   EXPECT_EQ(0, response_id);
     45   EXPECT_EQ(0, deleteable_response_rowid);
     46 
     47   std::set<GURL> origins;
     48   EXPECT_TRUE(db.FindOriginsWithGroups(&origins));
     49   EXPECT_TRUE(origins.empty());
     50 }
     51 
     52 TEST(AppCacheDatabaseTest, ReCreate) {
     53   // Real files on disk for this test.
     54   base::ScopedTempDir temp_dir;
     55   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
     56   const base::FilePath kDbFile = temp_dir.path().AppendASCII("appcache.db");
     57   const base::FilePath kNestedDir = temp_dir.path().AppendASCII("nested");
     58   const base::FilePath kOtherFile =  kNestedDir.AppendASCII("other_file");
     59   EXPECT_TRUE(base::CreateDirectory(kNestedDir));
     60   EXPECT_EQ(3, file_util::WriteFile(kOtherFile, "foo", 3));
     61 
     62   AppCacheDatabase db(kDbFile);
     63   EXPECT_FALSE(db.LazyOpen(false));
     64   EXPECT_TRUE(db.LazyOpen(true));
     65 
     66   EXPECT_TRUE(base::PathExists(kDbFile));
     67   EXPECT_TRUE(base::DirectoryExists(kNestedDir));
     68   EXPECT_TRUE(base::PathExists(kOtherFile));
     69 
     70   EXPECT_TRUE(db.DeleteExistingAndCreateNewDatabase());
     71 
     72   EXPECT_TRUE(base::PathExists(kDbFile));
     73   EXPECT_FALSE(base::DirectoryExists(kNestedDir));
     74   EXPECT_FALSE(base::PathExists(kOtherFile));
     75 }
     76 
     77 #ifdef NDEBUG
     78 // Only run in release builds because sql::Connection and familiy
     79 // crank up DLOG(FATAL)'ness and this test presents it with
     80 // intentionally bad data which causes debug builds to exit instead
     81 // of run to completion. In release builds, errors the are delivered
     82 // to the consumer so  we can test the error handling of the consumer.
     83 // TODO: crbug/328576
     84 TEST(AppCacheDatabaseTest, QuickIntegrityCheck) {
     85   // Real files on disk for this test too, a corrupt database file.
     86   base::ScopedTempDir temp_dir;
     87   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
     88   base::FilePath mock_dir = temp_dir.path().AppendASCII("mock");
     89   ASSERT_TRUE(base::CreateDirectory(mock_dir));
     90 
     91   const base::FilePath kDbFile = mock_dir.AppendASCII("appcache.db");
     92   const base::FilePath kOtherFile = mock_dir.AppendASCII("other_file");
     93   EXPECT_EQ(3, file_util::WriteFile(kOtherFile, "foo", 3));
     94 
     95   // First create a valid db file.
     96   AppCacheDatabase db(kDbFile);
     97   EXPECT_TRUE(db.LazyOpen(true));
     98   EXPECT_TRUE(base::PathExists(kOtherFile));
     99   EXPECT_TRUE(base::PathExists(kDbFile));
    100   db.CloseConnection();
    101 
    102   // Break it.
    103   ASSERT_TRUE(sql::test::CorruptSizeInHeader(kDbFile));
    104 
    105   // Reopening will notice the corruption and delete/recreate the directory.
    106   {
    107     sql::ScopedErrorIgnorer ignore_errors;
    108     ignore_errors.IgnoreError(SQLITE_CORRUPT);
    109     EXPECT_TRUE(db.LazyOpen(true));
    110     EXPECT_FALSE(base::PathExists(kOtherFile));
    111     EXPECT_TRUE(base::PathExists(kDbFile));
    112     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    113   }
    114 }
    115 #endif  // NDEBUG
    116 
    117 TEST(AppCacheDatabaseTest, ExperimentalFlags) {
    118   const char kExperimentFlagsKey[] = "ExperimentFlags";
    119   std::string kInjectedFlags("exp1,exp2");
    120 
    121   // Real files on disk for this test.
    122   base::ScopedTempDir temp_dir;
    123   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
    124   const base::FilePath kDbFile = temp_dir.path().AppendASCII("appcache.db");
    125   const base::FilePath kOtherFile =  temp_dir.path().AppendASCII("other_file");
    126   EXPECT_EQ(3, file_util::WriteFile(kOtherFile, "foo", 3));
    127   EXPECT_TRUE(base::PathExists(kOtherFile));
    128 
    129   AppCacheDatabase db(kDbFile);
    130   EXPECT_TRUE(db.LazyOpen(true));
    131 
    132   // Inject a non empty flags value, and verify it got there.
    133   EXPECT_TRUE(db.meta_table_->SetValue(kExperimentFlagsKey, kInjectedFlags));
    134   std::string flags;
    135   EXPECT_TRUE(db.meta_table_->GetValue(kExperimentFlagsKey, &flags));
    136   EXPECT_EQ(kInjectedFlags, flags);
    137   db.CloseConnection();
    138 
    139   // If flags don't match the expected value, empty string by default,
    140   // the database should be recreated and other files should be cleared out.
    141   EXPECT_TRUE(db.LazyOpen(false));
    142   EXPECT_TRUE(db.meta_table_->GetValue(kExperimentFlagsKey, &flags));
    143   EXPECT_TRUE(flags.empty());
    144   EXPECT_FALSE(base::PathExists(kOtherFile));
    145 }
    146 
    147 TEST(AppCacheDatabaseTest, EntryRecords) {
    148   const base::FilePath kEmptyPath;
    149   AppCacheDatabase db(kEmptyPath);
    150   EXPECT_TRUE(db.LazyOpen(true));
    151 
    152   sql::ScopedErrorIgnorer ignore_errors;
    153   // TODO(shess): Suppressing SQLITE_CONSTRAINT because the code
    154   // expects that and handles the resulting error.  Consider revising
    155   // the code to use INSERT OR IGNORE (which would not throw
    156   // SQLITE_CONSTRAINT) and then check ChangeCount() to see if any
    157   // changes were made.
    158   ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
    159 
    160   AppCacheDatabase::EntryRecord entry;
    161 
    162   entry.cache_id = 1;
    163   entry.url = GURL("http://blah/1");
    164   entry.flags = AppCacheEntry::MASTER;
    165   entry.response_id = 1;
    166   entry.response_size = 100;
    167   EXPECT_TRUE(db.InsertEntry(&entry));
    168   EXPECT_FALSE(db.InsertEntry(&entry));
    169 
    170   entry.cache_id = 2;
    171   entry.url = GURL("http://blah/2");
    172   entry.flags = AppCacheEntry::EXPLICIT;
    173   entry.response_id = 2;
    174   entry.response_size = 200;
    175   EXPECT_TRUE(db.InsertEntry(&entry));
    176 
    177   entry.cache_id = 2;
    178   entry.url = GURL("http://blah/3");
    179   entry.flags = AppCacheEntry::MANIFEST;
    180   entry.response_id = 3;
    181   entry.response_size = 300;
    182   EXPECT_TRUE(db.InsertEntry(&entry));
    183 
    184   std::vector<AppCacheDatabase::EntryRecord> found;
    185 
    186   EXPECT_TRUE(db.FindEntriesForCache(1, &found));
    187   EXPECT_EQ(1U, found.size());
    188   EXPECT_EQ(1, found[0].cache_id);
    189   EXPECT_EQ(GURL("http://blah/1"), found[0].url);
    190   EXPECT_EQ(AppCacheEntry::MASTER, found[0].flags);
    191   EXPECT_EQ(1, found[0].response_id);
    192   EXPECT_EQ(100, found[0].response_size);
    193   found.clear();
    194 
    195   EXPECT_TRUE(db.AddEntryFlags(GURL("http://blah/1"), 1,
    196                                AppCacheEntry::FOREIGN));
    197   EXPECT_TRUE(db.FindEntriesForCache(1, &found));
    198   EXPECT_EQ(1U, found.size());
    199   EXPECT_EQ(AppCacheEntry::MASTER | AppCacheEntry::FOREIGN, found[0].flags);
    200   found.clear();
    201 
    202   EXPECT_TRUE(db.FindEntriesForCache(2, &found));
    203   EXPECT_EQ(2U, found.size());
    204   EXPECT_EQ(2, found[0].cache_id);
    205   EXPECT_EQ(GURL("http://blah/2"), found[0].url);
    206   EXPECT_EQ(AppCacheEntry::EXPLICIT, found[0].flags);
    207   EXPECT_EQ(2, found[0].response_id);
    208   EXPECT_EQ(200, found[0].response_size);
    209   EXPECT_EQ(2, found[1].cache_id);
    210   EXPECT_EQ(GURL("http://blah/3"), found[1].url);
    211   EXPECT_EQ(AppCacheEntry::MANIFEST, found[1].flags);
    212   EXPECT_EQ(3, found[1].response_id);
    213   EXPECT_EQ(300, found[1].response_size);
    214   found.clear();
    215 
    216   EXPECT_TRUE(db.DeleteEntriesForCache(2));
    217   EXPECT_TRUE(db.FindEntriesForCache(2, &found));
    218   EXPECT_TRUE(found.empty());
    219   found.clear();
    220 
    221   EXPECT_TRUE(db.DeleteEntriesForCache(1));
    222   EXPECT_FALSE(db.AddEntryFlags(GURL("http://blah/1"), 1,
    223                                 AppCacheEntry::FOREIGN));
    224 
    225   ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    226 }
    227 
    228 TEST(AppCacheDatabaseTest, CacheRecords) {
    229   const base::FilePath kEmptyPath;
    230   AppCacheDatabase db(kEmptyPath);
    231   EXPECT_TRUE(db.LazyOpen(true));
    232 
    233   sql::ScopedErrorIgnorer ignore_errors;
    234   // TODO(shess): See EntryRecords test.
    235   ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
    236 
    237   const AppCacheDatabase::CacheRecord kZeroRecord;
    238   AppCacheDatabase::CacheRecord record;
    239   EXPECT_FALSE(db.FindCache(1, &record));
    240 
    241   record.cache_id = 1;
    242   record.group_id = 1;
    243   record.online_wildcard = true;
    244   record.update_time = kZeroTime;
    245   record.cache_size = 100;
    246   EXPECT_TRUE(db.InsertCache(&record));
    247   EXPECT_FALSE(db.InsertCache(&record));
    248 
    249   record = kZeroRecord;
    250   EXPECT_TRUE(db.FindCache(1, &record));
    251   EXPECT_EQ(1, record.cache_id);
    252   EXPECT_EQ(1, record.group_id);
    253   EXPECT_TRUE(record.online_wildcard);
    254   EXPECT_TRUE(kZeroTime == record.update_time);
    255   EXPECT_EQ(100, record.cache_size);
    256 
    257   record = kZeroRecord;
    258   EXPECT_TRUE(db.FindCacheForGroup(1, &record));
    259   EXPECT_EQ(1, record.cache_id);
    260   EXPECT_EQ(1, record.group_id);
    261   EXPECT_TRUE(record.online_wildcard);
    262   EXPECT_TRUE(kZeroTime == record.update_time);
    263   EXPECT_EQ(100, record.cache_size);
    264 
    265   EXPECT_TRUE(db.DeleteCache(1));
    266   EXPECT_FALSE(db.FindCache(1, &record));
    267   EXPECT_FALSE(db.FindCacheForGroup(1, &record));
    268 
    269   EXPECT_TRUE(db.DeleteCache(1));
    270 
    271   ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    272 }
    273 
    274 TEST(AppCacheDatabaseTest, GroupRecords) {
    275   const base::FilePath kEmptyPath;
    276   AppCacheDatabase db(kEmptyPath);
    277   EXPECT_TRUE(db.LazyOpen(true));
    278 
    279   sql::ScopedErrorIgnorer ignore_errors;
    280   // TODO(shess): See EntryRecords test.
    281   ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
    282 
    283   const GURL kManifestUrl("http://blah/manifest");
    284   const GURL kOrigin(kManifestUrl.GetOrigin());
    285   const base::Time kLastAccessTime = base::Time::Now();
    286   const base::Time kCreationTime =
    287       kLastAccessTime - base::TimeDelta::FromDays(7);
    288 
    289   const AppCacheDatabase::GroupRecord kZeroRecord;
    290   AppCacheDatabase::GroupRecord record;
    291   std::vector<AppCacheDatabase::GroupRecord> records;
    292 
    293   // Behavior with an empty table
    294   EXPECT_FALSE(db.FindGroup(1, &record));
    295   EXPECT_FALSE(db.FindGroupForManifestUrl(kManifestUrl, &record));
    296   EXPECT_TRUE(db.DeleteGroup(1));
    297   EXPECT_TRUE(db.FindGroupsForOrigin(kOrigin, &records));
    298   EXPECT_TRUE(records.empty());
    299   EXPECT_FALSE(db.FindGroupForCache(1, &record));
    300 
    301   record.group_id = 1;
    302   record.manifest_url = kManifestUrl;
    303   record.origin = kOrigin;
    304   record.last_access_time = kLastAccessTime;
    305   record.creation_time = kCreationTime;
    306   EXPECT_TRUE(db.InsertGroup(&record));
    307   EXPECT_FALSE(db.InsertGroup(&record));
    308 
    309   record.group_id = 2;
    310   EXPECT_FALSE(db.InsertGroup(&record));
    311 
    312   record = kZeroRecord;
    313   EXPECT_TRUE(db.FindGroup(1, &record));
    314   EXPECT_EQ(1, record.group_id);
    315   EXPECT_EQ(kManifestUrl, record.manifest_url);
    316   EXPECT_EQ(kOrigin, record.origin);
    317   EXPECT_EQ(kCreationTime.ToInternalValue(),
    318             record.creation_time.ToInternalValue());
    319   EXPECT_EQ(kLastAccessTime.ToInternalValue(),
    320             record.last_access_time.ToInternalValue());
    321 
    322   record = kZeroRecord;
    323   EXPECT_TRUE(db.FindGroupForManifestUrl(kManifestUrl, &record));
    324   EXPECT_EQ(1, record.group_id);
    325   EXPECT_EQ(kManifestUrl, record.manifest_url);
    326   EXPECT_EQ(kOrigin, record.origin);
    327   EXPECT_EQ(kCreationTime.ToInternalValue(),
    328             record.creation_time.ToInternalValue());
    329   EXPECT_EQ(kLastAccessTime.ToInternalValue(),
    330             record.last_access_time.ToInternalValue());
    331 
    332   record.group_id = 2;
    333   record.manifest_url = kOrigin;
    334   record.origin = kOrigin;
    335   record.last_access_time = kLastAccessTime;
    336   record.creation_time = kCreationTime;
    337   EXPECT_TRUE(db.InsertGroup(&record));
    338 
    339   record = kZeroRecord;
    340   EXPECT_TRUE(db.FindGroupForManifestUrl(kOrigin, &record));
    341   EXPECT_EQ(2, record.group_id);
    342   EXPECT_EQ(kOrigin, record.manifest_url);
    343   EXPECT_EQ(kOrigin, record.origin);
    344   EXPECT_EQ(kCreationTime.ToInternalValue(),
    345             record.creation_time.ToInternalValue());
    346   EXPECT_EQ(kLastAccessTime.ToInternalValue(),
    347             record.last_access_time.ToInternalValue());
    348 
    349   EXPECT_TRUE(db.FindGroupsForOrigin(kOrigin, &records));
    350   EXPECT_EQ(2U, records.size());
    351   EXPECT_EQ(1, records[0].group_id);
    352   EXPECT_EQ(kManifestUrl, records[0].manifest_url);
    353   EXPECT_EQ(kOrigin, records[0].origin);
    354   EXPECT_EQ(2, records[1].group_id);
    355   EXPECT_EQ(kOrigin, records[1].manifest_url);
    356   EXPECT_EQ(kOrigin, records[1].origin);
    357 
    358   EXPECT_TRUE(db.DeleteGroup(1));
    359 
    360   records.clear();
    361   EXPECT_TRUE(db.FindGroupsForOrigin(kOrigin, &records));
    362   EXPECT_EQ(1U, records.size());
    363   EXPECT_EQ(2, records[0].group_id);
    364   EXPECT_EQ(kOrigin, records[0].manifest_url);
    365   EXPECT_EQ(kOrigin, records[0].origin);
    366   EXPECT_EQ(kCreationTime.ToInternalValue(),
    367             record.creation_time.ToInternalValue());
    368   EXPECT_EQ(kLastAccessTime.ToInternalValue(),
    369             record.last_access_time.ToInternalValue());
    370 
    371   std::set<GURL> origins;
    372   EXPECT_TRUE(db.FindOriginsWithGroups(&origins));
    373   EXPECT_EQ(1U, origins.size());
    374   EXPECT_EQ(kOrigin, *(origins.begin()));
    375 
    376   const GURL kManifest2("http://blah2/manifest");
    377   const GURL kOrigin2(kManifest2.GetOrigin());
    378   record.group_id = 1;
    379   record.manifest_url = kManifest2;
    380   record.origin = kOrigin2;
    381   EXPECT_TRUE(db.InsertGroup(&record));
    382 
    383   origins.clear();
    384   EXPECT_TRUE(db.FindOriginsWithGroups(&origins));
    385   EXPECT_EQ(2U, origins.size());
    386   EXPECT_TRUE(origins.end() != origins.find(kOrigin));
    387   EXPECT_TRUE(origins.end()  != origins.find(kOrigin2));
    388 
    389   AppCacheDatabase::CacheRecord cache_record;
    390   cache_record.cache_id = 1;
    391   cache_record.group_id = 1;
    392   cache_record.online_wildcard = true;
    393   cache_record.update_time = kZeroTime;
    394   EXPECT_TRUE(db.InsertCache(&cache_record));
    395 
    396   record = kZeroRecord;
    397   EXPECT_TRUE(db.FindGroupForCache(1, &record));
    398   EXPECT_EQ(1, record.group_id);
    399   EXPECT_EQ(kManifest2, record.manifest_url);
    400   EXPECT_EQ(kOrigin2, record.origin);
    401 
    402   ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    403 }
    404 
    405 TEST(AppCacheDatabaseTest, NamespaceRecords) {
    406   const base::FilePath kEmptyPath;
    407   AppCacheDatabase db(kEmptyPath);
    408   EXPECT_TRUE(db.LazyOpen(true));
    409 
    410   sql::ScopedErrorIgnorer ignore_errors;
    411   // TODO(shess): See EntryRecords test.
    412   ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
    413 
    414   const GURL kFooNameSpace1("http://foo/namespace1");
    415   const GURL kFooNameSpace2("http://foo/namespace2");
    416   const GURL kFooFallbackEntry("http://foo/entry");
    417   const GURL kFooOrigin(kFooNameSpace1.GetOrigin());
    418   const GURL kBarNameSpace1("http://bar/namespace1");
    419   const GURL kBarNameSpace2("http://bar/namespace2");
    420   const GURL kBarFallbackEntry("http://bar/entry");
    421   const GURL kBarOrigin(kBarNameSpace1.GetOrigin());
    422 
    423   const AppCacheDatabase::NamespaceRecord kZeroRecord;
    424   AppCacheDatabase::NamespaceRecord record;
    425   std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
    426   std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
    427 
    428   // Behavior with an empty table
    429   EXPECT_TRUE(db.FindNamespacesForCache(1, &intercepts, &fallbacks));
    430   EXPECT_TRUE(fallbacks.empty());
    431   EXPECT_TRUE(db.FindNamespacesForOrigin(kFooOrigin, &intercepts, &fallbacks));
    432   EXPECT_TRUE(fallbacks.empty());
    433   EXPECT_TRUE(db.DeleteNamespacesForCache(1));
    434 
    435   // Two records for two differenent caches in the Foo origin.
    436   record.cache_id = 1;
    437   record.origin = kFooOrigin;
    438   record.namespace_.namespace_url = kFooNameSpace1;
    439   record.namespace_.target_url = kFooFallbackEntry;
    440   EXPECT_TRUE(db.InsertNamespace(&record));
    441   EXPECT_FALSE(db.InsertNamespace(&record));
    442 
    443   record.cache_id = 2;
    444   record.origin = kFooOrigin;
    445   record.namespace_.namespace_url = kFooNameSpace2;
    446   record.namespace_.target_url = kFooFallbackEntry;
    447   EXPECT_TRUE(db.InsertNamespace(&record));
    448 
    449   fallbacks.clear();
    450   EXPECT_TRUE(db.FindNamespacesForCache(1, &intercepts, &fallbacks));
    451   EXPECT_EQ(1U, fallbacks.size());
    452   EXPECT_EQ(1, fallbacks[0].cache_id);
    453   EXPECT_EQ(kFooOrigin, fallbacks[0].origin);
    454   EXPECT_EQ(kFooNameSpace1, fallbacks[0].namespace_.namespace_url);
    455   EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url);
    456   EXPECT_FALSE(fallbacks[0].namespace_.is_pattern);
    457 
    458   fallbacks.clear();
    459   EXPECT_TRUE(db.FindNamespacesForCache(2, &intercepts, &fallbacks));
    460   EXPECT_EQ(1U, fallbacks.size());
    461   EXPECT_EQ(2, fallbacks[0].cache_id);
    462   EXPECT_EQ(kFooOrigin, fallbacks[0].origin);
    463   EXPECT_EQ(kFooNameSpace2, fallbacks[0].namespace_.namespace_url);
    464   EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url);
    465   EXPECT_FALSE(fallbacks[0].namespace_.is_pattern);
    466 
    467   fallbacks.clear();
    468   EXPECT_TRUE(db.FindNamespacesForOrigin(kFooOrigin, &intercepts, &fallbacks));
    469   EXPECT_EQ(2U, fallbacks.size());
    470   EXPECT_EQ(1, fallbacks[0].cache_id);
    471   EXPECT_EQ(kFooOrigin, fallbacks[0].origin);
    472   EXPECT_EQ(kFooNameSpace1, fallbacks[0].namespace_.namespace_url);
    473   EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url);
    474   EXPECT_FALSE(fallbacks[0].namespace_.is_pattern);
    475   EXPECT_EQ(2, fallbacks[1].cache_id);
    476   EXPECT_EQ(kFooOrigin, fallbacks[1].origin);
    477   EXPECT_EQ(kFooNameSpace2, fallbacks[1].namespace_.namespace_url);
    478   EXPECT_EQ(kFooFallbackEntry, fallbacks[1].namespace_.target_url);
    479   EXPECT_FALSE(fallbacks[1].namespace_.is_pattern);
    480 
    481   EXPECT_TRUE(db.DeleteNamespacesForCache(1));
    482   fallbacks.clear();
    483   EXPECT_TRUE(db.FindNamespacesForOrigin(kFooOrigin, &intercepts, &fallbacks));
    484   EXPECT_EQ(1U, fallbacks.size());
    485   EXPECT_EQ(2, fallbacks[0].cache_id);
    486   EXPECT_EQ(kFooOrigin, fallbacks[0].origin);
    487   EXPECT_EQ(kFooNameSpace2, fallbacks[0].namespace_.namespace_url);
    488   EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url);
    489   EXPECT_FALSE(fallbacks[0].namespace_.is_pattern);
    490 
    491   // Two more records for the same cache in the Bar origin.
    492   record.cache_id = 3;
    493   record.origin = kBarOrigin;
    494   record.namespace_.namespace_url = kBarNameSpace1;
    495   record.namespace_.target_url = kBarFallbackEntry;
    496   record.namespace_.is_pattern = true;
    497   EXPECT_TRUE(db.InsertNamespace(&record));
    498 
    499   record.cache_id = 3;
    500   record.origin = kBarOrigin;
    501   record.namespace_.namespace_url = kBarNameSpace2;
    502   record.namespace_.target_url = kBarFallbackEntry;
    503   record.namespace_.is_pattern = true;
    504   EXPECT_TRUE(db.InsertNamespace(&record));
    505 
    506   fallbacks.clear();
    507   EXPECT_TRUE(db.FindNamespacesForCache(3, &intercepts, &fallbacks));
    508   EXPECT_EQ(2U, fallbacks.size());
    509   EXPECT_TRUE(fallbacks[0].namespace_.is_pattern);
    510   EXPECT_TRUE(fallbacks[1].namespace_.is_pattern);
    511 
    512   fallbacks.clear();
    513   EXPECT_TRUE(db.FindNamespacesForOrigin(kBarOrigin, &intercepts, &fallbacks));
    514   EXPECT_EQ(2U, fallbacks.size());
    515   EXPECT_TRUE(fallbacks[0].namespace_.is_pattern);
    516   EXPECT_TRUE(fallbacks[1].namespace_.is_pattern);
    517 
    518   ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    519 }
    520 
    521 TEST(AppCacheDatabaseTest, OnlineWhiteListRecords) {
    522   const base::FilePath kEmptyPath;
    523   AppCacheDatabase db(kEmptyPath);
    524   EXPECT_TRUE(db.LazyOpen(true));
    525 
    526   const GURL kFooNameSpace1("http://foo/namespace1");
    527   const GURL kFooNameSpace2("http://foo/namespace2");
    528   const GURL kBarNameSpace1("http://bar/namespace1");
    529 
    530   const AppCacheDatabase::OnlineWhiteListRecord kZeroRecord;
    531   AppCacheDatabase::OnlineWhiteListRecord record;
    532   std::vector<AppCacheDatabase::OnlineWhiteListRecord> records;
    533 
    534   // Behavior with an empty table
    535   EXPECT_TRUE(db.FindOnlineWhiteListForCache(1, &records));
    536   EXPECT_TRUE(records.empty());
    537   EXPECT_TRUE(db.DeleteOnlineWhiteListForCache(1));
    538 
    539   record.cache_id = 1;
    540   record.namespace_url = kFooNameSpace1;
    541   EXPECT_TRUE(db.InsertOnlineWhiteList(&record));
    542   record.namespace_url = kFooNameSpace2;
    543   record.is_pattern = true;
    544   EXPECT_TRUE(db.InsertOnlineWhiteList(&record));
    545   records.clear();
    546   EXPECT_TRUE(db.FindOnlineWhiteListForCache(1, &records));
    547   EXPECT_EQ(2U, records.size());
    548   EXPECT_EQ(1, records[0].cache_id);
    549   EXPECT_EQ(kFooNameSpace1, records[0].namespace_url);
    550   EXPECT_FALSE(records[0].is_pattern);
    551   EXPECT_EQ(1, records[1].cache_id);
    552   EXPECT_EQ(kFooNameSpace2, records[1].namespace_url);
    553   EXPECT_TRUE(records[1].is_pattern);
    554 
    555   record.cache_id = 2;
    556   record.namespace_url = kBarNameSpace1;
    557   EXPECT_TRUE(db.InsertOnlineWhiteList(&record));
    558   records.clear();
    559   EXPECT_TRUE(db.FindOnlineWhiteListForCache(2, &records));
    560   EXPECT_EQ(1U, records.size());
    561 
    562   EXPECT_TRUE(db.DeleteOnlineWhiteListForCache(1));
    563   records.clear();
    564   EXPECT_TRUE(db.FindOnlineWhiteListForCache(1, &records));
    565   EXPECT_TRUE(records.empty());
    566 }
    567 
    568 TEST(AppCacheDatabaseTest, DeletableResponseIds) {
    569   const base::FilePath kEmptyPath;
    570   AppCacheDatabase db(kEmptyPath);
    571   EXPECT_TRUE(db.LazyOpen(true));
    572 
    573   sql::ScopedErrorIgnorer ignore_errors;
    574   // TODO(shess): See EntryRecords test.
    575   ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
    576 
    577   std::vector<int64> ids;
    578 
    579   EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 100));
    580   EXPECT_TRUE(ids.empty());
    581   ids.push_back(0);
    582   EXPECT_TRUE(db.DeleteDeletableResponseIds(ids));
    583   EXPECT_TRUE(db.InsertDeletableResponseIds(ids));
    584 
    585   ids.clear();
    586   EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 100));
    587   EXPECT_EQ(1U, ids.size());
    588   EXPECT_EQ(0, ids[0]);
    589 
    590   int64 unused, deleteable_response_rowid;
    591   unused = deleteable_response_rowid = 0;
    592   EXPECT_TRUE(db.FindLastStorageIds(&unused, &unused, &unused,
    593                                     &deleteable_response_rowid));
    594   EXPECT_EQ(1, deleteable_response_rowid);
    595 
    596 
    597   // Expected to fail due to the duplicate id, 0 is already in the table.
    598   ids.clear();
    599   ids.push_back(0);
    600   ids.push_back(1);
    601   EXPECT_FALSE(db.InsertDeletableResponseIds(ids));
    602 
    603   ids.clear();
    604   for (int i = 1; i < 10; ++i)
    605     ids.push_back(i);
    606   EXPECT_TRUE(db.InsertDeletableResponseIds(ids));
    607   EXPECT_TRUE(db.FindLastStorageIds(&unused, &unused, &unused,
    608                                     &deleteable_response_rowid));
    609   EXPECT_EQ(10, deleteable_response_rowid);
    610 
    611   ids.clear();
    612   EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 100));
    613   EXPECT_EQ(10U, ids.size());
    614   for (int i = 0; i < 10; ++i)
    615     EXPECT_EQ(i, ids[i]);
    616 
    617   // Ensure the limit is respected.
    618   ids.clear();
    619   EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 5));
    620   EXPECT_EQ(5U, ids.size());
    621   for (int i = 0; i < static_cast<int>(ids.size()); ++i)
    622     EXPECT_EQ(i, ids[i]);
    623 
    624   // Ensure the max_rowid is respected (the first rowid is 1).
    625   ids.clear();
    626   EXPECT_TRUE(db.GetDeletableResponseIds(&ids, 5, 100));
    627   EXPECT_EQ(5U, ids.size());
    628   for (int i = 0; i < static_cast<int>(ids.size()); ++i)
    629     EXPECT_EQ(i, ids[i]);
    630 
    631   // Ensure that we can delete from the table.
    632   EXPECT_TRUE(db.DeleteDeletableResponseIds(ids));
    633   ids.clear();
    634   EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 100));
    635   EXPECT_EQ(5U, ids.size());
    636   for (int i = 0; i < static_cast<int>(ids.size()); ++i)
    637     EXPECT_EQ(i + 5, ids[i]);
    638 
    639   ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    640 }
    641 
    642 TEST(AppCacheDatabaseTest, OriginUsage) {
    643   const GURL kManifestUrl("http://blah/manifest");
    644   const GURL kManifestUrl2("http://blah/manifest2");
    645   const GURL kOrigin(kManifestUrl.GetOrigin());
    646   const GURL kOtherOriginManifestUrl("http://other/manifest");
    647   const GURL kOtherOrigin(kOtherOriginManifestUrl.GetOrigin());
    648 
    649   const base::FilePath kEmptyPath;
    650   AppCacheDatabase db(kEmptyPath);
    651   EXPECT_TRUE(db.LazyOpen(true));
    652 
    653   std::vector<AppCacheDatabase::CacheRecord> cache_records;
    654   EXPECT_EQ(0, db.GetOriginUsage(kOrigin));
    655   EXPECT_TRUE(db.FindCachesForOrigin(kOrigin, &cache_records));
    656   EXPECT_TRUE(cache_records.empty());
    657 
    658   AppCacheDatabase::GroupRecord group_record;
    659   group_record.group_id = 1;
    660   group_record.manifest_url = kManifestUrl;
    661   group_record.origin = kOrigin;
    662   EXPECT_TRUE(db.InsertGroup(&group_record));
    663   AppCacheDatabase::CacheRecord cache_record;
    664   cache_record.cache_id = 1;
    665   cache_record.group_id = 1;
    666   cache_record.online_wildcard = true;
    667   cache_record.update_time = kZeroTime;
    668   cache_record.cache_size = 100;
    669   EXPECT_TRUE(db.InsertCache(&cache_record));
    670 
    671   EXPECT_EQ(100, db.GetOriginUsage(kOrigin));
    672 
    673   group_record.group_id = 2;
    674   group_record.manifest_url = kManifestUrl2;
    675   group_record.origin = kOrigin;
    676   EXPECT_TRUE(db.InsertGroup(&group_record));
    677   cache_record.cache_id = 2;
    678   cache_record.group_id = 2;
    679   cache_record.online_wildcard = true;
    680   cache_record.update_time = kZeroTime;
    681   cache_record.cache_size = 1000;
    682   EXPECT_TRUE(db.InsertCache(&cache_record));
    683 
    684   EXPECT_EQ(1100, db.GetOriginUsage(kOrigin));
    685 
    686   group_record.group_id = 3;
    687   group_record.manifest_url = kOtherOriginManifestUrl;
    688   group_record.origin = kOtherOrigin;
    689   EXPECT_TRUE(db.InsertGroup(&group_record));
    690   cache_record.cache_id = 3;
    691   cache_record.group_id = 3;
    692   cache_record.online_wildcard = true;
    693   cache_record.update_time = kZeroTime;
    694   cache_record.cache_size = 5000;
    695   EXPECT_TRUE(db.InsertCache(&cache_record));
    696 
    697   EXPECT_EQ(5000, db.GetOriginUsage(kOtherOrigin));
    698 
    699   EXPECT_TRUE(db.FindCachesForOrigin(kOrigin, &cache_records));
    700   EXPECT_EQ(2U, cache_records.size());
    701   cache_records.clear();
    702   EXPECT_TRUE(db.FindCachesForOrigin(kOtherOrigin, &cache_records));
    703   EXPECT_EQ(1U, cache_records.size());
    704 
    705   std::map<GURL, int64> usage_map;
    706   EXPECT_TRUE(db.GetAllOriginUsage(&usage_map));
    707   EXPECT_EQ(2U, usage_map.size());
    708   EXPECT_EQ(1100, usage_map[kOrigin]);
    709   EXPECT_EQ(5000, usage_map[kOtherOrigin]);
    710 }
    711 
    712 #if defined(APPCACHE_USE_SIMPLE_CACHE)
    713 // There is no such upgrade path in this case.
    714 #else
    715 TEST(AppCacheDatabaseTest, UpgradeSchema3to5) {
    716   // Real file on disk for this test.
    717   base::ScopedTempDir temp_dir;
    718   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
    719   const base::FilePath kDbFile = temp_dir.path().AppendASCII("upgrade3.db");
    720 
    721   const GURL kMockOrigin("http://mockorigin/");
    722   const char kNamespaceUrlFormat[] = "namespace%d";
    723   const char kTargetUrlFormat[] = "target%d";
    724   const int kNumNamespaces = 10;
    725 
    726   // Create a v3 schema based database containing some fallback records.
    727   {
    728     const int kVersion3 = 3;
    729     const char kGroupsTable[] = "Groups";
    730     const char kCachesTable[] = "Caches";
    731     const char kEntriesTable[] = "Entries";
    732     const char kFallbackNameSpacesTable[] = "FallbackNameSpaces";
    733     const char kOnlineWhiteListsTable[] = "OnlineWhiteLists";
    734     const char kDeletableResponseIdsTable[] = "DeletableResponseIds";
    735 
    736     const struct {
    737       const char* table_name;
    738       const char* columns;
    739     } kTables3[] = {
    740       { kGroupsTable,
    741         "(group_id INTEGER PRIMARY KEY,"
    742         " origin TEXT,"
    743         " manifest_url TEXT,"
    744         " creation_time INTEGER,"
    745         " last_access_time INTEGER)" },
    746 
    747       { kCachesTable,
    748         "(cache_id INTEGER PRIMARY KEY,"
    749         " group_id INTEGER,"
    750         " online_wildcard INTEGER CHECK(online_wildcard IN (0, 1)),"
    751         " update_time INTEGER,"
    752         " cache_size INTEGER)" },  // intentionally not normalized
    753 
    754       { kEntriesTable,
    755         "(cache_id INTEGER,"
    756         " url TEXT,"
    757         " flags INTEGER,"
    758         " response_id INTEGER,"
    759         " response_size INTEGER)" },
    760 
    761       { kFallbackNameSpacesTable,
    762         "(cache_id INTEGER,"
    763         " origin TEXT,"  // intentionally not normalized
    764         " namespace_url TEXT,"
    765         " fallback_entry_url TEXT)" },
    766 
    767       { kOnlineWhiteListsTable,
    768         "(cache_id INTEGER,"
    769         " namespace_url TEXT)" },
    770 
    771       { kDeletableResponseIdsTable,
    772         "(response_id INTEGER NOT NULL)" },
    773     };
    774 
    775     const struct {
    776       const char* index_name;
    777       const char* table_name;
    778       const char* columns;
    779       bool unique;
    780     } kIndexes3[] = {
    781       { "GroupsOriginIndex",
    782         kGroupsTable,
    783         "(origin)",
    784         false },
    785 
    786       { "GroupsManifestIndex",
    787         kGroupsTable,
    788         "(manifest_url)",
    789         true },
    790 
    791       { "CachesGroupIndex",
    792         kCachesTable,
    793         "(group_id)",
    794         false },
    795 
    796       { "EntriesCacheIndex",
    797         kEntriesTable,
    798         "(cache_id)",
    799         false },
    800 
    801       { "EntriesCacheAndUrlIndex",
    802         kEntriesTable,
    803         "(cache_id, url)",
    804         true },
    805 
    806       { "EntriesResponseIdIndex",
    807         kEntriesTable,
    808         "(response_id)",
    809         true },
    810 
    811       { "FallbackNameSpacesCacheIndex",
    812         kFallbackNameSpacesTable,
    813         "(cache_id)",
    814         false },
    815 
    816       { "FallbackNameSpacesOriginIndex",
    817         kFallbackNameSpacesTable,
    818         "(origin)",
    819         false },
    820 
    821       { "FallbackNameSpacesCacheAndUrlIndex",
    822         kFallbackNameSpacesTable,
    823         "(cache_id, namespace_url)",
    824         true },
    825 
    826       { "OnlineWhiteListCacheIndex",
    827         kOnlineWhiteListsTable,
    828         "(cache_id)",
    829         false },
    830 
    831       { "DeletableResponsesIdIndex",
    832         kDeletableResponseIdsTable,
    833         "(response_id)",
    834         true },
    835     };
    836 
    837     const int kTableCount3 = ARRAYSIZE_UNSAFE(kTables3);
    838     const int kIndexCount3 = ARRAYSIZE_UNSAFE(kIndexes3);
    839 
    840     sql::Connection connection;
    841     EXPECT_TRUE(connection.Open(kDbFile));
    842 
    843     sql::Transaction transaction(&connection);
    844     EXPECT_TRUE(transaction.Begin());
    845 
    846     sql::MetaTable meta_table;
    847     EXPECT_TRUE(meta_table.Init(&connection, kVersion3, kVersion3));
    848 
    849     for (int i = 0; i < kTableCount3; ++i) {
    850       std::string sql("CREATE TABLE ");
    851       sql += kTables3[i].table_name;
    852       sql += kTables3[i].columns;
    853       EXPECT_TRUE(connection.Execute(sql.c_str()));
    854     }
    855 
    856     for (int i = 0; i < kIndexCount3; ++i) {
    857       std::string sql;
    858       if (kIndexes3[i].unique)
    859         sql += "CREATE UNIQUE INDEX ";
    860       else
    861         sql += "CREATE INDEX ";
    862       sql += kIndexes3[i].index_name;
    863       sql += " ON ";
    864       sql += kIndexes3[i].table_name;
    865       sql += kIndexes3[i].columns;
    866       EXPECT_TRUE(connection.Execute(sql.c_str()));
    867     }
    868 
    869     const char* kSql =
    870         "INSERT INTO FallbackNameSpaces"
    871         "  (cache_id, origin, namespace_url, fallback_entry_url)"
    872         "  VALUES (?, ?, ?, ?)";
    873 
    874     sql::Statement statement;
    875     statement.Assign(connection.GetUniqueStatement(kSql));
    876     EXPECT_TRUE(statement.is_valid());
    877     for (int i = 0; i < kNumNamespaces; ++i) {
    878       GURL namespace_url(
    879           kMockOrigin.Resolve(base::StringPrintf(kNamespaceUrlFormat, i)));
    880       GURL target_url(
    881           kMockOrigin.Resolve(base::StringPrintf(kTargetUrlFormat, i)));
    882       statement.BindInt64(0, i);
    883       statement.BindString(1, kMockOrigin.spec().c_str());
    884       statement.BindString(2, namespace_url.spec().c_str());
    885       statement.BindString(3, target_url.spec().c_str());
    886       ASSERT_TRUE(statement.Run());
    887       statement.Reset(true);
    888     }
    889 
    890     EXPECT_TRUE(transaction.Commit());
    891   }
    892 
    893   // Open that database and verify that it got updated.
    894   AppCacheDatabase db(kDbFile);
    895   EXPECT_TRUE(db.LazyOpen(true));
    896 
    897   EXPECT_FALSE(db.db_->DoesTableExist("FallbackNameSpaces"));
    898   EXPECT_FALSE(db.db_->DoesIndexExist("FallbackNamesSpacesCacheIndex"));
    899   EXPECT_FALSE(db.db_->DoesIndexExist("FallbackNameSpacesOriginIndex"));
    900   EXPECT_FALSE(db.db_->DoesIndexExist("FallbackNameSpacesCacheAndUrlIndex"));
    901 
    902   EXPECT_TRUE(db.db_->DoesTableExist("Namespaces"));
    903   EXPECT_TRUE(db.db_->DoesIndexExist("NamespacesCacheIndex"));
    904   EXPECT_TRUE(db.db_->DoesIndexExist("NamespacesOriginIndex"));
    905   EXPECT_TRUE(db.db_->DoesIndexExist("NamespacesCacheAndUrlIndex"));
    906   EXPECT_TRUE(db.db_->DoesColumnExist("Namespaces", "is_pattern"));
    907   EXPECT_TRUE(db.db_->DoesColumnExist("OnlineWhiteLists", "is_pattern"));
    908 
    909   EXPECT_EQ(5, db.meta_table_->GetVersionNumber());
    910   EXPECT_EQ(5, db.meta_table_->GetCompatibleVersionNumber());
    911 
    912   std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
    913   std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
    914   EXPECT_TRUE(db.FindNamespacesForOrigin(kMockOrigin, &intercepts,
    915                                          &fallbacks));
    916   EXPECT_TRUE(intercepts.empty());
    917   EXPECT_EQ(kNumNamespaces, static_cast<int>(fallbacks.size()));
    918 
    919   for (int i = 0; i < kNumNamespaces; ++i) {
    920     GURL expected_namespace_url(
    921         kMockOrigin.Resolve(base::StringPrintf(kNamespaceUrlFormat, i)));
    922     GURL expected_target_url(
    923         kMockOrigin.Resolve(base::StringPrintf(kTargetUrlFormat, i)));
    924 
    925     EXPECT_EQ(i, fallbacks[i].cache_id);
    926     EXPECT_EQ(FALLBACK_NAMESPACE, fallbacks[i].namespace_.type);
    927     EXPECT_EQ(kMockOrigin, fallbacks[i].origin);
    928     EXPECT_EQ(expected_namespace_url, fallbacks[i].namespace_.namespace_url);
    929     EXPECT_EQ(expected_target_url, fallbacks[i].namespace_.target_url);
    930     EXPECT_FALSE(fallbacks[i].namespace_.is_pattern);
    931   }
    932 }
    933 #endif  // !APPCACHE_USE_SIMPLE_CACHE
    934 
    935 #if defined(APPCACHE_USE_SIMPLE_CACHE)
    936 // There is no such upgrade path in this case.
    937 #else
    938 TEST(AppCacheDatabaseTest, UpgradeSchema4to5) {
    939   // Real file on disk for this test.
    940   base::ScopedTempDir temp_dir;
    941   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
    942   const base::FilePath kDbFile = temp_dir.path().AppendASCII("upgrade4.db");
    943 
    944   const GURL kMockOrigin("http://mockorigin/");
    945   const char kNamespaceUrlFormat[] = "namespace%d";
    946   const char kWhitelistUrlFormat[] = "whitelist%d";
    947   const char kTargetUrlFormat[] = "target%d";
    948   const int kNumNamespaces = 10;
    949   const int kWhitelistCacheId = 1;
    950 
    951   // Create a v4 schema based database containing some fallback records.
    952   {
    953     const int kVersion4 = 4;
    954     const char kGroupsTable[] = "Groups";
    955     const char kCachesTable[] = "Caches";
    956     const char kEntriesTable[] = "Entries";
    957     const char kNamespacesTable[] = "Namespaces";
    958     const char kOnlineWhiteListsTable[] = "OnlineWhiteLists";
    959     const char kDeletableResponseIdsTable[] = "DeletableResponseIds";
    960 
    961     struct TableInfo {
    962       const char* table_name;
    963       const char* columns;
    964     };
    965 
    966     struct IndexInfo {
    967       const char* index_name;
    968       const char* table_name;
    969       const char* columns;
    970       bool unique;
    971     };
    972 
    973     const TableInfo kTables4[] = {
    974       { kGroupsTable,
    975         "(group_id INTEGER PRIMARY KEY,"
    976         " origin TEXT,"
    977         " manifest_url TEXT,"
    978         " creation_time INTEGER,"
    979         " last_access_time INTEGER)" },
    980 
    981       { kCachesTable,
    982         "(cache_id INTEGER PRIMARY KEY,"
    983         " group_id INTEGER,"
    984         " online_wildcard INTEGER CHECK(online_wildcard IN (0, 1)),"
    985         " update_time INTEGER,"
    986         " cache_size INTEGER)" },  // intentionally not normalized
    987 
    988       { kEntriesTable,
    989         "(cache_id INTEGER,"
    990         " url TEXT,"
    991         " flags INTEGER,"
    992         " response_id INTEGER,"
    993         " response_size INTEGER)" },
    994 
    995       { kNamespacesTable,
    996         "(cache_id INTEGER,"
    997         " origin TEXT,"  // intentionally not normalized
    998         " type INTEGER,"
    999         " namespace_url TEXT,"
   1000         " target_url TEXT)" },
   1001 
   1002       { kOnlineWhiteListsTable,
   1003         "(cache_id INTEGER,"
   1004         " namespace_url TEXT)" },
   1005 
   1006       { kDeletableResponseIdsTable,
   1007         "(response_id INTEGER NOT NULL)" },
   1008     };
   1009 
   1010     const IndexInfo kIndexes4[] = {
   1011       { "GroupsOriginIndex",
   1012         kGroupsTable,
   1013         "(origin)",
   1014         false },
   1015 
   1016       { "GroupsManifestIndex",
   1017         kGroupsTable,
   1018         "(manifest_url)",
   1019         true },
   1020 
   1021       { "CachesGroupIndex",
   1022         kCachesTable,
   1023         "(group_id)",
   1024         false },
   1025 
   1026       { "EntriesCacheIndex",
   1027         kEntriesTable,
   1028         "(cache_id)",
   1029         false },
   1030 
   1031       { "EntriesCacheAndUrlIndex",
   1032         kEntriesTable,
   1033         "(cache_id, url)",
   1034         true },
   1035 
   1036       { "EntriesResponseIdIndex",
   1037         kEntriesTable,
   1038         "(response_id)",
   1039         true },
   1040 
   1041       { "NamespacesCacheIndex",
   1042         kNamespacesTable,
   1043         "(cache_id)",
   1044         false },
   1045 
   1046       { "NamespacesOriginIndex",
   1047         kNamespacesTable,
   1048         "(origin)",
   1049         false },
   1050 
   1051       { "NamespacesCacheAndUrlIndex",
   1052         kNamespacesTable,
   1053         "(cache_id, namespace_url)",
   1054         true },
   1055 
   1056       { "OnlineWhiteListCacheIndex",
   1057         kOnlineWhiteListsTable,
   1058         "(cache_id)",
   1059         false },
   1060 
   1061       { "DeletableResponsesIdIndex",
   1062         kDeletableResponseIdsTable,
   1063         "(response_id)",
   1064         true },
   1065     };
   1066 
   1067     const int kTableCount4 = ARRAYSIZE_UNSAFE(kTables4);
   1068     const int kIndexCount4 = ARRAYSIZE_UNSAFE(kIndexes4);
   1069 
   1070     sql::Connection connection;
   1071     EXPECT_TRUE(connection.Open(kDbFile));
   1072 
   1073     sql::Transaction transaction(&connection);
   1074     EXPECT_TRUE(transaction.Begin());
   1075 
   1076     sql::MetaTable meta_table;
   1077     EXPECT_TRUE(meta_table.Init(&connection, kVersion4, kVersion4));
   1078 
   1079     for (int i = 0; i < kTableCount4; ++i) {
   1080       std::string sql("CREATE TABLE ");
   1081       sql += kTables4[i].table_name;
   1082       sql += kTables4[i].columns;
   1083       EXPECT_TRUE(connection.Execute(sql.c_str()));
   1084     }
   1085 
   1086     for (int i = 0; i < kIndexCount4; ++i) {
   1087       std::string sql;
   1088       if (kIndexes4[i].unique)
   1089         sql += "CREATE UNIQUE INDEX ";
   1090       else
   1091         sql += "CREATE INDEX ";
   1092       sql += kIndexes4[i].index_name;
   1093       sql += " ON ";
   1094       sql += kIndexes4[i].table_name;
   1095       sql += kIndexes4[i].columns;
   1096       EXPECT_TRUE(connection.Execute(sql.c_str()));
   1097     }
   1098 
   1099     const char* kNamespacesSql =
   1100         "INSERT INTO Namespaces"
   1101         "  (cache_id, origin, type, namespace_url, target_url)"
   1102         "  VALUES (?, ?, ?, ?, ?)";
   1103     sql::Statement statement;
   1104     statement.Assign(connection.GetUniqueStatement(kNamespacesSql));
   1105     EXPECT_TRUE(statement.is_valid());
   1106     for (int i = 0; i < kNumNamespaces; ++i) {
   1107       GURL namespace_url(
   1108           kMockOrigin.Resolve(base::StringPrintf(kNamespaceUrlFormat, i)));
   1109       GURL target_url(
   1110           kMockOrigin.Resolve(base::StringPrintf(kTargetUrlFormat, i)));
   1111       statement.BindInt64(0, i);
   1112       statement.BindString(1, kMockOrigin.spec().c_str());
   1113       statement.BindInt(2, FALLBACK_NAMESPACE);
   1114       statement.BindString(3, namespace_url.spec().c_str());
   1115       statement.BindString(4, target_url.spec().c_str());
   1116       ASSERT_TRUE(statement.Run());
   1117       statement.Reset(true);
   1118     }
   1119 
   1120     const char* kWhitelistsSql =
   1121         "INSERT INTO OnlineWhiteLists"
   1122         "  (cache_id, namespace_url)"
   1123         "  VALUES (?, ?)";
   1124     statement.Assign(connection.GetUniqueStatement(kWhitelistsSql));
   1125     EXPECT_TRUE(statement.is_valid());
   1126     for (int i = 0; i < kNumNamespaces; ++i) {
   1127       GURL namespace_url(
   1128           kMockOrigin.Resolve(base::StringPrintf(kWhitelistUrlFormat, i)));
   1129       statement.BindInt64(0, kWhitelistCacheId);
   1130       statement.BindString(1, namespace_url.spec().c_str());
   1131       ASSERT_TRUE(statement.Run());
   1132       statement.Reset(true);
   1133     }
   1134 
   1135     EXPECT_TRUE(transaction.Commit());
   1136   }
   1137 
   1138   // Open that database and verify that it got upgraded to v5.
   1139   AppCacheDatabase db(kDbFile);
   1140   EXPECT_TRUE(db.LazyOpen(true));
   1141   EXPECT_TRUE(db.db_->DoesColumnExist("Namespaces", "is_pattern"));
   1142   EXPECT_TRUE(db.db_->DoesColumnExist("OnlineWhiteLists", "is_pattern"));
   1143   EXPECT_EQ(5, db.meta_table_->GetVersionNumber());
   1144   EXPECT_EQ(5, db.meta_table_->GetCompatibleVersionNumber());
   1145 
   1146   std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
   1147   std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
   1148   EXPECT_TRUE(db.FindNamespacesForOrigin(kMockOrigin, &intercepts,
   1149                                          &fallbacks));
   1150   EXPECT_TRUE(intercepts.empty());
   1151   EXPECT_EQ(kNumNamespaces, static_cast<int>(fallbacks.size()));
   1152 
   1153   std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists;
   1154   EXPECT_TRUE(db.FindOnlineWhiteListForCache(kWhitelistCacheId, &whitelists));
   1155   EXPECT_EQ(kNumNamespaces, static_cast<int>(whitelists.size()));
   1156 
   1157   for (int i = 0; i < kNumNamespaces; ++i) {
   1158     GURL expected_namespace_url(
   1159         kMockOrigin.Resolve(base::StringPrintf(kNamespaceUrlFormat, i)));
   1160     GURL expected_target_url(
   1161         kMockOrigin.Resolve(base::StringPrintf(kTargetUrlFormat, i)));
   1162     GURL expected_whitelist_url(
   1163         kMockOrigin.Resolve(base::StringPrintf(kWhitelistUrlFormat, i)));
   1164 
   1165     EXPECT_EQ(i, fallbacks[i].cache_id);
   1166     EXPECT_EQ(FALLBACK_NAMESPACE, fallbacks[i].namespace_.type);
   1167     EXPECT_EQ(kMockOrigin, fallbacks[i].origin);
   1168     EXPECT_EQ(expected_namespace_url, fallbacks[i].namespace_.namespace_url);
   1169     EXPECT_EQ(expected_target_url, fallbacks[i].namespace_.target_url);
   1170     EXPECT_FALSE(fallbacks[i].namespace_.is_pattern);
   1171     EXPECT_EQ(expected_whitelist_url, whitelists[i].namespace_url);
   1172     EXPECT_FALSE(whitelists[i].is_pattern);
   1173   }
   1174 }
   1175 #endif  // !APPCACHE_USE_SIMPLE_CACHE
   1176 
   1177 }  // namespace appcache
   1178