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