Files
tubestation/toolkit/components/url-classifier/tests/gtest/TestUrlClassifierTableUpdateV4.cpp
Henry Chang 4543c61a48 Bug 1339050 - Asynchronously apply safebrowsing DB update. r=francois,gcp
A new function Classifier::AsyncApplyUpdates() is implemented for async update.
Besides, all public Classifier interfaces become "worker thread only" and
we remove DBServiceWorker::ApplyUpdatesBackground/Foreground.

In DBServiceWorker::FinishUpdate, instead of calling Classifier::ApplyUpdates,
we call Classifier::AsyncApplyUpdates and install a callback for notifying
the update observer when update is finished. The callback will occur on the
caller thread (i.e. worker thread.)

As for the shutdown issue, when the main thread is notified to shut down,
we at first *synchronously* dispatch an event to the worker thread to
shut down the update thread. After getting synchronized with all other
threads, we send last two events "CancelUpdate" and "CloseDb" to notify
dangling update (i.e. BeginUpdate is called but FinishUpdate isn't)
and do cleanup work.

MozReview-Commit-ID: DXZvA2eFKlc
2017-04-06 07:07:56 +08:00

761 lines
21 KiB
C++

/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
#include "Common.h"
#include "Classifier.h"
#include "HashStore.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsIFile.h"
#include "nsIThread.h"
#include "string.h"
#include "gtest/gtest.h"
#include "nsThreadUtils.h"
using namespace mozilla;
using namespace mozilla::safebrowsing;
typedef nsCString _Prefix;
typedef nsTArray<_Prefix> _PrefixArray;
#define GTEST_SAFEBROWSING_DIR NS_LITERAL_CSTRING("safebrowsing")
#define GTEST_TABLE NS_LITERAL_CSTRING("gtest-malware-proto")
#define GTEST_PREFIXFILE NS_LITERAL_CSTRING("gtest-malware-proto.pset")
// This function removes common elements of inArray and outArray from
// outArray. This is used by partial update testcase to ensure partial update
// data won't contain prefixes we already have.
static void
RemoveIntersection(const _PrefixArray& inArray, _PrefixArray& outArray)
{
for (uint32_t i = 0; i < inArray.Length(); i++) {
int32_t idx = outArray.BinaryIndexOf(inArray[i]);
if (idx >= 0) {
outArray.RemoveElementAt(idx);
}
}
}
// This fucntion removes elements from outArray by index specified in
// removal array.
static void
RemoveElements(const nsTArray<uint32_t>& removal, _PrefixArray& outArray)
{
for (int32_t i = removal.Length() - 1; i >= 0; i--) {
outArray.RemoveElementAt(removal[i]);
}
}
static void
MergeAndSortArray(const _PrefixArray& array1,
const _PrefixArray& array2,
_PrefixArray& output)
{
output.Clear();
output.AppendElements(array1);
output.AppendElements(array2);
output.Sort();
}
static void
CalculateCheckSum(_PrefixArray& prefixArray, nsCString& checksum)
{
prefixArray.Sort();
nsresult rv;
nsCOMPtr<nsICryptoHash> cryptoHash =
do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
cryptoHash->Init(nsICryptoHash::SHA256);
for (uint32_t i = 0; i < prefixArray.Length(); i++) {
const _Prefix& prefix = prefixArray[i];
cryptoHash->Update(reinterpret_cast<uint8_t*>(
const_cast<char*>(prefix.get())), prefix.Length());
}
cryptoHash->Finish(false, checksum);
}
// N: Number of prefixes, MIN/MAX: minimum/maximum prefix size
// This function will append generated prefixes to outArray.
static void
CreateRandomSortedPrefixArray(uint32_t N,
uint32_t MIN,
uint32_t MAX,
_PrefixArray& outArray)
{
outArray.SetCapacity(outArray.Length() + N);
const uint32_t range = (MAX - MIN + 1);
for (uint32_t i = 0; i < N; i++) {
uint32_t prefixSize = (rand() % range) + MIN;
_Prefix prefix;
prefix.SetLength(prefixSize);
while (true) {
char* dst = prefix.BeginWriting();
for (uint32_t j = 0; j < prefixSize; j++) {
dst[j] = rand() % 256;
}
if (!outArray.Contains(prefix)) {
outArray.AppendElement(prefix);
break;
}
}
}
outArray.Sort();
}
// N: Number of removal indices, MAX: maximum index
static void
CreateRandomRemovalIndices(uint32_t N,
uint32_t MAX,
nsTArray<uint32_t>& outArray)
{
for (uint32_t i = 0; i < N; i++) {
uint32_t idx = rand() % MAX;
if (!outArray.Contains(idx)) {
outArray.InsertElementSorted(idx);
}
}
}
// Function to generate TableUpdateV4.
static void
GenerateUpdateData(bool fullUpdate,
PrefixStringMap& add,
nsTArray<uint32_t>* removal,
nsCString* checksum,
nsTArray<TableUpdate*>& tableUpdates)
{
TableUpdateV4* tableUpdate = new TableUpdateV4(GTEST_TABLE);
tableUpdate->SetFullUpdate(fullUpdate);
for (auto iter = add.ConstIter(); !iter.Done(); iter.Next()) {
nsCString* pstring = iter.Data();
std::string str(pstring->BeginReading(), pstring->Length());
tableUpdate->NewPrefixes(iter.Key(), str);
}
if (removal) {
tableUpdate->NewRemovalIndices(removal->Elements(), removal->Length());
}
if (checksum) {
std::string stdChecksum;
stdChecksum.assign(const_cast<char*>(checksum->BeginReading()), checksum->Length());
tableUpdate->NewChecksum(stdChecksum);
}
tableUpdates.AppendElement(tableUpdate);
}
static void
VerifyPrefixSet(PrefixStringMap& expected)
{
// Verify the prefix set is written to disk.
nsCOMPtr<nsIFile> file;
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
file->AppendNative(GTEST_SAFEBROWSING_DIR);
file->AppendNative(GTEST_PREFIXFILE);
RefPtr<VariableLengthPrefixSet> load = new VariableLengthPrefixSet;
load->Init(GTEST_TABLE);
PrefixStringMap prefixesInFile;
load->LoadFromFile(file);
load->GetPrefixes(prefixesInFile);
for (auto iter = expected.ConstIter(); !iter.Done(); iter.Next()) {
nsCString* expectedPrefix = iter.Data();
nsCString* resultPrefix = prefixesInFile.Get(iter.Key());
ASSERT_TRUE(*resultPrefix == *expectedPrefix);
}
}
static void
Clear()
{
nsCOMPtr<nsIFile> file;
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
UniquePtr<Classifier> classifier(new Classifier());
classifier->Open(*file);
classifier->Reset();
}
static void
testUpdateFail(nsTArray<TableUpdate*>& tableUpdates)
{
nsCOMPtr<nsIFile> file;
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
UniquePtr<Classifier> classifier(new Classifier());
classifier->Open(*file);
nsresult rv = SyncApplyUpdates(classifier.get(), &tableUpdates);
ASSERT_TRUE(NS_FAILED(rv));
}
static void
testUpdate(nsTArray<TableUpdate*>& tableUpdates,
PrefixStringMap& expected)
{
nsCOMPtr<nsIFile> file;
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
{
// Force nsIUrlClassifierUtils loading on main thread
// because nsIUrlClassifierDBService will not run in advance
// in gtest.
nsresult rv;
nsCOMPtr<nsIUrlClassifierUtils> dummy =
do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID, &rv);
ASSERT_TRUE(NS_SUCCEEDED(rv));
}
UniquePtr<Classifier> classifier(new Classifier());
classifier->Open(*file);
nsresult rv = SyncApplyUpdates(classifier.get(), &tableUpdates);
ASSERT_TRUE(rv == NS_OK);
VerifyPrefixSet(expected);
}
static void
testFullUpdate(PrefixStringMap& add, nsCString* checksum)
{
nsTArray<TableUpdate*> tableUpdates;
GenerateUpdateData(true, add, nullptr, checksum, tableUpdates);
testUpdate(tableUpdates, add);
}
static void
testPartialUpdate(PrefixStringMap& add,
nsTArray<uint32_t>* removal,
nsCString* checksum,
PrefixStringMap& expected)
{
nsTArray<TableUpdate*> tableUpdates;
GenerateUpdateData(false, add, removal, checksum, tableUpdates);
testUpdate(tableUpdates, expected);
}
static void
testOpenLookupCache()
{
nsCOMPtr<nsIFile> file;
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
file->AppendNative(GTEST_SAFEBROWSING_DIR);
RunTestInNewThread([&] () -> void {
LookupCacheV4 cache(nsCString(GTEST_TABLE), EmptyCString(), file);
nsresult rv = cache.Init();
ASSERT_EQ(rv, NS_OK);
rv = cache.Open();
ASSERT_EQ(rv, NS_OK);
});
}
// Tests start from here.
TEST(UrlClassifierTableUpdateV4, FixLenghtPSetFullUpdate)
{
srand(time(NULL));
_PrefixArray array;
PrefixStringMap map;
nsCString checksum;
CreateRandomSortedPrefixArray(5000, 4, 4, array);
PrefixArrayToPrefixStringMap(array, map);
CalculateCheckSum(array, checksum);
testFullUpdate(map, &checksum);
Clear();
}
TEST(UrlClassifierTableUpdateV4, VariableLenghtPSetFullUpdate)
{
_PrefixArray array;
PrefixStringMap map;
nsCString checksum;
CreateRandomSortedPrefixArray(5000, 5, 32, array);
PrefixArrayToPrefixStringMap(array, map);
CalculateCheckSum(array, checksum);
testFullUpdate(map, &checksum);
Clear();
}
// This test contain both variable length prefix set and fixed-length prefix set
TEST(UrlClassifierTableUpdateV4, MixedPSetFullUpdate)
{
_PrefixArray array;
PrefixStringMap map;
nsCString checksum;
CreateRandomSortedPrefixArray(5000, 4, 4, array);
CreateRandomSortedPrefixArray(1000, 5, 32, array);
PrefixArrayToPrefixStringMap(array, map);
CalculateCheckSum(array, checksum);
testFullUpdate(map, &checksum);
Clear();
}
TEST(UrlClassifierTableUpdateV4, PartialUpdateWithRemoval)
{
_PrefixArray fArray;
// Apply a full update first.
{
PrefixStringMap fMap;
nsCString checksum;
CreateRandomSortedPrefixArray(10000, 4, 4, fArray);
CreateRandomSortedPrefixArray(2000, 5, 32, fArray);
PrefixArrayToPrefixStringMap(fArray, fMap);
CalculateCheckSum(fArray, checksum);
testFullUpdate(fMap, &checksum);
}
// Apply a partial update with removal.
{
_PrefixArray pArray, mergedArray;
PrefixStringMap pMap, mergedMap;
nsCString checksum;
CreateRandomSortedPrefixArray(5000, 4, 4, pArray);
CreateRandomSortedPrefixArray(1000, 5, 32, pArray);
RemoveIntersection(fArray, pArray);
PrefixArrayToPrefixStringMap(pArray, pMap);
// Remove 1/5 of elements of original prefix set.
nsTArray<uint32_t> removal;
CreateRandomRemovalIndices(fArray.Length() / 5, fArray.Length(), removal);
RemoveElements(removal, fArray);
// Calculate the expected prefix map.
MergeAndSortArray(fArray, pArray, mergedArray);
PrefixArrayToPrefixStringMap(mergedArray, mergedMap);
CalculateCheckSum(mergedArray, checksum);
testPartialUpdate(pMap, &removal, &checksum, mergedMap);
}
Clear();
}
TEST(UrlClassifierTableUpdateV4, PartialUpdateWithoutRemoval)
{
_PrefixArray fArray;
// Apply a full update first.
{
PrefixStringMap fMap;
nsCString checksum;
CreateRandomSortedPrefixArray(10000, 4, 4, fArray);
CreateRandomSortedPrefixArray(2000, 5, 32, fArray);
PrefixArrayToPrefixStringMap(fArray, fMap);
CalculateCheckSum(fArray, checksum);
testFullUpdate(fMap, &checksum);
}
// Apply a partial update without removal
{
_PrefixArray pArray, mergedArray;
PrefixStringMap pMap, mergedMap;
nsCString checksum;
CreateRandomSortedPrefixArray(5000, 4, 4, pArray);
CreateRandomSortedPrefixArray(1000, 5, 32, pArray);
RemoveIntersection(fArray, pArray);
PrefixArrayToPrefixStringMap(pArray, pMap);
// Calculate the expected prefix map.
MergeAndSortArray(fArray, pArray, mergedArray);
PrefixArrayToPrefixStringMap(mergedArray, mergedMap);
CalculateCheckSum(mergedArray, checksum);
testPartialUpdate(pMap, nullptr, &checksum, mergedMap);
}
Clear();
}
// Expect failure because partial update contains prefix already
// in old prefix set.
TEST(UrlClassifierTableUpdateV4, PartialUpdatePrefixAlreadyExist)
{
_PrefixArray fArray;
// Apply a full update fist.
{
PrefixStringMap fMap;
nsCString checksum;
CreateRandomSortedPrefixArray(1000, 4, 32, fArray);
PrefixArrayToPrefixStringMap(fArray, fMap);
CalculateCheckSum(fArray, checksum);
testFullUpdate(fMap, &checksum);
}
// Apply a partial update which contains a prefix in previous full update.
// This should cause an update error.
{
_PrefixArray pArray;
PrefixStringMap pMap;
nsTArray<TableUpdate*> tableUpdates;
// Pick one prefix from full update prefix and add it to partial update.
// This should result a failure when call ApplyUpdates.
pArray.AppendElement(fArray[rand() % fArray.Length()]);
CreateRandomSortedPrefixArray(200, 4, 32, pArray);
PrefixArrayToPrefixStringMap(pArray, pMap);
GenerateUpdateData(false, pMap, nullptr, nullptr, tableUpdates);
testUpdateFail(tableUpdates);
}
Clear();
}
// Test apply partial update directly without applying an full update first.
TEST(UrlClassifierTableUpdateV4, OnlyPartialUpdate)
{
_PrefixArray pArray;
PrefixStringMap pMap;
nsCString checksum;
CreateRandomSortedPrefixArray(5000, 4, 4, pArray);
CreateRandomSortedPrefixArray(1000, 5, 32, pArray);
PrefixArrayToPrefixStringMap(pArray, pMap);
CalculateCheckSum(pArray, checksum);
testPartialUpdate(pMap, nullptr, &checksum, pMap);
Clear();
}
// Test partial update without any ADD prefixes, only removalIndices.
TEST(UrlClassifierTableUpdateV4, PartialUpdateOnlyRemoval)
{
_PrefixArray fArray;
// Apply a full update first.
{
PrefixStringMap fMap;
nsCString checksum;
CreateRandomSortedPrefixArray(5000, 4, 4, fArray);
CreateRandomSortedPrefixArray(1000, 5, 32, fArray);
PrefixArrayToPrefixStringMap(fArray, fMap);
CalculateCheckSum(fArray, checksum);
testFullUpdate(fMap, &checksum);
}
// Apply a partial update without add prefix, only contain removal indices.
{
_PrefixArray pArray;
PrefixStringMap pMap, mergedMap;
nsCString checksum;
// Remove 1/5 of elements of original prefix set.
nsTArray<uint32_t> removal;
CreateRandomRemovalIndices(fArray.Length() / 5, fArray.Length(), removal);
RemoveElements(removal, fArray);
PrefixArrayToPrefixStringMap(fArray, mergedMap);
CalculateCheckSum(fArray, checksum);
testPartialUpdate(pMap, &removal, &checksum, mergedMap);
}
Clear();
}
// Test one tableupdate array contains full update and multiple partial updates.
TEST(UrlClassifierTableUpdateV4, MultipleTableUpdates)
{
_PrefixArray fArray, pArray, mergedArray;
PrefixStringMap fMap, pMap, mergedMap;
nsCString checksum;
nsTArray<TableUpdate*> tableUpdates;
// Generate first full udpate
CreateRandomSortedPrefixArray(10000, 4, 4, fArray);
CreateRandomSortedPrefixArray(2000, 5, 32, fArray);
PrefixArrayToPrefixStringMap(fArray, fMap);
CalculateCheckSum(fArray, checksum);
GenerateUpdateData(true, fMap, nullptr, &checksum, tableUpdates);
// Generate second partial update
CreateRandomSortedPrefixArray(3000, 4, 4, pArray);
CreateRandomSortedPrefixArray(1000, 5, 32, pArray);
RemoveIntersection(fArray, pArray);
PrefixArrayToPrefixStringMap(pArray, pMap);
MergeAndSortArray(fArray, pArray, mergedArray);
CalculateCheckSum(mergedArray, checksum);
GenerateUpdateData(false, pMap, nullptr, &checksum, tableUpdates);
// Generate thrid partial update
fArray.AppendElements(pArray);
fArray.Sort();
pArray.Clear();
CreateRandomSortedPrefixArray(3000, 4, 4, pArray);
CreateRandomSortedPrefixArray(1000, 5, 32, pArray);
RemoveIntersection(fArray, pArray);
PrefixArrayToPrefixStringMap(pArray, pMap);
// Remove 1/5 of elements of original prefix set.
nsTArray<uint32_t> removal;
CreateRandomRemovalIndices(fArray.Length() / 5, fArray.Length(), removal);
RemoveElements(removal, fArray);
MergeAndSortArray(fArray, pArray, mergedArray);
PrefixArrayToPrefixStringMap(mergedArray, mergedMap);
CalculateCheckSum(mergedArray, checksum);
GenerateUpdateData(false, pMap, &removal, &checksum, tableUpdates);
testUpdate(tableUpdates, mergedMap);
Clear();
}
// Test apply full update first, and then apply multiple partial updates
// in one tableupdate array.
TEST(UrlClassifierTableUpdateV4, MultiplePartialUpdateTableUpdates)
{
_PrefixArray fArray;
// Apply a full update first
{
PrefixStringMap fMap;
nsCString checksum;
// Generate first full udpate
CreateRandomSortedPrefixArray(10000, 4, 4, fArray);
CreateRandomSortedPrefixArray(3000, 5, 32, fArray);
PrefixArrayToPrefixStringMap(fArray, fMap);
CalculateCheckSum(fArray, checksum);
testFullUpdate(fMap, &checksum);
}
// Apply multiple partial updates in one table update
{
_PrefixArray pArray, mergedArray;
PrefixStringMap pMap, mergedMap;
nsCString checksum;
nsTArray<uint32_t> removal;
nsTArray<TableUpdate*> tableUpdates;
// Generate first partial update
CreateRandomSortedPrefixArray(3000, 4, 4, pArray);
CreateRandomSortedPrefixArray(1000, 5, 32, pArray);
RemoveIntersection(fArray, pArray);
PrefixArrayToPrefixStringMap(pArray, pMap);
// Remove 1/5 of elements of original prefix set.
CreateRandomRemovalIndices(fArray.Length() / 5, fArray.Length(), removal);
RemoveElements(removal, fArray);
MergeAndSortArray(fArray, pArray, mergedArray);
CalculateCheckSum(mergedArray, checksum);
GenerateUpdateData(false, pMap, &removal, &checksum, tableUpdates);
fArray.AppendElements(pArray);
fArray.Sort();
pArray.Clear();
removal.Clear();
// Generate second partial update.
CreateRandomSortedPrefixArray(2000, 4, 4, pArray);
CreateRandomSortedPrefixArray(1000, 5, 32, pArray);
RemoveIntersection(fArray, pArray);
PrefixArrayToPrefixStringMap(pArray, pMap);
// Remove 1/5 of elements of original prefix set.
CreateRandomRemovalIndices(fArray.Length() / 5, fArray.Length(), removal);
RemoveElements(removal, fArray);
MergeAndSortArray(fArray, pArray, mergedArray);
PrefixArrayToPrefixStringMap(mergedArray, mergedMap);
CalculateCheckSum(mergedArray, checksum);
GenerateUpdateData(false, pMap, &removal, &checksum, tableUpdates);
testUpdate(tableUpdates, mergedMap);
}
Clear();
}
// Test removal indices are larger than the original prefix set.
TEST(UrlClassifierTableUpdateV4, RemovalIndexTooLarge)
{
_PrefixArray fArray;
// Apply a full update first
{
PrefixStringMap fMap;
nsCString checksum;
CreateRandomSortedPrefixArray(1000, 4, 32, fArray);
PrefixArrayToPrefixStringMap(fArray, fMap);
CalculateCheckSum(fArray, checksum);
testFullUpdate(fMap, &checksum);
}
// Apply a partial update with removal indice array larger than
// old prefix set(fArray). This should cause an error.
{
_PrefixArray pArray;
PrefixStringMap pMap;
nsTArray<uint32_t> removal;
nsTArray<TableUpdate*> tableUpdates;
CreateRandomSortedPrefixArray(200, 4, 32, pArray);
RemoveIntersection(fArray, pArray);
PrefixArrayToPrefixStringMap(pArray, pMap);
for (uint32_t i = 0; i < fArray.Length() + 1 ;i++) {
removal.AppendElement(i);
}
GenerateUpdateData(false, pMap, &removal, nullptr, tableUpdates);
testUpdateFail(tableUpdates);
}
Clear();
}
TEST(UrlClassifierTableUpdateV4, ChecksumMismatch)
{
// Apply a full update first
{
_PrefixArray fArray;
PrefixStringMap fMap;
nsCString checksum;
CreateRandomSortedPrefixArray(1000, 4, 32, fArray);
PrefixArrayToPrefixStringMap(fArray, fMap);
CalculateCheckSum(fArray, checksum);
testFullUpdate(fMap, &checksum);
}
// Apply a partial update with incorrect checksum
{
_PrefixArray pArray;
PrefixStringMap pMap;
nsCString checksum;
nsTArray<TableUpdate*> tableUpdates;
CreateRandomSortedPrefixArray(200, 4, 32, pArray);
PrefixArrayToPrefixStringMap(pArray, pMap);
// Checksum should be calculated with both old prefix set and add prefix set,
// here we only calculate checksum with add prefix set to check if applyUpdate
// will return failure.
CalculateCheckSum(pArray, checksum);
GenerateUpdateData(false, pMap, nullptr, &checksum, tableUpdates);
testUpdateFail(tableUpdates);
}
Clear();
}
TEST(UrlClassifierTableUpdateV4, ApplyUpdateThenLoad)
{
// Apply update with checksum
{
_PrefixArray fArray;
PrefixStringMap fMap;
nsCString checksum;
CreateRandomSortedPrefixArray(1000, 4, 32, fArray);
PrefixArrayToPrefixStringMap(fArray, fMap);
CalculateCheckSum(fArray, checksum);
testFullUpdate(fMap, &checksum);
// Open lookup cache will load prefix set and verify the checksum
testOpenLookupCache();
}
Clear();
// Apply update without checksum
{
_PrefixArray fArray;
PrefixStringMap fMap;
CreateRandomSortedPrefixArray(1000, 4, 32, fArray);
PrefixArrayToPrefixStringMap(fArray, fMap);
testFullUpdate(fMap, nullptr);
testOpenLookupCache();
}
Clear();
}
// This test is used to avoid an eror from nsICryptoHash
TEST(UrlClassifierTableUpdateV4, ApplyUpdateWithFixedChecksum)
{
_PrefixArray fArray = { _Prefix("enus"), _Prefix("apollo"), _Prefix("mars"),
_Prefix("Hecatonchires cyclopes"),
_Prefix("vesta"), _Prefix("neptunus"), _Prefix("jupiter"),
_Prefix("diana"), _Prefix("minerva"), _Prefix("ceres"),
_Prefix("Aidos,Adephagia,Adikia,Aletheia"),
_Prefix("hecatonchires"), _Prefix("alcyoneus"), _Prefix("hades"),
_Prefix("vulcanus"), _Prefix("juno"), _Prefix("mercury"),
_Prefix("Stheno, Euryale and Medusa")
};
fArray.Sort();
PrefixStringMap fMap;
PrefixArrayToPrefixStringMap(fArray, fMap);
nsCString checksum("\xae\x18\x94\xd7\xd0\x83\x5f\xc1"
"\x58\x59\x5c\x2c\x72\xb9\x6e\x5e"
"\xf4\xe8\x0a\x6b\xff\x5e\x6b\x81"
"\x65\x34\x06\x16\x06\x59\xa0\x67");
testFullUpdate(fMap, &checksum);
// Open lookup cache will load prefix set and verify the checksum
testOpenLookupCache();
Clear();
}