Bug 1311935 - P4. GTest for safebrowsing v4 caching. r?francois draft
authorDimiL <dlee@mozilla.com>
Fri, 07 Apr 2017 14:31:04 +0800
changeset 560080 43a239d6aec70db372f53363019d1153d3761aa7
parent 560079 a319000da3b1422e1cf2daea13d306eb0bbac6ec
child 623605 9f8f524a7774d03f9e175727a4863d9d2738b095
push id53321
push userdlee@mozilla.com
push dateTue, 11 Apr 2017 03:51:38 +0000
reviewersfrancois
bugs1311935
milestone55.0a1
Bug 1311935 - P4. GTest for safebrowsing v4 caching. r?francois MozReview-Commit-ID: ExR5NJUvzNg
toolkit/components/url-classifier/tests/gtest/Common.cpp
toolkit/components/url-classifier/tests/gtest/Common.h
toolkit/components/url-classifier/tests/gtest/TestCachingV4.cpp
toolkit/components/url-classifier/tests/gtest/TestLookupCacheV4.cpp
toolkit/components/url-classifier/tests/gtest/moz.build
--- a/toolkit/components/url-classifier/tests/gtest/Common.cpp
+++ b/toolkit/components/url-classifier/tests/gtest/Common.cpp
@@ -5,16 +5,19 @@
 #include "nsTArray.h"
 #include "nsIThread.h"
 #include "nsThreadUtils.h"
 #include "nsUrlClassifierUtils.h"
 
 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<typename Function>
 void RunTestInNewThread(Function&& aFunction) {
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(mozilla::Forward<Function>(aFunction));
   nsCOMPtr<nsIThread> testingThread;
   nsresult rv =
     NS_NewNamedThread("Testing Thread", getter_AddRefs(testingThread), r);
   ASSERT_EQ(rv, NS_OK);
   testingThread->Shutdown();
@@ -152,8 +155,27 @@ GeneratePrefix(const nsCString& aFragmen
   nsCOMPtr<nsICryptoHash> cryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID);
   complete.FromPlaintext(aFragment, cryptoHash);
 
   nsCString hash;
   hash.Assign((const char *)complete.buf, aLength);
   return hash;
 }
 
+UniquePtr<LookupCacheV4>
+SetupLookupCacheV4(const _PrefixArray& prefixArray)
+{
+  nsCOMPtr<nsIFile> file;
+  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+
+  file->AppendNative(GTEST_SAFEBROWSING_DIR);
+
+  UniquePtr<LookupCacheV4> cache = MakeUnique<LookupCacheV4>(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);
+}
--- a/toolkit/components/url-classifier/tests/gtest/Common.h
+++ b/toolkit/components/url-classifier/tests/gtest/Common.h
@@ -1,22 +1,26 @@
 #include "HashStore.h"
+#include "LookupCacheV4.h"
 #include "nsIFile.h"
 #include "nsTArray.h"
 #include "gtest/gtest.h"
 
 using namespace mozilla;
 using namespace mozilla::safebrowsing;
 
 namespace mozilla {
 namespace safebrowsing {
     class Classifier;
 }
 }
 
+typedef nsCString _Fragment;
+typedef nsTArray<nsCString> _PrefixArray;
+
 template<typename Function>
 void RunTestInNewThread(Function&& aFunction);
 
 // Synchronously apply updates by calling Classifier::AsyncApplyUpdates.
 nsresult SyncApplyUpdates(Classifier* aClassifier,
                           nsTArray<TableUpdate*>* aUpdates);
 
 // Return nsIFile with root directory - NS_APP_USER_PROFILE_50_DIR
@@ -34,8 +38,11 @@ void ApplyUpdate(TableUpdate* update);
 void PrefixArrayToPrefixStringMap(const nsTArray<nsCString>& prefixArray,
                                   PrefixStringMap& out);
 
 nsresult PrefixArrayToAddPrefixArrayV2(const nsTArray<nsCString>& prefixArray,
                                        AddPrefixArray& out);
 
 // Generate a hash prefix from string
 nsCString GeneratePrefix(const nsCString& aFragment, uint8_t aLength);
+
+// Create a LookupCacheV4 object with sepecified prefix array.
+UniquePtr<LookupCacheV4> SetupLookupCacheV4(const _PrefixArray& prefixArray);
new file mode 100644
--- /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<LookupCacheV4> 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<nsICryptoHash> 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<nsICryptoHash> 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<nsICryptoHash> 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<LookupCacheV4> 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<LookupCacheV4> 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<LookupCacheV4> 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());
+}
--- a/toolkit/components/url-classifier/tests/gtest/TestLookupCacheV4.cpp
+++ b/toolkit/components/url-classifier/tests/gtest/TestLookupCacheV4.cpp
@@ -1,41 +1,14 @@
 /* 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 "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<nsCString> _PrefixArray;
-
-static UniquePtr<LookupCacheV4>
-SetupLookupCacheV4(const _PrefixArray& prefixArray)
-{
-  nsCOMPtr<nsIFile> file;
-  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
-
-  file->AppendNative(GTEST_SAFEBROWSING_DIR);
-
-  UniquePtr<LookupCacheV4> cache = MakeUnique<LookupCacheV4>(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)
 {
   _PrefixArray array = { GeneratePrefix(_Fragment("bravo.com/"), 32),
                          GeneratePrefix(_Fragment("browsing.com/"), 8),
                          GeneratePrefix(_Fragment("gound.com/"), 5),
                          GeneratePrefix(_Fragment("small.com/"), 4)
                        };
--- a/toolkit/components/url-classifier/tests/gtest/moz.build
+++ b/toolkit/components/url-classifier/tests/gtest/moz.build
@@ -5,16 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 LOCAL_INCLUDES += [
     '../..',
 ]
 
 UNIFIED_SOURCES += [
     'Common.cpp',
+    'TestCachingV4.cpp',
     'TestChunkSet.cpp',
     'TestClassifier.cpp',
     'TestFailUpdate.cpp',
     'TestFindFullHash.cpp',
     'TestLookupCacheV4.cpp',
     'TestPerProviderDirectory.cpp',
     'TestProtocolParser.cpp',
     'TestRiceDeltaDecoder.cpp',