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