// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "testing/gtest/include/gtest/gtest.h" #include "webkit/appcache/appcache.h" #include "webkit/appcache/appcache_frontend_impl.h" #include "webkit/appcache/appcache_host.h" #include "webkit/appcache/mock_appcache_service.h" namespace appcache { class AppCacheTest : public testing::Test { }; TEST(AppCacheTest, CleanupUnusedCache) { MockAppCacheService service; AppCacheFrontendImpl frontend; scoped_refptr cache(new AppCache(&service, 111)); cache->set_complete(true); scoped_refptr group( new AppCacheGroup(&service, GURL("http://blah/manifest"), 111)); group->AddCache(cache); AppCacheHost host1(1, &frontend, &service); AppCacheHost host2(2, &frontend, &service); host1.AssociateCompleteCache(cache.get()); host2.AssociateCompleteCache(cache.get()); host1.AssociateNoCache(GURL()); host2.AssociateNoCache(GURL()); } TEST(AppCacheTest, AddModifyRemoveEntry) { MockAppCacheService service; scoped_refptr cache(new AppCache(&service, 111)); EXPECT_TRUE(cache->entries().empty()); EXPECT_EQ(0L, cache->cache_size()); const GURL kFooUrl("http://foo.com"); const int64 kFooResponseId = 1; const int64 kFooSize = 100; AppCacheEntry entry1(AppCacheEntry::MASTER, kFooResponseId, kFooSize); cache->AddEntry(kFooUrl, entry1); EXPECT_EQ(entry1.types(), cache->GetEntry(kFooUrl)->types()); EXPECT_EQ(1UL, cache->entries().size()); EXPECT_EQ(kFooSize, cache->cache_size()); const GURL kBarUrl("http://bar.com"); const int64 kBarResponseId = 2; const int64 kBarSize = 200; AppCacheEntry entry2(AppCacheEntry::FALLBACK, kBarResponseId, kBarSize); EXPECT_TRUE(cache->AddOrModifyEntry(kBarUrl, entry2)); EXPECT_EQ(entry2.types(), cache->GetEntry(kBarUrl)->types()); EXPECT_EQ(2UL, cache->entries().size()); EXPECT_EQ(kFooSize + kBarSize, cache->cache_size()); // Expected to return false when an existing entry is modified. AppCacheEntry entry3(AppCacheEntry::EXPLICIT); EXPECT_FALSE(cache->AddOrModifyEntry(kFooUrl, entry3)); EXPECT_EQ((AppCacheEntry::MASTER | AppCacheEntry::EXPLICIT), cache->GetEntry(kFooUrl)->types()); // Only the type should be modified. EXPECT_EQ(kFooResponseId, cache->GetEntry(kFooUrl)->response_id()); EXPECT_EQ(kFooSize, cache->GetEntry(kFooUrl)->response_size()); EXPECT_EQ(kFooSize + kBarSize, cache->cache_size()); EXPECT_EQ(entry2.types(), cache->GetEntry(kBarUrl)->types()); // unchanged cache->RemoveEntry(kBarUrl); EXPECT_EQ(kFooSize, cache->cache_size()); cache->RemoveEntry(kFooUrl); EXPECT_EQ(0L, cache->cache_size()); EXPECT_TRUE(cache->entries().empty()); } TEST(AppCacheTest, InitializeWithManifest) { MockAppCacheService service; scoped_refptr cache(new AppCache(&service, 1234)); EXPECT_TRUE(cache->fallback_namespaces_.empty()); EXPECT_TRUE(cache->online_whitelist_namespaces_.empty()); EXPECT_FALSE(cache->online_whitelist_all_); Manifest manifest; manifest.explicit_urls.insert("http://one.com"); manifest.explicit_urls.insert("http://two.com"); manifest.fallback_namespaces.push_back( Namespace(FALLBACK_NAMESPACE, GURL("http://fb1.com"), GURL("http://fbone.com"))); manifest.online_whitelist_namespaces.push_back(GURL("http://w1.com")); manifest.online_whitelist_namespaces.push_back(GURL("http://w2.com")); manifest.online_whitelist_all = true; cache->InitializeWithManifest(&manifest); const std::vector& fallbacks = cache->fallback_namespaces_; size_t expected = 1; EXPECT_EQ(expected, fallbacks.size()); EXPECT_EQ(GURL("http://fb1.com"), fallbacks[0].namespace_url); EXPECT_EQ(GURL("http://fbone.com"), fallbacks[0].target_url); const std::vector& whitelist = cache->online_whitelist_namespaces_; expected = 2; EXPECT_EQ(expected, whitelist.size()); EXPECT_EQ(GURL("http://w1.com"), whitelist[0]); EXPECT_EQ(GURL("http://w2.com"), whitelist[1]); EXPECT_TRUE(cache->online_whitelist_all_); // Ensure collections in manifest were taken over by the cache rather than // copied. EXPECT_TRUE(manifest.fallback_namespaces.empty()); EXPECT_TRUE(manifest.online_whitelist_namespaces.empty()); } TEST(AppCacheTest, FindResponseForRequest) { MockAppCacheService service; const GURL kOnlineNamespaceUrl("http://blah/online_namespace"); const GURL kFallbackEntryUrl1("http://blah/fallback_entry1"); const GURL kFallbackNamespaceUrl1("http://blah/fallback_namespace/"); const GURL kFallbackEntryUrl2("http://blah/fallback_entry2"); const GURL kFallbackNamespaceUrl2("http://blah/fallback_namespace/longer"); const GURL kManifestUrl("http://blah/manifest"); const GURL kForeignExplicitEntryUrl("http://blah/foreign"); const GURL kInOnlineNamespaceUrl( "http://blah/online_namespace/network"); const GURL kExplicitInOnlineNamespaceUrl( "http://blah/online_namespace/explicit"); const GURL kFallbackTestUrl1("http://blah/fallback_namespace/1"); const GURL kFallbackTestUrl2("http://blah/fallback_namespace/longer2"); const GURL kInterceptNamespace("http://blah/intercept_namespace/"); const GURL kInterceptNamespaceWithinFallback( "http://blah/fallback_namespace/intercept_namespace/"); const GURL kInterceptNamespaceEntry("http://blah/intercept_entry"); const GURL kOnlineNamespaceWithinOtherNamespaces( "http://blah/fallback_namespace/intercept_namespace/1/online"); const int64 kFallbackResponseId1 = 1; const int64 kFallbackResponseId2 = 2; const int64 kManifestResponseId = 3; const int64 kForeignExplicitResponseId = 4; const int64 kExplicitInOnlineNamespaceResponseId = 5; const int64 kInterceptResponseId = 6; Manifest manifest; manifest.online_whitelist_namespaces.push_back(kOnlineNamespaceUrl); manifest.online_whitelist_namespaces.push_back( kOnlineNamespaceWithinOtherNamespaces); manifest.fallback_namespaces.push_back( Namespace(FALLBACK_NAMESPACE, kFallbackNamespaceUrl1, kFallbackEntryUrl1)); manifest.fallback_namespaces.push_back( Namespace(FALLBACK_NAMESPACE, kFallbackNamespaceUrl2, kFallbackEntryUrl2)); manifest.intercept_namespaces.push_back( Namespace(INTERCEPT_NAMESPACE, kInterceptNamespace, kInterceptNamespaceEntry)); manifest.intercept_namespaces.push_back( Namespace(INTERCEPT_NAMESPACE, kInterceptNamespaceWithinFallback, kInterceptNamespaceEntry)); // Create a cache with some namespaces and entries. scoped_refptr cache(new AppCache(&service, 1234)); cache->InitializeWithManifest(&manifest); cache->AddEntry( kFallbackEntryUrl1, AppCacheEntry(AppCacheEntry::FALLBACK, kFallbackResponseId1)); cache->AddEntry( kFallbackEntryUrl2, AppCacheEntry(AppCacheEntry::FALLBACK, kFallbackResponseId2)); cache->AddEntry( kManifestUrl, AppCacheEntry(AppCacheEntry::MANIFEST, kManifestResponseId)); cache->AddEntry( kForeignExplicitEntryUrl, AppCacheEntry(AppCacheEntry::EXPLICIT | AppCacheEntry::FOREIGN, kForeignExplicitResponseId)); cache->AddEntry( kExplicitInOnlineNamespaceUrl, AppCacheEntry(AppCacheEntry::EXPLICIT, kExplicitInOnlineNamespaceResponseId)); cache->AddEntry( kInterceptNamespaceEntry, AppCacheEntry(AppCacheEntry::INTERCEPT, kInterceptResponseId)); cache->set_complete(true); // See that we get expected results from FindResponseForRequest bool found = false; AppCacheEntry entry; AppCacheEntry fallback_entry; GURL intercept_namespace; GURL fallback_namespace; bool network_namespace = false; found = cache->FindResponseForRequest(GURL("http://blah/miss"), &entry, &intercept_namespace, &fallback_entry, &fallback_namespace, &network_namespace); EXPECT_FALSE(found); found = cache->FindResponseForRequest(kForeignExplicitEntryUrl, &entry, &intercept_namespace, &fallback_entry, &fallback_namespace, &network_namespace); EXPECT_TRUE(found); EXPECT_EQ(kForeignExplicitResponseId, entry.response_id()); EXPECT_FALSE(fallback_entry.has_response_id()); EXPECT_FALSE(network_namespace); entry = AppCacheEntry(); // reset found = cache->FindResponseForRequest(kManifestUrl, &entry, &intercept_namespace, &fallback_entry, &fallback_namespace, &network_namespace); EXPECT_TRUE(found); EXPECT_EQ(kManifestResponseId, entry.response_id()); EXPECT_FALSE(fallback_entry.has_response_id()); EXPECT_FALSE(network_namespace); entry = AppCacheEntry(); // reset found = cache->FindResponseForRequest(kInOnlineNamespaceUrl, &entry, &intercept_namespace, &fallback_entry, &fallback_namespace, &network_namespace); EXPECT_TRUE(found); EXPECT_FALSE(entry.has_response_id()); EXPECT_FALSE(fallback_entry.has_response_id()); EXPECT_TRUE(network_namespace); network_namespace = false; // reset found = cache->FindResponseForRequest(kExplicitInOnlineNamespaceUrl, &entry, &intercept_namespace, &fallback_entry, &fallback_namespace, &network_namespace); EXPECT_TRUE(found); EXPECT_EQ(kExplicitInOnlineNamespaceResponseId, entry.response_id()); EXPECT_FALSE(fallback_entry.has_response_id()); EXPECT_FALSE(network_namespace); entry = AppCacheEntry(); // reset found = cache->FindResponseForRequest(kFallbackTestUrl1, &entry, &intercept_namespace, &fallback_entry, &fallback_namespace, &network_namespace); EXPECT_TRUE(found); EXPECT_FALSE(entry.has_response_id()); EXPECT_EQ(kFallbackResponseId1, fallback_entry.response_id()); EXPECT_EQ(kFallbackEntryUrl1, cache->GetFallbackEntryUrl(fallback_namespace)); EXPECT_FALSE(network_namespace); fallback_entry = AppCacheEntry(); // reset found = cache->FindResponseForRequest(kFallbackTestUrl2, &entry, &intercept_namespace, &fallback_entry, &fallback_namespace, &network_namespace); EXPECT_TRUE(found); EXPECT_FALSE(entry.has_response_id()); EXPECT_EQ(kFallbackResponseId2, fallback_entry.response_id()); EXPECT_EQ(kFallbackEntryUrl2, cache->GetFallbackEntryUrl(fallback_namespace)); EXPECT_FALSE(network_namespace); fallback_entry = AppCacheEntry(); // reset found = cache->FindResponseForRequest(kOnlineNamespaceWithinOtherNamespaces, &entry, &intercept_namespace, &fallback_entry, &fallback_namespace, &network_namespace); EXPECT_TRUE(found); EXPECT_FALSE(entry.has_response_id()); EXPECT_FALSE(fallback_entry.has_response_id()); EXPECT_TRUE(network_namespace); fallback_entry = AppCacheEntry(); // reset found = cache->FindResponseForRequest( kOnlineNamespaceWithinOtherNamespaces.Resolve("online_resource"), &entry, &intercept_namespace, &fallback_entry, &fallback_namespace, &network_namespace); EXPECT_TRUE(found); EXPECT_FALSE(entry.has_response_id()); EXPECT_FALSE(fallback_entry.has_response_id()); EXPECT_TRUE(network_namespace); fallback_namespace = GURL(); found = cache->FindResponseForRequest( kInterceptNamespace.Resolve("intercept_me"), &entry, &intercept_namespace, &fallback_entry, &fallback_namespace, &network_namespace); EXPECT_TRUE(found); EXPECT_EQ(kInterceptResponseId, entry.response_id()); EXPECT_EQ(kInterceptNamespaceEntry, cache->GetInterceptEntryUrl(intercept_namespace)); EXPECT_FALSE(fallback_entry.has_response_id()); EXPECT_TRUE(fallback_namespace.is_empty()); EXPECT_FALSE(network_namespace); entry = AppCacheEntry(); // reset found = cache->FindResponseForRequest( kInterceptNamespaceWithinFallback.Resolve("intercept_me"), &entry, &intercept_namespace, &fallback_entry, &fallback_namespace, &network_namespace); EXPECT_TRUE(found); EXPECT_EQ(kInterceptResponseId, entry.response_id()); EXPECT_EQ(kInterceptNamespaceEntry, cache->GetInterceptEntryUrl(intercept_namespace)); EXPECT_FALSE(fallback_entry.has_response_id()); EXPECT_TRUE(fallback_namespace.is_empty()); EXPECT_FALSE(network_namespace); } TEST(AppCacheTest, ToFromDatabaseRecords) { // Setup a cache with some entries. const int64 kCacheId = 1234; const int64 kGroupId = 4321; const GURL kManifestUrl("http://foo.com/manifest"); const GURL kInterceptUrl("http://foo.com/intercept.html"); const GURL kFallbackUrl("http://foo.com/fallback.html"); const std::string kData( "CACHE MANIFEST\r" "CHROMIUM-INTERCEPT:\r" "/intercept return /intercept.html\r" "FALLBACK:\r" "/ /fallback.html\r" "NETWORK:\r" "/whitelist\r" "*\r"); MockAppCacheService service; scoped_refptr group = new AppCacheGroup(&service, kManifestUrl, kGroupId); scoped_refptr cache(new AppCache(&service, kCacheId)); Manifest manifest; EXPECT_TRUE( ParseManifest(kManifestUrl, kData.c_str(), kData.length(), manifest)); cache->InitializeWithManifest(&manifest); cache->AddEntry( kManifestUrl, AppCacheEntry(AppCacheEntry::MANIFEST, 1, 1)); cache->AddEntry( kInterceptUrl, AppCacheEntry(AppCacheEntry::INTERCEPT, 3, 3)); cache->AddEntry( kFallbackUrl, AppCacheEntry(AppCacheEntry::FALLBACK, 2, 2)); // Get it to produce database records and verify them. AppCacheDatabase::CacheRecord cache_record; std::vector entries; std::vector intercepts; std::vector fallbacks; std::vector whitelists; cache->ToDatabaseRecords( group, &cache_record, &entries, &intercepts, &fallbacks, &whitelists); EXPECT_EQ(kCacheId, cache_record.cache_id); EXPECT_EQ(kGroupId, cache_record.group_id); EXPECT_TRUE(cache_record.online_wildcard); EXPECT_EQ(1 + 2 + 3, cache_record.cache_size); EXPECT_EQ(3u, entries.size()); EXPECT_EQ(1u, intercepts.size()); EXPECT_EQ(1u, fallbacks.size()); EXPECT_EQ(1u, whitelists.size()); cache = NULL; // Create a new AppCache and populate it with those records and verify. cache = new AppCache(&service, kCacheId); cache->InitializeWithDatabaseRecords( cache_record, entries, intercepts, fallbacks, whitelists); EXPECT_TRUE(cache->online_whitelist_all_); EXPECT_EQ(3u, cache->entries().size()); EXPECT_TRUE(cache->GetEntry(kManifestUrl)); EXPECT_TRUE(cache->GetEntry(kInterceptUrl)); EXPECT_TRUE(cache->GetEntry(kFallbackUrl)); EXPECT_EQ(kInterceptUrl, cache->GetInterceptEntryUrl(GURL("http://foo.com/intercept"))); EXPECT_EQ(kFallbackUrl, cache->GetFallbackEntryUrl(GURL("http://foo.com/"))); EXPECT_EQ(1 + 2 + 3, cache->cache_size()); } } // namespace appacache