blob: e62d769f3add8a9dd03c8deaade11106a3f4d453 [file] [log] [blame]
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
* Copyright (C) 2016-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "Counters.h"
#include "MoveOnly.h"
#include "RefLogger.h"
#include <string>
#include <wtf/HashCountedSet.h>
#include <wtf/text/StringHash.h>
namespace TestWebKitAPI {
typedef WTF::HashCountedSet<int> IntHashCountedSet;
TEST(WTF_HashCountedSet, HashTableIteratorComparison)
{
IntHashCountedSet hashCountedSet;
hashCountedSet.add(1);
ASSERT_TRUE(hashCountedSet.begin() != hashCountedSet.end());
ASSERT_FALSE(hashCountedSet.begin() == hashCountedSet.end());
IntHashCountedSet::const_iterator begin = hashCountedSet.begin();
ASSERT_TRUE(begin == hashCountedSet.begin());
ASSERT_TRUE(hashCountedSet.begin() == begin);
ASSERT_TRUE(begin != hashCountedSet.end());
ASSERT_TRUE(hashCountedSet.end() != begin);
ASSERT_FALSE(begin != hashCountedSet.begin());
ASSERT_FALSE(hashCountedSet.begin() != begin);
ASSERT_FALSE(begin == hashCountedSet.end());
ASSERT_FALSE(hashCountedSet.end() == begin);
}
struct TestDoubleHashTraits : HashTraits<double> {
static const int minimumTableSize = 8;
};
typedef HashCountedSet<double, DefaultHash<double>, TestDoubleHashTraits> DoubleHashCountedSet;
static int bucketForKey(double key)
{
return DefaultHash<double>::hash(key) & (TestDoubleHashTraits::minimumTableSize - 1);
}
TEST(WTF_HashCountedSet, DoubleHashCollisions)
{
// The "clobber" key here is one that ends up stealing the bucket that the -0 key
// originally wants to be in. This makes the 0 and -0 keys collide and the test then
// fails unless the FloatHash::equals() implementation can distinguish them.
const double clobberKey = 6;
const double zeroKey = 0;
const double negativeZeroKey = -zeroKey;
DoubleHashCountedSet hashCountedSet;
hashCountedSet.add(clobberKey);
ASSERT_EQ(hashCountedSet.count(clobberKey), 1u);
ASSERT_EQ(hashCountedSet.count(zeroKey), 0u);
ASSERT_EQ(hashCountedSet.count(negativeZeroKey), 0u);
hashCountedSet.add(zeroKey);
hashCountedSet.add(negativeZeroKey);
ASSERT_EQ(bucketForKey(clobberKey), bucketForKey(negativeZeroKey));
ASSERT_EQ(hashCountedSet.count(clobberKey), 1u);
ASSERT_EQ(hashCountedSet.count(zeroKey), 1u);
ASSERT_EQ(hashCountedSet.count(negativeZeroKey), 1u);
hashCountedSet.add(clobberKey);
hashCountedSet.add(zeroKey);
hashCountedSet.add(negativeZeroKey);
ASSERT_EQ(hashCountedSet.count(clobberKey), 2u);
ASSERT_EQ(hashCountedSet.count(zeroKey), 2u);
ASSERT_EQ(hashCountedSet.count(negativeZeroKey), 2u);
hashCountedSet.add(clobberKey, 12);
hashCountedSet.add(zeroKey, 15);
hashCountedSet.add(negativeZeroKey, 17);
ASSERT_EQ(hashCountedSet.count(clobberKey), 14u);
ASSERT_EQ(hashCountedSet.count(zeroKey), 17u);
ASSERT_EQ(hashCountedSet.count(negativeZeroKey), 19u);
}
TEST(WTF_HashCountedSet, DoubleHashCollisionsInitialCount)
{
// The "clobber" key here is one that ends up stealing the bucket that the -0 key
// originally wants to be in. This makes the 0 and -0 keys collide and the test then
// fails unless the FloatHash::equals() implementation can distinguish them.
const double clobberKey = 6;
const double zeroKey = 0;
const double negativeZeroKey = -zeroKey;
DoubleHashCountedSet hashCountedSet;
hashCountedSet.add(clobberKey, 5);
ASSERT_EQ(hashCountedSet.count(clobberKey), 5u);
ASSERT_EQ(hashCountedSet.count(zeroKey), 0u);
ASSERT_EQ(hashCountedSet.count(negativeZeroKey), 0u);
hashCountedSet.add(zeroKey, 22);
hashCountedSet.add(negativeZeroKey, 0);
ASSERT_EQ(bucketForKey(clobberKey), bucketForKey(negativeZeroKey));
ASSERT_EQ(hashCountedSet.count(clobberKey), 5u);
ASSERT_EQ(hashCountedSet.count(zeroKey), 22u);
ASSERT_EQ(hashCountedSet.count(negativeZeroKey), 0u);
hashCountedSet.add(clobberKey);
hashCountedSet.add(zeroKey);
hashCountedSet.add(negativeZeroKey);
ASSERT_EQ(hashCountedSet.count(clobberKey), 6u);
ASSERT_EQ(hashCountedSet.count(zeroKey), 23u);
ASSERT_EQ(hashCountedSet.count(negativeZeroKey), 1u);
}
TEST(WTF_HashCountedSet, MoveOnlyKeys)
{
HashCountedSet<MoveOnly> moveOnlyKeys;
for (size_t i = 0; i < 100; ++i) {
MoveOnly moveOnly(i + 1);
moveOnlyKeys.add(WTFMove(moveOnly));
}
for (size_t i = 0; i < 100; ++i) {
auto it = moveOnlyKeys.find(MoveOnly(i + 1));
ASSERT_FALSE(it == moveOnlyKeys.end());
ASSERT_EQ(it->value, 1u);
}
for (size_t i = 0; i < 100; ++i)
ASSERT_FALSE(moveOnlyKeys.add(MoveOnly(i + 1)).isNewEntry);
for (size_t i = 0; i < 100; ++i)
ASSERT_FALSE(moveOnlyKeys.remove(MoveOnly(i + 1)));
for (size_t i = 0; i < 100; ++i)
ASSERT_TRUE(moveOnlyKeys.remove(MoveOnly(i + 1)));
ASSERT_TRUE(moveOnlyKeys.isEmpty());
}
TEST(WTF_HashCountedSet, MoveOnlyKeysInitialCount)
{
HashCountedSet<MoveOnly> moveOnlyKeys;
for (size_t i = 0; i < 100; ++i) {
MoveOnly moveOnly(i + 1);
moveOnlyKeys.add(WTFMove(moveOnly), i + 1);
}
for (size_t i = 0; i < 100; ++i) {
auto it = moveOnlyKeys.find(MoveOnly(i + 1));
ASSERT_FALSE(it == moveOnlyKeys.end());
ASSERT_EQ(it->value, i + 1);
}
for (size_t i = 0; i < 100; ++i)
ASSERT_FALSE(moveOnlyKeys.add(MoveOnly(i + 1)).isNewEntry);
for (size_t i = 0; i < 100; ++i)
ASSERT_EQ(moveOnlyKeys.count(MoveOnly(i + 1)), i + 2);
for (size_t i = 0; i < 100; ++i)
ASSERT_FALSE(moveOnlyKeys.remove(MoveOnly(i + 1)));
for (size_t i = 0; i < 100; ++i)
ASSERT_EQ(moveOnlyKeys.count(MoveOnly(i + 1)), i + 1);
}
TEST(WTF_HashCountedSet, InitializerList)
{
HashCountedSet<String> hashCountedSet = {
"one"_s,
"two"_s,
"three"_s,
"four"_s,
"four"_s,
"four"_s,
"four"_s,
};
EXPECT_EQ(4u, hashCountedSet.size());
EXPECT_EQ(hashCountedSet.count("one"_s), 1u);
EXPECT_EQ(hashCountedSet.count("two"_s), 1u);
EXPECT_EQ(hashCountedSet.count("three"_s), 1u);
EXPECT_EQ(hashCountedSet.count("four"_s), 4u);
}
TEST(WTF_HashCountedSet, InitializerListInitialCount)
{
HashCountedSet<String> hashCountedSet = {
{ String("one"_s), 1u },
{ String("two"_s), 2u },
{ String("three"_s), 3u },
{ String("four"_s), 4u },
};
EXPECT_EQ(4u, hashCountedSet.size());
EXPECT_EQ(hashCountedSet.count("one"_s), 1u);
EXPECT_EQ(hashCountedSet.count("two"_s), 2u);
EXPECT_EQ(hashCountedSet.count("three"_s), 3u);
EXPECT_EQ(hashCountedSet.count("four"_s), 4u);
}
TEST(WTF_HashCountedSet, UniquePtrKey)
{
ConstructorDestructorCounter::TestingScope scope;
HashCountedSet<std::unique_ptr<ConstructorDestructorCounter>> hashCountedSet;
auto uniquePtr = makeUnique<ConstructorDestructorCounter>();
hashCountedSet.add(WTFMove(uniquePtr));
EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount);
hashCountedSet.clear();
EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount);
}
TEST(WTF_HashCountedSet, UniquePtrKeyInitialCount)
{
ConstructorDestructorCounter::TestingScope scope;
HashCountedSet<std::unique_ptr<ConstructorDestructorCounter>> hashCountedSet;
auto uniquePtr = makeUnique<ConstructorDestructorCounter>();
hashCountedSet.add(WTFMove(uniquePtr), 12);
EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount);
hashCountedSet.clear();
EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount);
}
TEST(WTF_HashCountedSet, UniquePtrKey_CustomDeleter)
{
ConstructorDestructorCounter::TestingScope constructorDestructorCounterScope;
DeleterCounter<ConstructorDestructorCounter>::TestingScope deleterCounterScope;
HashCountedSet<std::unique_ptr<ConstructorDestructorCounter, DeleterCounter<ConstructorDestructorCounter>>> hashCountedSet;
std::unique_ptr<ConstructorDestructorCounter, DeleterCounter<ConstructorDestructorCounter>> uniquePtr(new ConstructorDestructorCounter(), DeleterCounter<ConstructorDestructorCounter>());
hashCountedSet.add(WTFMove(uniquePtr));
EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount);
EXPECT_EQ(0u, DeleterCounter<ConstructorDestructorCounter>::deleterCount());
hashCountedSet.clear();
EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount);
EXPECT_EQ(1u, DeleterCounter<ConstructorDestructorCounter>::deleterCount());
}
TEST(WTF_HashCountedSet, UniquePtrKey_FindUsingRawPointer)
{
HashCountedSet<std::unique_ptr<int>> hashCountedSet;
auto uniquePtr = makeUniqueWithoutFastMallocCheck<int>(5);
int* ptr = uniquePtr.get();
hashCountedSet.add(WTFMove(uniquePtr));
auto it = hashCountedSet.find(ptr);
ASSERT_TRUE(it != hashCountedSet.end());
EXPECT_EQ(ptr, it->key.get());
EXPECT_EQ(1u, it->value);
}
TEST(WTF_HashCountedSet, UniquePtrKey_ContainsUsingRawPointer)
{
HashCountedSet<std::unique_ptr<int>> hashCountedSet;
auto uniquePtr = makeUniqueWithoutFastMallocCheck<int>(5);
int* ptr = uniquePtr.get();
hashCountedSet.add(WTFMove(uniquePtr));
EXPECT_EQ(true, hashCountedSet.contains(ptr));
}
TEST(WTF_HashCountedSet, UniquePtrKey_GetUsingRawPointer)
{
HashCountedSet<std::unique_ptr<int>> hashCountedSet;
auto uniquePtr = makeUniqueWithoutFastMallocCheck<int>(5);
int* ptr = uniquePtr.get();
hashCountedSet.add(WTFMove(uniquePtr));
int value = hashCountedSet.count(ptr);
EXPECT_EQ(1, value);
}
TEST(WTF_HashCountedSet, UniquePtrKey_RemoveUsingRawPointer)
{
ConstructorDestructorCounter::TestingScope scope;
HashCountedSet<std::unique_ptr<ConstructorDestructorCounter>> hashCountedSet;
auto uniquePtr = makeUnique<ConstructorDestructorCounter>();
ConstructorDestructorCounter* ptr = uniquePtr.get();
hashCountedSet.add(WTFMove(uniquePtr));
EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount);
bool result = hashCountedSet.remove(ptr);
EXPECT_EQ(true, result);
EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount);
EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount);
}
TEST(WTF_HashCountedSet, RefPtrKey_Add)
{
{
DerivedRefLogger a("a");
HashCountedSet<RefPtr<RefLogger>> hashCountedSet;
RefPtr<RefLogger> ptr(&a);
hashCountedSet.add(ptr);
ASSERT_STREQ("ref(a) ref(a) ", takeLogStr().c_str());
EXPECT_EQ(1U, hashCountedSet.count(ptr));
EXPECT_EQ(1U, hashCountedSet.count(ptr.get()));
}
EXPECT_STREQ("deref(a) deref(a) ", takeLogStr().c_str());
}
TEST(WTF_HashCountedSet, RefPtrKey_AddUsingRelease)
{
{
DerivedRefLogger a("a");
HashCountedSet<RefPtr<RefLogger>> hashCountedSet;
RefPtr<RefLogger> ptr(&a);
hashCountedSet.add(WTFMove(ptr));
}
EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
}
TEST(WTF_HashCountedSet, RefPtrKey_AddUsingMove)
{
{
DerivedRefLogger a("a");
HashCountedSet<RefPtr<RefLogger>> hashCountedSet;
RefPtr<RefLogger> ptr(&a);
hashCountedSet.add(WTFMove(ptr));
}
EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
}
TEST(WTF_HashCountedSet, RefPtrKey_AddUsingRaw)
{
{
DerivedRefLogger a("a");
HashCountedSet<RefPtr<RefLogger>> hashCountedSet;
RefPtr<RefLogger> ptr(&a);
hashCountedSet.add(ptr.get());
EXPECT_STREQ("ref(a) ref(a) ", takeLogStr().c_str());
EXPECT_EQ(1U, hashCountedSet.count(ptr));
EXPECT_EQ(1U, hashCountedSet.count(ptr.get()));
}
EXPECT_STREQ("deref(a) deref(a) ", takeLogStr().c_str());
}
TEST(WTF_HashCountedSet, RefPtrKey_AddKeyAlreadyPresent)
{
{
DerivedRefLogger a("a");
HashCountedSet<RefPtr<RefLogger>> hashCountedSet;
{
RefPtr<RefLogger> ptr(&a);
hashCountedSet.add(ptr);
}
EXPECT_STREQ("ref(a) ref(a) deref(a) ", takeLogStr().c_str());
{
RefPtr<RefLogger> ptr2(&a);
auto addResult = hashCountedSet.add(ptr2);
EXPECT_FALSE(addResult.isNewEntry);
}
EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
}
EXPECT_STREQ("deref(a) ", takeLogStr().c_str());
}
TEST(WTF_HashCountedSet, RefPtrKey_AddUsingReleaseKeyAlreadyPresent)
{
{
DerivedRefLogger a("a");
HashCountedSet<RefPtr<RefLogger>> hashCountedSet;
{
RefPtr<RefLogger> ptr(&a);
hashCountedSet.add(ptr);
}
EXPECT_STREQ("ref(a) ref(a) deref(a) ", takeLogStr().c_str());
{
RefPtr<RefLogger> ptr2(&a);
auto addResult = hashCountedSet.add(WTFMove(ptr2));
EXPECT_FALSE(addResult.isNewEntry);
}
EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
}
EXPECT_STREQ("deref(a) ", takeLogStr().c_str());
}
TEST(WTF_HashCountedSet, RefPtrKey_AddUsingMoveKeyAlreadyPresent)
{
{
DerivedRefLogger a("a");
HashCountedSet<RefPtr<RefLogger>> hashCountedSet;
{
RefPtr<RefLogger> ptr(&a);
hashCountedSet.add(ptr);
}
EXPECT_STREQ("ref(a) ref(a) deref(a) ", takeLogStr().c_str());
{
RefPtr<RefLogger> ptr2(&a);
auto addResult = hashCountedSet.add(WTFMove(ptr2));
EXPECT_FALSE(addResult.isNewEntry);
}
EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str());
}
EXPECT_STREQ("deref(a) ", takeLogStr().c_str());
}
TEST(WTF_HashCountedSet, Values)
{
HashCountedSet<int> set { 1, 1, 2, 3, 3 };
auto vector = copyToVector(set.values());
EXPECT_EQ(3U, vector.size());
std::sort(vector.begin(), vector.end());
EXPECT_EQ(1, vector[0]);
EXPECT_EQ(2, vector[1]);
EXPECT_EQ(3, vector[2]);
}
} // namespace TestWebKitAPI