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