/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */
// This test mostly focuses on edge cases. But more coverage of normal // operations wouldn't be a bad thing.
#ifdef XP_UNIX # include <unistd.h> # include <sys/types.h> # include <sys/wait.h> #endif
// We can test that certain operations cause expected aborts by forking // and then checking that the child aborted in the expected way (i.e. via // MOZ_CRASH). We skip this for the following configurations. // - On Windows, because it doesn't have fork(). // - On non-DEBUG builds, because the crashes cause the crash reporter to pop // up when running this test locally, which is surprising and annoying. // - On ASAN builds, because ASAN alters the way a MOZ_CRASHing process // terminates, which makes it harder to test if the right thing has occurred. staticvoid TestCrashyOperation(constchar* label, void (*aCrashyOperation)()) { #ifdefined(XP_UNIX) && defined(DEBUG) && !defined(MOZ_ASAN) // We're about to trigger a crash. When it happens don't pause to allow GDB // to be attached.
SAVE_GDB_SLEEP_LOCAL();
int pid = fork();
ASSERT_NE(pid, -1);
if (pid == 0) { // Disable the crashreporter -- writing a crash dump in the child will // prevent the parent from writing a subsequent dump. Crashes here are // expected, so we don't want their stacks to show up in the log anyway.
mozilla::gtest::DisableCrashReporter();
// Child: perform the crashy operation.
FILE* stderr_dup = fdopen(dup(fileno(stderr)), "w"); // We don't want MOZ_CRASH from the crashy operation to print out its // error message and stack-trace, which would be confusing and irrelevant.
fclose(stderr);
aCrashyOperation();
fprintf(stderr_dup, "TestCrashyOperation %s: didn't crash?!\n", label);
ASSERT_TRUE(false); // shouldn't reach here
}
// Parent: check that child crashed as expected. int status;
ASSERT_NE(waitpid(pid, &status, 0), -1);
// The path taken here depends on the platform and configuration.
ASSERT_TRUE(WIFEXITED(status) || WTERMSIG(status)); if (WIFEXITED(status)) { // This occurs if the ah_crap_handler() is run, i.e. we caught the crash. // It returns the number of the caught signal. int signum = WEXITSTATUS(status); if (signum != SIGSEGV && signum != SIGBUS) {
fprintf(stderr, "TestCrashyOperation %s: 'exited' failure: %d\n", label,
signum);
ASSERT_TRUE(false);
}
} elseif (WIFSIGNALED(status)) { // This one occurs if we didn't catch the crash. The exit code is the // number of the terminating signal. int signum = WTERMSIG(status); if (signum != SIGSEGV && signum != SIGBUS) {
fprintf(stderr, "TestCrashyOperation %s: 'signaled' failure: %d\n", label,
signum);
ASSERT_TRUE(false);
}
}
staticvoid InitCapacityOk_InitialEntryStoreTooBig() { // Try the smallest disallowed power-of-two entry store size, which is 2^32 // bytes (which overflows to 0). (Note that the 2^23 *length* gets converted // to a 2^24 *capacity*.)
PLDHashTable t(PLDHashTable::StubOps(), (uint32_t)1 << 8, (uint32_t)1 << 23);
}
staticvoid InitCapacityOk_EntrySizeTooBig() { // Try the smallest disallowed entry size, which is 256 bytes.
PLDHashTable t(PLDHashTable::StubOps(), 256);
}
TEST(PLDHashTableTest, InitCapacityOk)
{ // Try the largest allowed capacity. With kMaxCapacity==1<<26, this // would allocate (if we added an element) 0.5GB of entry store on 32-bit // platforms and 1GB on 64-bit platforms.
PLDHashTable t1(PLDHashTable::StubOps(), sizeof(PLDHashEntryStub),
PLDHashTable::kMaxInitialLength);
// Try the largest allowed power-of-two entry store size, which is 2^31 bytes // (Note that the 2^23 *length* gets converted to a 2^24 *capacity*.)
PLDHashTable t2(PLDHashTable::StubOps(), (uint32_t)1 << 7, (uint32_t)1 << 23);
// Try a too-large capacity (which aborts).
TestCrashyOperation("length too big", InitCapacityOk_InitialLengthTooBig);
// Try a large capacity combined with a large entry size that when multiplied // overflow (causing abort).
TestCrashyOperation("entry store too big",
InitCapacityOk_InitialEntryStoreTooBig);
// Try the largest allowed entry size.
PLDHashTable t3(PLDHashTable::StubOps(), 255);
// Try an overly large entry size.
TestCrashyOperation("entry size too big", InitCapacityOk_EntrySizeTooBig);
// Ideally we'd also try a large-but-ok capacity that almost but doesn't // quite overflow, but that would result in allocating slightly less than 4 // GiB of entry storage. That would be very likely to fail on 32-bit // platforms, so such a test wouldn't be reliable.
}
// PLDHashTable allocates entry storage lazily. Check that all the non-add // operations work appropriately when the table is empty and the storage // hasn't yet been allocated.
// A trivial hash function is good enough here. It's also super-fast for the // GrowToMaxCapacity test because we insert the integers 0.., which means it's // collision-free. static PLDHashNumber TrivialHash(constvoid* key) { return (PLDHashNumber)(size_t)key;
}
// Explicitly test the move constructor. We do this because, due to copy // elision, compilers might optimize away move constructor calls for normal // iterator use.
{
PLDHashTable::Iterator iter1(&t);
PLDHashTable::Iterator iter2(std::move(iter1));
}
// Iterate through the empty table. for (PLDHashTable::Iterator iter(&t); !iter.Done(); iter.Next()) {
(void)iter.Get();
ASSERT_TRUE(false); // shouldn't hit this
}
// Add three entries.
t.Add((constvoid*)77);
t.Add((constvoid*)88);
t.Add((constvoid*)99);
// Check the iterator goes through each entry once. bool saw77 = false, saw88 = false, saw99 = false; int n = 0; for (auto iter(t.Iter()); !iter.Done(); iter.Next()) { auto entry = static_cast<PLDHashEntryStub*>(iter.Get()); if (entry->key == (constvoid*)77) {
saw77 = true;
} if (entry->key == (constvoid*)88) {
saw88 = true;
} if (entry->key == (constvoid*)99) {
saw99 = true;
}
n++;
}
ASSERT_TRUE(saw77 && saw88 && saw99 && n == 3);
t.Clear();
// First, we insert 64 items, which results in a capacity of 128, and a load // factor of 50%. for (intptr_t i = 0; i < 64; i++) {
t.Add((constvoid*)i);
}
ASSERT_EQ(t.EntryCount(), 64u);
ASSERT_EQ(t.Capacity(), 128u);
// The first removing iterator does no removing; capacity and entry count are // unchanged. for (PLDHashTable::Iterator iter(&t); !iter.Done(); iter.Next()) {
(void)iter.Get();
}
ASSERT_EQ(t.EntryCount(), 64u);
ASSERT_EQ(t.Capacity(), 128u);
// The second removing iterator removes 16 items. This reduces the load // factor to 37.5% (48 / 128), which isn't low enough to shrink the table. for (auto iter = t.Iter(); !iter.Done(); iter.Next()) { auto entry = static_cast<PLDHashEntryStub*>(iter.Get()); if ((intptr_t)(entry->key) % 4 == 0) {
iter.Remove();
}
}
ASSERT_EQ(t.EntryCount(), 48u);
ASSERT_EQ(t.Capacity(), 128u);
// The third removing iterator removes another 16 items. This reduces // the load factor to 25% (32 / 128), so the table is shrunk. for (auto iter = t.Iter(); !iter.Done(); iter.Next()) { auto entry = static_cast<PLDHashEntryStub*>(iter.Get()); if ((intptr_t)(entry->key) % 2 == 0) {
iter.Remove();
}
}
ASSERT_EQ(t.EntryCount(), 32u);
ASSERT_EQ(t.Capacity(), 64u);
// The fourth removing iterator removes all remaining items. This reduces // the capacity to the minimum. for (auto iter = t.Iter(); !iter.Done(); iter.Next()) {
iter.Remove();
}
ASSERT_EQ(t.EntryCount(), 0u);
ASSERT_EQ(t.Capacity(), unsigned(PLDHashTable::kMinCapacity));
}
// This test involves resizing a table repeatedly up to 512 MiB in size. On // 32-bit platforms (Win32, Android) it sometimes OOMs, causing the test to // fail. (See bug 931062 and bug 1267227.) Therefore, we only run it on 64-bit // platforms where OOM is much less likely. // // Also, it's slow, and so should always be last. #ifdef HAVE_64BIT_BUILD
TEST(PLDHashTableTest, GrowToMaxCapacity)
{ // This is infallible.
PLDHashTable* t = new PLDHashTable(&trivialOps, sizeof(PLDHashEntryStub), 128);
// Keep inserting elements until failure occurs because the table is full.
size_t numInserted = 0; while (true) { if (!t->Add((constvoid*)numInserted, mozilla::fallible)) { break;
}
numInserted++;
}
// We stop when the element count is 96.875% of PLDHashTable::kMaxCapacity // (see MaxLoadOnGrowthFailure()). if (numInserted !=
PLDHashTable::kMaxCapacity - (PLDHashTable::kMaxCapacity >> 5)) { delete t;
ASSERT_TRUE(false);
}
delete t;
} #endif
¤ Dauer der Verarbeitung: 0.31 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.