From 83e6d8500ca17fbdeb4a0fc9d9e25fc15721429b Mon Sep 17 00:00:00 2001 From: DimiL Date: Fri, 7 Apr 2017 14:31:04 +0800 Subject: [PATCH] Bug 1311935 - P4. GTest for safebrowsing v4 caching. r=francois MozReview-Commit-ID: ExR5NJUvzNg --HG-- extra : rebase_source : 868f508256c06724e24f8a7c91171aaae72200d2 --- .../url-classifier/tests/gtest/Common.cpp | 22 ++ .../url-classifier/tests/gtest/Common.h | 7 + .../tests/gtest/TestCachingV4.cpp | 232 ++++++++++++++++++ .../tests/gtest/TestLookupCacheV4.cpp | 27 -- .../url-classifier/tests/gtest/moz.build | 1 + 5 files changed, 262 insertions(+), 27 deletions(-) create mode 100644 toolkit/components/url-classifier/tests/gtest/TestCachingV4.cpp diff --git a/toolkit/components/url-classifier/tests/gtest/Common.cpp b/toolkit/components/url-classifier/tests/gtest/Common.cpp index 34a4c7a969d0..81a58c48bb13 100644 --- a/toolkit/components/url-classifier/tests/gtest/Common.cpp +++ b/toolkit/components/url-classifier/tests/gtest/Common.cpp @@ -10,6 +10,9 @@ using namespace mozilla; using namespace mozilla::safebrowsing; +#define GTEST_SAFEBROWSING_DIR NS_LITERAL_CSTRING("safebrowsing") +#define GTEST_TABLE NS_LITERAL_CSTRING("gtest-malware-proto") + template void RunTestInNewThread(Function&& aFunction) { nsCOMPtr r = NS_NewRunnableFunction(mozilla::Forward(aFunction)); @@ -157,3 +160,22 @@ GeneratePrefix(const nsCString& aFragment, uint8_t aLength) return hash; } +UniquePtr +SetupLookupCacheV4(const _PrefixArray& prefixArray) +{ + nsCOMPtr file; + NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file)); + + file->AppendNative(GTEST_SAFEBROWSING_DIR); + + UniquePtr cache = MakeUnique(GTEST_TABLE, EmptyCString(), file); + nsresult rv = cache->Init(); + EXPECT_EQ(rv, NS_OK); + + PrefixStringMap map; + PrefixArrayToPrefixStringMap(prefixArray, map); + rv = cache->Build(map); + EXPECT_EQ(rv, NS_OK); + + return Move(cache); +} diff --git a/toolkit/components/url-classifier/tests/gtest/Common.h b/toolkit/components/url-classifier/tests/gtest/Common.h index 4be8c7884b4c..0756b04f3368 100644 --- a/toolkit/components/url-classifier/tests/gtest/Common.h +++ b/toolkit/components/url-classifier/tests/gtest/Common.h @@ -1,4 +1,5 @@ #include "HashStore.h" +#include "LookupCacheV4.h" #include "nsIFile.h" #include "nsTArray.h" #include "gtest/gtest.h" @@ -12,6 +13,9 @@ namespace safebrowsing { } } +typedef nsCString _Fragment; +typedef nsTArray _PrefixArray; + template void RunTestInNewThread(Function&& aFunction); @@ -39,3 +43,6 @@ nsresult PrefixArrayToAddPrefixArrayV2(const nsTArray& prefixArray, // Generate a hash prefix from string nsCString GeneratePrefix(const nsCString& aFragment, uint8_t aLength); + +// Create a LookupCacheV4 object with sepecified prefix array. +UniquePtr SetupLookupCacheV4(const _PrefixArray& prefixArray); diff --git a/toolkit/components/url-classifier/tests/gtest/TestCachingV4.cpp b/toolkit/components/url-classifier/tests/gtest/TestCachingV4.cpp new file mode 100644 index 000000000000..717311c371d1 --- /dev/null +++ b/toolkit/components/url-classifier/tests/gtest/TestCachingV4.cpp @@ -0,0 +1,232 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "Common.h" + +#define EXPIRED_TIME_SEC (PR_Now() / PR_USEC_PER_SEC - 3600) +#define NOTEXPIRED_TIME_SEC (PR_Now() / PR_USEC_PER_SEC + 3600) + +static void +SetupCacheEntry(LookupCacheV4* aLookupCache, + const nsCString& aCompletion, + bool aNegExpired = false, + bool aPosExpired = false) +{ + FullHashResponseMap map; + CachedFullHashResponse* response = map.LookupOrAdd( + GeneratePrefix(aCompletion, PREFIX_SIZE)); + + response->negativeCacheExpirySec = aNegExpired ? EXPIRED_TIME_SEC : NOTEXPIRED_TIME_SEC; + response->fullHashes.Put(GeneratePrefix(aCompletion, COMPLETE_SIZE), + aPosExpired ? EXPIRED_TIME_SEC : NOTEXPIRED_TIME_SEC); + + aLookupCache->AddFullHashResponseToCache(map); +} + +void +TestCache(const Completion aCompletion, + bool aExpectedHas, + bool aExpectedConfirmed, + bool aExpectedFromCache, + LookupCacheV4* aCache = nullptr) +{ + bool has, fromCache, confirmed; + uint32_t matchLength; + TableFreshnessMap dummy; + + if (aCache) { + aCache->Has(aCompletion, dummy, 0, &has, &matchLength, &confirmed, &fromCache); + } else { + _PrefixArray array = { GeneratePrefix(_Fragment("cache.notexpired.com/"), 10), + GeneratePrefix(_Fragment("cache.expired.com/"), 8), + GeneratePrefix(_Fragment("gound.com/"), 5), + GeneratePrefix(_Fragment("small.com/"), 4) + }; + + UniquePtr cache = SetupLookupCacheV4(array); + + // Create an expired entry and a non-expired entry + SetupCacheEntry(cache.get(), _Fragment("cache.notexpired.com/")); + SetupCacheEntry(cache.get(), _Fragment("cache.expired.com/"), true, true); + + cache->Has(aCompletion, dummy, 0, &has, &matchLength, &confirmed, &fromCache); + } + + EXPECT_EQ(has, aExpectedHas); + EXPECT_EQ(confirmed, aExpectedConfirmed); + EXPECT_EQ(fromCache, aExpectedFromCache); +} + +void +TestCache(const _Fragment& aFragment, + bool aExpectedHas, + bool aExpectedConfirmed, + bool aExpectedFromCache, + LookupCacheV4* aCache = nullptr) +{ + Completion lookupHash; + nsCOMPtr cryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID); + lookupHash.FromPlaintext(aFragment, cryptoHash); + + TestCache(lookupHash, aExpectedHas, aExpectedConfirmed, aExpectedFromCache, aCache); +} + +// This testcase check the returned result of |Has| API if fullhash cannot match +// any prefix in the local database. +TEST(CachingV4, NotFound) +{ + TestCache(_Fragment("nomatch.com/"), false, false, false); +} + +// This testcase check the returned result of |Has| API if fullhash find a match +// in the local database but not in the cache. +TEST(CachingV4, NotInCache) +{ + TestCache(_Fragment("gound.com/"), true, false, false); +} + +// This testcase check the returned result of |Has| API if fullhash matches +// a cache entry in positive cache. +TEST(CachingV4, InPositiveCacheNotExpired) +{ + TestCache(_Fragment("cache.notexpired.com/"), true, true, true); +} + +// This testcase check the returned result of |Has| API if fullhash matches +// a cache entry in positive cache but that it is expired. +TEST(CachingV4, InPositiveCacheExpired) +{ + TestCache(_Fragment("cache.expired.com/"), true, false, true); +} + +// This testcase check the returned result of |Has| API if fullhash matches +// a cache entry in negative cache. +TEST(CachingV4, InNegativeCacheNotExpired) +{ + // Create a fullhash whose prefix matches the prefix in negative cache + // but completion doesn't match any fullhash in positive cache. + nsCOMPtr cryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID); + + Completion prefix; + prefix.FromPlaintext(_Fragment("cache.notexpired.com/"), cryptoHash); + + Completion fullhash; + fullhash.FromPlaintext(_Fragment("firefox.com/"), cryptoHash); + + // Overwrite the 4-byte prefix of `fullhash` so that it conflicts with `prefix`. + // Since "cache.notexpired.com" is added to database in TestCache as a + // 10-byte prefix, we should copy more than 10 bytes to fullhash to ensure + // it can match the prefix in database. + memcpy(fullhash.buf, prefix.buf, 10); + + TestCache(fullhash, false, false, true); +} + +// This testcase check the returned result of |Has| API if fullhash matches +// a cache entry in negative cache but that entry is expired. +TEST(CachingV4, InNegativeCacheExpired) +{ + // Create a fullhash whose prefix is in the cache. + nsCOMPtr cryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID); + + Completion prefix; + prefix.FromPlaintext(_Fragment("cache.expired.com/"), cryptoHash); + + Completion fullhash; + fullhash.FromPlaintext(_Fragment("firefox.com/"), cryptoHash); + + memcpy(fullhash.buf, prefix.buf, 10); + + TestCache(fullhash, true, false, true); +} + +#define CACHED_URL _Fragment("cache.com/") +#define NEG_CACHE_EXPIRED_URL _Fragment("cache.negExpired.com/") +#define POS_CACHE_EXPIRED_URL _Fragment("cache.posExpired.com/") +#define BOTH_CACHE_EXPIRED_URL _Fragment("cache.negAndposExpired.com/") + +// This testcase create 4 cache entries. +// 1. unexpired entry. +// 2. an entry whose negative cache time is expired but whose positive cache +// is not expired. +// 3. an entry whose positive cache time is expired +// 4. an entry whose negative cache time and positive cache time are expired +// After calling |InvalidateExpiredCacheEntry| API, entries with expired +// negative time should be removed from cache(2 & 4) +TEST(CachingV4, InvalidateExpiredCacheEntry) +{ + _PrefixArray array = { GeneratePrefix(CACHED_URL, 10), + GeneratePrefix(NEG_CACHE_EXPIRED_URL, 8), + GeneratePrefix(POS_CACHE_EXPIRED_URL, 5), + GeneratePrefix(BOTH_CACHE_EXPIRED_URL, 4) + }; + + UniquePtr cache = SetupLookupCacheV4(array); + + SetupCacheEntry(cache.get(), CACHED_URL, false, false); + SetupCacheEntry(cache.get(), NEG_CACHE_EXPIRED_URL, true, false); + SetupCacheEntry(cache.get(), POS_CACHE_EXPIRED_URL, false, true); + SetupCacheEntry(cache.get(), BOTH_CACHE_EXPIRED_URL, true, true); + + // Before invalidate + TestCache(CACHED_URL, true, true, true, cache.get()); + TestCache(NEG_CACHE_EXPIRED_URL, true, true, true, cache.get()); + TestCache(POS_CACHE_EXPIRED_URL, true, false, true, cache.get()); + TestCache(BOTH_CACHE_EXPIRED_URL, true, false, true, cache.get()); + + // Call InvalidateExpiredCacheEntry to remove cache entries whose negative cache + // time is expired + cache->InvalidateExpiredCacheEntry(); + + // After invalidate, NEG_CACHE_EXPIRED_URL & BOTH_CACHE_EXPIRED_URL should + // not be found in cache. + TestCache(NEG_CACHE_EXPIRED_URL, true, false, false, cache.get()); + TestCache(BOTH_CACHE_EXPIRED_URL, true, false, false, cache.get()); + + // Other entries should remain the same result. + TestCache(CACHED_URL, true, true, true, cache.get()); + TestCache(POS_CACHE_EXPIRED_URL, true, false, true, cache.get()); +} + +// This testcase check if an cache entry whose negative cache time is expired +// and it doesn't have any postive cache entries in it, it should be removed +// from cache after calling |Has|. +TEST(CachingV4, NegativeCacheExpire) +{ + _PrefixArray array = { GeneratePrefix(NEG_CACHE_EXPIRED_URL, 8) }; + UniquePtr cache = SetupLookupCacheV4(array); + + FullHashResponseMap map; + CachedFullHashResponse* response = map.LookupOrAdd( + GeneratePrefix(NEG_CACHE_EXPIRED_URL, PREFIX_SIZE)); + + response->negativeCacheExpirySec = EXPIRED_TIME_SEC; + + cache->AddFullHashResponseToCache(map); + + // The first time we should found it in the cache but the result is not + // confirmed(because it is expired). + TestCache(NEG_CACHE_EXPIRED_URL, true, false, true, cache.get()); + + // The second time it should not be found in the cache again + TestCache(NEG_CACHE_EXPIRED_URL, true, false, false, cache.get()); +} + +// This testcase check we only lookup cache with 4-bytes prefix +TEST(CachingV4, Ensure4BytesLookup) +{ + _PrefixArray array = { GeneratePrefix(CACHED_URL, 8) }; + UniquePtr cache = SetupLookupCacheV4(array); + + FullHashResponseMap map; + CachedFullHashResponse* response = map.LookupOrAdd( + GeneratePrefix(CACHED_URL, 5)); + + response->negativeCacheExpirySec = NOTEXPIRED_TIME_SEC; + response->fullHashes.Put(GeneratePrefix(CACHED_URL, COMPLETE_SIZE), + NOTEXPIRED_TIME_SEC); + cache->AddFullHashResponseToCache(map); + + TestCache(CACHED_URL, true, false, false, cache.get()); +} diff --git a/toolkit/components/url-classifier/tests/gtest/TestLookupCacheV4.cpp b/toolkit/components/url-classifier/tests/gtest/TestLookupCacheV4.cpp index 817f1b952f3e..20f43615a393 100644 --- a/toolkit/components/url-classifier/tests/gtest/TestLookupCacheV4.cpp +++ b/toolkit/components/url-classifier/tests/gtest/TestLookupCacheV4.cpp @@ -2,35 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "LookupCacheV4.h" #include "Common.h" -#define GTEST_SAFEBROWSING_DIR NS_LITERAL_CSTRING("safebrowsing") -#define GTEST_TABLE NS_LITERAL_CSTRING("gtest-malware-proto") - -typedef nsCString _Fragment; -typedef nsTArray _PrefixArray; - -static UniquePtr -SetupLookupCacheV4(const _PrefixArray& prefixArray) -{ - nsCOMPtr file; - NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file)); - - file->AppendNative(GTEST_SAFEBROWSING_DIR); - - UniquePtr cache = MakeUnique(GTEST_TABLE, EmptyCString(), file); - nsresult rv = cache->Init(); - EXPECT_EQ(rv, NS_OK); - - PrefixStringMap map; - PrefixArrayToPrefixStringMap(prefixArray, map); - rv = cache->Build(map); - EXPECT_EQ(rv, NS_OK); - - return Move(cache); -} - void TestHasPrefix(const _Fragment& aFragment, bool aExpectedHas, bool aExpectedComplete) { diff --git a/toolkit/components/url-classifier/tests/gtest/moz.build b/toolkit/components/url-classifier/tests/gtest/moz.build index f7ae42038947..0697f95fc24b 100644 --- a/toolkit/components/url-classifier/tests/gtest/moz.build +++ b/toolkit/components/url-classifier/tests/gtest/moz.build @@ -10,6 +10,7 @@ LOCAL_INCLUDES += [ UNIFIED_SOURCES += [ 'Common.cpp', + 'TestCachingV4.cpp', 'TestChunkSet.cpp', 'TestClassifier.cpp', 'TestFailUpdate.cpp',