[Offline pages] Moving browser code to android path
[chromium-blink-merge.git] / content / browser / appcache / appcache_database_unittest.cc
blobf786df6c560bc43a0cbe6096a2bd64ffb56965b5
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.
5 #include "base/bind.h"
6 #include "base/files/file_util.h"
7 #include "base/files/scoped_temp_dir.h"
8 #include "base/strings/stringprintf.h"
9 #include "content/browser/appcache/appcache_database.h"
10 #include "content/browser/appcache/appcache_entry.h"
11 #include "sql/connection.h"
12 #include "sql/meta_table.h"
13 #include "sql/statement.h"
14 #include "sql/test/scoped_error_ignorer.h"
15 #include "sql/test/test_helpers.h"
16 #include "sql/transaction.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "third_party/sqlite/sqlite3.h"
20 namespace {
22 const base::Time kZeroTime;
24 } // namespace
26 namespace content {
28 class AppCacheDatabaseTest {};
30 TEST(AppCacheDatabaseTest, LazyOpen) {
31 // Use an empty file path to use an in-memory sqlite database.
32 const base::FilePath kEmptyPath;
33 AppCacheDatabase db(kEmptyPath);
35 EXPECT_FALSE(db.LazyOpen(false));
36 EXPECT_TRUE(db.LazyOpen(true));
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);
47 std::set<GURL> origins;
48 EXPECT_TRUE(db.FindOriginsWithGroups(&origins));
49 EXPECT_TRUE(origins.empty());
52 TEST(AppCacheDatabaseTest, ReCreate) {
53 // Real files on disk for this test.
54 base::ScopedTempDir temp_dir;
55 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
56 const base::FilePath kDbFile = temp_dir.path().AppendASCII("appcache.db");
57 const base::FilePath kNestedDir = temp_dir.path().AppendASCII("nested");
58 const base::FilePath kOtherFile = kNestedDir.AppendASCII("other_file");
59 EXPECT_TRUE(base::CreateDirectory(kNestedDir));
60 EXPECT_EQ(3, base::WriteFile(kOtherFile, "foo", 3));
62 AppCacheDatabase db(kDbFile);
63 EXPECT_FALSE(db.LazyOpen(false));
64 EXPECT_TRUE(db.LazyOpen(true));
66 EXPECT_TRUE(base::PathExists(kDbFile));
67 EXPECT_TRUE(base::DirectoryExists(kNestedDir));
68 EXPECT_TRUE(base::PathExists(kOtherFile));
70 EXPECT_TRUE(db.DeleteExistingAndCreateNewDatabase());
72 EXPECT_TRUE(base::PathExists(kDbFile));
73 EXPECT_FALSE(base::DirectoryExists(kNestedDir));
74 EXPECT_FALSE(base::PathExists(kOtherFile));
77 #ifdef NDEBUG
78 // Only run in release builds because sql::Connection and familiy
79 // crank up DLOG(FATAL)'ness and this test presents it with
80 // intentionally bad data which causes debug builds to exit instead
81 // of run to completion. In release builds, errors the are delivered
82 // to the consumer so we can test the error handling of the consumer.
83 // TODO: crbug/328576
84 TEST(AppCacheDatabaseTest, QuickIntegrityCheck) {
85 // Real files on disk for this test too, a corrupt database file.
86 base::ScopedTempDir temp_dir;
87 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
88 base::FilePath mock_dir = temp_dir.path().AppendASCII("mock");
89 ASSERT_TRUE(base::CreateDirectory(mock_dir));
91 const base::FilePath kDbFile = mock_dir.AppendASCII("appcache.db");
92 const base::FilePath kOtherFile = mock_dir.AppendASCII("other_file");
93 EXPECT_EQ(3, base::WriteFile(kOtherFile, "foo", 3));
95 // First create a valid db file.
97 AppCacheDatabase db(kDbFile);
98 EXPECT_TRUE(db.LazyOpen(true));
99 EXPECT_TRUE(base::PathExists(kOtherFile));
100 EXPECT_TRUE(base::PathExists(kDbFile));
103 // Break it.
104 ASSERT_TRUE(sql::test::CorruptSizeInHeader(kDbFile));
106 // Reopening will notice the corruption and delete/recreate the directory.
108 sql::ScopedErrorIgnorer ignore_errors;
109 ignore_errors.IgnoreError(SQLITE_CORRUPT);
110 AppCacheDatabase db(kDbFile);
111 EXPECT_TRUE(db.LazyOpen(true));
112 EXPECT_FALSE(base::PathExists(kOtherFile));
113 EXPECT_TRUE(base::PathExists(kDbFile));
114 EXPECT_TRUE(ignore_errors.CheckIgnoredErrors());
117 #endif // NDEBUG
119 TEST(AppCacheDatabaseTest, WasCorrutionDetected) {
120 // Real files on disk for this test too, a corrupt database file.
121 base::ScopedTempDir temp_dir;
122 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
123 const base::FilePath kDbFile = temp_dir.path().AppendASCII("appcache.db");
125 // First create a valid db file.
126 AppCacheDatabase db(kDbFile);
127 EXPECT_TRUE(db.LazyOpen(true));
128 EXPECT_TRUE(base::PathExists(kDbFile));
129 EXPECT_FALSE(db.was_corruption_detected());
131 // Break it.
132 ASSERT_TRUE(sql::test::CorruptSizeInHeader(kDbFile));
134 // See the the corruption is detected and reported.
136 sql::ScopedErrorIgnorer ignore_errors;
137 ignore_errors.IgnoreError(SQLITE_CORRUPT);
138 std::map<GURL, int64> usage_map;
139 EXPECT_FALSE(db.GetAllOriginUsage(&usage_map));
140 EXPECT_TRUE(db.was_corruption_detected());
141 EXPECT_TRUE(base::PathExists(kDbFile));
142 EXPECT_TRUE(ignore_errors.CheckIgnoredErrors());
146 TEST(AppCacheDatabaseTest, ExperimentalFlags) {
147 const char kExperimentFlagsKey[] = "ExperimentFlags";
148 std::string kInjectedFlags("exp1,exp2");
150 // Real files on disk for this test.
151 base::ScopedTempDir temp_dir;
152 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
153 const base::FilePath kDbFile = temp_dir.path().AppendASCII("appcache.db");
154 const base::FilePath kOtherFile = temp_dir.path().AppendASCII("other_file");
155 EXPECT_EQ(3, base::WriteFile(kOtherFile, "foo", 3));
156 EXPECT_TRUE(base::PathExists(kOtherFile));
158 // Inject a non empty flags value, and verify it got there.
160 AppCacheDatabase db(kDbFile);
161 EXPECT_TRUE(db.LazyOpen(true));
162 EXPECT_TRUE(db.meta_table_->SetValue(kExperimentFlagsKey, kInjectedFlags));
163 std::string flags;
164 EXPECT_TRUE(db.meta_table_->GetValue(kExperimentFlagsKey, &flags));
165 EXPECT_EQ(kInjectedFlags, flags);
168 // If flags don't match the expected value, empty string by default,
169 // the database should be recreated and other files should be cleared out.
171 AppCacheDatabase db(kDbFile);
172 EXPECT_TRUE(db.LazyOpen(false));
173 std::string flags;
174 EXPECT_TRUE(db.meta_table_->GetValue(kExperimentFlagsKey, &flags));
175 EXPECT_TRUE(flags.empty());
176 EXPECT_FALSE(base::PathExists(kOtherFile));
180 TEST(AppCacheDatabaseTest, EntryRecords) {
181 const base::FilePath kEmptyPath;
182 AppCacheDatabase db(kEmptyPath);
183 EXPECT_TRUE(db.LazyOpen(true));
185 sql::ScopedErrorIgnorer ignore_errors;
186 // TODO(shess): Suppressing SQLITE_CONSTRAINT because the code
187 // expects that and handles the resulting error. Consider revising
188 // the code to use INSERT OR IGNORE (which would not throw
189 // SQLITE_CONSTRAINT) and then check ChangeCount() to see if any
190 // changes were made.
191 ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
193 AppCacheDatabase::EntryRecord entry;
195 entry.cache_id = 1;
196 entry.url = GURL("http://blah/1");
197 entry.flags = AppCacheEntry::MASTER;
198 entry.response_id = 1;
199 entry.response_size = 100;
200 EXPECT_TRUE(db.InsertEntry(&entry));
201 EXPECT_FALSE(db.InsertEntry(&entry));
203 entry.cache_id = 2;
204 entry.url = GURL("http://blah/2");
205 entry.flags = AppCacheEntry::EXPLICIT;
206 entry.response_id = 2;
207 entry.response_size = 200;
208 EXPECT_TRUE(db.InsertEntry(&entry));
210 entry.cache_id = 2;
211 entry.url = GURL("http://blah/3");
212 entry.flags = AppCacheEntry::MANIFEST;
213 entry.response_id = 3;
214 entry.response_size = 300;
215 EXPECT_TRUE(db.InsertEntry(&entry));
217 std::vector<AppCacheDatabase::EntryRecord> found;
219 EXPECT_TRUE(db.FindEntriesForCache(1, &found));
220 EXPECT_EQ(1U, found.size());
221 EXPECT_EQ(1, found[0].cache_id);
222 EXPECT_EQ(GURL("http://blah/1"), found[0].url);
223 EXPECT_EQ(AppCacheEntry::MASTER, found[0].flags);
224 EXPECT_EQ(1, found[0].response_id);
225 EXPECT_EQ(100, found[0].response_size);
226 found.clear();
228 EXPECT_TRUE(db.AddEntryFlags(GURL("http://blah/1"), 1,
229 AppCacheEntry::FOREIGN));
230 EXPECT_TRUE(db.FindEntriesForCache(1, &found));
231 EXPECT_EQ(1U, found.size());
232 EXPECT_EQ(AppCacheEntry::MASTER | AppCacheEntry::FOREIGN, found[0].flags);
233 found.clear();
235 EXPECT_TRUE(db.FindEntriesForCache(2, &found));
236 EXPECT_EQ(2U, found.size());
237 EXPECT_EQ(2, found[0].cache_id);
238 EXPECT_EQ(GURL("http://blah/2"), found[0].url);
239 EXPECT_EQ(AppCacheEntry::EXPLICIT, found[0].flags);
240 EXPECT_EQ(2, found[0].response_id);
241 EXPECT_EQ(200, found[0].response_size);
242 EXPECT_EQ(2, found[1].cache_id);
243 EXPECT_EQ(GURL("http://blah/3"), found[1].url);
244 EXPECT_EQ(AppCacheEntry::MANIFEST, found[1].flags);
245 EXPECT_EQ(3, found[1].response_id);
246 EXPECT_EQ(300, found[1].response_size);
247 found.clear();
249 EXPECT_TRUE(db.DeleteEntriesForCache(2));
250 EXPECT_TRUE(db.FindEntriesForCache(2, &found));
251 EXPECT_TRUE(found.empty());
252 found.clear();
254 EXPECT_TRUE(db.DeleteEntriesForCache(1));
255 EXPECT_FALSE(db.AddEntryFlags(GURL("http://blah/1"), 1,
256 AppCacheEntry::FOREIGN));
258 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
261 TEST(AppCacheDatabaseTest, CacheRecords) {
262 const base::FilePath kEmptyPath;
263 AppCacheDatabase db(kEmptyPath);
264 EXPECT_TRUE(db.LazyOpen(true));
266 sql::ScopedErrorIgnorer ignore_errors;
267 // TODO(shess): See EntryRecords test.
268 ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
270 const AppCacheDatabase::CacheRecord kZeroRecord;
271 AppCacheDatabase::CacheRecord record;
272 EXPECT_FALSE(db.FindCache(1, &record));
274 record.cache_id = 1;
275 record.group_id = 1;
276 record.online_wildcard = true;
277 record.update_time = kZeroTime;
278 record.cache_size = 100;
279 EXPECT_TRUE(db.InsertCache(&record));
280 EXPECT_FALSE(db.InsertCache(&record));
282 record = kZeroRecord;
283 EXPECT_TRUE(db.FindCache(1, &record));
284 EXPECT_EQ(1, record.cache_id);
285 EXPECT_EQ(1, record.group_id);
286 EXPECT_TRUE(record.online_wildcard);
287 EXPECT_TRUE(kZeroTime == record.update_time);
288 EXPECT_EQ(100, record.cache_size);
290 record = kZeroRecord;
291 EXPECT_TRUE(db.FindCacheForGroup(1, &record));
292 EXPECT_EQ(1, record.cache_id);
293 EXPECT_EQ(1, record.group_id);
294 EXPECT_TRUE(record.online_wildcard);
295 EXPECT_TRUE(kZeroTime == record.update_time);
296 EXPECT_EQ(100, record.cache_size);
298 EXPECT_TRUE(db.DeleteCache(1));
299 EXPECT_FALSE(db.FindCache(1, &record));
300 EXPECT_FALSE(db.FindCacheForGroup(1, &record));
302 EXPECT_TRUE(db.DeleteCache(1));
304 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
307 TEST(AppCacheDatabaseTest, GroupRecords) {
308 const base::FilePath kEmptyPath;
309 AppCacheDatabase db(kEmptyPath);
310 EXPECT_TRUE(db.LazyOpen(true));
312 sql::ScopedErrorIgnorer ignore_errors;
313 // TODO(shess): See EntryRecords test.
314 ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
316 const GURL kManifestUrl("http://blah/manifest");
317 const GURL kOrigin(kManifestUrl.GetOrigin());
318 const base::Time kLastAccessTime = base::Time::Now();
319 const base::Time kCreationTime =
320 kLastAccessTime - base::TimeDelta::FromDays(7);
322 const AppCacheDatabase::GroupRecord kZeroRecord;
323 AppCacheDatabase::GroupRecord record;
324 std::vector<AppCacheDatabase::GroupRecord> records;
326 // Behavior with an empty table
327 EXPECT_FALSE(db.FindGroup(1, &record));
328 EXPECT_FALSE(db.FindGroupForManifestUrl(kManifestUrl, &record));
329 EXPECT_TRUE(db.DeleteGroup(1));
330 EXPECT_TRUE(db.FindGroupsForOrigin(kOrigin, &records));
331 EXPECT_TRUE(records.empty());
332 EXPECT_FALSE(db.FindGroupForCache(1, &record));
334 record.group_id = 1;
335 record.manifest_url = kManifestUrl;
336 record.origin = kOrigin;
337 record.last_access_time = kLastAccessTime;
338 record.creation_time = kCreationTime;
339 EXPECT_TRUE(db.InsertGroup(&record));
340 EXPECT_FALSE(db.InsertGroup(&record));
342 record.group_id = 2;
343 EXPECT_FALSE(db.InsertGroup(&record));
345 record = kZeroRecord;
346 EXPECT_TRUE(db.FindGroup(1, &record));
347 EXPECT_EQ(1, record.group_id);
348 EXPECT_EQ(kManifestUrl, record.manifest_url);
349 EXPECT_EQ(kOrigin, record.origin);
350 EXPECT_EQ(kCreationTime.ToInternalValue(),
351 record.creation_time.ToInternalValue());
352 EXPECT_EQ(kLastAccessTime.ToInternalValue(),
353 record.last_access_time.ToInternalValue());
355 record = kZeroRecord;
356 EXPECT_TRUE(db.FindGroupForManifestUrl(kManifestUrl, &record));
357 EXPECT_EQ(1, record.group_id);
358 EXPECT_EQ(kManifestUrl, record.manifest_url);
359 EXPECT_EQ(kOrigin, record.origin);
360 EXPECT_EQ(kCreationTime.ToInternalValue(),
361 record.creation_time.ToInternalValue());
362 EXPECT_EQ(kLastAccessTime.ToInternalValue(),
363 record.last_access_time.ToInternalValue());
365 record.group_id = 2;
366 record.manifest_url = kOrigin;
367 record.origin = kOrigin;
368 record.last_access_time = kLastAccessTime;
369 record.creation_time = kCreationTime;
370 EXPECT_TRUE(db.InsertGroup(&record));
372 record = kZeroRecord;
373 EXPECT_TRUE(db.FindGroupForManifestUrl(kOrigin, &record));
374 EXPECT_EQ(2, record.group_id);
375 EXPECT_EQ(kOrigin, record.manifest_url);
376 EXPECT_EQ(kOrigin, record.origin);
377 EXPECT_EQ(kCreationTime.ToInternalValue(),
378 record.creation_time.ToInternalValue());
379 EXPECT_EQ(kLastAccessTime.ToInternalValue(),
380 record.last_access_time.ToInternalValue());
382 EXPECT_TRUE(db.FindGroupsForOrigin(kOrigin, &records));
383 EXPECT_EQ(2U, records.size());
384 EXPECT_EQ(1, records[0].group_id);
385 EXPECT_EQ(kManifestUrl, records[0].manifest_url);
386 EXPECT_EQ(kOrigin, records[0].origin);
387 EXPECT_EQ(2, records[1].group_id);
388 EXPECT_EQ(kOrigin, records[1].manifest_url);
389 EXPECT_EQ(kOrigin, records[1].origin);
391 EXPECT_TRUE(db.DeleteGroup(1));
393 records.clear();
394 EXPECT_TRUE(db.FindGroupsForOrigin(kOrigin, &records));
395 EXPECT_EQ(1U, records.size());
396 EXPECT_EQ(2, records[0].group_id);
397 EXPECT_EQ(kOrigin, records[0].manifest_url);
398 EXPECT_EQ(kOrigin, records[0].origin);
399 EXPECT_EQ(kCreationTime.ToInternalValue(),
400 record.creation_time.ToInternalValue());
401 EXPECT_EQ(kLastAccessTime.ToInternalValue(),
402 record.last_access_time.ToInternalValue());
404 std::set<GURL> origins;
405 EXPECT_TRUE(db.FindOriginsWithGroups(&origins));
406 EXPECT_EQ(1U, origins.size());
407 EXPECT_EQ(kOrigin, *(origins.begin()));
409 const GURL kManifest2("http://blah2/manifest");
410 const GURL kOrigin2(kManifest2.GetOrigin());
411 record.group_id = 1;
412 record.manifest_url = kManifest2;
413 record.origin = kOrigin2;
414 EXPECT_TRUE(db.InsertGroup(&record));
416 origins.clear();
417 EXPECT_TRUE(db.FindOriginsWithGroups(&origins));
418 EXPECT_EQ(2U, origins.size());
419 EXPECT_TRUE(origins.end() != origins.find(kOrigin));
420 EXPECT_TRUE(origins.end() != origins.find(kOrigin2));
422 AppCacheDatabase::CacheRecord cache_record;
423 cache_record.cache_id = 1;
424 cache_record.group_id = 1;
425 cache_record.online_wildcard = true;
426 cache_record.update_time = kZeroTime;
427 EXPECT_TRUE(db.InsertCache(&cache_record));
429 record = kZeroRecord;
430 EXPECT_TRUE(db.FindGroupForCache(1, &record));
431 EXPECT_EQ(1, record.group_id);
432 EXPECT_EQ(kManifest2, record.manifest_url);
433 EXPECT_EQ(kOrigin2, record.origin);
435 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
438 TEST(AppCacheDatabaseTest, GroupAccessAndEvictionTimes) {
439 const base::FilePath kEmptyPath;
440 AppCacheDatabase db(kEmptyPath);
441 EXPECT_TRUE(db.LazyOpen(true));
443 const GURL kManifestUrl("http://blah/manifest");
444 const GURL kOrigin(kManifestUrl.GetOrigin());
445 const base::Time kDayOne =
446 base::Time() + base::TimeDelta::FromDays(1);
447 const base::Time kDayTwo = kDayOne + base::TimeDelta::FromDays(1);
449 // See that the methods behave as expected with an empty db.
450 // To accomodate lazy updating, for consistency, none of them fail
451 // given ids not found in the db.
452 EXPECT_TRUE(db.UpdateEvictionTimes(1, kDayOne, kDayTwo));
453 EXPECT_TRUE(db.UpdateLastAccessTime(1, kDayOne));
454 EXPECT_TRUE(db.CommitLazyLastAccessTimes());
455 EXPECT_TRUE(db.LazyUpdateLastAccessTime(1, kDayTwo));
456 EXPECT_TRUE(db.CommitLazyLastAccessTimes());
458 // Insert a group at DAY1
459 AppCacheDatabase::GroupRecord record;
460 record.group_id = 1;
461 record.manifest_url = kManifestUrl;
462 record.origin = kOrigin;
463 record.creation_time = kDayOne;
464 record.last_access_time = kDayOne;
465 record.last_full_update_check_time = kDayOne;
466 record.first_evictable_error_time = kDayOne;
467 EXPECT_TRUE(db.InsertGroup(&record));
469 // Verify the round trip.
470 record = AppCacheDatabase::GroupRecord();
471 EXPECT_TRUE(db.FindGroup(1, &record));
472 EXPECT_EQ(kDayOne, record.last_access_time);
473 EXPECT_EQ(kDayOne, record.last_full_update_check_time);
474 EXPECT_EQ(kDayOne, record.first_evictable_error_time);
476 // Update the times to DAY2 and verify.
477 EXPECT_TRUE(db.UpdateEvictionTimes(1, kDayTwo, kDayTwo));
478 EXPECT_TRUE(db.UpdateLastAccessTime(1, kDayTwo));
479 record = AppCacheDatabase::GroupRecord();
480 EXPECT_TRUE(db.FindGroup(1, &record));
481 EXPECT_EQ(kDayTwo, record.last_access_time);
482 EXPECT_EQ(kDayTwo, record.last_full_update_check_time);
483 EXPECT_EQ(kDayTwo, record.first_evictable_error_time);
485 // Lazy update back to DAY1 and verify its reflected without having committed.
486 EXPECT_TRUE(db.lazy_last_access_times_.empty());
487 EXPECT_TRUE(db.LazyUpdateLastAccessTime(1, kDayOne));
488 EXPECT_FALSE(db.lazy_last_access_times_.empty());
489 record = AppCacheDatabase::GroupRecord();
490 EXPECT_TRUE(db.FindGroup(1, &record));
491 EXPECT_EQ(kDayOne, record.last_access_time);
493 // Commit the lazy value and verify it sticks.
494 EXPECT_TRUE(db.CommitLazyLastAccessTimes());
495 EXPECT_TRUE(db.lazy_last_access_times_.empty());
496 record = AppCacheDatabase::GroupRecord();
497 EXPECT_TRUE(db.FindGroup(1, &record));
498 EXPECT_EQ(kDayOne, record.last_access_time);
500 // Verify a bad lazy group id doesn't fail to commit the good ones on DAY2.
501 EXPECT_TRUE(db.LazyUpdateLastAccessTime(1, kDayTwo));
502 EXPECT_TRUE(db.LazyUpdateLastAccessTime(2, kDayTwo));
503 EXPECT_EQ(2u, db.lazy_last_access_times_.size());
504 EXPECT_TRUE(db.CommitLazyLastAccessTimes());
505 EXPECT_TRUE(db.lazy_last_access_times_.empty());
506 record = AppCacheDatabase::GroupRecord();
507 EXPECT_TRUE(db.FindGroup(1, &record));
508 EXPECT_EQ(kDayTwo, record.last_access_time);
511 TEST(AppCacheDatabaseTest, NamespaceRecords) {
512 const base::FilePath kEmptyPath;
513 AppCacheDatabase db(kEmptyPath);
514 EXPECT_TRUE(db.LazyOpen(true));
516 sql::ScopedErrorIgnorer ignore_errors;
517 // TODO(shess): See EntryRecords test.
518 ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
520 const GURL kFooNameSpace1("http://foo/namespace1");
521 const GURL kFooNameSpace2("http://foo/namespace2");
522 const GURL kFooFallbackEntry("http://foo/entry");
523 const GURL kFooOrigin(kFooNameSpace1.GetOrigin());
524 const GURL kBarNameSpace1("http://bar/namespace1");
525 const GURL kBarNameSpace2("http://bar/namespace2");
526 const GURL kBarFallbackEntry("http://bar/entry");
527 const GURL kBarOrigin(kBarNameSpace1.GetOrigin());
529 const AppCacheDatabase::NamespaceRecord kZeroRecord;
530 AppCacheDatabase::NamespaceRecord record;
531 std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
532 std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
534 // Behavior with an empty table
535 EXPECT_TRUE(db.FindNamespacesForCache(1, &intercepts, &fallbacks));
536 EXPECT_TRUE(fallbacks.empty());
537 EXPECT_TRUE(db.FindNamespacesForOrigin(kFooOrigin, &intercepts, &fallbacks));
538 EXPECT_TRUE(fallbacks.empty());
539 EXPECT_TRUE(db.DeleteNamespacesForCache(1));
541 // Two records for two differenent caches in the Foo origin.
542 record.cache_id = 1;
543 record.origin = kFooOrigin;
544 record.namespace_.namespace_url = kFooNameSpace1;
545 record.namespace_.target_url = kFooFallbackEntry;
546 EXPECT_TRUE(db.InsertNamespace(&record));
547 EXPECT_FALSE(db.InsertNamespace(&record));
549 record.cache_id = 2;
550 record.origin = kFooOrigin;
551 record.namespace_.namespace_url = kFooNameSpace2;
552 record.namespace_.target_url = kFooFallbackEntry;
553 EXPECT_TRUE(db.InsertNamespace(&record));
555 fallbacks.clear();
556 EXPECT_TRUE(db.FindNamespacesForCache(1, &intercepts, &fallbacks));
557 EXPECT_EQ(1U, fallbacks.size());
558 EXPECT_EQ(1, fallbacks[0].cache_id);
559 EXPECT_EQ(kFooOrigin, fallbacks[0].origin);
560 EXPECT_EQ(kFooNameSpace1, fallbacks[0].namespace_.namespace_url);
561 EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url);
562 EXPECT_FALSE(fallbacks[0].namespace_.is_pattern);
564 fallbacks.clear();
565 EXPECT_TRUE(db.FindNamespacesForCache(2, &intercepts, &fallbacks));
566 EXPECT_EQ(1U, fallbacks.size());
567 EXPECT_EQ(2, fallbacks[0].cache_id);
568 EXPECT_EQ(kFooOrigin, fallbacks[0].origin);
569 EXPECT_EQ(kFooNameSpace2, fallbacks[0].namespace_.namespace_url);
570 EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url);
571 EXPECT_FALSE(fallbacks[0].namespace_.is_pattern);
573 fallbacks.clear();
574 EXPECT_TRUE(db.FindNamespacesForOrigin(kFooOrigin, &intercepts, &fallbacks));
575 EXPECT_EQ(2U, fallbacks.size());
576 EXPECT_EQ(1, fallbacks[0].cache_id);
577 EXPECT_EQ(kFooOrigin, fallbacks[0].origin);
578 EXPECT_EQ(kFooNameSpace1, fallbacks[0].namespace_.namespace_url);
579 EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url);
580 EXPECT_FALSE(fallbacks[0].namespace_.is_pattern);
581 EXPECT_EQ(2, fallbacks[1].cache_id);
582 EXPECT_EQ(kFooOrigin, fallbacks[1].origin);
583 EXPECT_EQ(kFooNameSpace2, fallbacks[1].namespace_.namespace_url);
584 EXPECT_EQ(kFooFallbackEntry, fallbacks[1].namespace_.target_url);
585 EXPECT_FALSE(fallbacks[1].namespace_.is_pattern);
587 EXPECT_TRUE(db.DeleteNamespacesForCache(1));
588 fallbacks.clear();
589 EXPECT_TRUE(db.FindNamespacesForOrigin(kFooOrigin, &intercepts, &fallbacks));
590 EXPECT_EQ(1U, fallbacks.size());
591 EXPECT_EQ(2, fallbacks[0].cache_id);
592 EXPECT_EQ(kFooOrigin, fallbacks[0].origin);
593 EXPECT_EQ(kFooNameSpace2, fallbacks[0].namespace_.namespace_url);
594 EXPECT_EQ(kFooFallbackEntry, fallbacks[0].namespace_.target_url);
595 EXPECT_FALSE(fallbacks[0].namespace_.is_pattern);
597 // Two more records for the same cache in the Bar origin.
598 record.cache_id = 3;
599 record.origin = kBarOrigin;
600 record.namespace_.namespace_url = kBarNameSpace1;
601 record.namespace_.target_url = kBarFallbackEntry;
602 record.namespace_.is_pattern = true;
603 EXPECT_TRUE(db.InsertNamespace(&record));
605 record.cache_id = 3;
606 record.origin = kBarOrigin;
607 record.namespace_.namespace_url = kBarNameSpace2;
608 record.namespace_.target_url = kBarFallbackEntry;
609 record.namespace_.is_pattern = true;
610 EXPECT_TRUE(db.InsertNamespace(&record));
612 fallbacks.clear();
613 EXPECT_TRUE(db.FindNamespacesForCache(3, &intercepts, &fallbacks));
614 EXPECT_EQ(2U, fallbacks.size());
615 EXPECT_TRUE(fallbacks[0].namespace_.is_pattern);
616 EXPECT_TRUE(fallbacks[1].namespace_.is_pattern);
618 fallbacks.clear();
619 EXPECT_TRUE(db.FindNamespacesForOrigin(kBarOrigin, &intercepts, &fallbacks));
620 EXPECT_EQ(2U, fallbacks.size());
621 EXPECT_TRUE(fallbacks[0].namespace_.is_pattern);
622 EXPECT_TRUE(fallbacks[1].namespace_.is_pattern);
624 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
627 TEST(AppCacheDatabaseTest, OnlineWhiteListRecords) {
628 const base::FilePath kEmptyPath;
629 AppCacheDatabase db(kEmptyPath);
630 EXPECT_TRUE(db.LazyOpen(true));
632 const GURL kFooNameSpace1("http://foo/namespace1");
633 const GURL kFooNameSpace2("http://foo/namespace2");
634 const GURL kBarNameSpace1("http://bar/namespace1");
636 const AppCacheDatabase::OnlineWhiteListRecord kZeroRecord;
637 AppCacheDatabase::OnlineWhiteListRecord record;
638 std::vector<AppCacheDatabase::OnlineWhiteListRecord> records;
640 // Behavior with an empty table
641 EXPECT_TRUE(db.FindOnlineWhiteListForCache(1, &records));
642 EXPECT_TRUE(records.empty());
643 EXPECT_TRUE(db.DeleteOnlineWhiteListForCache(1));
645 record.cache_id = 1;
646 record.namespace_url = kFooNameSpace1;
647 EXPECT_TRUE(db.InsertOnlineWhiteList(&record));
648 record.namespace_url = kFooNameSpace2;
649 record.is_pattern = true;
650 EXPECT_TRUE(db.InsertOnlineWhiteList(&record));
651 records.clear();
652 EXPECT_TRUE(db.FindOnlineWhiteListForCache(1, &records));
653 EXPECT_EQ(2U, records.size());
654 EXPECT_EQ(1, records[0].cache_id);
655 EXPECT_EQ(kFooNameSpace1, records[0].namespace_url);
656 EXPECT_FALSE(records[0].is_pattern);
657 EXPECT_EQ(1, records[1].cache_id);
658 EXPECT_EQ(kFooNameSpace2, records[1].namespace_url);
659 EXPECT_TRUE(records[1].is_pattern);
661 record.cache_id = 2;
662 record.namespace_url = kBarNameSpace1;
663 EXPECT_TRUE(db.InsertOnlineWhiteList(&record));
664 records.clear();
665 EXPECT_TRUE(db.FindOnlineWhiteListForCache(2, &records));
666 EXPECT_EQ(1U, records.size());
668 EXPECT_TRUE(db.DeleteOnlineWhiteListForCache(1));
669 records.clear();
670 EXPECT_TRUE(db.FindOnlineWhiteListForCache(1, &records));
671 EXPECT_TRUE(records.empty());
674 TEST(AppCacheDatabaseTest, DeletableResponseIds) {
675 const base::FilePath kEmptyPath;
676 AppCacheDatabase db(kEmptyPath);
677 EXPECT_TRUE(db.LazyOpen(true));
679 sql::ScopedErrorIgnorer ignore_errors;
680 // TODO(shess): See EntryRecords test.
681 ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
683 std::vector<int64> ids;
685 EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 100));
686 EXPECT_TRUE(ids.empty());
687 ids.push_back(0);
688 EXPECT_TRUE(db.DeleteDeletableResponseIds(ids));
689 EXPECT_TRUE(db.InsertDeletableResponseIds(ids));
691 ids.clear();
692 EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 100));
693 EXPECT_EQ(1U, ids.size());
694 EXPECT_EQ(0, ids[0]);
696 int64 unused, deleteable_response_rowid;
697 unused = deleteable_response_rowid = 0;
698 EXPECT_TRUE(db.FindLastStorageIds(&unused, &unused, &unused,
699 &deleteable_response_rowid));
700 EXPECT_EQ(1, deleteable_response_rowid);
703 // Expected to fail due to the duplicate id, 0 is already in the table.
704 ids.clear();
705 ids.push_back(0);
706 ids.push_back(1);
707 EXPECT_FALSE(db.InsertDeletableResponseIds(ids));
709 ids.clear();
710 for (int i = 1; i < 10; ++i)
711 ids.push_back(i);
712 EXPECT_TRUE(db.InsertDeletableResponseIds(ids));
713 EXPECT_TRUE(db.FindLastStorageIds(&unused, &unused, &unused,
714 &deleteable_response_rowid));
715 EXPECT_EQ(10, deleteable_response_rowid);
717 ids.clear();
718 EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 100));
719 EXPECT_EQ(10U, ids.size());
720 for (int i = 0; i < 10; ++i)
721 EXPECT_EQ(i, ids[i]);
723 // Ensure the limit is respected.
724 ids.clear();
725 EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 5));
726 EXPECT_EQ(5U, ids.size());
727 for (int i = 0; i < static_cast<int>(ids.size()); ++i)
728 EXPECT_EQ(i, ids[i]);
730 // Ensure the max_rowid is respected (the first rowid is 1).
731 ids.clear();
732 EXPECT_TRUE(db.GetDeletableResponseIds(&ids, 5, 100));
733 EXPECT_EQ(5U, ids.size());
734 for (int i = 0; i < static_cast<int>(ids.size()); ++i)
735 EXPECT_EQ(i, ids[i]);
737 // Ensure that we can delete from the table.
738 EXPECT_TRUE(db.DeleteDeletableResponseIds(ids));
739 ids.clear();
740 EXPECT_TRUE(db.GetDeletableResponseIds(&ids, kint64max, 100));
741 EXPECT_EQ(5U, ids.size());
742 for (int i = 0; i < static_cast<int>(ids.size()); ++i)
743 EXPECT_EQ(i + 5, ids[i]);
745 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
748 TEST(AppCacheDatabaseTest, OriginUsage) {
749 const GURL kManifestUrl("http://blah/manifest");
750 const GURL kManifestUrl2("http://blah/manifest2");
751 const GURL kOrigin(kManifestUrl.GetOrigin());
752 const GURL kOtherOriginManifestUrl("http://other/manifest");
753 const GURL kOtherOrigin(kOtherOriginManifestUrl.GetOrigin());
755 const base::FilePath kEmptyPath;
756 AppCacheDatabase db(kEmptyPath);
757 EXPECT_TRUE(db.LazyOpen(true));
759 std::vector<AppCacheDatabase::CacheRecord> cache_records;
760 EXPECT_EQ(0, db.GetOriginUsage(kOrigin));
761 EXPECT_TRUE(db.FindCachesForOrigin(kOrigin, &cache_records));
762 EXPECT_TRUE(cache_records.empty());
764 AppCacheDatabase::GroupRecord group_record;
765 group_record.group_id = 1;
766 group_record.manifest_url = kManifestUrl;
767 group_record.origin = kOrigin;
768 EXPECT_TRUE(db.InsertGroup(&group_record));
769 AppCacheDatabase::CacheRecord cache_record;
770 cache_record.cache_id = 1;
771 cache_record.group_id = 1;
772 cache_record.online_wildcard = true;
773 cache_record.update_time = kZeroTime;
774 cache_record.cache_size = 100;
775 EXPECT_TRUE(db.InsertCache(&cache_record));
777 EXPECT_EQ(100, db.GetOriginUsage(kOrigin));
779 group_record.group_id = 2;
780 group_record.manifest_url = kManifestUrl2;
781 group_record.origin = kOrigin;
782 EXPECT_TRUE(db.InsertGroup(&group_record));
783 cache_record.cache_id = 2;
784 cache_record.group_id = 2;
785 cache_record.online_wildcard = true;
786 cache_record.update_time = kZeroTime;
787 cache_record.cache_size = 1000;
788 EXPECT_TRUE(db.InsertCache(&cache_record));
790 EXPECT_EQ(1100, db.GetOriginUsage(kOrigin));
792 group_record.group_id = 3;
793 group_record.manifest_url = kOtherOriginManifestUrl;
794 group_record.origin = kOtherOrigin;
795 EXPECT_TRUE(db.InsertGroup(&group_record));
796 cache_record.cache_id = 3;
797 cache_record.group_id = 3;
798 cache_record.online_wildcard = true;
799 cache_record.update_time = kZeroTime;
800 cache_record.cache_size = 5000;
801 EXPECT_TRUE(db.InsertCache(&cache_record));
803 EXPECT_EQ(5000, db.GetOriginUsage(kOtherOrigin));
805 EXPECT_TRUE(db.FindCachesForOrigin(kOrigin, &cache_records));
806 EXPECT_EQ(2U, cache_records.size());
807 cache_records.clear();
808 EXPECT_TRUE(db.FindCachesForOrigin(kOtherOrigin, &cache_records));
809 EXPECT_EQ(1U, cache_records.size());
811 std::map<GURL, int64> usage_map;
812 EXPECT_TRUE(db.GetAllOriginUsage(&usage_map));
813 EXPECT_EQ(2U, usage_map.size());
814 EXPECT_EQ(1100, usage_map[kOrigin]);
815 EXPECT_EQ(5000, usage_map[kOtherOrigin]);
818 #if defined(APPCACHE_USE_SIMPLE_CACHE)
819 // There is no such upgrade path in this case.
820 #else
821 TEST(AppCacheDatabaseTest, UpgradeSchema4to7) {
822 // Real file on disk for this test.
823 base::ScopedTempDir temp_dir;
824 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
825 const base::FilePath kDbFile = temp_dir.path().AppendASCII("upgrade4.db");
827 const GURL kMockOrigin("http://mockorigin/");
828 const char kNamespaceUrlFormat[] = "namespace%d";
829 const char kWhitelistUrlFormat[] = "whitelist%d";
830 const char kTargetUrlFormat[] = "target%d";
831 const int kNumNamespaces = 10;
832 const int kWhitelistCacheId = 1;
834 // Create a v4 schema based database containing some fallback records.
836 const int kVersion4 = 4;
837 const char kGroupsTable[] = "Groups";
838 const char kCachesTable[] = "Caches";
839 const char kEntriesTable[] = "Entries";
840 const char kNamespacesTable[] = "Namespaces";
841 const char kOnlineWhiteListsTable[] = "OnlineWhiteLists";
842 const char kDeletableResponseIdsTable[] = "DeletableResponseIds";
844 struct TableInfo {
845 const char* table_name;
846 const char* columns;
849 struct IndexInfo {
850 const char* index_name;
851 const char* table_name;
852 const char* columns;
853 bool unique;
856 const TableInfo kTables4[] = {
857 { kGroupsTable,
858 "(group_id INTEGER PRIMARY KEY,"
859 " origin TEXT,"
860 " manifest_url TEXT,"
861 " creation_time INTEGER,"
862 " last_access_time INTEGER)" },
864 { kCachesTable,
865 "(cache_id INTEGER PRIMARY KEY,"
866 " group_id INTEGER,"
867 " online_wildcard INTEGER CHECK(online_wildcard IN (0, 1)),"
868 " update_time INTEGER,"
869 " cache_size INTEGER)" }, // intentionally not normalized
871 { kEntriesTable,
872 "(cache_id INTEGER,"
873 " url TEXT,"
874 " flags INTEGER,"
875 " response_id INTEGER,"
876 " response_size INTEGER)" },
878 { kNamespacesTable,
879 "(cache_id INTEGER,"
880 " origin TEXT," // intentionally not normalized
881 " type INTEGER,"
882 " namespace_url TEXT,"
883 " target_url TEXT)" },
885 { kOnlineWhiteListsTable,
886 "(cache_id INTEGER,"
887 " namespace_url TEXT)" },
889 { kDeletableResponseIdsTable,
890 "(response_id INTEGER NOT NULL)" },
893 const IndexInfo kIndexes4[] = {
894 { "GroupsOriginIndex",
895 kGroupsTable,
896 "(origin)",
897 false },
899 { "GroupsManifestIndex",
900 kGroupsTable,
901 "(manifest_url)",
902 true },
904 { "CachesGroupIndex",
905 kCachesTable,
906 "(group_id)",
907 false },
909 { "EntriesCacheIndex",
910 kEntriesTable,
911 "(cache_id)",
912 false },
914 { "EntriesCacheAndUrlIndex",
915 kEntriesTable,
916 "(cache_id, url)",
917 true },
919 { "EntriesResponseIdIndex",
920 kEntriesTable,
921 "(response_id)",
922 true },
924 { "NamespacesCacheIndex",
925 kNamespacesTable,
926 "(cache_id)",
927 false },
929 { "NamespacesOriginIndex",
930 kNamespacesTable,
931 "(origin)",
932 false },
934 { "NamespacesCacheAndUrlIndex",
935 kNamespacesTable,
936 "(cache_id, namespace_url)",
937 true },
939 { "OnlineWhiteListCacheIndex",
940 kOnlineWhiteListsTable,
941 "(cache_id)",
942 false },
944 { "DeletableResponsesIdIndex",
945 kDeletableResponseIdsTable,
946 "(response_id)",
947 true },
950 const int kTableCount4 = arraysize(kTables4);
951 const int kIndexCount4 = arraysize(kIndexes4);
953 sql::Connection connection;
954 EXPECT_TRUE(connection.Open(kDbFile));
956 sql::Transaction transaction(&connection);
957 EXPECT_TRUE(transaction.Begin());
959 sql::MetaTable meta_table;
960 EXPECT_TRUE(meta_table.Init(&connection, kVersion4, kVersion4));
962 for (int i = 0; i < kTableCount4; ++i) {
963 std::string sql("CREATE TABLE ");
964 sql += kTables4[i].table_name;
965 sql += kTables4[i].columns;
966 EXPECT_TRUE(connection.Execute(sql.c_str()));
969 for (int i = 0; i < kIndexCount4; ++i) {
970 std::string sql;
971 if (kIndexes4[i].unique)
972 sql += "CREATE UNIQUE INDEX ";
973 else
974 sql += "CREATE INDEX ";
975 sql += kIndexes4[i].index_name;
976 sql += " ON ";
977 sql += kIndexes4[i].table_name;
978 sql += kIndexes4[i].columns;
979 EXPECT_TRUE(connection.Execute(sql.c_str()));
982 const char* kNamespacesSql =
983 "INSERT INTO Namespaces"
984 " (cache_id, origin, type, namespace_url, target_url)"
985 " VALUES (?, ?, ?, ?, ?)";
986 sql::Statement statement;
987 statement.Assign(connection.GetUniqueStatement(kNamespacesSql));
988 EXPECT_TRUE(statement.is_valid());
989 for (int i = 0; i < kNumNamespaces; ++i) {
990 GURL namespace_url(
991 kMockOrigin.Resolve(base::StringPrintf(kNamespaceUrlFormat, i)));
992 GURL target_url(
993 kMockOrigin.Resolve(base::StringPrintf(kTargetUrlFormat, i)));
994 statement.BindInt64(0, i);
995 statement.BindString(1, kMockOrigin.spec().c_str());
996 statement.BindInt(2, APPCACHE_FALLBACK_NAMESPACE);
997 statement.BindString(3, namespace_url.spec().c_str());
998 statement.BindString(4, target_url.spec().c_str());
999 ASSERT_TRUE(statement.Run());
1000 statement.Reset(true);
1003 const char* kWhitelistsSql =
1004 "INSERT INTO OnlineWhiteLists"
1005 " (cache_id, namespace_url)"
1006 " VALUES (?, ?)";
1007 statement.Assign(connection.GetUniqueStatement(kWhitelistsSql));
1008 EXPECT_TRUE(statement.is_valid());
1009 for (int i = 0; i < kNumNamespaces; ++i) {
1010 GURL namespace_url(
1011 kMockOrigin.Resolve(base::StringPrintf(kWhitelistUrlFormat, i)));
1012 statement.BindInt64(0, kWhitelistCacheId);
1013 statement.BindString(1, namespace_url.spec().c_str());
1014 ASSERT_TRUE(statement.Run());
1015 statement.Reset(true);
1018 EXPECT_TRUE(transaction.Commit());
1021 // Open that database and verify that it got upgraded to v7.
1022 AppCacheDatabase db(kDbFile);
1023 EXPECT_TRUE(db.LazyOpen(true));
1024 EXPECT_TRUE(db.db_->DoesColumnExist("Namespaces", "is_pattern"));
1025 EXPECT_TRUE(db.db_->DoesColumnExist("OnlineWhiteLists", "is_pattern"));
1026 EXPECT_TRUE(db.db_->DoesColumnExist("Groups",
1027 "last_full_update_check_time"));
1028 EXPECT_TRUE(db.db_->DoesColumnExist("Groups",
1029 "first_evictable_error_time"));
1030 EXPECT_EQ(7, db.meta_table_->GetVersionNumber());
1031 EXPECT_EQ(7, db.meta_table_->GetCompatibleVersionNumber());
1033 std::vector<AppCacheDatabase::NamespaceRecord> intercepts;
1034 std::vector<AppCacheDatabase::NamespaceRecord> fallbacks;
1035 EXPECT_TRUE(db.FindNamespacesForOrigin(kMockOrigin, &intercepts,
1036 &fallbacks));
1037 EXPECT_TRUE(intercepts.empty());
1038 EXPECT_EQ(kNumNamespaces, static_cast<int>(fallbacks.size()));
1040 std::vector<AppCacheDatabase::OnlineWhiteListRecord> whitelists;
1041 EXPECT_TRUE(db.FindOnlineWhiteListForCache(kWhitelistCacheId, &whitelists));
1042 EXPECT_EQ(kNumNamespaces, static_cast<int>(whitelists.size()));
1044 for (int i = 0; i < kNumNamespaces; ++i) {
1045 GURL expected_namespace_url(
1046 kMockOrigin.Resolve(base::StringPrintf(kNamespaceUrlFormat, i)));
1047 GURL expected_target_url(
1048 kMockOrigin.Resolve(base::StringPrintf(kTargetUrlFormat, i)));
1049 GURL expected_whitelist_url(
1050 kMockOrigin.Resolve(base::StringPrintf(kWhitelistUrlFormat, i)));
1052 EXPECT_EQ(i, fallbacks[i].cache_id);
1053 EXPECT_EQ(APPCACHE_FALLBACK_NAMESPACE, fallbacks[i].namespace_.type);
1054 EXPECT_EQ(kMockOrigin, fallbacks[i].origin);
1055 EXPECT_EQ(expected_namespace_url, fallbacks[i].namespace_.namespace_url);
1056 EXPECT_EQ(expected_target_url, fallbacks[i].namespace_.target_url);
1057 EXPECT_FALSE(fallbacks[i].namespace_.is_pattern);
1058 EXPECT_EQ(expected_whitelist_url, whitelists[i].namespace_url);
1059 EXPECT_FALSE(whitelists[i].is_pattern);
1062 #endif // !APPCACHE_USE_SIMPLE_CACHE
1064 // Verify last_full_update_check_time and first_evictable_error_time.
1065 TEST(AppCacheDatabaseTest, UpgradeSchema5or6to7) {
1066 // Real file on disk for this test.
1067 base::ScopedTempDir temp_dir;
1068 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
1069 const base::FilePath kDbFile =
1070 temp_dir.path().AppendASCII("upgrade5or6to7.db");
1072 const GURL kMockOrigin("http://mockorigin/");
1073 const base::Time kMockTime = base::Time::Now();
1075 // Create a v5or6 schema based database containing two groups, one
1076 // that has an associated cache as expected, and one which erroneously
1077 // is missing its cache record.
1079 // The SQL schema is the same in these two cases.
1080 #if defined(APPCACHE_USE_SIMPLE_CACHE)
1081 const int kVersionN = 6;
1082 #else
1083 const int kVersionN = 5;
1084 #endif // !APPCACHE_USE_SIMPLE_CACHE
1086 const char kGroupsTable[] = "Groups";
1087 const char kCachesTable[] = "Caches";
1088 const char kEntriesTable[] = "Entries";
1089 const char kNamespacesTable[] = "Namespaces";
1090 const char kOnlineWhiteListsTable[] = "OnlineWhiteLists";
1091 const char kDeletableResponseIdsTable[] = "DeletableResponseIds";
1093 struct TableInfo {
1094 const char* table_name;
1095 const char* columns;
1098 struct IndexInfo {
1099 const char* index_name;
1100 const char* table_name;
1101 const char* columns;
1102 bool unique;
1105 const TableInfo kTables5[] = {
1106 { kGroupsTable,
1107 "(group_id INTEGER PRIMARY KEY,"
1108 " origin TEXT,"
1109 " manifest_url TEXT,"
1110 " creation_time INTEGER,"
1111 " last_access_time INTEGER)" },
1113 { kCachesTable,
1114 "(cache_id INTEGER PRIMARY KEY,"
1115 " group_id INTEGER,"
1116 " online_wildcard INTEGER CHECK(online_wildcard IN (0, 1)),"
1117 " update_time INTEGER,"
1118 " cache_size INTEGER)" }, // intentionally not normalized
1120 { kEntriesTable,
1121 "(cache_id INTEGER,"
1122 " url TEXT,"
1123 " flags INTEGER,"
1124 " response_id INTEGER,"
1125 " response_size INTEGER)" },
1127 { kNamespacesTable,
1128 "(cache_id INTEGER,"
1129 " origin TEXT," // intentionally not normalized
1130 " type INTEGER,"
1131 " namespace_url TEXT,"
1132 " target_url TEXT,"
1133 " is_pattern INTEGER CHECK(is_pattern IN (0, 1)))" },
1135 { kOnlineWhiteListsTable,
1136 "(cache_id INTEGER,"
1137 " namespace_url TEXT,"
1138 " is_pattern INTEGER CHECK(is_pattern IN (0, 1)))" },
1140 { kDeletableResponseIdsTable,
1141 "(response_id INTEGER NOT NULL)" },
1144 const IndexInfo kIndexes5[] = {
1145 { "GroupsOriginIndex",
1146 kGroupsTable,
1147 "(origin)",
1148 false },
1150 { "GroupsManifestIndex",
1151 kGroupsTable,
1152 "(manifest_url)",
1153 true },
1155 { "CachesGroupIndex",
1156 kCachesTable,
1157 "(group_id)",
1158 false },
1160 { "EntriesCacheIndex",
1161 kEntriesTable,
1162 "(cache_id)",
1163 false },
1165 { "EntriesCacheAndUrlIndex",
1166 kEntriesTable,
1167 "(cache_id, url)",
1168 true },
1170 { "EntriesResponseIdIndex",
1171 kEntriesTable,
1172 "(response_id)",
1173 true },
1175 { "NamespacesCacheIndex",
1176 kNamespacesTable,
1177 "(cache_id)",
1178 false },
1180 { "NamespacesOriginIndex",
1181 kNamespacesTable,
1182 "(origin)",
1183 false },
1185 { "NamespacesCacheAndUrlIndex",
1186 kNamespacesTable,
1187 "(cache_id, namespace_url)",
1188 true },
1190 { "OnlineWhiteListCacheIndex",
1191 kOnlineWhiteListsTable,
1192 "(cache_id)",
1193 false },
1195 { "DeletableResponsesIdIndex",
1196 kDeletableResponseIdsTable,
1197 "(response_id)",
1198 true },
1201 const int kTableCount5 = arraysize(kTables5);
1202 const int kIndexCount5 = arraysize(kIndexes5);
1204 sql::Connection connection;
1205 EXPECT_TRUE(connection.Open(kDbFile));
1207 sql::Transaction transaction(&connection);
1208 EXPECT_TRUE(transaction.Begin());
1210 sql::MetaTable meta_table;
1211 EXPECT_TRUE(meta_table.Init(&connection, kVersionN, kVersionN));
1213 for (int i = 0; i < kTableCount5; ++i) {
1214 std::string sql("CREATE TABLE ");
1215 sql += kTables5[i].table_name;
1216 sql += kTables5[i].columns;
1217 EXPECT_TRUE(connection.Execute(sql.c_str()));
1220 for (int i = 0; i < kIndexCount5; ++i) {
1221 std::string sql;
1222 if (kIndexes5[i].unique)
1223 sql += "CREATE UNIQUE INDEX ";
1224 else
1225 sql += "CREATE INDEX ";
1226 sql += kIndexes5[i].index_name;
1227 sql += " ON ";
1228 sql += kIndexes5[i].table_name;
1229 sql += kIndexes5[i].columns;
1230 EXPECT_TRUE(connection.Execute(sql.c_str()));
1233 sql::Statement statement;
1235 const GURL kMockManifestUrl(kMockOrigin.Resolve("mockmanifest"));
1236 const GURL kMockManifest2Url(kMockOrigin.Resolve("mockmanifest2"));
1238 const char* kInsertGroup =
1239 "INSERT INTO Groups"
1240 " (group_id, origin, manifest_url, creation_time, last_access_time)"
1241 " VALUES (?, ?, ?, ?, ?)";
1242 statement.Assign(connection.GetUniqueStatement(kInsertGroup));
1243 EXPECT_TRUE(statement.is_valid());
1244 statement.BindInt64(0, 1);
1245 statement.BindString(1, kMockOrigin.spec().c_str());
1246 statement.BindString(2, kMockManifestUrl.spec().c_str());
1247 statement.BindInt64(3, kMockTime.ToInternalValue());
1248 statement.BindInt64(4, kMockTime.ToInternalValue());
1249 ASSERT_TRUE(statement.Run());
1250 statement.Reset(true);
1251 statement.BindInt64(0, 2);
1252 statement.BindString(1, kMockOrigin.spec().c_str());
1253 statement.BindString(2, kMockManifest2Url.spec().c_str());
1254 statement.BindInt64(3, kMockTime.ToInternalValue());
1255 statement.BindInt64(4, kMockTime.ToInternalValue());
1256 ASSERT_TRUE(statement.Run());
1257 statement.Reset(true);
1259 const char* kInsertCache =
1260 "INSERT INTO Caches"
1261 " (cache_id, group_id, online_wildcard, update_time, cache_size)"
1262 " VALUES (?, ?, ?, ?, ?)";
1263 statement.Assign(connection.GetUniqueStatement(kInsertCache));
1264 EXPECT_TRUE(statement.is_valid());
1265 statement.BindInt64(0, 1);
1266 statement.BindInt64(1, 1);
1267 statement.BindInt(2, 0);
1268 statement.BindInt64(3, kMockTime.ToInternalValue());
1269 statement.BindInt64(4, 1000);
1270 ASSERT_TRUE(statement.Run());
1271 statement.Reset(true);
1273 EXPECT_TRUE(transaction.Commit());
1276 // Open that database and verify that it got upgraded to v7.
1277 AppCacheDatabase db(kDbFile);
1278 EXPECT_TRUE(db.LazyOpen(true));
1279 EXPECT_TRUE(db.db_->DoesColumnExist("Groups",
1280 "last_full_update_check_time"));
1281 EXPECT_TRUE(db.db_->DoesColumnExist("Groups",
1282 "first_evictable_error_time"));
1283 EXPECT_EQ(7, db.meta_table_->GetVersionNumber());
1284 EXPECT_EQ(7, db.meta_table_->GetCompatibleVersionNumber());
1286 AppCacheDatabase::GroupRecord group;
1287 EXPECT_TRUE(db.FindGroup(1, &group));
1288 EXPECT_EQ(kMockTime, group.last_full_update_check_time);
1289 EXPECT_EQ(kZeroTime, group.first_evictable_error_time);
1290 EXPECT_TRUE(db.FindGroup(2, &group));
1291 EXPECT_EQ(kZeroTime, group.last_full_update_check_time);
1292 EXPECT_EQ(kZeroTime, group.first_evictable_error_time);
1295 } // namespace content