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