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