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