diff options
Diffstat (limited to 'Tools/TestWebKitAPI/Tests/WTF')
43 files changed, 11091 insertions, 0 deletions
diff --git a/Tools/TestWebKitAPI/Tests/WTF/AtomicString.cpp b/Tools/TestWebKitAPI/Tests/WTF/AtomicString.cpp new file mode 100644 index 000000000..e23943143 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/AtomicString.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 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 <wtf/text/AtomicString.h> + +namespace TestWebKitAPI { + +TEST(WTF, AtomicStringCreationFromLiteral) +{ + AtomicString stringWithTemplate("Template Literal", AtomicString::ConstructFromLiteral); + ASSERT_EQ(strlen("Template Literal"), stringWithTemplate.length()); + ASSERT_TRUE(stringWithTemplate == "Template Literal"); + ASSERT_TRUE(stringWithTemplate.string().is8Bit()); + + const char* programmaticStringData = "Explicit Size Literal"; + AtomicString programmaticString(programmaticStringData, strlen(programmaticStringData), AtomicString::ConstructFromLiteral); + ASSERT_EQ(strlen(programmaticStringData), programmaticString.length()); + ASSERT_TRUE(programmaticString.string().is8Bit()); + ASSERT_EQ(programmaticStringData, reinterpret_cast<const char*>(programmaticString.string().characters8())); +} + +TEST(WTF, AtomicStringCreationFromLiteralUniqueness) +{ + AtomicString string1("Template Literal", AtomicString::ConstructFromLiteral); + AtomicString string2("Template Literal", AtomicString::ConstructFromLiteral); + ASSERT_EQ(string1.impl(), string2.impl()); + + AtomicString string3("Template Literal"); + ASSERT_EQ(string1.impl(), string3.impl()); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/BloomFilter.cpp b/Tools/TestWebKitAPI/Tests/WTF/BloomFilter.cpp new file mode 100644 index 000000000..802165211 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/BloomFilter.cpp @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2015 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 <wtf/BloomFilter.h> +#include <wtf/RandomNumber.h> +#include <wtf/SHA1.h> + +namespace TestWebKitAPI { + +static Vector<unsigned> generateRandomHashes(size_t hashCount) +{ + Vector<unsigned> hashes; + for (unsigned i = 0; i < hashCount; ++i) + hashes.append(static_cast<unsigned>(randomNumber() * std::numeric_limits<unsigned>::max())); + return hashes; +} + +static Vector<SHA1::Digest> generateRandomDigests(size_t hashCount) +{ + Vector<SHA1::Digest> hashes; + SHA1 sha1; + for (unsigned i = 0; i < hashCount; ++i) { + double random = randomNumber(); + sha1.addBytes(reinterpret_cast<uint8_t*>(&random), sizeof(double)); + SHA1::Digest digest; + sha1.computeHash(digest); + hashes.append(digest); + } + return hashes; +} + +TEST(WTF_BloomFilter, Basic) +{ + const unsigned hashCount = 1000; + auto hashes = generateRandomHashes(hashCount); + + BloomFilter<16> filter; + for (auto hash : hashes) + filter.add(hash); + + for (auto hash : hashes) + EXPECT_TRUE(filter.mayContain(hash)); + + auto moreHashes = generateRandomHashes(hashCount); + unsigned mayContainCount = 0; + for (auto hash : moreHashes) + mayContainCount += filter.mayContain(hash) ? 1 : 0; + // False positive rate is ~0.09% so this should always be true. + EXPECT_TRUE(mayContainCount < hashCount / 10); + + for (auto hash : moreHashes) + filter.add(hash); + + for (auto hash : hashes) + EXPECT_TRUE(filter.mayContain(hash)); + for (auto hash : moreHashes) + EXPECT_TRUE(filter.mayContain(hash)); +} + +TEST(WTF_BloomFilter, BasicDigest) +{ + const unsigned hashCount = 1000; + auto hashes = generateRandomDigests(hashCount); + + BloomFilter<20> filter; + for (auto hash : hashes) + filter.add(hash); + + for (auto hash : hashes) + EXPECT_TRUE(filter.mayContain(hash)); + + auto moreHashes = generateRandomDigests(hashCount); + unsigned mayContainCount = 0; + for (auto hash : moreHashes) + mayContainCount += filter.mayContain(hash) ? 1 : 0; + // False positive rate is ~0.000004% so this should always be true. + EXPECT_TRUE(mayContainCount < hashCount / 10); + + for (auto hash : moreHashes) + filter.add(hash); + + for (auto hash : hashes) + EXPECT_TRUE(filter.mayContain(hash)); + for (auto hash : moreHashes) + EXPECT_TRUE(filter.mayContain(hash)); +} + +TEST(WTF_BloomFilter, BasicCounting) +{ + const unsigned hashCount = 1000; + auto hashes = generateRandomHashes(hashCount); + + CountingBloomFilter<16> filter; + for (auto hash : hashes) + filter.add(hash); + + for (auto hash : hashes) + EXPECT_TRUE(filter.mayContain(hash)); + + for (auto hash : hashes) + filter.add(hash); + + for (auto hash : hashes) + EXPECT_TRUE(filter.mayContain(hash)); + + for (auto hash : hashes) + filter.remove(hash); + + for (auto hash : hashes) + EXPECT_TRUE(filter.mayContain(hash)); + + auto moreHashes = generateRandomHashes(hashCount); + unsigned mayContainCount = 0; + for (auto hash : moreHashes) + mayContainCount += filter.mayContain(hash) ? 1 : 0; + // False positive rate is ~0.09% so this should always be true. + EXPECT_TRUE(mayContainCount < hashCount / 10); + + for (auto hash : moreHashes) + filter.add(hash); + for (auto hash : hashes) + filter.remove(hash); + + for (auto hash : moreHashes) + EXPECT_TRUE(filter.mayContain(hash)); + + for (auto hash : moreHashes) + filter.remove(hash); + + for (auto hash : hashes) + EXPECT_TRUE(!filter.mayContain(hash)); + for (auto hash : moreHashes) + EXPECT_TRUE(!filter.mayContain(hash)); +} + +TEST(WTF_BloomFilter, Clear) +{ + const unsigned hashCount = 1000; + auto hashes = generateRandomHashes(hashCount); + + BloomFilter<16> filter; + for (auto hash : hashes) + filter.add(hash); + + filter.clear(); + + for (auto hash : hashes) + EXPECT_TRUE(!filter.mayContain(hash)); +} + +TEST(WTF_BloomFilter, ClearCounting) +{ + const unsigned hashCount = 1000; + auto hashes = generateRandomHashes(hashCount); + + CountingBloomFilter<16> filter; + for (auto hash : hashes) + filter.add(hash); + for (auto hash : hashes) + filter.add(hash); + + filter.clear(); + + for (auto hash : hashes) + EXPECT_TRUE(!filter.mayContain(hash)); +} + +TEST(WTF_BloomFilter, CountingOverflow) +{ + const unsigned hashCount = 1000; + auto hashes = generateRandomHashes(hashCount); + + CountingBloomFilter<16> filter; + for (auto hash : hashes) + filter.add(hash); + + for (unsigned i = 0; i < filter.maximumCount() + 100; ++i) + filter.add(hashes[0]); + + for (auto hash : hashes) + EXPECT_TRUE(filter.mayContain(hash)); + + for (auto hash : hashes) + filter.remove(hash); + + unsigned mayContainCount = 0; + for (auto hash : hashes) { + if (hash == hashes[0]) + EXPECT_TRUE(filter.mayContain(hash)); + else + mayContainCount += filter.mayContain(hash) ? 1 : 0; + } + // False positive rate should be very low. + EXPECT_TRUE(mayContainCount < hashCount / 100); + + for (unsigned i = 0; i < filter.maximumCount() + 100; ++i) + filter.remove(hashes[0]); + + // The bucket has overflowed and is stuck. + EXPECT_TRUE(filter.mayContain(hashes[0])); +} + +TEST(WTF_BloomFilter, Combine) +{ + const unsigned hashCount = 1000; + auto hashes = generateRandomHashes(hashCount); + + BloomFilter<16> filter; + for (auto hash : hashes) + filter.add(hash); + + auto moreHashes = generateRandomHashes(hashCount); + + BloomFilter<16> anotherFilter; + for (auto hash : moreHashes) + anotherFilter.add(hash); + + filter.add(anotherFilter); + + for (auto hash : hashes) + EXPECT_TRUE(filter.mayContain(hash)); + for (auto hash : moreHashes) + EXPECT_TRUE(filter.mayContain(hash)); +} + +} diff --git a/Tools/TestWebKitAPI/Tests/WTF/CString.cpp b/Tools/TestWebKitAPI/Tests/WTF/CString.cpp new file mode 100644 index 000000000..735d7dd8b --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/CString.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2012 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 <wtf/text/CString.h> + +TEST(WTF, CStringNullStringConstructor) +{ + CString string; + ASSERT_TRUE(string.isNull()); + ASSERT_EQ(string.data(), static_cast<const char*>(0)); + ASSERT_EQ(string.length(), static_cast<size_t>(0)); + + CString stringFromCharPointer(static_cast<const char*>(0)); + ASSERT_TRUE(stringFromCharPointer.isNull()); + ASSERT_EQ(stringFromCharPointer.data(), static_cast<const char*>(0)); + ASSERT_EQ(stringFromCharPointer.length(), static_cast<size_t>(0)); + + CString stringFromCharAndLength(static_cast<const char*>(0), 0); + ASSERT_TRUE(stringFromCharAndLength.isNull()); + ASSERT_EQ(stringFromCharAndLength.data(), static_cast<const char*>(0)); + ASSERT_EQ(stringFromCharAndLength.length(), static_cast<size_t>(0)); +} + +TEST(WTF, CStringEmptyEmptyConstructor) +{ + const char* emptyString = ""; + CString string(emptyString); + ASSERT_FALSE(string.isNull()); + ASSERT_EQ(string.length(), static_cast<size_t>(0)); + ASSERT_EQ(string.data()[0], 0); + + CString stringWithLength(emptyString, 0); + ASSERT_FALSE(stringWithLength.isNull()); + ASSERT_EQ(stringWithLength.length(), static_cast<size_t>(0)); + ASSERT_EQ(stringWithLength.data()[0], 0); +} + +TEST(WTF, CStringEmptyRegularConstructor) +{ + const char* referenceString = "WebKit"; + + CString string(referenceString); + ASSERT_FALSE(string.isNull()); + ASSERT_EQ(string.length(), strlen(referenceString)); + ASSERT_STREQ(referenceString, string.data()); + + CString stringWithLength(referenceString, 6); + ASSERT_FALSE(stringWithLength.isNull()); + ASSERT_EQ(stringWithLength.length(), strlen(referenceString)); + ASSERT_STREQ(referenceString, stringWithLength.data()); +} + +TEST(WTF, CStringUninitializedConstructor) +{ + char* buffer; + CString emptyString = CString::newUninitialized(0, buffer); + ASSERT_FALSE(emptyString.isNull()); + ASSERT_EQ(buffer, emptyString.data()); + ASSERT_EQ(buffer[0], 0); + + const size_t length = 25; + CString uninitializedString = CString::newUninitialized(length, buffer); + ASSERT_FALSE(uninitializedString.isNull()); + ASSERT_EQ(buffer, uninitializedString.data()); + ASSERT_EQ(uninitializedString.data()[length], 0); +} + +TEST(WTF, CStringZeroTerminated) +{ + const char* referenceString = "WebKit"; + CString stringWithLength(referenceString, 3); + ASSERT_EQ(stringWithLength.data()[3], 0); +} + +TEST(WTF, CStringCopyOnWrite) +{ + const char* initialString = "Webkit"; + CString string(initialString); + CString copy = string; + + string.mutableData()[3] = 'K'; + ASSERT_TRUE(string != copy); + ASSERT_STREQ(string.data(), "WebKit"); + ASSERT_STREQ(copy.data(), initialString); +} + +TEST(WTF, CStringComparison) +{ + // Comparison with another CString. + CString a; + CString b; + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + a = "a"; + b = CString(); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + a = "a"; + b = "b"; + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + a = "a"; + b = "a"; + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + a = "a"; + b = "aa"; + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + a = ""; + b = ""; + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + a = ""; + b = CString(); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + a = "a"; + b = ""; + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + + // Comparison with a const char*. + CString c; + const char* d = 0; + ASSERT_TRUE(c == d); + ASSERT_FALSE(c != d); + c = "c"; + d = 0; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); + c = CString(); + d = "d"; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); + c = "c"; + d = "d"; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); + c = "c"; + d = "c"; + ASSERT_TRUE(c == d); + ASSERT_FALSE(c != d); + c = "c"; + d = "cc"; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); + c = "cc"; + d = "c"; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); + c = ""; + d = ""; + ASSERT_TRUE(c == d); + ASSERT_FALSE(c != d); + c = ""; + d = 0; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); + c = CString(); + d = ""; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); + c = "a"; + d = ""; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); + c = ""; + d = "b"; + ASSERT_FALSE(c == d); + ASSERT_TRUE(c != d); +} diff --git a/Tools/TestWebKitAPI/Tests/WTF/CheckedArithmeticOperations.cpp b/Tools/TestWebKitAPI/Tests/WTF/CheckedArithmeticOperations.cpp new file mode 100644 index 000000000..d6b548316 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/CheckedArithmeticOperations.cpp @@ -0,0 +1,487 @@ +/* + * Copyright (C) 2011, 2015 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 <wtf/CheckedArithmetic.h> + +namespace TestWebKitAPI { + +class OverflowCrashLogger { +protected: + void overflowed() + { + m_overflowCount++; + } + + void clearOverflow() + { + m_overflowCount = 0; + } + + static void crash() + { + s_didCrash = true; + } + +public: + void reset() + { + m_overflowCount = 0; + s_didCrash = false; + } + + bool hasOverflowed() const { return m_overflowCount > 0; } + int overflowCount() const { return m_overflowCount; } + + bool didCrash() const { return s_didCrash; } + +private: + int m_overflowCount { 0 }; + static bool s_didCrash; +}; + +bool OverflowCrashLogger::s_didCrash = false; + +template <typename type> +static void resetOverflow(Checked<type, OverflowCrashLogger>& value) +{ + value.reset(); + value = 100; + value *= std::numeric_limits<type>::max(); +} + +#define CheckedArithmeticTest(type, Coercer, MixedSignednessTester) \ + TEST(WTF, Checked_##type) \ + { \ + typedef Coercer<type> CoercerType; \ + typedef MixedSignednessTester<type, CoercerType> MixedSignednessTesterType; \ + CheckedArithmeticTester<type, CoercerType, MixedSignednessTesterType>::run(); \ + } + +#define coerceLiteral(x) Coercer::coerce(x) + +template <typename type, typename Coercer, typename MixedSignednessTester> +class CheckedArithmeticTester { +public: + static void run() + { + Checked<type, RecordOverflow> value; + EXPECT_EQ(coerceLiteral(0), value.unsafeGet()); + EXPECT_EQ(std::numeric_limits<type>::max(), (value + std::numeric_limits<type>::max()).unsafeGet()); + EXPECT_EQ(std::numeric_limits<type>::max(), (std::numeric_limits<type>::max() + value).unsafeGet()); + EXPECT_EQ(std::numeric_limits<type>::min(), (value + std::numeric_limits<type>::min()).unsafeGet()); + EXPECT_EQ(std::numeric_limits<type>::min(), (std::numeric_limits<type>::min() + value).unsafeGet()); + + EXPECT_EQ(coerceLiteral(0), (value * coerceLiteral(0)).unsafeGet()); + EXPECT_EQ(coerceLiteral(0), (coerceLiteral(0) * value).unsafeGet()); + EXPECT_EQ(coerceLiteral(0), (value * value).unsafeGet()); + EXPECT_EQ(coerceLiteral(0), (value - coerceLiteral(0)).unsafeGet()); + EXPECT_EQ(coerceLiteral(0), (coerceLiteral(0) - value).unsafeGet()); + EXPECT_EQ(coerceLiteral(0), (value - value).unsafeGet()); + EXPECT_EQ(coerceLiteral(0), (value++).unsafeGet()); + EXPECT_EQ(coerceLiteral(1), (value--).unsafeGet()); + EXPECT_EQ(coerceLiteral(1), (++value).unsafeGet()); + EXPECT_EQ(coerceLiteral(0), (--value).unsafeGet()); + EXPECT_EQ(coerceLiteral(10), (value += coerceLiteral(10)).unsafeGet()); + EXPECT_EQ(coerceLiteral(10), value.unsafeGet()); + EXPECT_EQ(coerceLiteral(100), (value *= coerceLiteral(10)).unsafeGet()); + EXPECT_EQ(coerceLiteral(100), value.unsafeGet()); + EXPECT_EQ(coerceLiteral(0), (value -= coerceLiteral(100)).unsafeGet()); + EXPECT_EQ(coerceLiteral(0), value.unsafeGet()); + value = 10; + EXPECT_EQ(coerceLiteral(10), value.unsafeGet()); + EXPECT_EQ(coerceLiteral(0), (value - coerceLiteral(10)).unsafeGet()); + EXPECT_EQ(coerceLiteral(10), value.unsafeGet()); + + value = std::numeric_limits<type>::min(); + EXPECT_EQ(true, (Checked<type, RecordOverflow>(value - coerceLiteral(1))).hasOverflowed()); + EXPECT_EQ(true, !((value--).hasOverflowed())); + EXPECT_EQ(true, value.hasOverflowed()); + value = std::numeric_limits<type>::max(); + EXPECT_EQ(true, !value.hasOverflowed()); + EXPECT_EQ(true, (Checked<type, RecordOverflow>(value + coerceLiteral(1))).hasOverflowed()); + EXPECT_EQ(true, !(value++).hasOverflowed()); + EXPECT_EQ(true, value.hasOverflowed()); + value = std::numeric_limits<type>::max(); + EXPECT_EQ(true, (value += coerceLiteral(1)).hasOverflowed()); + EXPECT_EQ(true, value.hasOverflowed()); + + value = 10; + type _value = 0; + EXPECT_EQ(true, CheckedState::DidNotOverflow == (value * Checked<type, RecordOverflow>(0)).safeGet(_value)); + _value = 0; + EXPECT_EQ(true, CheckedState::DidNotOverflow == (Checked<type, RecordOverflow>(0) * value).safeGet(_value)); + _value = 0; + EXPECT_EQ(true, CheckedState::DidOverflow == (value * Checked<type, RecordOverflow>(std::numeric_limits<type>::max())).safeGet(_value)); + _value = 0; + EXPECT_EQ(true, CheckedState::DidOverflow == (Checked<type, RecordOverflow>(std::numeric_limits<type>::max()) * value).safeGet(_value)); + value = 0; + _value = 0; + EXPECT_EQ(true, CheckedState::DidNotOverflow == (value * Checked<type, RecordOverflow>(std::numeric_limits<type>::max())).safeGet(_value)); + _value = 0; + EXPECT_EQ(true, CheckedState::DidNotOverflow == (Checked<type, RecordOverflow>(std::numeric_limits<type>::max()) * value).safeGet(_value)); + value = 1; + _value = 0; + EXPECT_EQ(true, CheckedState::DidNotOverflow == (value * Checked<type, RecordOverflow>(std::numeric_limits<type>::max())).safeGet(_value)); + _value = 0; + EXPECT_EQ(true, CheckedState::DidNotOverflow == (Checked<type, RecordOverflow>(std::numeric_limits<type>::max()) * value).safeGet(_value)); + _value = 0; + value = 0; + EXPECT_EQ(true, CheckedState::DidNotOverflow == (value * Checked<type, RecordOverflow>(std::numeric_limits<type>::max())).safeGet(_value)); + _value = 0; + EXPECT_EQ(true, CheckedState::DidNotOverflow == (Checked<type, RecordOverflow>(std::numeric_limits<type>::max()) * (type)0).safeGet(_value)); + _value = 0; + value = 1; + EXPECT_EQ(true, CheckedState::DidNotOverflow == (value * Checked<type, RecordOverflow>(std::numeric_limits<type>::max())).safeGet(_value)); + _value = 0; + EXPECT_EQ(true, CheckedState::DidNotOverflow == (Checked<type, RecordOverflow>(std::numeric_limits<type>::max()) * (type)1).safeGet(_value)); + _value = 0; + value = 2; + EXPECT_EQ(true, CheckedState::DidOverflow == (value * Checked<type, RecordOverflow>(std::numeric_limits<type>::max())).safeGet(_value)); + _value = 0; + EXPECT_EQ(true, CheckedState::DidOverflow == (Checked<type, RecordOverflow>(std::numeric_limits<type>::max()) * (type)2).safeGet(_value)); + value = 10; + EXPECT_EQ(true, (value * Checked<type, RecordOverflow>(std::numeric_limits<type>::max())).hasOverflowed()); + + + Checked<type, OverflowCrashLogger> nvalue; // to hold a not overflowed value. + Checked<type, OverflowCrashLogger> ovalue; // to hold an overflowed value. + bool unused; + + _value = 75; + type _largeValue = 100; + type _smallValue = 50; + + value = _smallValue; + nvalue = _value; + ovalue = _value; + + // Make sure the OverflowCrashLogger is working as expected. + EXPECT_EQ(false, (ovalue.hasOverflowed())); + EXPECT_EQ(true, (resetOverflow(ovalue), ovalue.hasOverflowed())); + EXPECT_EQ(false, (resetOverflow(ovalue), ovalue.didCrash())); + EXPECT_EQ(true, (unused = (ovalue == ovalue), ovalue.didCrash())); + EXPECT_EQ(false, (resetOverflow(ovalue), ovalue.didCrash())); + + EXPECT_EQ(false, nvalue.hasOverflowed()); + EXPECT_EQ(false, nvalue.didCrash()); + + // Test operator== that should not overflow nor crash. + EXPECT_EQ(true, (nvalue == nvalue)); + EXPECT_EQ(true, (nvalue == Checked<type, OverflowCrashLogger>(_value))); + EXPECT_EQ(false, (nvalue == value)); + EXPECT_EQ(true, (nvalue == _value)); + EXPECT_EQ(false, (nvalue == Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max()))); + EXPECT_EQ(false, (nvalue == std::numeric_limits<type>::max())); + + EXPECT_EQ(false, nvalue.hasOverflowed()); + EXPECT_EQ(false, nvalue.didCrash()); + + // Test operator!= that should not overflow nor crash. + EXPECT_EQ(false, (nvalue != nvalue)); + EXPECT_EQ(false, (nvalue != Checked<type, OverflowCrashLogger>(_value))); + EXPECT_EQ(true, (nvalue != value)); + EXPECT_EQ(false, (nvalue != _value)); + EXPECT_EQ(true, (nvalue != Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max()))); + EXPECT_EQ(true, (nvalue != std::numeric_limits<type>::max())); + + EXPECT_EQ(false, nvalue.hasOverflowed()); + EXPECT_EQ(false, nvalue.didCrash()); + + // Test operator< that should not overflow nor crash. + EXPECT_EQ(false, (nvalue < nvalue)); + EXPECT_EQ(false, (nvalue < value)); + EXPECT_EQ(true, (nvalue < Checked<type, OverflowCrashLogger>(_largeValue))); + EXPECT_EQ(false, (nvalue < Checked<type, OverflowCrashLogger>(_value))); + EXPECT_EQ(false, (nvalue < Checked<type, OverflowCrashLogger>(_smallValue))); + EXPECT_EQ(true, (nvalue < _largeValue)); + EXPECT_EQ(false, (nvalue < _value)); + EXPECT_EQ(false, (nvalue < _smallValue)); + EXPECT_EQ(true, (nvalue < Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max()))); + EXPECT_EQ(true, (nvalue < std::numeric_limits<type>::max())); + + EXPECT_EQ(false, nvalue.hasOverflowed()); + EXPECT_EQ(false, nvalue.didCrash()); + + // Test operator<= that should not overflow nor crash. + EXPECT_EQ(true, (nvalue <= nvalue)); + EXPECT_EQ(false, (nvalue <= value)); + EXPECT_EQ(true, (nvalue <= Checked<type, OverflowCrashLogger>(_largeValue))); + EXPECT_EQ(true, (nvalue <= Checked<type, OverflowCrashLogger>(_value))); + EXPECT_EQ(false, (nvalue <= Checked<type, OverflowCrashLogger>(_smallValue))); + EXPECT_EQ(true, (nvalue <= _largeValue)); + EXPECT_EQ(true, (nvalue <= _value)); + EXPECT_EQ(false, (nvalue <= _smallValue)); + EXPECT_EQ(true, (nvalue <= Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max()))); + EXPECT_EQ(true, (nvalue <= std::numeric_limits<type>::max())); + + EXPECT_EQ(false, nvalue.hasOverflowed()); + EXPECT_EQ(false, nvalue.didCrash()); + + // Test operator> that should not overflow nor crash. + EXPECT_EQ(false, (nvalue > nvalue)); + EXPECT_EQ(true, (nvalue > value)); + EXPECT_EQ(false, (nvalue > Checked<type, OverflowCrashLogger>(_largeValue))); + EXPECT_EQ(false, (nvalue > Checked<type, OverflowCrashLogger>(_value))); + EXPECT_EQ(true, (nvalue > Checked<type, OverflowCrashLogger>(_smallValue))); + EXPECT_EQ(false, (nvalue > _largeValue)); + EXPECT_EQ(false, (nvalue > _value)); + EXPECT_EQ(true, (nvalue > _smallValue)); + EXPECT_EQ(false, (nvalue > Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max()))); + EXPECT_EQ(false, (nvalue > std::numeric_limits<type>::max())); + + EXPECT_EQ(false, nvalue.hasOverflowed()); + EXPECT_EQ(false, nvalue.didCrash()); + + // Test operator>= that should not overflow nor crash. + EXPECT_EQ(true, (nvalue >= nvalue)); + EXPECT_EQ(true, (nvalue >= value)); + EXPECT_EQ(false, (nvalue >= Checked<type, OverflowCrashLogger>(_largeValue))); + EXPECT_EQ(true, (nvalue >= Checked<type, OverflowCrashLogger>(_value))); + EXPECT_EQ(true, (nvalue >= Checked<type, OverflowCrashLogger>(_smallValue))); + EXPECT_EQ(false, (nvalue >= _largeValue)); + EXPECT_EQ(true, (nvalue >= _value)); + EXPECT_EQ(true, (nvalue >= _smallValue)); + EXPECT_EQ(false, (nvalue >= Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max()))); + EXPECT_EQ(false, (nvalue >= std::numeric_limits<type>::max())); + + EXPECT_EQ(false, nvalue.hasOverflowed()); + EXPECT_EQ(false, nvalue.didCrash()); + + // Test operator== with an overflowed value. + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue == ovalue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue == Checked<type, OverflowCrashLogger>(_value)), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue == value), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue == _value), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue == _value * std::numeric_limits<type>::max()), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue == Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue == std::numeric_limits<type>::max()), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue == nvalue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (nvalue == ovalue), ovalue.didCrash())); + + EXPECT_EQ(false, nvalue.hasOverflowed()); + + // Test operator!= with an overflowed value. + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue != ovalue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue != Checked<type, OverflowCrashLogger>(_value)), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue != value), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue != _value), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue != _value * std::numeric_limits<type>::max()), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue != Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue != std::numeric_limits<type>::max()), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue != nvalue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (nvalue != ovalue), ovalue.didCrash())); + + EXPECT_EQ(false, nvalue.hasOverflowed()); + + // Test operator< with an overflowed value. + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < ovalue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < value), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < Checked<type, OverflowCrashLogger>(_largeValue)), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < Checked<type, OverflowCrashLogger>(_value)), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < Checked<type, OverflowCrashLogger>(_smallValue)), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < _largeValue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < _value), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < _smallValue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < std::numeric_limits<type>::max()), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue < nvalue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (nvalue < ovalue), ovalue.didCrash())); + + EXPECT_EQ(false, nvalue.hasOverflowed()); + + // Test operator<= with an overflowed value. + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= ovalue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= value), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= Checked<type, OverflowCrashLogger>(_largeValue)), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= Checked<type, OverflowCrashLogger>(_value)), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= Checked<type, OverflowCrashLogger>(_smallValue)), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= _largeValue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= _value), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= _smallValue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= std::numeric_limits<type>::max()), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue <= nvalue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (nvalue <= ovalue), ovalue.didCrash())); + + EXPECT_EQ(false, nvalue.hasOverflowed()); + + // Test operator> with an overflowed value. + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > ovalue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > value), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > Checked<type, OverflowCrashLogger>(_largeValue)), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > Checked<type, OverflowCrashLogger>(_value)), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > Checked<type, OverflowCrashLogger>(_smallValue)), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > _largeValue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > _value), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > _smallValue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > std::numeric_limits<type>::max()), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue > nvalue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (nvalue > ovalue), ovalue.didCrash())); + + EXPECT_EQ(false, nvalue.hasOverflowed()); + + // Test operator>= with an overflowed value. + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= ovalue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= value), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= Checked<type, OverflowCrashLogger>(_largeValue)), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= Checked<type, OverflowCrashLogger>(_value)), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= Checked<type, OverflowCrashLogger>(_smallValue)), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= _largeValue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= _value), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= _smallValue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= Checked<type, OverflowCrashLogger>(std::numeric_limits<type>::max())), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= std::numeric_limits<type>::max()), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (ovalue >= nvalue), ovalue.didCrash())); + EXPECT_EQ(true, (resetOverflow(ovalue), unused = (nvalue >= ovalue), ovalue.didCrash())); + + EXPECT_EQ(false, nvalue.hasOverflowed()); + + MixedSignednessTester::run(); + } +}; + +template <typename type, typename Coercer> +class AllowMixedSignednessTest { +public: + static void run() + { + Checked<type, RecordOverflow> value; + value = 10; + + EXPECT_EQ(coerceLiteral(0), (value + -10).unsafeGet()); + EXPECT_EQ(0U, (value - 10U).unsafeGet()); + EXPECT_EQ(coerceLiteral(0), (-10 + value).unsafeGet()); + EXPECT_EQ(0U, (10U - value).unsafeGet()); + value = std::numeric_limits<type>::min(); + EXPECT_EQ(true, (Checked<type, RecordOverflow>(value - 1)).hasOverflowed()); + EXPECT_EQ(true, !(value--).hasOverflowed()); + EXPECT_EQ(true, value.hasOverflowed()); + value = std::numeric_limits<type>::max(); + EXPECT_EQ(true, !value.hasOverflowed()); + EXPECT_EQ(true, (Checked<type, RecordOverflow>(value + 1)).hasOverflowed()); + EXPECT_EQ(true, !(value++).hasOverflowed()); + EXPECT_EQ(true, value.hasOverflowed()); + value = std::numeric_limits<type>::max(); + EXPECT_EQ(true, (value += 1).hasOverflowed()); + EXPECT_EQ(true, value.hasOverflowed()); + value = std::numeric_limits<type>::min(); + EXPECT_EQ(true, (value - 1U).hasOverflowed()); + EXPECT_EQ(true, !(value--).hasOverflowed()); + EXPECT_EQ(true, value.hasOverflowed()); + value = std::numeric_limits<type>::max(); + EXPECT_EQ(true, !value.hasOverflowed()); + EXPECT_EQ(true, (Checked<type, RecordOverflow>(value + 1U)).hasOverflowed()); + EXPECT_EQ(true, !(value++).hasOverflowed()); + EXPECT_EQ(true, value.hasOverflowed()); + value = std::numeric_limits<type>::max(); + EXPECT_EQ(true, (value += 1U).hasOverflowed()); + EXPECT_EQ(true, value.hasOverflowed()); + } +}; + +template <typename type, typename Coercer> +class IgnoreMixedSignednessTest { +public: + static void run() { } +}; + +template <typename type> class CoerceLiteralToUnsigned { +public: + static unsigned coerce(type x) { return static_cast<unsigned>(x); } +}; + +template <typename type> class CoerceLiteralNop { +public: + static type coerce(type x) { return x; } +}; + +CheckedArithmeticTest(int8_t, CoerceLiteralNop, IgnoreMixedSignednessTest) +CheckedArithmeticTest(int16_t, CoerceLiteralNop, IgnoreMixedSignednessTest) +CheckedArithmeticTest(int32_t, CoerceLiteralNop, AllowMixedSignednessTest) +CheckedArithmeticTest(uint32_t, CoerceLiteralToUnsigned, AllowMixedSignednessTest) +CheckedArithmeticTest(int64_t, CoerceLiteralNop, IgnoreMixedSignednessTest) +CheckedArithmeticTest(uint64_t, CoerceLiteralToUnsigned, IgnoreMixedSignednessTest) + +TEST(CheckedArithmeticTest, IsInBounds) +{ + // bigger precision, signed, signed + EXPECT_TRUE(WTF::isInBounds<int32_t>(std::numeric_limits<int16_t>::max())); + EXPECT_TRUE(WTF::isInBounds<int32_t>(std::numeric_limits<int16_t>::min())); + + // bigger precision, unsigned, signed + EXPECT_TRUE(WTF::isInBounds<uint32_t>(std::numeric_limits<int32_t>::max())); + EXPECT_FALSE(WTF::isInBounds<uint32_t>(std::numeric_limits<int16_t>::min())); + + EXPECT_FALSE(WTF::isInBounds<uint32_t>((int32_t)-1)); + EXPECT_FALSE(WTF::isInBounds<uint16_t>((int32_t)-1)); + EXPECT_FALSE(WTF::isInBounds<unsigned long>((int)-1)); + + EXPECT_TRUE(WTF::isInBounds<uint32_t>((int32_t)1)); + EXPECT_TRUE(WTF::isInBounds<uint32_t>((int16_t)1)); + EXPECT_TRUE(WTF::isInBounds<unsigned>((int)1)); + + EXPECT_TRUE(WTF::isInBounds<uint32_t>((int32_t)0)); + EXPECT_TRUE(WTF::isInBounds<uint16_t>((int32_t)0)); + EXPECT_TRUE(WTF::isInBounds<uint32_t>((int16_t)0)); + EXPECT_TRUE(WTF::isInBounds<unsigned>((int)0)); + + EXPECT_TRUE(WTF::isInBounds<uint32_t>(std::numeric_limits<int32_t>::max())); + EXPECT_TRUE(WTF::isInBounds<uint32_t>(std::numeric_limits<int16_t>::max())); + EXPECT_TRUE(WTF::isInBounds<unsigned>(std::numeric_limits<int>::max())); + + // bigger precision, signed, unsigned + EXPECT_TRUE(WTF::isInBounds<int32_t>(std::numeric_limits<uint16_t>::max())); + EXPECT_FALSE(WTF::isInBounds<int32_t>(std::numeric_limits<uint32_t>::max())); + EXPECT_TRUE(WTF::isInBounds<int32_t>((uint32_t)0)); + + // bigger precision, unsigned, unsigned + EXPECT_TRUE(WTF::isInBounds<uint32_t>(std::numeric_limits<uint16_t>::max())); + EXPECT_TRUE(WTF::isInBounds<uint32_t>(std::numeric_limits<uint16_t>::min())); + + // lower precision, signed signed + EXPECT_FALSE(WTF::isInBounds<int16_t>(std::numeric_limits<int32_t>::max())); + EXPECT_FALSE(WTF::isInBounds<int16_t>(std::numeric_limits<int32_t>::min())); + EXPECT_TRUE(WTF::isInBounds<int16_t>((int32_t)-1)); + EXPECT_TRUE(WTF::isInBounds<int16_t>((int32_t)0)); + EXPECT_TRUE(WTF::isInBounds<int16_t>((int32_t)1)); + // lower precision, unsigned, signed + EXPECT_FALSE(WTF::isInBounds<uint16_t>(std::numeric_limits<int32_t>::max())); + EXPECT_FALSE(WTF::isInBounds<uint16_t>(std::numeric_limits<int32_t>::min())); + EXPECT_FALSE(WTF::isInBounds<uint16_t>((int32_t)-1)); + EXPECT_TRUE(WTF::isInBounds<uint16_t>((int32_t)0)); + EXPECT_TRUE(WTF::isInBounds<uint16_t>((int32_t)1)); + // lower precision, signed, unsigned + EXPECT_FALSE(WTF::isInBounds<int16_t>(std::numeric_limits<uint32_t>::max())); + EXPECT_TRUE(WTF::isInBounds<int16_t>((uint32_t)0)); + EXPECT_TRUE(WTF::isInBounds<int16_t>((uint32_t)1)); + // lower precision, unsigned, unsigned + EXPECT_FALSE(WTF::isInBounds<uint16_t>(std::numeric_limits<uint32_t>::max())); + EXPECT_TRUE(WTF::isInBounds<uint16_t>((uint32_t)0)); + EXPECT_TRUE(WTF::isInBounds<uint16_t>((uint32_t)1)); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/Condition.cpp b/Tools/TestWebKitAPI/Tests/WTF/Condition.cpp new file mode 100644 index 000000000..c450d8953 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/Condition.cpp @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2015 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 <mutex> +#include <thread> +#include <wtf/Condition.h> +#include <wtf/DataLog.h> +#include <wtf/Deque.h> +#include <wtf/Lock.h> +#include <wtf/StringPrintStream.h> +#include <wtf/Threading.h> +#include <wtf/Vector.h> + +using namespace WTF; + +namespace TestWebKitAPI { + +namespace { + +const bool verbose = false; + +enum NotifyStyle { + AlwaysNotifyOne, + TacticallyNotifyAll +}; + +template<typename Functor> +void wait(Condition& condition, std::unique_lock<Lock>& locker, const Functor& predicate, std::chrono::microseconds timeout) +{ + if (timeout == std::chrono::microseconds::max()) + condition.wait(locker, predicate); + else { + // This tests timeouts in the sense that it verifies that we can call wait() again after a + // timeout happened. That's a non-trivial piece of functionality since upon timeout the + // ParkingLot has to remove us from the queue. + while (!predicate()) + condition.waitFor(locker, timeout, predicate); + } +} + +void notify(NotifyStyle notifyStyle, Condition& condition, bool shouldNotify) +{ + switch (notifyStyle) { + case AlwaysNotifyOne: + condition.notifyOne(); + break; + case TacticallyNotifyAll: + if (shouldNotify) + condition.notifyAll(); + break; + } +} + +void runTest( + unsigned numProducers, + unsigned numConsumers, + unsigned maxQueueSize, + unsigned numMessagesPerProducer, + NotifyStyle notifyStyle, + std::chrono::microseconds timeout = std::chrono::microseconds::max(), + std::chrono::microseconds delay = std::chrono::microseconds::zero()) +{ + Deque<unsigned> queue; + bool shouldContinue = true; + Lock lock; + Condition emptyCondition; + Condition fullCondition; + + Vector<ThreadIdentifier> consumerThreads; + Vector<ThreadIdentifier> producerThreads; + + Vector<unsigned> received; + Lock receivedLock; + + for (unsigned i = numConsumers; i--;) { + ThreadIdentifier threadIdentifier = createThread( + "Consumer thread", + [&] () { + for (;;) { + unsigned result; + unsigned shouldNotify = false; + { + std::unique_lock<Lock> locker(lock); + wait( + emptyCondition, locker, + [&] () { + if (verbose) + dataLog(toString(currentThread(), ": Checking consumption predicate with shouldContinue = ", shouldContinue, ", queue.size() == ", queue.size(), "\n")); + return !shouldContinue || !queue.isEmpty(); + }, + timeout); + if (!shouldContinue && queue.isEmpty()) + return; + shouldNotify = queue.size() == maxQueueSize; + result = queue.takeFirst(); + } + notify(notifyStyle, fullCondition, shouldNotify); + + { + std::lock_guard<Lock> locker(receivedLock); + received.append(result); + } + } + }); + consumerThreads.append(threadIdentifier); + } + + std::this_thread::sleep_for(delay); + + for (unsigned i = numProducers; i--;) { + ThreadIdentifier threadIdentifier = createThread( + "Producer Thread", + [&] () { + for (unsigned i = 0; i < numMessagesPerProducer; ++i) { + bool shouldNotify = false; + { + std::unique_lock<Lock> locker(lock); + wait( + fullCondition, locker, + [&] () { + if (verbose) + dataLog(toString(currentThread(), ": Checking production predicate with shouldContinue = ", shouldContinue, ", queue.size() == ", queue.size(), "\n")); + return queue.size() < maxQueueSize; + }, + timeout); + shouldNotify = queue.isEmpty(); + queue.append(i); + } + notify(notifyStyle, emptyCondition, shouldNotify); + } + }); + producerThreads.append(threadIdentifier); + } + + for (ThreadIdentifier threadIdentifier : producerThreads) + waitForThreadCompletion(threadIdentifier); + + { + std::lock_guard<Lock> locker(lock); + shouldContinue = false; + } + emptyCondition.notifyAll(); + + for (ThreadIdentifier threadIdentifier : consumerThreads) + waitForThreadCompletion(threadIdentifier); + + EXPECT_EQ(numProducers * numMessagesPerProducer, received.size()); + std::sort(received.begin(), received.end()); + for (unsigned messageIndex = 0; messageIndex < numMessagesPerProducer; ++messageIndex) { + for (unsigned producerIndex = 0; producerIndex < numProducers; ++producerIndex) + EXPECT_EQ(messageIndex, received[messageIndex * numProducers + producerIndex]); + } +} + +} // anonymous namespace + +TEST(WTF_Condition, OneProducerOneConsumerOneSlot) +{ + runTest(1, 1, 1, 100000, TacticallyNotifyAll); +} + +TEST(WTF_Condition, OneProducerOneConsumerOneSlotTimeout) +{ + runTest( + 1, 1, 1, 100000, TacticallyNotifyAll, + std::chrono::microseconds(10000), + std::chrono::microseconds(1000000)); +} + +TEST(WTF_Condition, OneProducerOneConsumerHundredSlots) +{ + runTest(1, 1, 100, 1000000, TacticallyNotifyAll); +} + +TEST(WTF_Condition, TenProducersOneConsumerOneSlot) +{ + runTest(10, 1, 1, 10000, TacticallyNotifyAll); +} + +TEST(WTF_Condition, TenProducersOneConsumerHundredSlotsNotifyAll) +{ + runTest(10, 1, 100, 10000, TacticallyNotifyAll); +} + +TEST(WTF_Condition, TenProducersOneConsumerHundredSlotsNotifyOne) +{ + runTest(10, 1, 100, 10000, AlwaysNotifyOne); +} + +TEST(WTF_Condition, OneProducerTenConsumersOneSlot) +{ + runTest(1, 10, 1, 10000, TacticallyNotifyAll); +} + +TEST(WTF_Condition, OneProducerTenConsumersHundredSlotsNotifyAll) +{ + runTest(1, 10, 100, 100000, TacticallyNotifyAll); +} + +TEST(WTF_Condition, OneProducerTenConsumersHundredSlotsNotifyOne) +{ + runTest(1, 10, 100, 100000, AlwaysNotifyOne); +} + +TEST(WTF_Condition, TenProducersTenConsumersOneSlot) +{ + runTest(10, 10, 1, 50000, TacticallyNotifyAll); +} + +TEST(WTF_Condition, TenProducersTenConsumersHundredSlotsNotifyAll) +{ + runTest(10, 10, 100, 50000, TacticallyNotifyAll); +} + +TEST(WTF_Condition, TenProducersTenConsumersHundredSlotsNotifyOne) +{ + runTest(10, 10, 100, 50000, AlwaysNotifyOne); +} + +TEST(WTF_Condition, TimeoutTimesOut) +{ + Lock lock; + Condition condition; + + lock.lock(); + bool result = condition.waitFor( + lock, std::chrono::microseconds(10000), [] () -> bool { return false; }); + lock.unlock(); + + EXPECT_FALSE(result); +} + +} // namespace TestWebKitAPI + diff --git a/Tools/TestWebKitAPI/Tests/WTF/DateMath.cpp b/Tools/TestWebKitAPI/Tests/WTF/DateMath.cpp new file mode 100644 index 000000000..463041d48 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/DateMath.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2015 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 "Test.h" +#include <wtf/DateMath.h> + +namespace TestWebKitAPI { + +// Note: The results of these function look weird if you do not understand the following mappings: +// dayOfWeek: [0, 6] 0 being Monday, day: [1, 31], month: [0, 11], year: ex: 2011, +// hours: [0, 23], minutes: [0, 59], seconds: [0, 59], utcOffset: [-720,720]. + +TEST(WTF_DateMath, dateToDaysFrom1970) +{ + EXPECT_EQ(0.0, dateToDaysFrom1970(1970, 0, 1)); + EXPECT_EQ(157.0, dateToDaysFrom1970(1970, 5, 7)); + EXPECT_EQ(-145.0, dateToDaysFrom1970(1969, 7, 9)); + EXPECT_EQ(16322, dateToDaysFrom1970(2014, 8, 9)); +} + + +TEST(WTF_DateMath, isLeapYear) +{ + EXPECT_TRUE(isLeapYear(1804)); + EXPECT_FALSE(isLeapYear(1900)); + EXPECT_TRUE(isLeapYear(1968)); + EXPECT_TRUE(isLeapYear(1976)); + EXPECT_TRUE(isLeapYear(2000)); + EXPECT_FALSE(isLeapYear(2010)); + EXPECT_TRUE(isLeapYear(2012)); + EXPECT_FALSE(isLeapYear(2100)); +} + +TEST(WTF_DateMath, msToYear) +{ + EXPECT_EQ(1962, msToYear(-220953600000)); + EXPECT_EQ(1970, msToYear(0)); + EXPECT_EQ(1970, msToYear(100)); + EXPECT_EQ(1977, msToYear(220953600000)); + EXPECT_EQ(2013, msToYear(1365318000000)); +} + +TEST(WTF_DateMath, msToDays) +{ + EXPECT_EQ(0, msToDays(0)); + EXPECT_EQ(2557, msToDays(220953600000)); + EXPECT_EQ(255, msToDays(22095360000)); + EXPECT_EQ(25, msToDays(2209536000)); + EXPECT_EQ(2, msToDays(220953600)); + EXPECT_EQ(0, msToDays(22095360)); + EXPECT_EQ(0, msToDays(2209536)); +} + +TEST(WTF_DateMath, msToMinutes) +{ + EXPECT_EQ(0, msToMinutes(0)); + EXPECT_EQ(0, msToMinutes(220953600000)); + EXPECT_EQ(36, msToMinutes(22095360000)); + EXPECT_EQ(36, msToMinutes(22095360000)); + EXPECT_EQ(45, msToMinutes(2209536000)); + EXPECT_EQ(22, msToMinutes(220953600)); + EXPECT_EQ(8, msToMinutes(22095360)); + EXPECT_EQ(36, msToMinutes(2209536)); +} + +TEST(WTF_DateMath, msToHours) +{ + EXPECT_EQ(0, msToHours(0)); + EXPECT_EQ(8, msToHours(220953600000)); + EXPECT_EQ(17, msToHours(22095360000)); + EXPECT_EQ(13, msToHours(2209536000)); + EXPECT_EQ(13, msToHours(220953600)); + EXPECT_EQ(6, msToHours(22095360)); + EXPECT_EQ(0, msToHours(2209536)); +} + +TEST(WTF_DateMath, dayInYear) +{ + EXPECT_EQ(59, dayInYear(2015, 2, 1)); + EXPECT_EQ(60, dayInYear(2012, 2, 1)); + EXPECT_EQ(0, dayInYear(2015, 0, 1)); + EXPECT_EQ(31, dayInYear(2015, 1, 1)); +} + +TEST(WTF_DateMath, monthFromDayInYear) +{ + EXPECT_EQ(2, monthFromDayInYear(59, false)); + EXPECT_EQ(1, monthFromDayInYear(59, true)); + EXPECT_EQ(2, monthFromDayInYear(60, true)); + EXPECT_EQ(0, monthFromDayInYear(0, false)); + EXPECT_EQ(0, monthFromDayInYear(0, true)); + EXPECT_EQ(1, monthFromDayInYear(31, true)); + EXPECT_EQ(1, monthFromDayInYear(31, false)); +} + +TEST(WTF_DateMath, dayInMonthFromDayInYear) +{ + EXPECT_EQ(1, dayInMonthFromDayInYear(0, false)); + EXPECT_EQ(1, dayInMonthFromDayInYear(0, true)); + EXPECT_EQ(1, dayInMonthFromDayInYear(59, false)); + EXPECT_EQ(29, dayInMonthFromDayInYear(59, true)); + EXPECT_EQ(1, dayInMonthFromDayInYear(60, true)); + EXPECT_EQ(1, dayInMonthFromDayInYear(0, false)); + EXPECT_EQ(1, dayInMonthFromDayInYear(0, true)); + EXPECT_EQ(31, dayInMonthFromDayInYear(30, true)); + EXPECT_EQ(31, dayInMonthFromDayInYear(30, false)); + EXPECT_EQ(31, dayInMonthFromDayInYear(365, true)); + EXPECT_EQ(32, dayInMonthFromDayInYear(365, false)); + EXPECT_EQ(32, dayInMonthFromDayInYear(366, true)); +} + +TEST(WTF_DateMath, calculateLocalTimeOffset) +{ + // DST Start: April 30, 1967 (02:00 am) + LocalTimeOffset dstStart1967 = calculateLocalTimeOffset(-84301200000, WTF::LocalTime); + EXPECT_TRUE(dstStart1967.isDST); + EXPECT_EQ(-25200000, dstStart1967.offset); + + // November 1, 1967 (02:00 am) + LocalTimeOffset dstAlmostEnd1967 = calculateLocalTimeOffset(-68317200000, WTF::LocalTime); + EXPECT_TRUE(dstAlmostEnd1967.isDST); + EXPECT_EQ(-25200000, dstAlmostEnd1967.offset); + + // DST End: November 11, 1967 (02:00 am) + LocalTimeOffset dstEnd1967 = calculateLocalTimeOffset(-67536000000, WTF::LocalTime); + EXPECT_FALSE(dstEnd1967.isDST); + EXPECT_EQ(-25200000, dstStart1967.offset); + + // DST Start: April 3, 1988 (02:00 am) + LocalTimeOffset dstStart1988 = calculateLocalTimeOffset(576054000000, WTF::LocalTime); + EXPECT_TRUE(dstStart1988.isDST); + EXPECT_EQ(-25200000, dstStart1988.offset); + + // DST End: November 4, 2012 (02:00 am) + LocalTimeOffset dstEnd2012 = calculateLocalTimeOffset(1352012400000, WTF::LocalTime); + EXPECT_FALSE(dstEnd2012.isDST); + EXPECT_EQ(-28800000, dstEnd2012.offset); + + // DST Begin: March 8, 2015 + LocalTimeOffset dstBegin2015 = calculateLocalTimeOffset(1425801600000, WTF::LocalTime); + EXPECT_TRUE(dstBegin2015.isDST); + EXPECT_EQ(-25200000, dstBegin2015.offset); + + LocalTimeOffset dstBegin2015UTC = calculateLocalTimeOffset(1425801600000, WTF::UTCTime); + EXPECT_FALSE(dstBegin2015UTC.isDST); + EXPECT_EQ(-28800000, dstBegin2015UTC.offset); + + // DST End: November 1, 2015 + LocalTimeOffset dstEnd2015 = calculateLocalTimeOffset(1446361200000, WTF::LocalTime); + EXPECT_FALSE(dstEnd2015.isDST); + EXPECT_EQ(-28800000, dstEnd2015.offset); + + // DST Begin: March 13, 2016 + LocalTimeOffset dstBegin2016 = calculateLocalTimeOffset(1458111600000, WTF::LocalTime); + EXPECT_TRUE(dstBegin2016.isDST); + EXPECT_EQ(-25200000, dstBegin2016.offset); + + // DST End: November 6, 2016 + LocalTimeOffset dstEnd2016 = calculateLocalTimeOffset(1478415600000, WTF::LocalTime); + EXPECT_FALSE(dstEnd2016.isDST); + EXPECT_EQ(-28800000, dstEnd2016.offset); + + // DST Begin: March 12, 2017 + LocalTimeOffset dstBegin2017 = calculateLocalTimeOffset(1489305600000, WTF::LocalTime); + EXPECT_TRUE(dstBegin2017.isDST); + EXPECT_EQ(-25200000, dstBegin2017.offset); + + // DST End: November 5, 2017 + LocalTimeOffset dstEnd2017 = calculateLocalTimeOffset(1509865200000, WTF::LocalTime); + EXPECT_FALSE(dstEnd2017.isDST); + EXPECT_EQ(-28800000, dstEnd2017.offset); + + // DST Begin: March 11, 2018 + LocalTimeOffset dstBegin2018 = calculateLocalTimeOffset(1520755200000, WTF::LocalTime); + EXPECT_TRUE(dstBegin2018.isDST); + EXPECT_EQ(-25200000, dstBegin2018.offset); + + // DST End: November 4, 2018 + LocalTimeOffset dstEnd2018 = calculateLocalTimeOffset(1541314800000, WTF::LocalTime); + EXPECT_FALSE(dstEnd2018.isDST); + EXPECT_EQ(-28800000, dstEnd2018.offset); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/Deque.cpp b/Tools/TestWebKitAPI/Tests/WTF/Deque.cpp new file mode 100644 index 000000000..db9b0ac13 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/Deque.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2011 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 "MoveOnly.h" +#include <wtf/Deque.h> + +namespace TestWebKitAPI { + +TEST(WTF_Deque, Iterator) +{ + Deque<int> deque; + deque.append(11); + deque.prepend(10); + deque.append(12); + deque.append(13); + + Deque<int>::iterator it = deque.begin(); + Deque<int>::iterator end = deque.end(); + EXPECT_TRUE(end != it); + + EXPECT_EQ(10, *it); + ++it; + EXPECT_EQ(11, *it); + ++it; + EXPECT_EQ(12, *it); + ++it; + EXPECT_EQ(13, *it); + ++it; + + EXPECT_TRUE(end == it); +} + +TEST(WTF_Deque, InitializerList) +{ + Deque<int> deque = { 1, 2, 3, 4 }; + + EXPECT_EQ(4u, deque.size()); + + auto it = deque.begin(); + auto end = deque.end(); + EXPECT_TRUE(end != it); + + EXPECT_EQ(1, *it); + ++it; + EXPECT_EQ(2, *it); + ++it; + EXPECT_EQ(3, *it); + ++it; + EXPECT_EQ(4, *it); + ++it; + + EXPECT_TRUE(end == it); +} + +TEST(WTF, DequeReverseIterator) +{ + Deque<int> deque; + deque.append(11); + deque.prepend(10); + deque.append(12); + deque.append(13); + + Deque<int>::reverse_iterator it = deque.rbegin(); + Deque<int>::reverse_iterator end = deque.rend(); + EXPECT_TRUE(end != it); + + EXPECT_EQ(13, *it); + ++it; + EXPECT_EQ(12, *it); + ++it; + EXPECT_EQ(11, *it); + ++it; + EXPECT_EQ(10, *it); + ++it; + + EXPECT_TRUE(end == it); +} + +TEST(WTF_Deque, Remove) +{ + Deque<int> deque; + deque.append(11); + deque.prepend(10); + deque.append(12); + deque.append(13); + + EXPECT_EQ(10, deque.first()); + EXPECT_EQ(13, deque.last()); + + deque.removeLast(); + EXPECT_EQ(10, deque.first()); + EXPECT_EQ(12, deque.last()); + + deque.removeFirst(); + EXPECT_EQ(11, deque.first()); + EXPECT_EQ(12, deque.last()); + + deque.removeFirst(); + EXPECT_EQ(12, deque.first()); + EXPECT_EQ(12, deque.last()); + + deque.removeLast(); + EXPECT_TRUE(deque.isEmpty()); +} + +TEST(WTF_Deque, MoveOnly) +{ + Deque<MoveOnly> deque; + + deque.append(MoveOnly(1)); + deque.prepend(MoveOnly(0)); + + EXPECT_EQ(0U, deque.first().value()); + EXPECT_EQ(1U, deque.last().value()); + + auto first = deque.takeFirst(); + EXPECT_EQ(0U, first.value()); + + auto last = deque.takeLast(); + EXPECT_EQ(1U, last.value()); +} + +TEST(WTF_Deque, MoveConstructor) +{ + Deque<MoveOnly, 4> deque; + + for (unsigned i = 0; i < 10; ++i) + deque.append(MoveOnly(i)); + + EXPECT_EQ(10u, deque.size()); + + Deque<MoveOnly, 4> deque2 = WTF::move(deque); + + EXPECT_EQ(10u, deque2.size()); + + unsigned i = 0; + for (auto& element : deque2) { + EXPECT_EQ(i, element.value()); + ++i; + } +} + +TEST(WTF_Deque, MoveAssignmentOperator) +{ + Deque<MoveOnly, 4> deque1; + + for (unsigned i = 0; i < 10; ++i) + deque1.append(MoveOnly(i)); + + EXPECT_EQ(10u, deque1.size()); + + Deque<MoveOnly, 4> deque2; + for (unsigned i = 0; i < 10; ++i) + deque2.append(MoveOnly(i * 2)); + + deque1 = WTF::move(deque2); + + EXPECT_EQ(10u, deque2.size()); + + unsigned i = 0; + for (auto& element : deque1) { + EXPECT_EQ(i * 2, element.value()); + ++i; + } +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/Functional.cpp b/Tools/TestWebKitAPI/Tests/WTF/Functional.cpp new file mode 100644 index 000000000..cf8c3c39c --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/Functional.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2011 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 <wtf/RefCounted.h> +#include <wtf/Functional.h> + +namespace TestWebKitAPI { + +static int returnFortyTwo() +{ + return 42; +} + +TEST(FunctionalTest, Basic) +{ + Function<int ()> emptyFunction; + ASSERT_TRUE(emptyFunction.isNull()); + + Function<int ()> returnFortyTwoFunction = bind(returnFortyTwo); + ASSERT_FALSE(returnFortyTwoFunction.isNull()); + ASSERT_EQ(42, returnFortyTwoFunction()); +} + +static int multiplyByTwo(int n) +{ + return n * 2; +} + +static double multiplyByOneAndAHalf(double d) +{ + return d * 1.5; +} + +TEST(FunctionalTest, UnaryBind) +{ + Function<int ()> multiplyFourByTwoFunction = bind(multiplyByTwo, 4); + ASSERT_EQ(8, multiplyFourByTwoFunction()); + + Function<double ()> multiplyByOneAndAHalfFunction = bind(multiplyByOneAndAHalf, 3); + ASSERT_EQ(4.5, multiplyByOneAndAHalfFunction()); +} + +static int multiply(int x, int y) +{ + return x * y; +} + +static int subtract(int x, int y) +{ + return x - y; +} + +TEST(FunctionalTest, BinaryBind) +{ + Function<int ()> multiplyFourByTwoFunction = bind(multiply, 4, 2); + ASSERT_EQ(8, multiplyFourByTwoFunction()); + + Function<int ()> subtractTwoFromFourFunction = bind(subtract, 4, 2); + ASSERT_EQ(2, subtractTwoFromFourFunction()); +} + +class A { +public: + explicit A(int i) + : m_i(i) + { + } + + int f() { return m_i; } + int addF(int j) { return m_i + j; } + +private: + int m_i; +}; + +TEST(FunctionalTest, MemberFunctionBind) +{ + A a(10); + Function<int ()> function1 = bind(&A::f, &a); + ASSERT_EQ(10, function1()); + + Function<int ()> function2 = bind(&A::addF, &a, 15); + ASSERT_EQ(25, function2()); +} + +class B { +public: + B() + : m_numRefCalls(0) + , m_numDerefCalls(0) + { + } + + ~B() + { + } + + void ref() + { + m_numRefCalls++; + } + + void deref() + { + m_numDerefCalls++; + } + + void f() { ASSERT_GT(m_numRefCalls, 0); } + void g(int) { ASSERT_GT(m_numRefCalls, 0); } + + int m_numRefCalls; + int m_numDerefCalls; +}; + +TEST(FunctionalTest, MemberFunctionBindRefDeref) +{ + B b; + + { + Function<void ()> function1 = bind(&B::f, &b); + function1(); + + Function<void ()> function2 = bind(&B::g, &b, 10); + function2(); + } + + ASSERT_TRUE(b.m_numRefCalls == b.m_numDerefCalls); + ASSERT_GT(b.m_numRefCalls, 0); + +} + +class Number : public RefCounted<Number> { +public: + static PassRefPtr<Number> create(int value) + { + return adoptRef(new Number(value)); + } + + ~Number() + { + m_value = 0; + } + + int value() const { return m_value; } + +private: + explicit Number(int value) + : m_value(value) + { + } + + int m_value; +}; + +static int multiplyNumberByTwo(Number* number) +{ + return number->value() * 2; +} + +TEST(FunctionalTest, RefCountedStorage) +{ + RefPtr<Number> five = Number::create(5); + Function<int ()> multiplyFiveByTwoFunction = bind(multiplyNumberByTwo, five); + ASSERT_EQ(10, multiplyFiveByTwoFunction()); + + Function<int ()> multiplyFourByTwoFunction = bind(multiplyNumberByTwo, Number::create(4)); + ASSERT_EQ(8, multiplyFourByTwoFunction()); + + RefPtr<Number> six = Number::create(6); + Function<int ()> multiplySixByTwoFunction = bind(multiplyNumberByTwo, six.release()); + ASSERT_FALSE(six); + ASSERT_EQ(12, multiplySixByTwoFunction()); +} + +namespace RefAndDerefTests { + + template<typename T> struct RefCounted { + void ref(); + void deref(); + }; + struct Connection : RefCounted<Connection> { }; + COMPILE_ASSERT(WTF::HasRefAndDeref<Connection>::value, class_has_ref_and_deref); + + struct NoRefOrDeref { }; + COMPILE_ASSERT(!WTF::HasRefAndDeref<NoRefOrDeref>::value, class_has_no_ref_or_deref); + + struct RefOnly { void ref(); }; + COMPILE_ASSERT(!WTF::HasRefAndDeref<RefOnly>::value, class_has_ref_only); + + struct DerefOnly { void deref(); }; + COMPILE_ASSERT(!WTF::HasRefAndDeref<DerefOnly>::value, class_has_deref_only); + +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/HashMap.cpp b/Tools/TestWebKitAPI/Tests/WTF/HashMap.cpp new file mode 100644 index 000000000..a281f230a --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/HashMap.cpp @@ -0,0 +1,511 @@ +/* + * Copyright (C) 2011 Google 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/HashMap.h> +#include <wtf/text/StringHash.h> + +namespace TestWebKitAPI { + +typedef WTF::HashMap<int, int> IntHashMap; + +TEST(WTF_HashMap, HashTableIteratorComparison) +{ + IntHashMap map; + map.add(1, 2); + ASSERT_TRUE(map.begin() != map.end()); + ASSERT_FALSE(map.begin() == map.end()); + + IntHashMap::const_iterator begin = map.begin(); + ASSERT_TRUE(begin == map.begin()); + ASSERT_TRUE(map.begin() == begin); + ASSERT_TRUE(begin != map.end()); + ASSERT_TRUE(map.end() != begin); + ASSERT_FALSE(begin != map.begin()); + ASSERT_FALSE(map.begin() != begin); + ASSERT_FALSE(begin == map.end()); + ASSERT_FALSE(map.end() == begin); +} + +struct TestDoubleHashTraits : HashTraits<double> { + static const int minimumTableSize = 8; +}; + +typedef HashMap<double, int64_t, DefaultHash<double>::Hash, TestDoubleHashTraits> DoubleHashMap; + +static int bucketForKey(double key) +{ + return DefaultHash<double>::Hash::hash(key) & (TestDoubleHashTraits::minimumTableSize - 1); +} + +TEST(WTF_HashMap, 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; + + DoubleHashMap map; + + map.add(clobberKey, 1); + map.add(zeroKey, 2); + map.add(negativeZeroKey, 3); + + ASSERT_EQ(bucketForKey(clobberKey), bucketForKey(negativeZeroKey)); + ASSERT_EQ(map.get(clobberKey), 1); + ASSERT_EQ(map.get(zeroKey), 2); + ASSERT_EQ(map.get(negativeZeroKey), 3); +} + +TEST(WTF_HashMap, MoveOnlyValues) +{ + HashMap<unsigned, MoveOnly> moveOnlyValues; + + for (size_t i = 0; i < 100; ++i) { + MoveOnly moveOnly(i + 1); + moveOnlyValues.set(i + 1, WTF::move(moveOnly)); + } + + for (size_t i = 0; i < 100; ++i) { + auto it = moveOnlyValues.find(i + 1); + ASSERT_FALSE(it == moveOnlyValues.end()); + } + + for (size_t i = 0; i < 50; ++i) + ASSERT_EQ(moveOnlyValues.take(i + 1).value(), i + 1); + + for (size_t i = 50; i < 100; ++i) + ASSERT_TRUE(moveOnlyValues.remove(i + 1)); + + ASSERT_TRUE(moveOnlyValues.isEmpty()); +} + +TEST(WTF_HashMap, MoveOnlyKeys) +{ + HashMap<MoveOnly, unsigned> moveOnlyKeys; + + for (size_t i = 0; i < 100; ++i) { + MoveOnly moveOnly(i + 1); + moveOnlyKeys.set(WTF::move(moveOnly), i + 1); + } + + for (size_t i = 0; i < 100; ++i) { + auto it = moveOnlyKeys.find(MoveOnly(i + 1)); + ASSERT_FALSE(it == moveOnlyKeys.end()); + } + + for (size_t i = 0; i < 100; ++i) + ASSERT_FALSE(moveOnlyKeys.add(MoveOnly(i + 1), i + 1).isNewEntry); + + for (size_t i = 0; i < 100; ++i) + ASSERT_TRUE(moveOnlyKeys.remove(MoveOnly(i + 1))); + + ASSERT_TRUE(moveOnlyKeys.isEmpty()); +} + +TEST(WTF_HashMap, InitializerList) +{ + HashMap<unsigned, std::string> map = { + { 1, "one" }, + { 2, "two" }, + { 3, "three" }, + { 4, "four" }, + }; + + EXPECT_EQ(4u, map.size()); + + EXPECT_EQ("one", map.get(1)); + EXPECT_EQ("two", map.get(2)); + EXPECT_EQ("three", map.get(3)); + EXPECT_EQ("four", map.get(4)); + EXPECT_EQ(std::string(), map.get(5)); +} + +TEST(WTF_HashMap, EfficientGetter) +{ + HashMap<unsigned, CopyMoveCounter> map; + map.set(1, CopyMoveCounter()); + + { + CopyMoveCounter::TestingScope scope; + map.get(1); + EXPECT_EQ(0U, CopyMoveCounter::constructionCount); + EXPECT_EQ(1U, CopyMoveCounter::copyCount); + EXPECT_EQ(0U, CopyMoveCounter::moveCount); + } + + { + CopyMoveCounter::TestingScope scope; + map.get(2); + EXPECT_EQ(1U, CopyMoveCounter::constructionCount); + EXPECT_EQ(0U, CopyMoveCounter::copyCount); + EXPECT_EQ(1U, CopyMoveCounter::moveCount); + } +} + +TEST(WTF_HashMap, UniquePtrKey) +{ + ConstructorDestructorCounter::TestingScope scope; + + HashMap<std::unique_ptr<ConstructorDestructorCounter>, int> map; + + auto uniquePtr = std::make_unique<ConstructorDestructorCounter>(); + map.add(WTF::move(uniquePtr), 2); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount); + + map.clear(); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount); +} + +TEST(WTF_HashMap, UniquePtrKey_CustomDeleter) +{ + ConstructorDestructorCounter::TestingScope constructorDestructorCounterScope; + DeleterCounter<ConstructorDestructorCounter>::TestingScope deleterCounterScope; + + HashMap<std::unique_ptr<ConstructorDestructorCounter, DeleterCounter<ConstructorDestructorCounter>>, int> map; + + std::unique_ptr<ConstructorDestructorCounter, DeleterCounter<ConstructorDestructorCounter>> uniquePtr(new ConstructorDestructorCounter(), DeleterCounter<ConstructorDestructorCounter>()); + map.add(WTF::move(uniquePtr), 2); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount); + + EXPECT_EQ(0u, DeleterCounter<ConstructorDestructorCounter>::deleterCount); + + map.clear(); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount); + + EXPECT_EQ(1u, DeleterCounter<ConstructorDestructorCounter>::deleterCount); +} + +TEST(WTF_HashMap, UniquePtrKey_FindUsingRawPointer) +{ + HashMap<std::unique_ptr<int>, int> map; + + auto uniquePtr = std::make_unique<int>(5); + int* ptr = uniquePtr.get(); + map.add(WTF::move(uniquePtr), 2); + + auto it = map.find(ptr); + ASSERT_TRUE(it != map.end()); + EXPECT_EQ(ptr, it->key.get()); + EXPECT_EQ(2, it->value); +} + +TEST(WTF_HashMap, UniquePtrKey_ContainsUsingRawPointer) +{ + HashMap<std::unique_ptr<int>, int> map; + + auto uniquePtr = std::make_unique<int>(5); + int* ptr = uniquePtr.get(); + map.add(WTF::move(uniquePtr), 2); + + EXPECT_EQ(true, map.contains(ptr)); +} + +TEST(WTF_HashMap, UniquePtrKey_GetUsingRawPointer) +{ + HashMap<std::unique_ptr<int>, int> map; + + auto uniquePtr = std::make_unique<int>(5); + int* ptr = uniquePtr.get(); + map.add(WTF::move(uniquePtr), 2); + + int value = map.get(ptr); + EXPECT_EQ(2, value); +} + +TEST(WTF_HashMap, UniquePtrKey_RemoveUsingRawPointer) +{ + ConstructorDestructorCounter::TestingScope scope; + + HashMap<std::unique_ptr<ConstructorDestructorCounter>, int> map; + + auto uniquePtr = std::make_unique<ConstructorDestructorCounter>(); + ConstructorDestructorCounter* ptr = uniquePtr.get(); + map.add(WTF::move(uniquePtr), 2); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount); + + bool result = map.remove(ptr); + EXPECT_EQ(true, result); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount); +} + +TEST(WTF_HashMap, UniquePtrKey_TakeUsingRawPointer) +{ + ConstructorDestructorCounter::TestingScope scope; + + HashMap<std::unique_ptr<ConstructorDestructorCounter>, int> map; + + auto uniquePtr = std::make_unique<ConstructorDestructorCounter>(); + ConstructorDestructorCounter* ptr = uniquePtr.get(); + map.add(WTF::move(uniquePtr), 2); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount); + + int result = map.take(ptr); + EXPECT_EQ(2, result); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount); +} + +TEST(WTF_HashMap, RefPtrKey_Add) +{ + HashMap<RefPtr<RefLogger>, int> map; + + DerivedRefLogger a("a"); + RefPtr<RefLogger> ptr(&a); + map.add(ptr, 0); + + ASSERT_STREQ("ref(a) ref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_HashMap, RefPtrKey_AddUsingRelease) +{ + HashMap<RefPtr<RefLogger>, int> map; + + DerivedRefLogger a("a"); + RefPtr<RefLogger> ptr(&a); + map.add(ptr.release(), 0); + + EXPECT_STREQ("ref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_HashMap, RefPtrKey_AddUsingMove) +{ + HashMap<RefPtr<RefLogger>, int> map; + + DerivedRefLogger a("a"); + RefPtr<RefLogger> ptr(&a); + map.add(WTF::move(ptr), 0); + + EXPECT_STREQ("ref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_HashMap, RefPtrKey_AddUsingRaw) +{ + HashMap<RefPtr<RefLogger>, int> map; + + DerivedRefLogger a("a"); + RefPtr<RefLogger> ptr(&a); + map.add(ptr.get(), 0); + + EXPECT_STREQ("ref(a) ref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_HashMap, RefPtrKey_AddKeyAlreadyPresent) +{ + HashMap<RefPtr<RefLogger>, int> map; + + DerivedRefLogger a("a"); + + { + RefPtr<RefLogger> ptr(&a); + map.add(ptr, 0); + } + + EXPECT_STREQ("ref(a) ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr2(&a); + auto addResult = map.add(ptr2, 0); + EXPECT_FALSE(addResult.isNewEntry); + } + + EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_HashMap, RefPtrKey_AddUsingReleaseKeyAlreadyPresent) +{ + HashMap<RefPtr<RefLogger>, int> map; + + DerivedRefLogger a("a"); + + { + RefPtr<RefLogger> ptr(&a); + map.add(ptr, 0); + } + + EXPECT_STREQ("ref(a) ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr2(&a); + auto addResult = map.add(ptr2.release(), 0); + EXPECT_FALSE(addResult.isNewEntry); + } + + EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_HashMap, RefPtrKey_AddUsingMoveKeyAlreadyPresent) +{ + HashMap<RefPtr<RefLogger>, int> map; + + DerivedRefLogger a("a"); + + { + RefPtr<RefLogger> ptr(&a); + map.add(ptr, 0); + } + + EXPECT_STREQ("ref(a) ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr2(&a); + auto addResult = map.add(WTF::move(ptr2), 0); + EXPECT_FALSE(addResult.isNewEntry); + } + + EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_HashMap, RefPtrKey_Set) +{ + HashMap<RefPtr<RefLogger>, int> map; + + DerivedRefLogger a("a"); + RefPtr<RefLogger> ptr(&a); + map.set(ptr, 0); + + ASSERT_STREQ("ref(a) ref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_HashMap, RefPtrKey_SetUsingRelease) +{ + HashMap<RefPtr<RefLogger>, int> map; + + DerivedRefLogger a("a"); + RefPtr<RefLogger> ptr(&a); + map.set(ptr.release(), 0); + + EXPECT_STREQ("ref(a) ", takeLogStr().c_str()); +} + + +TEST(WTF_HashMap, RefPtrKey_SetUsingMove) +{ + HashMap<RefPtr<RefLogger>, int> map; + + DerivedRefLogger a("a"); + RefPtr<RefLogger> ptr(&a); + map.set(WTF::move(ptr), 0); + + EXPECT_STREQ("ref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_HashMap, RefPtrKey_SetUsingRaw) +{ + HashMap<RefPtr<RefLogger>, int> map; + + DerivedRefLogger a("a"); + RefPtr<RefLogger> ptr(&a); + map.set(ptr.get(), 0); + + EXPECT_STREQ("ref(a) ref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_HashMap, RefPtrKey_SetKeyAlreadyPresent) +{ + HashMap<RefPtr<RefLogger>, int> map; + + DerivedRefLogger a("a"); + + RefPtr<RefLogger> ptr(&a); + map.set(ptr, 0); + + EXPECT_STREQ("ref(a) ref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr2(&a); + auto addResult = map.set(ptr2, 1); + EXPECT_FALSE(addResult.isNewEntry); + EXPECT_EQ(1, map.get(ptr.get())); + } + + EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_HashMap, RefPtrKey_SetUsingReleaseKeyAlreadyPresent) +{ + HashMap<RefPtr<RefLogger>, int> map; + + DerivedRefLogger a("a"); + + RefPtr<RefLogger> ptr(&a); + map.set(ptr, 0); + + EXPECT_STREQ("ref(a) ref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr2(&a); + auto addResult = map.set(ptr2.release(), 1); + EXPECT_FALSE(addResult.isNewEntry); + EXPECT_EQ(1, map.get(ptr.get())); + } + + EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_HashMap, RefPtrKey_SetUsingMoveKeyAlreadyPresent) +{ + HashMap<RefPtr<RefLogger>, int> map; + + DerivedRefLogger a("a"); + + RefPtr<RefLogger> ptr(&a); + map.set(ptr, 0); + + EXPECT_STREQ("ref(a) ref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr2(&a); + auto addResult = map.set(WTF::move(ptr2), 1); + EXPECT_FALSE(addResult.isNewEntry); + EXPECT_EQ(1, map.get(ptr.get())); + } + + EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/HashSet.cpp b/Tools/TestWebKitAPI/Tests/WTF/HashSet.cpp new file mode 100644 index 000000000..79cb5e727 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/HashSet.cpp @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2012 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 <wtf/HashSet.h> + +namespace TestWebKitAPI { + +template<int initialCapacity> + struct InitialCapacityTestHashTraits : public WTF::UnsignedWithZeroKeyHashTraits<int> { + static const int minimumTableSize = initialCapacity; +}; + +template<unsigned size> +void testInitialCapacity() +{ + const unsigned initialCapacity = WTF::HashTableCapacityForSize<size>::value; + HashSet<int, DefaultHash<int>::Hash, InitialCapacityTestHashTraits<initialCapacity> > testSet; + + // Initial capacity is null. + ASSERT_EQ(0u, testSet.capacity()); + + // Adding items up to size should never change the capacity. + for (size_t i = 0; i < size; ++i) { + testSet.add(i); + ASSERT_EQ(initialCapacity, static_cast<unsigned>(testSet.capacity())); + } + + // Adding items up to less than half the capacity should not change the capacity. + unsigned capacityLimit = initialCapacity / 2 - 1; + for (size_t i = size; i < capacityLimit; ++i) { + testSet.add(i); + ASSERT_EQ(initialCapacity, static_cast<unsigned>(testSet.capacity())); + } + + // Adding one more item increase the capacity. + testSet.add(initialCapacity); + EXPECT_GT(static_cast<unsigned>(testSet.capacity()), initialCapacity); +} + +template<unsigned size> void generateTestCapacityUpToSize(); +template<> void generateTestCapacityUpToSize<0>() +{ +} +template<unsigned size> void generateTestCapacityUpToSize() +{ + generateTestCapacityUpToSize<size - 1>(); + testInitialCapacity<size>(); +} + +TEST(WTF_HashSet, InitialCapacity) +{ + generateTestCapacityUpToSize<128>(); +} + +TEST(WTF_HashSet, MoveOnly) +{ + HashSet<MoveOnly> hashSet; + + for (size_t i = 0; i < 100; ++i) { + MoveOnly moveOnly(i + 1); + hashSet.add(WTF::move(moveOnly)); + } + + for (size_t i = 0; i < 100; ++i) + EXPECT_TRUE(hashSet.contains(MoveOnly(i + 1))); + + for (size_t i = 0; i < 100; ++i) + EXPECT_TRUE(hashSet.remove(MoveOnly(i + 1))); + + EXPECT_TRUE(hashSet.isEmpty()); + + for (size_t i = 0; i < 100; ++i) + hashSet.add(MoveOnly(i + 1)); + + for (size_t i = 0; i < 100; ++i) + EXPECT_TRUE(hashSet.take(MoveOnly(i + 1)) == MoveOnly(i + 1)); + + EXPECT_TRUE(hashSet.isEmpty()); + + for (size_t i = 0; i < 100; ++i) + hashSet.add(MoveOnly(i + 1)); + + HashSet<MoveOnly> secondSet; + + for (size_t i = 0; i < 100; ++i) + secondSet.add(hashSet.takeAny()); + + EXPECT_TRUE(hashSet.isEmpty()); + + for (size_t i = 0; i < 100; ++i) + EXPECT_TRUE(secondSet.contains(MoveOnly(i + 1))); +} + + +TEST(WTF_HashSet, UniquePtrKey) +{ + ConstructorDestructorCounter::TestingScope scope; + + HashSet<std::unique_ptr<ConstructorDestructorCounter>> set; + + auto uniquePtr = std::make_unique<ConstructorDestructorCounter>(); + set.add(WTF::move(uniquePtr)); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount); + + set.clear(); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount); +} + +TEST(WTF_HashSet, UniquePtrKey_FindUsingRawPointer) +{ + HashSet<std::unique_ptr<int>> set; + + auto uniquePtr = std::make_unique<int>(5); + int* ptr = uniquePtr.get(); + set.add(WTF::move(uniquePtr)); + + auto it = set.find(ptr); + ASSERT_TRUE(it != set.end()); + EXPECT_EQ(ptr, it->get()); + EXPECT_EQ(5, *it->get()); +} + +TEST(WTF_HashSet, UniquePtrKey_ContainsUsingRawPointer) +{ + HashSet<std::unique_ptr<int>> set; + + auto uniquePtr = std::make_unique<int>(5); + int* ptr = uniquePtr.get(); + set.add(WTF::move(uniquePtr)); + + EXPECT_EQ(true, set.contains(ptr)); +} + +TEST(WTF_HashSet, UniquePtrKey_RemoveUsingRawPointer) +{ + ConstructorDestructorCounter::TestingScope scope; + + HashSet<std::unique_ptr<ConstructorDestructorCounter>> set; + + auto uniquePtr = std::make_unique<ConstructorDestructorCounter>(); + ConstructorDestructorCounter* ptr = uniquePtr.get(); + set.add(WTF::move(uniquePtr)); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount); + + bool result = set.remove(ptr); + EXPECT_EQ(true, result); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount); +} + +TEST(WTF_HashSet, UniquePtrKey_TakeUsingRawPointer) +{ + ConstructorDestructorCounter::TestingScope scope; + + HashSet<std::unique_ptr<ConstructorDestructorCounter>> set; + + auto uniquePtr = std::make_unique<ConstructorDestructorCounter>(); + ConstructorDestructorCounter* ptr = uniquePtr.get(); + set.add(WTF::move(uniquePtr)); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount); + + auto result = set.take(ptr); + EXPECT_EQ(ptr, result.get()); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount); + + result = nullptr; + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount); +} + +TEST(WTF_HashSet, CopyEmpty) +{ + { + HashSet<unsigned> foo; + HashSet<unsigned> bar(foo); + + EXPECT_EQ(0u, bar.capacity()); + EXPECT_EQ(0u, bar.size()); + } + { + HashSet<unsigned> foo({ 1, 5, 64, 42 }); + EXPECT_EQ(4u, foo.size()); + foo.remove(1); + foo.remove(5); + foo.remove(42); + foo.remove(64); + HashSet<unsigned> bar(foo); + + EXPECT_EQ(0u, bar.capacity()); + EXPECT_EQ(0u, bar.size()); + } +} + +TEST(WTF_HashSet, CopyAllocateAtLeastMinimumCapacity) +{ + HashSet<unsigned> foo({ 42 }); + EXPECT_EQ(1u, foo.size()); + HashSet<unsigned> bar(foo); + + EXPECT_EQ(8u, bar.capacity()); + EXPECT_EQ(1u, bar.size()); +} + +TEST(WTF_HashSet, CopyCapacityIsNotOnBoundary) +{ + // Starting at 4 because the minimum size is 8. + // With a size of 8, a medium load can be up to 3.3333->3. + // Adding 1 to 3 would reach max load. + // While correct, that's not really what we care about here. + for (unsigned size = 4; size < 100; ++size) { + HashSet<unsigned> source; + for (unsigned i = 1; i < size + 1; ++i) + source.add(i); + + HashSet<unsigned> copy1(source); + HashSet<unsigned> copy2(source); + HashSet<unsigned> copy3(source); + + EXPECT_EQ(size, copy1.size()); + EXPECT_EQ(size, copy2.size()); + EXPECT_EQ(size, copy3.size()); + for (unsigned i = 1; i < size + 1; ++i) { + EXPECT_TRUE(copy1.contains(i)); + EXPECT_TRUE(copy2.contains(i)); + EXPECT_TRUE(copy3.contains(i)); + } + EXPECT_FALSE(copy1.contains(size + 2)); + EXPECT_FALSE(copy2.contains(size + 2)); + EXPECT_FALSE(copy3.contains(size + 2)); + + EXPECT_TRUE(copy2.remove(1)); + EXPECT_EQ(copy1.capacity(), copy2.capacity()); + EXPECT_FALSE(copy2.contains(1)); + + EXPECT_TRUE(copy3.add(size + 2).isNewEntry); + EXPECT_EQ(copy1.capacity(), copy3.capacity()); + EXPECT_TRUE(copy3.contains(size + 2)); + } +} + + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/IntegerToStringConversion.cpp b/Tools/TestWebKitAPI/Tests/WTF/IntegerToStringConversion.cpp new file mode 100644 index 000000000..49a2de326 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/IntegerToStringConversion.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2012 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 <limits> +#include <wtf/StringExtras.h> +#include <wtf/text/CString.h> +#include <wtf/text/WTFString.h> + +template<typename IntegerType> struct PrintfFormatTrait { static const char format[]; }; + +template<> struct PrintfFormatTrait<short> { static const char format[]; }; +const char PrintfFormatTrait<short>::format[] = "%hd"; + +template<> struct PrintfFormatTrait<int> { static const char format[]; }; +const char PrintfFormatTrait<int>::format[] = "%d"; + +template<> struct PrintfFormatTrait<long> { static const char format[]; }; +const char PrintfFormatTrait<long>::format[] = "%ld"; + +template<> struct PrintfFormatTrait<long long> { static const char format[]; }; +#if OS(WINDOWS) +const char PrintfFormatTrait<long long>::format[] = "%I64i"; +#else +const char PrintfFormatTrait<long long>::format[] = "%lli"; +#endif // OS(WINDOWS) + +template<> struct PrintfFormatTrait<unsigned short> { static const char format[]; }; +const char PrintfFormatTrait<unsigned short>::format[] = "%hu"; + +template<> struct PrintfFormatTrait<unsigned> { static const char format[]; }; +const char PrintfFormatTrait<unsigned>::format[] = "%u"; + +template<> struct PrintfFormatTrait<unsigned long> { static const char format[]; }; +const char PrintfFormatTrait<unsigned long>::format[] = "%lu"; + +template<> struct PrintfFormatTrait<unsigned long long> { static const char format[]; }; +#if OS(WINDOWS) +const char PrintfFormatTrait<unsigned long long>::format[] = "%I64u"; +#else +const char PrintfFormatTrait<unsigned long long>::format[] = "%llu"; +#endif // OS(WINDOWS) + + +// FIXME: use snprintf from StringExtras.h +template<typename IntegerType> +void testBoundaries() +{ + const unsigned bufferSize = 256; + Vector<char, bufferSize> buffer; + buffer.resize(bufferSize); + + const IntegerType min = std::numeric_limits<IntegerType>::min(); + CString minStringData = String::number(min).latin1(); + snprintf(buffer.data(), bufferSize, PrintfFormatTrait<IntegerType>::format, min); + ASSERT_STREQ(buffer.data(), minStringData.data()); + + const IntegerType max = std::numeric_limits<IntegerType>::max(); + CString maxStringData = String::number(max).latin1(); + snprintf(buffer.data(), bufferSize, PrintfFormatTrait<IntegerType>::format, max); + ASSERT_STREQ(buffer.data(), maxStringData.data()); +} + +template<typename IntegerType> +void testNumbers() +{ + const unsigned bufferSize = 256; + Vector<char, bufferSize> buffer; + buffer.resize(bufferSize); + + for (int i = -100; i < 100; ++i) { + const IntegerType number = static_cast<IntegerType>(i); + CString numberStringData = String::number(number).latin1(); + snprintf(buffer.data(), bufferSize, PrintfFormatTrait<IntegerType>::format, number); + ASSERT_STREQ(buffer.data(), numberStringData.data()); + } +} + +TEST(WTF, IntegerToStringConversionSignedIntegerBoundaries) +{ + testBoundaries<short>(); + testBoundaries<int>(); + testBoundaries<long>(); + testBoundaries<long long>(); +} + +TEST(WTF, IntegerToStringConversionSignedIntegerRegularNumbers) +{ + testNumbers<short>(); + testNumbers<int>(); + testNumbers<long>(); + testNumbers<long long>(); +} + +TEST(WTF, IntegerToStringConversionUnsignedIntegerBoundaries) +{ + testBoundaries<unsigned short>(); + testBoundaries<unsigned int>(); + testBoundaries<unsigned long>(); + testBoundaries<unsigned long long>(); +} + +TEST(WTF, IntegerToStringConversionUnsignedIntegerRegularNumbers) +{ + testNumbers<unsigned short>(); + testNumbers<unsigned int>(); + testNumbers<unsigned long>(); + testNumbers<unsigned long long>(); +} diff --git a/Tools/TestWebKitAPI/Tests/WTF/ListHashSet.cpp b/Tools/TestWebKitAPI/Tests/WTF/ListHashSet.cpp new file mode 100644 index 000000000..d81dcfcfe --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/ListHashSet.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2012 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 "MoveOnly.h" +#include <wtf/ListHashSet.h> + +namespace TestWebKitAPI { + +TEST(WTF_ListHashSet, RemoveFirst) +{ + ListHashSet<int> list; + list.add(1); + list.add(2); + list.add(3); + + ASSERT_EQ(1, list.first()); + + list.removeFirst(); + ASSERT_EQ(2, list.first()); + + list.removeFirst(); + ASSERT_EQ(3, list.first()); + + list.removeFirst(); + ASSERT_TRUE(list.isEmpty()); +} + +TEST(WTF_ListHashSet, RemoveLast) +{ + ListHashSet<int> list; + list.add(1); + list.add(2); + list.add(3); + + ASSERT_EQ(3, list.last()); + + list.removeLast(); + ASSERT_EQ(2, list.last()); + + list.removeLast(); + ASSERT_EQ(1, list.last()); + + list.removeLast(); + ASSERT_TRUE(list.isEmpty()); +} + +TEST(WTF_ListHashSet, AppendOrMoveToLastNewItems) +{ + ListHashSet<int> list; + ListHashSet<int>::AddResult result = list.appendOrMoveToLast(1); + ASSERT_TRUE(result.isNewEntry); + result = list.add(2); + ASSERT_TRUE(result.isNewEntry); + result = list.appendOrMoveToLast(3); + ASSERT_TRUE(result.isNewEntry); + + ASSERT_EQ(list.size(), 3u); + + // The list should be in order 1, 2, 3. + ListHashSet<int>::iterator iterator = list.begin(); + ASSERT_EQ(1, *iterator); + ++iterator; + ASSERT_EQ(2, *iterator); + ++iterator; + ASSERT_EQ(3, *iterator); + ++iterator; +} + +TEST(WTF_ListHashSet, AppendOrMoveToLastWithDuplicates) +{ + ListHashSet<int> list; + + // Add a single element twice. + ListHashSet<int>::AddResult result = list.add(1); + ASSERT_TRUE(result.isNewEntry); + result = list.appendOrMoveToLast(1); + ASSERT_FALSE(result.isNewEntry); + ASSERT_EQ(1u, list.size()); + + list.add(2); + list.add(3); + ASSERT_EQ(3u, list.size()); + + // Appending 2 move it to the end. + ASSERT_EQ(3, list.last()); + result = list.appendOrMoveToLast(2); + ASSERT_FALSE(result.isNewEntry); + ASSERT_EQ(2, list.last()); + + // Inverse the list by moving each element to end end. + result = list.appendOrMoveToLast(3); + ASSERT_FALSE(result.isNewEntry); + result = list.appendOrMoveToLast(2); + ASSERT_FALSE(result.isNewEntry); + result = list.appendOrMoveToLast(1); + ASSERT_FALSE(result.isNewEntry); + ASSERT_EQ(3u, list.size()); + + ListHashSet<int>::iterator iterator = list.begin(); + ASSERT_EQ(3, *iterator); + ++iterator; + ASSERT_EQ(2, *iterator); + ++iterator; + ASSERT_EQ(1, *iterator); + ++iterator; +} + +TEST(WTF_ListHashSet, PrependOrMoveToLastNewItems) +{ + ListHashSet<int> list; + ListHashSet<int>::AddResult result = list.prependOrMoveToFirst(1); + ASSERT_TRUE(result.isNewEntry); + result = list.add(2); + ASSERT_TRUE(result.isNewEntry); + result = list.prependOrMoveToFirst(3); + ASSERT_TRUE(result.isNewEntry); + + ASSERT_EQ(list.size(), 3u); + + // The list should be in order 3, 1, 2. + ListHashSet<int>::iterator iterator = list.begin(); + ASSERT_EQ(3, *iterator); + ++iterator; + ASSERT_EQ(1, *iterator); + ++iterator; + ASSERT_EQ(2, *iterator); + ++iterator; +} + +TEST(WTF_ListHashSet, PrependOrMoveToLastWithDuplicates) +{ + ListHashSet<int> list; + + // Add a single element twice. + ListHashSet<int>::AddResult result = list.add(1); + ASSERT_TRUE(result.isNewEntry); + result = list.prependOrMoveToFirst(1); + ASSERT_FALSE(result.isNewEntry); + ASSERT_EQ(1u, list.size()); + + list.add(2); + list.add(3); + ASSERT_EQ(3u, list.size()); + + // Prepending 2 move it to the beginning. + ASSERT_EQ(1, list.first()); + result = list.prependOrMoveToFirst(2); + ASSERT_FALSE(result.isNewEntry); + ASSERT_EQ(2, list.first()); + + // Inverse the list by moving each element to the first position. + result = list.prependOrMoveToFirst(1); + ASSERT_FALSE(result.isNewEntry); + result = list.prependOrMoveToFirst(2); + ASSERT_FALSE(result.isNewEntry); + result = list.prependOrMoveToFirst(3); + ASSERT_FALSE(result.isNewEntry); + ASSERT_EQ(3u, list.size()); + + ListHashSet<int>::iterator iterator = list.begin(); + ASSERT_EQ(3, *iterator); + ++iterator; + ASSERT_EQ(2, *iterator); + ++iterator; + ASSERT_EQ(1, *iterator); + ++iterator; +} + +TEST(WTF_ListHashSet, ReverseIterator) +{ + ListHashSet<int> list; + + list.add(1); + list.add(2); + list.add(3); + + auto it = list.rbegin(); + ASSERT_EQ(3, *it); + ++it; + ASSERT_EQ(2, *it); + ++it; + ASSERT_EQ(1, *it); + ++it; + ASSERT_TRUE(it == list.rend()); + + const auto& listHashSet = list; + + auto constIt = listHashSet.rbegin(); + ASSERT_EQ(3, *constIt); + ++constIt; + ASSERT_EQ(2, *constIt); + ++constIt; + ASSERT_EQ(1, *constIt); + ++constIt; + ASSERT_TRUE(constIt == listHashSet.rend()); +} + +TEST(WTF_ListHashSet, MoveOnly) +{ + ListHashSet<MoveOnly> list; + list.add(MoveOnly(2)); + list.add(MoveOnly(4)); + + // { 2, 4 } + ASSERT_EQ(2U, list.first().value()); + ASSERT_EQ(4U, list.last().value()); + + list.appendOrMoveToLast(MoveOnly(3)); + + // { 2, 4, 3 } + ASSERT_EQ(3U, list.last().value()); + + // { 4, 3, 2 } + list.appendOrMoveToLast(MoveOnly(2)); + ASSERT_EQ(4U, list.first().value()); + ASSERT_EQ(2U, list.last().value()); + + list.prependOrMoveToFirst(MoveOnly(5)); + + // { 5, 2, 4, 3 } + ASSERT_EQ(5U, list.first().value()); + + list.prependOrMoveToFirst(MoveOnly(3)); + + // { 3, 5, 4, 2 } + ASSERT_EQ(3U, list.first().value()); + ASSERT_EQ(2U, list.last().value()); + + list.insertBefore(MoveOnly(4), MoveOnly(1)); + list.insertBefore(list.end(), MoveOnly(6)); + + // { 3, 5, 1, 4, 2, 6 } + ASSERT_EQ(3U, list.takeFirst().value()); + ASSERT_EQ(5U, list.takeFirst().value()); + ASSERT_EQ(1U, list.takeFirst().value()); + + // { 4, 2, 6 } + ASSERT_EQ(6U, list.takeLast().value()); + ASSERT_EQ(2U, list.takeLast().value()); + ASSERT_EQ(4U, list.takeLast().value()); + + ASSERT_TRUE(list.isEmpty()); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/Lock.cpp b/Tools/TestWebKitAPI/Tests/WTF/Lock.cpp new file mode 100644 index 000000000..fcf8a4bea --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/Lock.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2015 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 <wtf/Lock.h> +#include <wtf/Threading.h> +#include <wtf/ThreadingPrimitives.h> +#include <wtf/WordLock.h> + +using namespace WTF; + +namespace TestWebKitAPI { + +struct LockInspector { + template<typename LockType> + static bool isFullyReset(LockType& lock) + { + return lock.isFullyReset(); + } +}; + +template<typename LockType> +void runLockTest(unsigned numThreadGroups, unsigned numThreadsPerGroup, unsigned workPerCriticalSection, unsigned numIterations) +{ + std::unique_ptr<LockType[]> locks = std::make_unique<LockType[]>(numThreadGroups); + std::unique_ptr<double[]> words = std::make_unique<double[]>(numThreadGroups); + std::unique_ptr<ThreadIdentifier[]> threads = std::make_unique<ThreadIdentifier[]>(numThreadGroups * numThreadsPerGroup); + + for (unsigned threadGroupIndex = numThreadGroups; threadGroupIndex--;) { + words[threadGroupIndex] = 0; + + for (unsigned threadIndex = numThreadsPerGroup; threadIndex--;) { + threads[threadGroupIndex * numThreadsPerGroup + threadIndex] = createThread( + "Lock test thread", + [threadGroupIndex, &locks, &words, numIterations, workPerCriticalSection] () { + for (unsigned i = numIterations; i--;) { + locks[threadGroupIndex].lock(); + for (unsigned j = workPerCriticalSection; j--;) + words[threadGroupIndex]++; + locks[threadGroupIndex].unlock(); + } + }); + } + } + + for (unsigned threadIndex = numThreadGroups * numThreadsPerGroup; threadIndex--;) + waitForThreadCompletion(threads[threadIndex]); + + double expected = 0; + for (uint64_t i = static_cast<uint64_t>(numIterations) * workPerCriticalSection * numThreadsPerGroup; i--;) + expected++; + + for (unsigned threadGroupIndex = numThreadGroups; threadGroupIndex--;) + EXPECT_EQ(expected, words[threadGroupIndex]); + + // Now test that the locks correctly reset themselves. We expect that if a single thread locks + // each of the locks twice in a row, then the lock should be in a pristine state. + for (unsigned threadGroupIndex = numThreadGroups; threadGroupIndex--;) { + for (unsigned i = 2; i--;) { + locks[threadGroupIndex].lock(); + locks[threadGroupIndex].unlock(); + } + + EXPECT_EQ(true, LockInspector::isFullyReset(locks[threadGroupIndex])); + } +} + +TEST(WTF_WordLock, UncontendedShortSection) +{ + runLockTest<WordLock>(1, 1, 1, 10000000); +} + +TEST(WTF_WordLock, UncontendedLongSection) +{ + runLockTest<WordLock>(1, 1, 10000, 1000); +} + +TEST(WTF_WordLock, ContendedShortSection) +{ + runLockTest<WordLock>(1, 10, 1, 5000000); +} + +TEST(WTF_WordLock, ContendedLongSection) +{ + runLockTest<WordLock>(1, 10, 10000, 10000); +} + +TEST(WTF_WordLock, ManyContendedShortSections) +{ + runLockTest<WordLock>(10, 10, 1, 500000); +} + +TEST(WTF_WordLock, ManyContendedLongSections) +{ + runLockTest<WordLock>(10, 10, 10000, 500); +} + +TEST(WTF_Lock, UncontendedShortSection) +{ + runLockTest<Lock>(1, 1, 1, 10000000); +} + +TEST(WTF_Lock, UncontendedLongSection) +{ + runLockTest<Lock>(1, 1, 10000, 1000); +} + +TEST(WTF_Lock, ContendedShortSection) +{ + runLockTest<Lock>(1, 10, 1, 10000000); +} + +TEST(WTF_Lock, ContendedLongSection) +{ + runLockTest<Lock>(1, 10, 10000, 10000); +} + +TEST(WTF_Lock, ManyContendedShortSections) +{ + runLockTest<Lock>(10, 10, 1, 500000); +} + +TEST(WTF_Lock, ManyContendedLongSections) +{ + runLockTest<Lock>(10, 10, 10000, 1000); +} + +TEST(WTF_Lock, ManyContendedLongerSections) +{ + runLockTest<Lock>(10, 10, 100000, 1); +} + +TEST(WTF_Lock, SectionAddressCollision) +{ + runLockTest<Lock>(4, 2, 10000, 2000); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/MD5.cpp b/Tools/TestWebKitAPI/Tests/WTF/MD5.cpp new file mode 100644 index 000000000..2c862a9cf --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/MD5.cpp @@ -0,0 +1,47 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + */ + +#include "config.h" +#include <wtf/MD5.h> +#include <wtf/StringExtras.h> +#include <wtf/text/CString.h> + +namespace TestWebKitAPI { + +static void expectMD5(CString input, CString expected) +{ + MD5 md5; + md5.addBytes(reinterpret_cast<const uint8_t*>(input.data()), input.length()); + MD5::Digest digest; + md5.checksum(digest); + char* buf = 0; + CString actual = CString::newUninitialized(32, buf); + for (size_t i = 0; i < MD5::hashSize; i++, buf += 2) + snprintf(buf, 3, "%02x", digest[i]); + + ASSERT_EQ(expected.length(), actual.length()); + ASSERT_STREQ(expected.data(), actual.data()); +} + +TEST(WTF_MD5, Computation) +{ + // MD5 Test suite from http://www.ietf.org/rfc/rfc1321.txt. + expectMD5("", "d41d8cd98f00b204e9800998ecf8427e"); + expectMD5("a", "0cc175b9c0f1b6a831c399e269772661"); + expectMD5("abc", "900150983cd24fb0d6963f7d28e17f72"); + expectMD5("message digest", "f96b697d7cb7938d525a2f31aaf161d0"); + expectMD5("abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b"); + expectMD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "d174ab98d277d9f5a5611c2c9f419d9f"); + expectMD5("12345678901234567890123456789012345678901234567890123456789012345678901234567890", "57edf4a22be3c955ac49da2e2107b67a"); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/MathExtras.cpp b/Tools/TestWebKitAPI/Tests/WTF/MathExtras.cpp new file mode 100644 index 000000000..7cbb29766 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/MathExtras.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2012 Intel Corporation + * + * 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 <wtf/MathExtras.h> + +namespace TestWebKitAPI { + +TEST(WTF, Lrint) +{ + EXPECT_EQ(lrint(-7.5), -8); + EXPECT_EQ(lrint(-8.5), -8); + EXPECT_EQ(lrint(-0.5), 0); + EXPECT_EQ(lrint(0.5), 0); + EXPECT_EQ(lrint(-0.5), 0); + EXPECT_EQ(lrint(1.3), 1); + EXPECT_EQ(lrint(1.7), 2); + EXPECT_EQ(lrint(0), 0); + EXPECT_EQ(lrint(-0), 0); + if (sizeof(long int) == 8) { + // Largest double number with 0.5 precision and one halfway rounding case below. + EXPECT_EQ(lrint(pow(2.0, 52) - 0.5), pow(2.0, 52)); + EXPECT_EQ(lrint(pow(2.0, 52) - 1.5), pow(2.0, 52) - 2); + // Smallest double number with 0.5 precision and one halfway rounding case above. + EXPECT_EQ(lrint(-pow(2.0, 52) + 0.5), -pow(2.0, 52)); + EXPECT_EQ(lrint(-pow(2.0, 52) + 1.5), -pow(2.0, 52) + 2); + } +} + +TEST(WTF, clampToIntLong) +{ + if (sizeof(long) == sizeof(int)) + return; + + long maxInt = std::numeric_limits<int>::max(); + long minInt = std::numeric_limits<int>::min(); + long overflowInt = maxInt + 1; + long underflowInt = minInt - 1; + + EXPECT_GT(overflowInt, maxInt); + EXPECT_LT(underflowInt, minInt); + + EXPECT_EQ(clampTo<int>(maxInt), maxInt); + EXPECT_EQ(clampTo<int>(minInt), minInt); + + EXPECT_EQ(clampTo<int>(overflowInt), maxInt); + EXPECT_EQ(clampTo<int>(underflowInt), minInt); +} + +TEST(WTF, clampToIntLongLong) +{ + long long maxInt = std::numeric_limits<int>::max(); + long long minInt = std::numeric_limits<int>::min(); + long long overflowInt = maxInt + 1; + long long underflowInt = minInt - 1; + + EXPECT_GT(overflowInt, maxInt); + EXPECT_LT(underflowInt, minInt); + + EXPECT_EQ(clampTo<int>(maxInt), maxInt); + EXPECT_EQ(clampTo<int>(minInt), minInt); + + EXPECT_EQ(clampTo<int>(overflowInt), maxInt); + EXPECT_EQ(clampTo<int>(underflowInt), minInt); +} + +TEST(WTF, clampToIntegerFloat) +{ + // This test is inaccurate as floats will round the min / max integer + // due to the narrow mantissa. However it will properly checks within + // (close to the extreme) and outside the integer range. + float maxInt = std::numeric_limits<int>::max(); + float minInt = std::numeric_limits<int>::min(); + float overflowInt = maxInt * 1.1; + float underflowInt = minInt * 1.1; + + EXPECT_GT(overflowInt, maxInt); + EXPECT_LT(underflowInt, minInt); + + // If maxInt == 2^31 - 1 (ie on I32 architecture), the closest float used to represent it is 2^31. + EXPECT_NEAR(clampToInteger(maxInt), maxInt, 1); + EXPECT_EQ(clampToInteger(minInt), minInt); + + EXPECT_NEAR(clampToInteger(overflowInt), maxInt, 1); + EXPECT_EQ(clampToInteger(underflowInt), minInt); +} + +TEST(WTF, clampToIntegerDouble) +{ + double maxInt = std::numeric_limits<int>::max(); + double minInt = std::numeric_limits<int>::min(); + double overflowInt = maxInt + 1; + double underflowInt = minInt - 1; + + EXPECT_GT(overflowInt, maxInt); + EXPECT_LT(underflowInt, minInt); + + EXPECT_EQ(clampToInteger(maxInt), maxInt); + EXPECT_EQ(clampToInteger(minInt), minInt); + + EXPECT_EQ(clampToInteger(overflowInt), maxInt); + EXPECT_EQ(clampToInteger(underflowInt), minInt); +} + +TEST(WTF, clampToFloat) +{ + double maxFloat = std::numeric_limits<float>::max(); + double minFloat = -maxFloat; + double overflowFloat = maxFloat * 1.1; + double underflowFloat = minFloat * 1.1; + + EXPECT_GT(overflowFloat, maxFloat); + EXPECT_LT(underflowFloat, minFloat); + + EXPECT_EQ(clampToFloat(maxFloat), maxFloat); + EXPECT_EQ(clampToFloat(minFloat), minFloat); + + EXPECT_EQ(clampToFloat(overflowFloat), maxFloat); + EXPECT_EQ(clampToFloat(underflowFloat), minFloat); + + EXPECT_EQ(clampToFloat(std::numeric_limits<float>::infinity()), maxFloat); + EXPECT_EQ(clampToFloat(-std::numeric_limits<float>::infinity()), minFloat); +} + +TEST(WTF, clampToUnsignedLong) +{ + if (sizeof(unsigned long) == sizeof(unsigned)) + return; + + unsigned long maxUnsigned = std::numeric_limits<unsigned>::max(); + unsigned long overflowUnsigned = maxUnsigned + 1; + + EXPECT_GT(overflowUnsigned, maxUnsigned); + + EXPECT_EQ(clampTo<unsigned>(maxUnsigned), maxUnsigned); + + EXPECT_EQ(clampTo<unsigned>(overflowUnsigned), maxUnsigned); + EXPECT_EQ(clampTo<unsigned>(-1), 0u); +} + +TEST(WTF, clampToUnsignedLongLong) +{ + unsigned long long maxUnsigned = std::numeric_limits<unsigned>::max(); + unsigned long long overflowUnsigned = maxUnsigned + 1; + + EXPECT_GT(overflowUnsigned, maxUnsigned); + + EXPECT_EQ(clampTo<unsigned>(maxUnsigned), maxUnsigned); + + EXPECT_EQ(clampTo<unsigned>(overflowUnsigned), maxUnsigned); + EXPECT_EQ(clampTo<unsigned>(-1), 0u); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/MediaTime.cpp b/Tools/TestWebKitAPI/Tests/WTF/MediaTime.cpp new file mode 100644 index 000000000..248fa333b --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/MediaTime.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2012 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 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. + */ + +#define _USE_MATH_DEFINES 1 +#include "config.h" + +#include <wtf/MediaTime.h> + +using namespace std; + +namespace WTF { + +std::ostream& operator<<(std::ostream& out, const MediaTime& val) +{ + out << "{ "; + if (val.isInvalid()) + out << "invalid"; + else if (val.isPositiveInfinite()) + out << "+infinite"; + else if (val.isNegativeInfinite()) + out << "-infinite"; + else if (val.hasDoubleValue()) + out << "double: " << val.toDouble(); + else + out << "value: " << val.timeValue() << ", scale: " << val.timeScale(); + return out << " }"; +} + +} + +namespace TestWebKitAPI { + +TEST(WTF, MediaTime) +{ + // Comparison Operators + EXPECT_EQ(MediaTime::positiveInfiniteTime() > MediaTime::negativeInfiniteTime(), true); + EXPECT_EQ(MediaTime::negativeInfiniteTime() < MediaTime::positiveInfiniteTime(), true); + EXPECT_EQ(MediaTime::negativeInfiniteTime() == MediaTime::negativeInfiniteTime(), true); + EXPECT_EQ(MediaTime::positiveInfiniteTime() == MediaTime::positiveInfiniteTime(), true); + EXPECT_EQ(MediaTime::positiveInfiniteTime() != MediaTime::negativeInfiniteTime(), true); + EXPECT_EQ(MediaTime::invalidTime() == MediaTime::invalidTime(), true); + EXPECT_EQ(MediaTime::invalidTime() != MediaTime::invalidTime(), false); + EXPECT_EQ(MediaTime::invalidTime() != MediaTime::zeroTime(), true); + EXPECT_EQ(MediaTime::invalidTime() > MediaTime::negativeInfiniteTime(), true); + EXPECT_EQ(MediaTime::invalidTime() > MediaTime::positiveInfiniteTime(), true); + EXPECT_EQ(MediaTime::negativeInfiniteTime() < MediaTime::invalidTime(), true); + EXPECT_EQ(MediaTime::positiveInfiniteTime() < MediaTime::invalidTime(), true); + EXPECT_EQ(MediaTime::indefiniteTime() == MediaTime::indefiniteTime(), true); + EXPECT_EQ(MediaTime::indefiniteTime() != MediaTime::indefiniteTime(), false); + EXPECT_EQ(MediaTime::indefiniteTime() != MediaTime::zeroTime(), true); + EXPECT_EQ(MediaTime::indefiniteTime() > MediaTime::negativeInfiniteTime(), true); + EXPECT_EQ(MediaTime::indefiniteTime() < MediaTime::positiveInfiniteTime(), true); + EXPECT_EQ(MediaTime::negativeInfiniteTime() < MediaTime::indefiniteTime(), true); + EXPECT_EQ(MediaTime::positiveInfiniteTime() > MediaTime::indefiniteTime(), true); + EXPECT_EQ(MediaTime(1, 1) < MediaTime::indefiniteTime(), true); + EXPECT_EQ(MediaTime::indefiniteTime() > MediaTime(1, 1), true); + EXPECT_EQ(MediaTime(1, 1) < MediaTime(2, 1), true); + EXPECT_EQ(MediaTime(2, 1) > MediaTime(1, 1), true); + EXPECT_EQ(MediaTime(1, 1) != MediaTime(2, 1), true); + EXPECT_EQ(MediaTime(2, 1) == MediaTime(2, 1), true); + EXPECT_EQ(MediaTime(2, 1) == MediaTime(4, 2), true); + + // Addition Operators + EXPECT_EQ(MediaTime::positiveInfiniteTime() + MediaTime::positiveInfiniteTime(), MediaTime::positiveInfiniteTime()); + EXPECT_EQ(MediaTime::negativeInfiniteTime() + MediaTime::negativeInfiniteTime(), MediaTime::negativeInfiniteTime()); + EXPECT_EQ(MediaTime::positiveInfiniteTime() + MediaTime::negativeInfiniteTime(), MediaTime::invalidTime()); + EXPECT_EQ(MediaTime::negativeInfiniteTime() + MediaTime::positiveInfiniteTime(), MediaTime::invalidTime()); + EXPECT_EQ(MediaTime::positiveInfiniteTime() + MediaTime(1, 1), MediaTime::positiveInfiniteTime()); + EXPECT_EQ(MediaTime(1, 1) + MediaTime::positiveInfiniteTime(), MediaTime::positiveInfiniteTime()); + EXPECT_EQ(MediaTime::negativeInfiniteTime() + MediaTime(1, 1), MediaTime::negativeInfiniteTime()); + EXPECT_EQ(MediaTime(1, 1) + MediaTime::negativeInfiniteTime(), MediaTime::negativeInfiniteTime()); + EXPECT_EQ(MediaTime::invalidTime() + MediaTime::positiveInfiniteTime(), MediaTime::invalidTime()); + EXPECT_EQ(MediaTime::invalidTime() + MediaTime::negativeInfiniteTime(), MediaTime::invalidTime()); + EXPECT_EQ(MediaTime::invalidTime() + MediaTime::invalidTime(), MediaTime::invalidTime()); + EXPECT_EQ(MediaTime::invalidTime() + MediaTime(1, 1), MediaTime::invalidTime()); + EXPECT_EQ(MediaTime::indefiniteTime() + MediaTime::positiveInfiniteTime(), MediaTime::indefiniteTime()); + EXPECT_EQ(MediaTime::indefiniteTime() + MediaTime::negativeInfiniteTime(), MediaTime::indefiniteTime()); + EXPECT_EQ(MediaTime::indefiniteTime() + MediaTime::indefiniteTime(), MediaTime::indefiniteTime()); + EXPECT_EQ(MediaTime::indefiniteTime() + MediaTime(1, 1), MediaTime::indefiniteTime()); + EXPECT_EQ(MediaTime(1, 1) + MediaTime(1, 1), MediaTime(2, 1)); + EXPECT_EQ(MediaTime(1, 2) + MediaTime(1, 3), MediaTime(5, 6)); + EXPECT_EQ(MediaTime(1, numeric_limits<int32_t>::max()-1) + MediaTime(1, numeric_limits<int32_t>::max()-2), MediaTime(2, numeric_limits<int32_t>::max())); + + // Subtraction Operators + EXPECT_EQ(MediaTime::positiveInfiniteTime() - MediaTime::positiveInfiniteTime(), MediaTime::invalidTime()); + EXPECT_EQ(MediaTime::negativeInfiniteTime() - MediaTime::negativeInfiniteTime(), MediaTime::invalidTime()); + EXPECT_EQ(MediaTime::positiveInfiniteTime() - MediaTime::negativeInfiniteTime(), MediaTime::positiveInfiniteTime()); + EXPECT_EQ(MediaTime::negativeInfiniteTime() - MediaTime::positiveInfiniteTime(), MediaTime::negativeInfiniteTime()); + EXPECT_EQ(MediaTime::positiveInfiniteTime() - MediaTime(1, 1), MediaTime::positiveInfiniteTime()); + EXPECT_EQ(MediaTime(1, 1) - MediaTime::positiveInfiniteTime(), MediaTime::negativeInfiniteTime()); + EXPECT_EQ(MediaTime::negativeInfiniteTime() - MediaTime(1, 1), MediaTime::negativeInfiniteTime()); + EXPECT_EQ(MediaTime(1, 1) - MediaTime::negativeInfiniteTime(), MediaTime::positiveInfiniteTime()); + EXPECT_EQ(MediaTime::invalidTime() - MediaTime::positiveInfiniteTime(), MediaTime::invalidTime()); + EXPECT_EQ(MediaTime::invalidTime() - MediaTime::negativeInfiniteTime(), MediaTime::invalidTime()); + EXPECT_EQ(MediaTime::invalidTime() - MediaTime::invalidTime(), MediaTime::invalidTime()); + EXPECT_EQ(MediaTime::invalidTime() - MediaTime(1, 1), MediaTime::invalidTime()); + EXPECT_EQ(MediaTime::indefiniteTime() - MediaTime::positiveInfiniteTime(), MediaTime::indefiniteTime()); + EXPECT_EQ(MediaTime::indefiniteTime() - MediaTime::negativeInfiniteTime(), MediaTime::indefiniteTime()); + EXPECT_EQ(MediaTime::indefiniteTime() - MediaTime::indefiniteTime(), MediaTime::indefiniteTime()); + EXPECT_EQ(MediaTime::indefiniteTime() - MediaTime(1, 1), MediaTime::indefiniteTime()); + EXPECT_EQ(MediaTime(3, 1) - MediaTime(2, 1), MediaTime(1, 1)); + EXPECT_EQ(MediaTime(1, 2) - MediaTime(1, 3), MediaTime(1, 6)); + EXPECT_EQ(MediaTime(2, numeric_limits<int32_t>::max()-1) - MediaTime(1, numeric_limits<int32_t>::max()-2), MediaTime(1, numeric_limits<int32_t>::max())); + + // Multiplication Operators + EXPECT_EQ(MediaTime::positiveInfiniteTime(), MediaTime::positiveInfiniteTime() * 2); + EXPECT_EQ(MediaTime::negativeInfiniteTime(), MediaTime::negativeInfiniteTime() * 2); + EXPECT_EQ(MediaTime::negativeInfiniteTime(), MediaTime::positiveInfiniteTime() * -2); + EXPECT_EQ(MediaTime::positiveInfiniteTime(), MediaTime::negativeInfiniteTime() * -2); + EXPECT_EQ(MediaTime::invalidTime(), MediaTime::invalidTime() * 2); + EXPECT_EQ(MediaTime::invalidTime(), MediaTime::invalidTime() * -2); + EXPECT_EQ(MediaTime::indefiniteTime(), MediaTime::indefiniteTime() * 2); + EXPECT_EQ(MediaTime::indefiniteTime(), MediaTime::indefiniteTime() * -2); + EXPECT_EQ(MediaTime(6, 1), MediaTime(3, 1) * 2); + EXPECT_EQ(MediaTime(0, 1), MediaTime(0, 1) * 2); + EXPECT_EQ(MediaTime(int64_t(1) << 60, 1), MediaTime(int64_t(1) << 60, 2) * 2); + EXPECT_EQ(MediaTime::positiveInfiniteTime(), MediaTime(numeric_limits<int64_t>::max(), 1) * 2); + EXPECT_EQ(MediaTime::positiveInfiniteTime(), MediaTime(numeric_limits<int64_t>::min(), 1) * -2); + EXPECT_EQ(MediaTime::negativeInfiniteTime(), MediaTime(numeric_limits<int64_t>::max(), 1) * -2); + EXPECT_EQ(MediaTime::negativeInfiniteTime(), MediaTime(numeric_limits<int64_t>::min(), 1) * 2); + + // Constants + EXPECT_EQ(MediaTime::zeroTime(), MediaTime(0, 1)); + EXPECT_EQ(MediaTime::invalidTime(), MediaTime(-1, 1, 0)); + EXPECT_EQ(MediaTime::positiveInfiniteTime(), MediaTime(0, 1, MediaTime::PositiveInfinite)); + EXPECT_EQ(MediaTime::negativeInfiniteTime(), MediaTime(0, 1, MediaTime::NegativeInfinite)); + EXPECT_EQ(MediaTime::indefiniteTime(), MediaTime(0, 1, MediaTime::Indefinite)); + + // Absolute Functions + EXPECT_EQ(abs(MediaTime::positiveInfiniteTime()), MediaTime::positiveInfiniteTime()); + EXPECT_EQ(abs(MediaTime::negativeInfiniteTime()), MediaTime::positiveInfiniteTime()); + EXPECT_EQ(abs(MediaTime::invalidTime()), MediaTime::invalidTime()); + EXPECT_EQ(abs(MediaTime(1, 1)), MediaTime(1, 1)); + EXPECT_EQ(abs(MediaTime(-1, 1)), MediaTime(1, 1)); + EXPECT_EQ(abs(MediaTime(-1, -1)), MediaTime(-1, -1)); + EXPECT_EQ(abs(MediaTime(1, -1)), MediaTime(-1, -1)); + + // Floating Point Functions + EXPECT_EQ(MediaTime::createWithFloat(1.0f), MediaTime(1, 1)); + EXPECT_EQ(MediaTime::createWithFloat(1.5f), MediaTime(3, 2)); + EXPECT_EQ(MediaTime::createWithDouble(1.0), MediaTime(1, 1)); + EXPECT_EQ(MediaTime::createWithDouble(1.5), MediaTime(3, 2)); + EXPECT_EQ(MediaTime(1, 1).toFloat(), 1.0f); + EXPECT_EQ(MediaTime(3, 2).toFloat(), 1.5f); + EXPECT_EQ(MediaTime(1, 1).toDouble(), 1.0); + EXPECT_EQ(MediaTime(3, 2).toDouble(), 1.5); + EXPECT_EQ(MediaTime(1, 1 << 16).toFloat(), 1 / pow(2.0f, 16.0f)); + EXPECT_EQ(MediaTime(1, 1 << 30).toDouble(), 1 / pow(2.0, 30.0)); + EXPECT_EQ(MediaTime::createWithDouble(M_PI, 1 << 30), MediaTime(3373259426U, 1 << 30)); + EXPECT_EQ(MediaTime::createWithFloat(INFINITY), MediaTime::positiveInfiniteTime()); + EXPECT_EQ(MediaTime::createWithFloat(-INFINITY), MediaTime::negativeInfiniteTime()); + EXPECT_EQ(MediaTime::createWithFloat(NAN), MediaTime::invalidTime()); + EXPECT_EQ(MediaTime::createWithDouble(INFINITY), MediaTime::positiveInfiniteTime()); + EXPECT_EQ(MediaTime::createWithDouble(-INFINITY), MediaTime::negativeInfiniteTime()); + EXPECT_EQ(MediaTime::createWithDouble(NAN), MediaTime::invalidTime()); + + // Floating Point Round Trip + EXPECT_EQ(10.0123456789f, MediaTime::createWithFloat(10.0123456789f).toFloat()); + EXPECT_EQ(10.0123456789, MediaTime::createWithDouble(10.0123456789).toDouble()); + + // Floating Point Math + EXPECT_EQ(1.5 + 3.3, (MediaTime::createWithDouble(1.5) + MediaTime::createWithDouble(3.3)).toDouble()); + EXPECT_EQ(1.5 - 3.3, (MediaTime::createWithDouble(1.5) - MediaTime::createWithDouble(3.3)).toDouble()); + EXPECT_EQ(-3.3, (-MediaTime::createWithDouble(3.3)).toDouble()); + EXPECT_EQ(3.3 * 2, (MediaTime::createWithDouble(3.3) * 2).toDouble()); + + // Floating Point and non-Floating Point math + EXPECT_EQ(2.0, (MediaTime::createWithDouble(1.5) + MediaTime(1, 2)).toDouble()); + EXPECT_EQ(1.0, (MediaTime::createWithDouble(1.5) - MediaTime(1, 2)).toDouble()); + + // Overflow Behavior + EXPECT_EQ(MediaTime::createWithFloat(pow(2.0f, 64.0f)), MediaTime::positiveInfiniteTime()); + EXPECT_EQ(MediaTime::createWithFloat(-pow(2.0f, 64.0f)), MediaTime::negativeInfiniteTime()); + EXPECT_EQ(MediaTime::createWithFloat(pow(2.0f, 63.0f), 2).timeScale(), 1); + EXPECT_EQ(MediaTime::createWithFloat(pow(2.0f, 63.0f), 3).timeScale(), 1); + EXPECT_EQ(MediaTime::createWithDouble(pow(2.0, 64.0)), MediaTime::positiveInfiniteTime()); + EXPECT_EQ(MediaTime::createWithDouble(-pow(2.0, 64.0)), MediaTime::negativeInfiniteTime()); + EXPECT_EQ(MediaTime::createWithDouble(pow(2.0, 63.0), 2).timeScale(), 1); + EXPECT_EQ(MediaTime::createWithDouble(pow(2.0, 63.0), 3).timeScale(), 1); + EXPECT_EQ((MediaTime(numeric_limits<int64_t>::max(), 2) + MediaTime(numeric_limits<int64_t>::max(), 2)).timeScale(), 1); + EXPECT_EQ((MediaTime(numeric_limits<int64_t>::min(), 2) - MediaTime(numeric_limits<int64_t>::max(), 2)).timeScale(), 1); + EXPECT_EQ(MediaTime(numeric_limits<int64_t>::max(), 1) + MediaTime(numeric_limits<int64_t>::max(), 1), MediaTime::positiveInfiniteTime()); + EXPECT_EQ(MediaTime(numeric_limits<int64_t>::min(), 1) + MediaTime(numeric_limits<int64_t>::min(), 1), MediaTime::negativeInfiniteTime()); + EXPECT_EQ(MediaTime(numeric_limits<int64_t>::min(), 1) - MediaTime(numeric_limits<int64_t>::max(), 1), MediaTime::negativeInfiniteTime()); + EXPECT_EQ(MediaTime(numeric_limits<int64_t>::max(), 1) - MediaTime(numeric_limits<int64_t>::min(), 1), MediaTime::positiveInfiniteTime()); +} + +} + diff --git a/Tools/TestWebKitAPI/Tests/WTF/MetaAllocator.cpp b/Tools/TestWebKitAPI/Tests/WTF/MetaAllocator.cpp new file mode 100644 index 000000000..59af4f849 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/MetaAllocator.cpp @@ -0,0 +1,957 @@ +/* + * Copyright (C) 2011 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 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 <stdarg.h> +#include <wtf/MetaAllocator.h> +#include <wtf/Vector.h> + +#if OS(WINDOWS) +#undef small +#endif + +using namespace WTF; + +namespace TestWebKitAPI { + +class MetaAllocatorTest: public testing::Test { +public: + enum SanityCheckMode { RunSanityCheck, DontRunSanityCheck }; + + enum HeapGrowthMode { DontGrowHeap, ForTestDemandAllocCoalesce, ForTestDemandAllocDontCoalesce }; + + HeapGrowthMode currentHeapGrowthMode; + size_t allowAllocatePages; + size_t requestedNumPages; + + class SimpleTestAllocator: public MetaAllocator { + public: + SimpleTestAllocator(MetaAllocatorTest* parent) + : MetaAllocator(32) + , m_parent(parent) + { + addFreshFreeSpace(reinterpret_cast<void*>(basePage * pageSize()), defaultPagesInHeap * pageSize()); + } + + virtual ~SimpleTestAllocator() + { + EXPECT_TRUE(!m_parent->allocatorDestroyed); + m_parent->allocatorDestroyed = true; + } + + virtual void* allocateNewSpace(size_t& numPages) + { + switch (m_parent->currentHeapGrowthMode) { + case DontGrowHeap: + return 0; + + case ForTestDemandAllocCoalesce: + case ForTestDemandAllocDontCoalesce: { + EXPECT_TRUE(m_parent->allowAllocatePages); + EXPECT_TRUE(m_parent->allowAllocatePages >= numPages); + m_parent->requestedNumPages = numPages; + numPages = m_parent->allowAllocatePages; + + unsigned offset; + if (m_parent->currentHeapGrowthMode == ForTestDemandAllocCoalesce) + offset = 0; + else + offset = 1; + + void* result = reinterpret_cast<void*>((basePage + defaultPagesInHeap + offset) * pageSize()); + + m_parent->allowAllocatePages = 0; + m_parent->currentHeapGrowthMode = DontGrowHeap; + + for (size_t counter = 0; counter < numPages + offset; ++counter) { + m_parent->pageMap->append(false); + for (unsigned byteCounter = 0; byteCounter < pageSize(); ++byteCounter) + m_parent->memoryMap->append(false); + } + + m_parent->additionalPagesInHeap += numPages; + + return result; + } + + default: + CRASH(); + return 0; + } + } + + virtual void notifyNeedPage(void* page) + { + // the page should be both free and unmapped. + EXPECT_TRUE(!m_parent->pageState(reinterpret_cast<uintptr_t>(page) / pageSize())); + for (uintptr_t address = reinterpret_cast<uintptr_t>(page); address < reinterpret_cast<uintptr_t>(page) + pageSize(); ++address) + EXPECT_TRUE(!m_parent->byteState(reinterpret_cast<void*>(address))); + m_parent->pageState(reinterpret_cast<uintptr_t>(page) / pageSize()) = true; + } + + virtual void notifyPageIsFree(void* page) + { + // the page should be free of objects at this point, but it should still + // be mapped. + EXPECT_TRUE(m_parent->pageState(reinterpret_cast<uintptr_t>(page) / pageSize())); + for (uintptr_t address = reinterpret_cast<uintptr_t>(page); address < reinterpret_cast<uintptr_t>(page) + pageSize(); ++address) + EXPECT_TRUE(!m_parent->byteState(reinterpret_cast<void*>(address))); + m_parent->pageState(reinterpret_cast<uintptr_t>(page) / pageSize()) = false; + } + + private: + MetaAllocatorTest* m_parent; + }; + + static const unsigned basePage = 1; + static const unsigned defaultPagesInHeap = 100; + + unsigned additionalPagesInHeap; + + Vector<bool, 0>* memoryMap; + Vector<bool, 0>* pageMap; + bool allocatorDestroyed; + + SimpleTestAllocator* allocator; + + virtual void SetUp() + { + memoryMap = new Vector<bool, 0>(); + pageMap = new Vector<bool, 0>(); + + for (unsigned page = basePage; page < basePage + defaultPagesInHeap; ++page) { + pageMap->append(false); + for (unsigned byteInPage = 0; byteInPage < pageSize(); ++byteInPage) + memoryMap->append(false); + } + + allocatorDestroyed = false; + + currentHeapGrowthMode = DontGrowHeap; + allowAllocatePages = 0; + additionalPagesInHeap = 0; + requestedNumPages = 0; + + allocator = new SimpleTestAllocator(this); + } + + virtual void TearDown() + { + EXPECT_TRUE(currentHeapGrowthMode == DontGrowHeap); + EXPECT_EQ(allowAllocatePages, static_cast<size_t>(0)); + EXPECT_EQ(requestedNumPages, static_cast<size_t>(0)); + + // memory should be free. + for (unsigned page = basePage; page < basePage + defaultPagesInHeap; ++page) { + EXPECT_TRUE(!pageState(page)); + for (unsigned byteInPage = 0; byteInPage < pageSize(); ++byteInPage) + EXPECT_TRUE(!byteState(page * pageSize() + byteInPage)); + } + + // NOTE: this automatically tests that the allocator did not leak + // memory, so long as these tests are running with !defined(NDEBUG). + // See MetaAllocator::m_mallocBalance. + delete allocator; + + EXPECT_TRUE(allocatorDestroyed); + + delete memoryMap; + delete pageMap; + } + + MetaAllocatorHandle* allocate(size_t sizeInBytes, SanityCheckMode sanityCheckMode = RunSanityCheck) + { + MetaAllocatorHandle* handle = allocator->allocate(sizeInBytes, 0).leakRef(); + EXPECT_TRUE(handle); + EXPECT_EQ(handle->sizeInBytes(), sizeInBytes); + + uintptr_t startByte = reinterpret_cast<uintptr_t>(handle->start()); + uintptr_t endByte = startByte + sizeInBytes; + for (uintptr_t currentByte = startByte; currentByte < endByte; ++currentByte) { + EXPECT_TRUE(!byteState(currentByte)); + byteState(currentByte) = true; + EXPECT_TRUE(pageState(currentByte / pageSize())); + } + + if (sanityCheckMode == RunSanityCheck) + sanityCheck(); + + return handle; + } + + void free(MetaAllocatorHandle* handle, SanityCheckMode sanityCheckMode = RunSanityCheck) + { + EXPECT_TRUE(handle); + + notifyFree(handle->start(), handle->sizeInBytes()); + handle->deref(); + + if (sanityCheckMode == RunSanityCheck) + sanityCheck(); + } + + void notifyFree(void* start, size_t sizeInBytes) + { + uintptr_t startByte = reinterpret_cast<uintptr_t>(start); + uintptr_t endByte = startByte + sizeInBytes; + for (uintptr_t currentByte = startByte; currentByte < endByte; ++currentByte) { + EXPECT_TRUE(byteState(currentByte)); + byteState(currentByte) = false; + } + } + + void sanityCheck() + { +#ifndef NDEBUG + EXPECT_EQ(allocator->bytesReserved() - allocator->bytesAllocated(), allocator->debugFreeSpaceSize()); +#endif + EXPECT_EQ(allocator->bytesReserved(), (defaultPagesInHeap + additionalPagesInHeap) * pageSize()); + EXPECT_EQ(allocator->bytesAllocated(), bytesAllocated()); + EXPECT_EQ(allocator->bytesCommitted(), bytesCommitted()); + } + + void confirm(MetaAllocatorHandle* handle) + { + uintptr_t startByte = reinterpret_cast<uintptr_t>(handle->start()); + confirm(startByte, startByte + handle->sizeInBytes(), true); + } + + void confirmHighWatermark(MetaAllocatorHandle* handle) + { + confirm(reinterpret_cast<uintptr_t>(handle->end()), (basePage + defaultPagesInHeap) * pageSize(), false); + } + + void confirm(uintptr_t startByte, uintptr_t endByte, bool value) + { + for (uintptr_t currentByte = startByte; currentByte < endByte; ++currentByte) { + EXPECT_EQ(byteState(currentByte), value); + if (value) + EXPECT_TRUE(pageState(currentByte / pageSize())); + } + if (!value) { + uintptr_t firstFreePage = (startByte + pageSize() - 1) / pageSize(); + uintptr_t lastFreePage = (endByte - pageSize()) / pageSize(); + for (uintptr_t currentPage = firstFreePage; currentPage <= lastFreePage; ++currentPage) + EXPECT_TRUE(!pageState(currentPage)); + } + } + + size_t bytesAllocated() + { + size_t result = 0; + for (unsigned index = 0; index < memoryMap->size(); ++index) { + if (memoryMap->at(index)) + result++; + } + return result; + } + + size_t bytesCommitted() + { + size_t result = 0; + for (unsigned index = 0; index < pageMap->size(); ++index) { + if (pageMap->at(index)) + result++; + } + return result * pageSize(); + } + + bool& byteState(void* address) + { + return byteState(reinterpret_cast<uintptr_t>(address)); + } + + bool& byteState(uintptr_t address) + { + uintptr_t byteIndex = address - basePage * pageSize(); + return memoryMap->at(byteIndex); + } + + bool& pageState(uintptr_t page) + { + uintptr_t pageIndex = page - basePage; + return pageMap->at(pageIndex); + } + + // Test helpers + + void testOneAlloc(size_t size) + { + // Tests the most basic behavior: allocate one thing and free it. Also + // verifies that the state of pages is correct. + + MetaAllocatorHandle* handle = allocate(size); + EXPECT_EQ(handle->start(), reinterpret_cast<void*>(basePage * pageSize())); + EXPECT_EQ(handle->sizeInBytes(), size); + EXPECT_TRUE(pageState(basePage)); + + confirm(handle); + confirmHighWatermark(handle); + + free(handle); + } + + void testRepeatAllocFree(size_t firstSize, ...) + { + // Tests right-coalescing by repeatedly allocating and freeing. The idea + // is that if you allocate something and then free it, then the heap should + // look identical to what it was before the allocation due to a right-coalesce + // of the freed chunk and the already-free memory, and so subsequent + // allocations should behave the same as the first one. + + MetaAllocatorHandle* handle = allocate(firstSize); + EXPECT_EQ(handle->start(), reinterpret_cast<void*>(basePage * pageSize())); + EXPECT_EQ(handle->sizeInBytes(), firstSize); + + confirm(handle); + confirmHighWatermark(handle); + + free(handle); + + va_list argList; + va_start(argList, firstSize); + while (size_t sizeInBytes = va_arg(argList, int)) { + handle = allocate(sizeInBytes); + EXPECT_EQ(handle->start(), reinterpret_cast<void*>(basePage * pageSize())); + EXPECT_EQ(handle->sizeInBytes(), sizeInBytes); + + confirm(handle); + confirmHighWatermark(handle); + + free(handle); + } + va_end(argList); + } + + void testSimpleFullCoalesce(size_t firstSize, size_t secondSize, size_t thirdSize) + { + // Allocates something of size firstSize, then something of size secondSize, and then + // frees the first allocation, and then the second, and then attempts to allocate the + // third, asserting that it allocated at the base address of the heap. + + // Note that this test may cause right-allocation, which will cause the test to fail. + // Thus the correct way of running this test is to ensure that secondSize is + // picked in such a way that it never straddles a page. + + MetaAllocatorHandle* firstHandle = allocate(firstSize); + EXPECT_EQ(firstHandle->start(), reinterpret_cast<void*>(basePage * pageSize())); + EXPECT_EQ(firstHandle->sizeInBytes(), firstSize); + + confirm(firstHandle); + confirmHighWatermark(firstHandle); + + MetaAllocatorHandle* secondHandle = allocate(secondSize); + EXPECT_EQ(secondHandle->start(), reinterpret_cast<void*>(basePage * pageSize() + firstSize)); + EXPECT_EQ(secondHandle->sizeInBytes(), secondSize); + + confirm(firstHandle); + confirm(secondHandle); + confirmHighWatermark(secondHandle); + + free(firstHandle); + + confirm(secondHandle); + confirmHighWatermark(secondHandle); + + free(secondHandle); + + confirm(basePage * pageSize(), (basePage + defaultPagesInHeap) * pageSize(), false); + + MetaAllocatorHandle* thirdHandle = allocate(thirdSize); + EXPECT_EQ(thirdHandle->start(), reinterpret_cast<void*>(basePage * pageSize())); + EXPECT_EQ(thirdHandle->sizeInBytes(), thirdSize); + + confirm(thirdHandle); + confirmHighWatermark(thirdHandle); + + free(thirdHandle); + } + + enum TestFIFOAllocMode { FillAtEnd, EagerFill }; + void testFIFOAlloc(TestFIFOAllocMode mode, ...) + { + // This will test the simple case of no-coalesce (freeing the left-most + // chunk in memory when the chunk to the right of it is allocated) and + // fully exercise left-coalescing and full-coalescing. In EagerFill + // mode, this also tests perfect-fit allocation and no-coalescing free. + + size_t totalSize = 0; + + Vector<MetaAllocatorHandle*, 0> handles; + + va_list argList; + va_start(argList, mode); + while (size_t sizeInBytes = va_arg(argList, int)) { + MetaAllocatorHandle* handle = allocate(sizeInBytes); + EXPECT_EQ(handle->start(), reinterpret_cast<void*>(basePage * pageSize() + totalSize)); + EXPECT_EQ(handle->sizeInBytes(), sizeInBytes); + + confirm(handle); + confirmHighWatermark(handle); + + handles.append(handle); + totalSize += sizeInBytes; + } + va_end(argList); + + for (unsigned index = 0; index < handles.size(); ++index) + confirm(handles.at(index)); + + size_t sizeSoFar = 0; + for (unsigned index = 0; index < handles.size(); ++index) { + sizeSoFar += handles.at(index)->sizeInBytes(); + free(handles.at(index)); + if (mode == EagerFill) { + MetaAllocatorHandle* handle = allocate(sizeSoFar); + EXPECT_EQ(handle->start(), reinterpret_cast<void*>(basePage * pageSize())); + EXPECT_EQ(handle->sizeInBytes(), sizeSoFar); + + confirm(basePage * pageSize(), basePage * pageSize() + totalSize, true); + if (index < handles.size() - 1) + confirmHighWatermark(handles.last()); + else + confirmHighWatermark(handle); + + free(handle); + + confirm(basePage * pageSize(), basePage * pageSize() + sizeSoFar, false); + } + } + + ASSERT(sizeSoFar == totalSize); + + confirm(basePage * pageSize(), (basePage + defaultPagesInHeap) * pageSize(), false); + + if (mode == FillAtEnd) { + MetaAllocatorHandle* finalHandle = allocate(totalSize); + EXPECT_EQ(finalHandle->start(), reinterpret_cast<void*>(basePage * pageSize())); + EXPECT_EQ(finalHandle->sizeInBytes(), totalSize); + + confirm(finalHandle); + confirmHighWatermark(finalHandle); + + free(finalHandle); + } + } + + void testFillHeap(size_t sizeInBytes, size_t numAllocations) + { + Vector<MetaAllocatorHandle*, 0> handles; + + for (size_t index = 0; index < numAllocations; ++index) + handles.append(allocate(sizeInBytes, DontRunSanityCheck)); + + sanityCheck(); + + EXPECT_TRUE(!allocator->allocate(sizeInBytes, 0)); + + for (size_t index = 0; index < numAllocations; ++index) + free(handles.at(index), DontRunSanityCheck); + + sanityCheck(); + } + + void testRightAllocation(size_t firstLeftSize, size_t firstRightSize, size_t secondLeftSize, size_t secondRightSize) + { + MetaAllocatorHandle* firstLeft = allocate(firstLeftSize); + EXPECT_EQ(firstLeft->start(), reinterpret_cast<void*>(basePage * pageSize())); + + MetaAllocatorHandle* firstRight = allocate(firstRightSize); + EXPECT_EQ(firstRight->end(), reinterpret_cast<void*>((basePage + defaultPagesInHeap) * pageSize())); + + MetaAllocatorHandle* secondLeft = allocate(secondLeftSize); + EXPECT_EQ(secondLeft->start(), reinterpret_cast<void*>(basePage * pageSize() + firstLeft->sizeInBytes())); + + MetaAllocatorHandle* secondRight = allocate(secondRightSize); + EXPECT_EQ(secondRight->end(), reinterpret_cast<void*>((basePage + defaultPagesInHeap) * pageSize() - firstRight->sizeInBytes())); + + free(firstLeft); + free(firstRight); + free(secondLeft); + free(secondRight); + + MetaAllocatorHandle* final = allocate(defaultPagesInHeap * pageSize()); + EXPECT_EQ(final->start(), reinterpret_cast<void*>(basePage * pageSize())); + + free(final); + } + + void testBestFit(size_t firstSize, size_t step, unsigned numSlots, SanityCheckMode sanityCheckMode) + { + Vector<MetaAllocatorHandle*, 0> handlesToFree; + Vector<MetaAllocatorHandle*, 0> handles; + Vector<void*, 0> locations; + + size_t size = firstSize; + for (unsigned index = 0; index < numSlots; ++index) { + MetaAllocatorHandle* toFree = allocate(size, sanityCheckMode); + if (!handles.isEmpty()) { + while (toFree->start() != handles.last()->end()) { + handlesToFree.append(toFree); + toFree = allocate(size, sanityCheckMode); + } + } + + MetaAllocatorHandle* fragger = allocate(32, sanityCheckMode); + EXPECT_EQ(fragger->start(), toFree->end()); + + locations.append(toFree->start()); + + handlesToFree.append(toFree); + handles.append(fragger); + + size += step; + } + + ASSERT(locations.size() == numSlots); + + for (unsigned index = 0; index < handlesToFree.size(); ++index) + free(handlesToFree.at(index), sanityCheckMode); + + size = firstSize; + for (unsigned index = 0; index < numSlots; ++index) { + MetaAllocatorHandle* bestFit = allocate(size - 32, sanityCheckMode); + + EXPECT_TRUE(bestFit->start() == locations.at(index) + || bestFit->end() == reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(locations.at(index)) + size)); + + MetaAllocatorHandle* small = allocate(32, sanityCheckMode); + if (bestFit->start() == locations.at(index)) + EXPECT_EQ(small->start(), bestFit->end()); + else + EXPECT_EQ(small->end(), bestFit->start()); + + free(bestFit, sanityCheckMode); + free(small, sanityCheckMode); + + size += step; + } + + for (unsigned index = 0; index < numSlots; ++index) + free(handles.at(index), sanityCheckMode); + + MetaAllocatorHandle* final = allocate(defaultPagesInHeap * pageSize(), sanityCheckMode); + EXPECT_EQ(final->start(), reinterpret_cast<void*>(basePage * pageSize())); + + free(final, sanityCheckMode); + } + + void testShrink(size_t firstSize, size_t secondSize) + { + // Allocate the thing that will be shrunk + MetaAllocatorHandle* handle = allocate(firstSize); + + // Shrink it, and make sure that our state reflects the shrinkage. + notifyFree(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(handle->start()) + secondSize), firstSize - secondSize); + + handle->shrink(secondSize); + EXPECT_EQ(handle->sizeInBytes(), secondSize); + + sanityCheck(); + + // Assert that the heap is not empty. + EXPECT_TRUE(!allocator->allocate(defaultPagesInHeap * pageSize(), 0)); + + // Allocate the remainder of the heap. + MetaAllocatorHandle* remainder = allocate(defaultPagesInHeap * pageSize() - secondSize); + EXPECT_EQ(remainder->start(), handle->end()); + + free(remainder); + free(handle); + + // Assert that the heap is empty and finish up. + MetaAllocatorHandle* final = allocate(defaultPagesInHeap * pageSize()); + EXPECT_EQ(final->start(), reinterpret_cast<void*>(basePage * pageSize())); + + free(final); + } + + void testDemandAllocCoalesce(size_t firstSize, size_t numPages, size_t secondSize) + { + EXPECT_TRUE(!allocator->allocate((defaultPagesInHeap + numPages) * pageSize(), 0)); + + MetaAllocatorHandle* firstHandle = allocate(firstSize); + + EXPECT_TRUE(!allocator->allocate(secondSize, 0)); + EXPECT_TRUE(!allocator->allocate((defaultPagesInHeap + numPages) * pageSize(), 0)); + + currentHeapGrowthMode = ForTestDemandAllocCoalesce; + allowAllocatePages = numPages; + + MetaAllocatorHandle* secondHandle = allocate(secondSize); + + EXPECT_TRUE(currentHeapGrowthMode == DontGrowHeap); + EXPECT_EQ(allowAllocatePages, static_cast<size_t>(0)); + EXPECT_EQ(requestedNumPages, (secondSize + pageSize() - 1) / pageSize()); + EXPECT_EQ(secondHandle->start(), reinterpret_cast<void*>((basePage + defaultPagesInHeap) * pageSize())); + + requestedNumPages = 0; + + free(firstHandle); + free(secondHandle); + + free(allocate((defaultPagesInHeap + numPages) * pageSize())); + } + + void testDemandAllocDontCoalesce(size_t firstSize, size_t numPages, size_t secondSize) + { + free(allocate(firstSize)); + free(allocate(defaultPagesInHeap * pageSize())); + EXPECT_TRUE(!allocator->allocate((defaultPagesInHeap + numPages) * pageSize(), 0)); + + MetaAllocatorHandle* firstHandle = allocate(firstSize); + + EXPECT_TRUE(!allocator->allocate(secondSize, 0)); + EXPECT_TRUE(!allocator->allocate((defaultPagesInHeap + numPages) * pageSize(), 0)); + + currentHeapGrowthMode = ForTestDemandAllocDontCoalesce; + allowAllocatePages = numPages; + + MetaAllocatorHandle* secondHandle = allocate(secondSize); + + EXPECT_TRUE(currentHeapGrowthMode == DontGrowHeap); + EXPECT_EQ(allowAllocatePages, static_cast<size_t>(0)); + EXPECT_EQ(requestedNumPages, (secondSize + pageSize() - 1) / pageSize()); + EXPECT_EQ(secondHandle->start(), reinterpret_cast<void*>((basePage + defaultPagesInHeap + 1) * pageSize())); + + requestedNumPages = 0; + + EXPECT_TRUE(!allocator->allocate((defaultPagesInHeap + numPages) * pageSize(), 0)); + + free(firstHandle); + free(secondHandle); + + EXPECT_TRUE(!allocator->allocate((defaultPagesInHeap + numPages) * pageSize(), 0)); + + firstHandle = allocate(firstSize); + secondHandle = allocate(secondSize); + EXPECT_EQ(firstHandle->start(), reinterpret_cast<void*>(basePage * pageSize())); + EXPECT_EQ(secondHandle->start(), reinterpret_cast<void*>((basePage + defaultPagesInHeap + 1) * pageSize())); + free(firstHandle); + free(secondHandle); + } +}; + +TEST_F(MetaAllocatorTest, Empty) +{ + // Tests that creating and destroying an allocator works. +} + +TEST_F(MetaAllocatorTest, AllocZero) +{ + // Tests that allocating a zero-length block returns 0 and + // does not change anything in memory. + + ASSERT(!allocator->allocate(0, 0)); + + MetaAllocatorHandle* final = allocate(defaultPagesInHeap * pageSize()); + EXPECT_EQ(final->start(), reinterpret_cast<void*>(basePage * pageSize())); + free(final); +} + +TEST_F(MetaAllocatorTest, OneAlloc32) +{ + testOneAlloc(32); +} + +TEST_F(MetaAllocatorTest, OneAlloc64) +{ + testOneAlloc(64); +} + +TEST_F(MetaAllocatorTest, OneAllocTwoPages) +{ + testOneAlloc(pageSize() * 2); +} + +TEST_F(MetaAllocatorTest, RepeatAllocFree32Twice) +{ + testRepeatAllocFree(32, 32, 0); +} + +TEST_F(MetaAllocatorTest, RepeatAllocFree32Then64) +{ + testRepeatAllocFree(32, 64, 0); +} + +TEST_F(MetaAllocatorTest, RepeatAllocFree64Then32) +{ + testRepeatAllocFree(64, 32, 0); +} + +TEST_F(MetaAllocatorTest, RepeatAllocFree32TwiceThen64) +{ + testRepeatAllocFree(32, 32, 64, 0); +} + +TEST_F(MetaAllocatorTest, RepeatAllocFree32Then64Twice) +{ + testRepeatAllocFree(32, 64, 64, 0); +} + +TEST_F(MetaAllocatorTest, RepeatAllocFree64Then32Then64) +{ + testRepeatAllocFree(64, 32, 64, 0); +} + +TEST_F(MetaAllocatorTest, RepeatAllocFree32Thrice) +{ + testRepeatAllocFree(32, 32, 32, 0); +} + +TEST_F(MetaAllocatorTest, RepeatAllocFree32Then64Then32) +{ + testRepeatAllocFree(32, 32, 32, 0); +} + +TEST_F(MetaAllocatorTest, RepeatAllocFree64Then32Twice) +{ + testRepeatAllocFree(64, 32, 32, 0); +} + +TEST_F(MetaAllocatorTest, RepeatAllocFreeTwoPagesThen32) +{ + testRepeatAllocFree(static_cast<int>(pageSize() * 2), 32, 0); +} + +TEST_F(MetaAllocatorTest, RepeatAllocFree32ThenTwoPages) +{ + testRepeatAllocFree(32, static_cast<int>(pageSize() * 2), 0); +} + +TEST_F(MetaAllocatorTest, RepeatAllocFreePageThenTwoPages) +{ + testRepeatAllocFree(static_cast<int>(pageSize()), static_cast<int>(pageSize() * 2), 0); +} + +TEST_F(MetaAllocatorTest, RepeatAllocFreeTwoPagesThenPage) +{ + testRepeatAllocFree(static_cast<int>(pageSize() * 2), static_cast<int>(pageSize()), 0); +} + +TEST_F(MetaAllocatorTest, SimpleFullCoalesce32Plus32Then128) +{ + testSimpleFullCoalesce(32, 32, 128); +} + +TEST_F(MetaAllocatorTest, SimpleFullCoalesce32Plus64Then128) +{ + testSimpleFullCoalesce(32, 64, 128); +} + +TEST_F(MetaAllocatorTest, SimpleFullCoalesce64Plus32Then128) +{ + testSimpleFullCoalesce(64, 32, 128); +} + +TEST_F(MetaAllocatorTest, SimpleFullCoalesce32PlusPageLess32ThenPage) +{ + testSimpleFullCoalesce(32, pageSize() - 32, pageSize()); +} + +TEST_F(MetaAllocatorTest, SimpleFullCoalesce32PlusPageLess32ThenTwoPages) +{ + testSimpleFullCoalesce(32, pageSize() - 32, pageSize() * 2); +} + +TEST_F(MetaAllocatorTest, SimpleFullCoalescePagePlus32ThenTwoPages) +{ + testSimpleFullCoalesce(pageSize(), 32, pageSize() * 2); +} + +TEST_F(MetaAllocatorTest, SimpleFullCoalescePagePlusPageThenTwoPages) +{ + testSimpleFullCoalesce(pageSize(), pageSize(), pageSize() * 2); +} + +TEST_F(MetaAllocatorTest, FIFOAllocFillAtEnd32Twice) +{ + testFIFOAlloc(FillAtEnd, 32, 32, 0); +} + +TEST_F(MetaAllocatorTest, FIFOAllocFillAtEnd32Thrice) +{ + testFIFOAlloc(FillAtEnd, 32, 32, 32, 0); +} + +TEST_F(MetaAllocatorTest, FIFOAllocFillAtEnd32FourTimes) +{ + testFIFOAlloc(FillAtEnd, 32, 32, 32, 32, 0); +} + +TEST_F(MetaAllocatorTest, FIFOAllocFillAtEndPageLess32Then32ThenPageLess64Then64) +{ + testFIFOAlloc(FillAtEnd, static_cast<int>(pageSize() - 32), 32, static_cast<int>(pageSize() - 64), 64, 0); +} + +TEST_F(MetaAllocatorTest, FIFOAllocEagerFill32Twice) +{ + testFIFOAlloc(EagerFill, 32, 32, 0); +} + +TEST_F(MetaAllocatorTest, FIFOAllocEagerFill32Thrice) +{ + testFIFOAlloc(EagerFill, 32, 32, 32, 0); +} + +TEST_F(MetaAllocatorTest, FIFOAllocEagerFill32FourTimes) +{ + testFIFOAlloc(EagerFill, 32, 32, 32, 32, 0); +} + +TEST_F(MetaAllocatorTest, FIFOAllocEagerFillPageLess32Then32ThenPageLess64Then64) +{ + testFIFOAlloc(EagerFill, static_cast<int>(pageSize() - 32), 32, static_cast<int>(pageSize() - 64), 64, 0); +} + +TEST_F(MetaAllocatorTest, FillHeap32) +{ + testFillHeap(32, defaultPagesInHeap * pageSize() / 32); +} + +TEST_F(MetaAllocatorTest, FillHeapPage) +{ + testFillHeap(pageSize(), defaultPagesInHeap); +} + +TEST_F(MetaAllocatorTest, FillHeapTwoPages) +{ + testFillHeap(pageSize() * 2, defaultPagesInHeap / 2); +} + +TEST_F(MetaAllocatorTest, RightAllocation32ThenPageThen32ThenPage) +{ + testRightAllocation(32, pageSize(), 32, pageSize()); +} + +TEST_F(MetaAllocatorTest, RightAllocationQuarterPageThenPageThenQuarterPageThenPage) +{ + testRightAllocation(pageSize() / 4, pageSize(), pageSize() / 4, pageSize()); +} + +TEST_F(MetaAllocatorTest, BestFit64Plus64Thrice) +{ + testBestFit(64, 64, 3, RunSanityCheck); +} + +TEST_F(MetaAllocatorTest, BestFit64Plus64TenTimes) +{ + testBestFit(64, 64, 10, DontRunSanityCheck); +} + +TEST_F(MetaAllocatorTest, BestFit64Plus64HundredTimes) +{ + testBestFit(64, 64, 100, DontRunSanityCheck); +} + +TEST_F(MetaAllocatorTest, BestFit96Plus64Thrice) +{ + testBestFit(96, 64, 3, RunSanityCheck); +} + +TEST_F(MetaAllocatorTest, BestFit96Plus64TenTimes) +{ + testBestFit(96, 64, 10, DontRunSanityCheck); +} + +TEST_F(MetaAllocatorTest, BestFit96Plus64HundredTimes) +{ + testBestFit(96, 64, 100, DontRunSanityCheck); +} + +TEST_F(MetaAllocatorTest, BestFit96Plus96Thrice) +{ + testBestFit(96, 96, 3, RunSanityCheck); +} + +TEST_F(MetaAllocatorTest, BestFit96Plus96TenTimes) +{ + testBestFit(96, 96, 10, DontRunSanityCheck); +} + +TEST_F(MetaAllocatorTest, BestFit96Plus96EightyTimes) +{ + testBestFit(96, 96, 80, DontRunSanityCheck); +} + +TEST_F(MetaAllocatorTest, Shrink64To32) +{ + testShrink(64, 32); +} + +TEST_F(MetaAllocatorTest, ShrinkPageTo32) +{ + testShrink(pageSize(), 32); +} + +TEST_F(MetaAllocatorTest, ShrinkPageToPageLess32) +{ + testShrink(pageSize(), pageSize() - 32); +} + +TEST_F(MetaAllocatorTest, ShrinkTwoPagesTo32) +{ + testShrink(pageSize() * 2, 32); +} + +TEST_F(MetaAllocatorTest, ShrinkTwoPagesToPagePlus32) +{ + testShrink(pageSize() * 2, pageSize() + 32); +} + +TEST_F(MetaAllocatorTest, ShrinkTwoPagesToPage) +{ + testShrink(pageSize() * 2, pageSize()); +} + +TEST_F(MetaAllocatorTest, ShrinkTwoPagesToPageLess32) +{ + testShrink(pageSize() * 2, pageSize() - 32); +} + +TEST_F(MetaAllocatorTest, ShrinkTwoPagesToTwoPagesLess32) +{ + testShrink(pageSize() * 2, pageSize() * 2 - 32); +} + +TEST_F(MetaAllocatorTest, DemandAllocCoalescePageThenDoubleHeap) +{ + testDemandAllocCoalesce(pageSize(), defaultPagesInHeap, defaultPagesInHeap * pageSize()); +} + +TEST_F(MetaAllocatorTest, DemandAllocCoalescePageThenTripleHeap) +{ + testDemandAllocCoalesce(pageSize(), defaultPagesInHeap * 2, defaultPagesInHeap * pageSize()); +} + +TEST_F(MetaAllocatorTest, DemandAllocDontCoalescePageThenDoubleHeap) +{ + testDemandAllocDontCoalesce(pageSize(), defaultPagesInHeap, defaultPagesInHeap * pageSize()); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/MoveOnly.h b/Tools/TestWebKitAPI/Tests/WTF/MoveOnly.h new file mode 100644 index 000000000..cecc49152 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/MoveOnly.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef MoveOnly_h +#define MoveOnly_h + +#include <wtf/HashFunctions.h> +#include <wtf/HashTraits.h> + +class MoveOnly { +public: + MoveOnly() + : m_value(0) + { + } + + MoveOnly(unsigned value) + : m_value(value) + { + } + + unsigned value() const + { + return m_value; + } + + MoveOnly(MoveOnly&& other) + : m_value(other.m_value) + { + other.m_value = 0; + } + + MoveOnly& operator=(MoveOnly&& other) + { + if (this == &other) + return *this; + + m_value = other.m_value; + other.m_value = 0; + return *this; + } + + friend bool operator==(const MoveOnly& a, const MoveOnly& b) + { + return a.m_value == b.m_value; + } + +private: + unsigned m_value; +}; + +namespace WTF { + +template<> struct HashTraits<MoveOnly> : public GenericHashTraits<MoveOnly> { + static void constructDeletedValue(MoveOnly& slot) { slot = MoveOnly(std::numeric_limits<unsigned>::max()); } + static bool isDeletedValue(const MoveOnly& slot) { return slot.value() == std::numeric_limits<unsigned>::max(); } +}; + +template<> struct DefaultHash<MoveOnly> { + struct Hash { + static unsigned hash(const MoveOnly& key) + { + return intHash(key.value()); + } + + static bool equal(const MoveOnly& a, const MoveOnly& b) + { + return a == b; + } + + static const bool safeToCompareToEmptyOrDeleted = true; + }; +}; +} // namespace WTF + +#endif // MoveOnly_h diff --git a/Tools/TestWebKitAPI/Tests/WTF/NakedPtr.cpp b/Tools/TestWebKitAPI/Tests/WTF/NakedPtr.cpp new file mode 100644 index 000000000..414507f15 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/NakedPtr.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2015 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. ``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 + * 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 "RefLogger.h" +#include <wtf/NakedPtr.h> + +namespace TestWebKitAPI { + +// For these tests, we need a base class and a derived class. For this purpose, +// we reuse the RefLogger and DerivedRefLogger classes. + +TEST(WTF_NakedPtr, Basic) +{ + DerivedRefLogger a("a"); + + NakedPtr<RefLogger> empty; + ASSERT_EQ(nullptr, empty.get()); + + { + NakedPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + ASSERT_EQ(&a, &*ptr); + ASSERT_EQ(&a.name, &ptr->name); + } + + { + NakedPtr<RefLogger> ptr = &a; + ASSERT_EQ(&a, ptr.get()); + } + + { + NakedPtr<RefLogger> p1 = &a; + NakedPtr<RefLogger> p2(p1); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + + { + NakedPtr<RefLogger> p1 = &a; + NakedPtr<RefLogger> p2 = p1; + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + + { + NakedPtr<RefLogger> p1 = &a; + NakedPtr<RefLogger> p2 = WTF::move(p1); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + + { + NakedPtr<RefLogger> p1 = &a; + NakedPtr<RefLogger> p2(WTF::move(p1)); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + + { + NakedPtr<DerivedRefLogger> p1 = &a; + NakedPtr<RefLogger> p2 = p1; + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + + { + NakedPtr<DerivedRefLogger> p1 = &a; + NakedPtr<RefLogger> p2 = WTF::move(p1); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + + { + NakedPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + ptr.clear(); + ASSERT_EQ(nullptr, ptr.get()); + } +} + +TEST(WTF_NakedPtr, Assignment) +{ + DerivedRefLogger a("a"); + RefLogger b("b"); + DerivedRefLogger c("c"); + + { + NakedPtr<RefLogger> p1(&a); + NakedPtr<RefLogger> p2(&b); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&b, p2.get()); + p1 = p2; + ASSERT_EQ(&b, p1.get()); + ASSERT_EQ(&b, p2.get()); + } + + { + NakedPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + ptr = &b; + ASSERT_EQ(&b, ptr.get()); + } + + { + NakedPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + ptr = nullptr; + ASSERT_EQ(nullptr, ptr.get()); + } + + { + NakedPtr<RefLogger> p1(&a); + NakedPtr<RefLogger> p2(&b); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&b, p2.get()); + p1 = WTF::move(p2); + ASSERT_EQ(&b, p1.get()); + ASSERT_EQ(&b, p2.get()); + } + + { + NakedPtr<RefLogger> p1(&a); + NakedPtr<DerivedRefLogger> p2(&c); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&c, p2.get()); + p1 = p2; + ASSERT_EQ(&c, p1.get()); + ASSERT_EQ(&c, p2.get()); + } + + { + NakedPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + ptr = &c; + ASSERT_EQ(&c, ptr.get()); + } + + { + NakedPtr<RefLogger> p1(&a); + NakedPtr<DerivedRefLogger> p2(&c); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&c, p2.get()); + p1 = WTF::move(p2); + ASSERT_EQ(&c, p1.get()); + ASSERT_EQ(&c, p2.get()); + } + + { + NakedPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + ptr = ptr; + ASSERT_EQ(&a, ptr.get()); + } + + { + NakedPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + ptr = WTF::move(ptr); + ASSERT_EQ(&a, ptr.get()); + } +} + +TEST(WTF_NakedPtr, Swap) +{ + RefLogger a("a"); + RefLogger b("b"); + + { + NakedPtr<RefLogger> p1(&a); + NakedPtr<RefLogger> p2(&b); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&b, p2.get()); + p1.swap(p2); + ASSERT_EQ(&b, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + + { + NakedPtr<RefLogger> p1(&a); + NakedPtr<RefLogger> p2(&b); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&b, p2.get()); + std::swap(p1, p2); + ASSERT_EQ(&b, p1.get()); + ASSERT_EQ(&a, p2.get()); + } +} + +NakedPtr<RefLogger> nakedPtrFoo(RefLogger& logger) +{ + return NakedPtr<RefLogger>(&logger); +} + +TEST(WTF_NakedPtr, ReturnValue) +{ + DerivedRefLogger a("a"); + + { + auto ptr = nakedPtrFoo(a); + ASSERT_EQ(&a, ptr.get()); + ASSERT_EQ(&a, &*ptr); + ASSERT_EQ(&a.name, &ptr->name); + } +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/Optional.cpp b/Tools/TestWebKitAPI/Tests/WTF/Optional.cpp new file mode 100644 index 000000000..05f413b97 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/Optional.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2014 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 <wtf/Optional.h> + +namespace TestWebKitAPI { + +TEST(WTF_Optional, Disengaged) +{ + { + Optional<int> optional; + + EXPECT_FALSE(static_cast<bool>(optional)); + } + + { + Optional<int> optional { Nullopt }; + + EXPECT_FALSE(static_cast<bool>(optional)); + } +} + +TEST(WTF_Optional, Engaged) +{ + Optional<int> optional { 10 }; + + EXPECT_TRUE(static_cast<bool>(optional)); + EXPECT_EQ(10, optional.value()); +} + +TEST(WTF_Optional, Destructor) +{ + static bool didCallDestructor = false; + struct A { + ~A() + { + EXPECT_FALSE(didCallDestructor); + didCallDestructor = true; + } + }; + + { + Optional<A> optional { InPlace }; + + EXPECT_TRUE(static_cast<bool>(optional)); + } + + EXPECT_TRUE(didCallDestructor); +} + +TEST(WTF_Optional, Callback) +{ + bool called = false; + Optional<int> a; + int result = a.valueOrCompute([&] { + called = true; + return 300; + }); + EXPECT_TRUE(called); + EXPECT_EQ(result, 300); + + a = 250; + called = false; + result = a.valueOrCompute([&] { + called = true; + return 300; + }); + EXPECT_FALSE(called); + EXPECT_EQ(result, 250); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/ParkingLot.cpp b/Tools/TestWebKitAPI/Tests/WTF/ParkingLot.cpp new file mode 100644 index 000000000..cad99d519 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/ParkingLot.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2015 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 <condition_variable> +#include <mutex> +#include <thread> +#include <wtf/DataLog.h> +#include <wtf/HashSet.h> +#include <wtf/ListDump.h> +#include <wtf/ParkingLot.h> +#include <wtf/Threading.h> +#include <wtf/ThreadingPrimitives.h> + +using namespace WTF; + +namespace TestWebKitAPI { + +namespace { + +struct SingleLatchTest { + void initialize(unsigned numThreads) + { + // This implements a fair (FIFO) semaphore, and it starts out unavailable. + semaphore.store(0); + + for (unsigned i = numThreads; i--;) { + threads.append( + createThread( + "Parking Test Thread", + [&] () { + EXPECT_NE(0u, currentThread()); + + down(); + + std::lock_guard<std::mutex> locker(lock); + awake.add(currentThread()); + lastAwoken = currentThread(); + condition.notify_one(); + })); + } + } + + void unparkOne(unsigned singleUnparkIndex) + { + EXPECT_EQ(0u, lastAwoken); + + unsigned numWaitingOnAddress = 0; + Vector<ThreadIdentifier, 8> queue; + ParkingLot::forEach( + [&] (ThreadIdentifier threadIdentifier, const void* address) { + if (address != &semaphore) + return; + + queue.append(threadIdentifier); + + numWaitingOnAddress++; + }); + + EXPECT_LE(numWaitingOnAddress, threads.size() - singleUnparkIndex); + + up(); + + { + std::unique_lock<std::mutex> locker(lock); + while (awake.size() < singleUnparkIndex + 1) + condition.wait(locker); + EXPECT_NE(0u, lastAwoken); + if (!queue.isEmpty() && queue[0] != lastAwoken) { + dataLog("Woke up wrong thread: queue = ", listDump(queue), ", last awoken = ", lastAwoken, "\n"); + EXPECT_EQ(queue[0], lastAwoken); + } + lastAwoken = 0; + } + } + + void finish(unsigned numSingleUnparks) + { + unsigned numWaitingOnAddress = 0; + ParkingLot::forEach( + [&] (ThreadIdentifier, const void* address) { + if (address != &semaphore) + return; + + numWaitingOnAddress++; + }); + + EXPECT_LE(numWaitingOnAddress, threads.size() - numSingleUnparks); + + semaphore.store(threads.size() - numSingleUnparks); + ParkingLot::unparkAll(&semaphore); + + numWaitingOnAddress = 0; + ParkingLot::forEach( + [&] (ThreadIdentifier, const void* address) { + if (address != &semaphore) + return; + + numWaitingOnAddress++; + }); + + EXPECT_EQ(0u, numWaitingOnAddress); + + { + std::unique_lock<std::mutex> locker(lock); + while (awake.size() < threads.size()) + condition.wait(locker); + } + + for (ThreadIdentifier threadIdentifier : threads) + waitForThreadCompletion(threadIdentifier); + } + + // Semaphore operations. + void down() + { + for (;;) { + int oldSemaphoreValue = semaphore.load(); + int newSemaphoreValue = oldSemaphoreValue - 1; + if (!semaphore.compareExchangeWeak(oldSemaphoreValue, newSemaphoreValue)) + continue; + + if (oldSemaphoreValue > 0) { + // We acquired the semaphore. Done. + return; + } + + // We need to wait. + if (ParkingLot::compareAndPark(&semaphore, newSemaphoreValue)) { + // We did wait, and then got woken up. This means that someone who up'd the semaphore + // passed ownership onto us. + return; + } + + // We never parked, because the semaphore value changed. Undo our decrement and try again. + for (;;) { + int oldSemaphoreValue = semaphore.load(); + if (semaphore.compareExchangeWeak(oldSemaphoreValue, oldSemaphoreValue + 1)) + break; + } + } + } + void up() + { + int oldSemaphoreValue; + for (;;) { + oldSemaphoreValue = semaphore.load(); + if (semaphore.compareExchangeWeak(oldSemaphoreValue, oldSemaphoreValue + 1)) + break; + } + + // Check if anyone was waiting on the semaphore. If they were, then pass ownership to them. + if (oldSemaphoreValue < 0) + ParkingLot::unparkOne(&semaphore); + } + + Atomic<int> semaphore; + std::mutex lock; + std::condition_variable condition; + HashSet<ThreadIdentifier> awake; + Vector<ThreadIdentifier> threads; + ThreadIdentifier lastAwoken { 0 }; +}; + +void runParkingTest(unsigned numLatches, unsigned delay, unsigned numThreads, unsigned numSingleUnparks) +{ + std::unique_ptr<SingleLatchTest[]> tests = std::make_unique<SingleLatchTest[]>(numLatches); + + for (unsigned latchIndex = numLatches; latchIndex--;) + tests[latchIndex].initialize(numThreads); + + for (unsigned unparkIndex = 0; unparkIndex < numSingleUnparks; ++unparkIndex) { + std::this_thread::sleep_for(std::chrono::microseconds(delay)); + for (unsigned latchIndex = numLatches; latchIndex--;) + tests[latchIndex].unparkOne(unparkIndex); + } + + for (unsigned latchIndex = numLatches; latchIndex--;) + tests[latchIndex].finish(numSingleUnparks); +} + +void repeatParkingTest(unsigned numRepeats, unsigned numLatches, unsigned delay, unsigned numThreads, unsigned numSingleUnparks) +{ + while (numRepeats--) + runParkingTest(numLatches, delay, numThreads, numSingleUnparks); +} + +} // anonymous namespace + +TEST(WTF_ParkingLot, UnparkAllOneFast) +{ + repeatParkingTest(10000, 1, 0, 1, 0); +} + +TEST(WTF_ParkingLot, UnparkAllHundredFast) +{ + repeatParkingTest(100, 1, 0, 100, 0); +} + +TEST(WTF_ParkingLot, UnparkOneOneFast) +{ + repeatParkingTest(1000, 1, 0, 1, 1); +} + +TEST(WTF_ParkingLot, UnparkOneHundredFast) +{ + repeatParkingTest(20, 1, 0, 100, 100); +} + +TEST(WTF_ParkingLot, UnparkOneFiftyThenFiftyAllFast) +{ + repeatParkingTest(50, 1, 0, 100, 50); +} + +TEST(WTF_ParkingLot, UnparkAllOne) +{ + repeatParkingTest(100, 1, 10000, 1, 0); +} + +TEST(WTF_ParkingLot, UnparkAllHundred) +{ + repeatParkingTest(100, 1, 10000, 100, 0); +} + +TEST(WTF_ParkingLot, UnparkOneOne) +{ + repeatParkingTest(10, 1, 10000, 1, 1); +} + +TEST(WTF_ParkingLot, UnparkOneFifty) +{ + repeatParkingTest(1, 1, 10000, 50, 50); +} + +TEST(WTF_ParkingLot, UnparkOneFiftyThenFiftyAll) +{ + repeatParkingTest(2, 1, 10000, 100, 50); +} + +TEST(WTF_ParkingLot, HundredUnparkAllOneFast) +{ + repeatParkingTest(100, 100, 0, 1, 0); +} + +TEST(WTF_ParkingLot, HundredUnparkAllOne) +{ + repeatParkingTest(1, 100, 10000, 1, 0); +} + +} // namespace TestWebKitAPI + diff --git a/Tools/TestWebKitAPI/Tests/WTF/RedBlackTree.cpp b/Tools/TestWebKitAPI/Tests/WTF/RedBlackTree.cpp new file mode 100644 index 000000000..5e5c8adc4 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/RedBlackTree.cpp @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2011 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. + * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 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 <wtf/RedBlackTree.h> +#include <wtf/Vector.h> + +using namespace WTF; + +namespace TestWebKitAPI { + +class TestNode : public RedBlackTree<TestNode, char>::Node { +public: + TestNode(char key, unsigned value) + : m_key(key) + , m_value(value) + { + } + + char key() + { + return m_key; + } + + char m_key; + unsigned m_value; +}; + +class RedBlackTreeTest : public testing::Test { +public: + unsigned m_counter; + + virtual void SetUp() + { + m_counter = 0; + } + + virtual void TearDown() + { + } + + struct Pair { + char key; + unsigned value; + + Pair() { } + + Pair(char key, unsigned value) + : key(key) + , value(value) + { + } + + bool operator==(const Pair& other) const + { + return key == other.key; + } + + bool operator<(const Pair& other) const + { + return key < other.key; + } + }; + + typedef Vector<Pair, 16> PairVector; + + PairVector findExact(PairVector& asVector, char key) + { + PairVector result; + + for (size_t index = 0; index < asVector.size(); ++index) { + if (asVector.at(index).key == key) + result.append(asVector.at(index)); + } + + std::sort(result.begin(), result.end()); + + return result; + } + + void remove(PairVector& asVector, size_t index) + { + asVector.at(index) = asVector.last(); + asVector.removeLast(); + } + + PairVector findLeastGreaterThanOrEqual(PairVector& asVector, char key) + { + char bestKey = 0; // assignment to make gcc happy + bool foundKey = false; + + for (size_t index = 0; index < asVector.size(); ++index) { + if (asVector.at(index).key >= key) { + if (asVector.at(index).key < bestKey || !foundKey) { + foundKey = true; + bestKey = asVector.at(index).key; + } + } + } + + PairVector result; + + if (!foundKey) + return result; + + return findExact(asVector, bestKey); + } + + void assertFoundAndRemove(PairVector& asVector, char key, unsigned value) + { + bool found = false; + size_t foundIndex = 0; // make compilers happy + + for (size_t index = 0; index < asVector.size(); ++index) { + if (asVector.at(index).key == key + && asVector.at(index).value == value) { + EXPECT_TRUE(!found); + + found = true; + foundIndex = index; + } + } + + EXPECT_TRUE(found); + + remove(asVector, foundIndex); + } + + // This deliberately passes a copy of the vector. + void assertEqual(RedBlackTree<TestNode, char>& asTree, PairVector asVector) + { + for (TestNode* current = asTree.first(); current; current = current->successor()) + assertFoundAndRemove(asVector, current->m_key, current->m_value); + } + + void assertSameValuesForKey(RedBlackTree<TestNode, char>& asTree, TestNode* node, PairVector foundValues, char key) + { + if (node) { + EXPECT_EQ(node->m_key, key); + + TestNode* prevNode = node; + do { + node = prevNode; + prevNode = prevNode->predecessor(); + } while (prevNode && prevNode->m_key == key); + + EXPECT_EQ(node->m_key, key); + EXPECT_TRUE(!prevNode || prevNode->m_key < key); + + do { + assertFoundAndRemove(foundValues, node->m_key, node->m_value); + + node = node->successor(); + EXPECT_TRUE(!node || node->m_key >= key); + } while (node && node->m_key == key); + } + + EXPECT_TRUE(foundValues.isEmpty()); + } + + // The control string is a null-terminated list of commands. Each + // command is two characters, with the first identifying the operation + // and the second giving a key. The commands are: + // +x Add x + // *x Find all elements equal to x + // @x Find all elements that have the smallest key that is greater than or equal to x + // !x Remove all elements equal to x + void testDriver(const char* controlString) + { + PairVector asVector; + RedBlackTree<TestNode, char> asTree; + + for (const char* current = controlString; *current; current += 2) { + char command = current[0]; + char key = current[1]; + unsigned value = ++m_counter; + + ASSERT(command); + ASSERT(key); + + switch (command) { + case '+': { + TestNode* node = new TestNode(key, value); + asTree.insert(node); + asVector.append(Pair(key, value)); + break; + } + + case '*': { + TestNode* node = asTree.findExact(key); + if (node) + EXPECT_EQ(node->m_key, key); + assertSameValuesForKey(asTree, node, findExact(asVector, key), key); + break; + } + + case '@': { + TestNode* node = asTree.findLeastGreaterThanOrEqual(key); + if (node) { + EXPECT_TRUE(node->m_key >= key); + assertSameValuesForKey(asTree, node, findLeastGreaterThanOrEqual(asVector, key), node->m_key); + } else + EXPECT_TRUE(findLeastGreaterThanOrEqual(asVector, key).isEmpty()); + break; + } + + case '!': { + while (true) { + TestNode* node = asTree.remove(key); + if (node) { + EXPECT_EQ(node->m_key, key); + assertFoundAndRemove(asVector, node->m_key, node->m_value); + } else { + EXPECT_TRUE(findExact(asVector, key).isEmpty()); + break; + } + } + break; + } + + default: + ASSERT_NOT_REACHED(); + break; + } + + EXPECT_EQ(asTree.size(), asVector.size()); + assertEqual(asTree, asVector); + } + } +}; + +TEST_F(RedBlackTreeTest, Empty) +{ + testDriver(""); +} + +TEST_F(RedBlackTreeTest, EmptyGetFindRemove) +{ + testDriver("*x@y!z"); +} + +TEST_F(RedBlackTreeTest, SingleAdd) +{ + testDriver("+a"); +} + +TEST_F(RedBlackTreeTest, SingleAddGetFindRemoveNotFound) +{ + testDriver("+a*x@y!z"); +} + +TEST_F(RedBlackTreeTest, SingleAddGetFindRemove) +{ + testDriver("+a*a@a!a"); +} + +TEST_F(RedBlackTreeTest, TwoAdds) +{ + testDriver("+a+b"); +} + +TEST_F(RedBlackTreeTest, ThreeAdds) +{ + testDriver("+a+b+c"); +} + +TEST_F(RedBlackTreeTest, FourAdds) +{ + testDriver("+a+b+c+d"); +} + +TEST_F(RedBlackTreeTest, LotsOfRepeatAdds) +{ + testDriver("+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d"); +} + +TEST_F(RedBlackTreeTest, LotsOfRepeatAndUniqueAdds) +{ + testDriver("+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+e+f+g+h+i+j+k+l+m+n+o+p+q+r+s+t+u+v+x+y+z"); +} + +TEST_F(RedBlackTreeTest, LotsOfRepeatAndUniqueAddsAndGetsAndRemoves) +{ + testDriver("+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+a+b+c+d+e+f+g+h+i+j+k+l+m+n+o+p+q+r+s+t+u+v+x+y+z*a*b*c*d*e*f*g*h*i*j*k*l*m*n*o*p*q*r*s*t*u*v*w*x*y*z!a!b!c!d!e!f!g!h!i!j!k!l!m!n!o!p!q!r!s!t!u!v!w!x!y!z"); +} + +TEST_F(RedBlackTreeTest, SimpleBestFitSearch) +{ + testDriver("+d+d+m+w@d@m@w@a@g@q"); +} + +TEST_F(RedBlackTreeTest, BiggerBestFitSearch) +{ + testDriver("+d+d+d+d+d+d+d+d+d+d+f+f+f+f+f+f+f+h+h+i+j+k+l+m+o+p+q+r+z@a@b@c@d@e@f@g@h@i@j@k@l@m@n@o@p@q@r@s@t@u@v@w@x@y@z"); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/Ref.cpp b/Tools/TestWebKitAPI/Tests/WTF/Ref.cpp new file mode 100644 index 000000000..8c622d07b --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/Ref.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2013 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 "RefLogger.h" +#include <wtf/Ref.h> +#include <wtf/RefPtr.h> + +namespace TestWebKitAPI { + +TEST(WTF_Ref, Basic) +{ + DerivedRefLogger a("a"); + + { + Ref<RefLogger> ptr(a); + ASSERT_EQ(&a, ptr.ptr()); + ASSERT_EQ(&a.name, &ptr->name); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + + { + Ref<RefLogger> ptr(adoptRef(a)); + ASSERT_EQ(&a, ptr.ptr()); + ASSERT_EQ(&a.name, &ptr->name); + } + ASSERT_STREQ("deref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_Ref, Assignment) +{ + DerivedRefLogger a("a"); + RefLogger b("b"); + DerivedRefLogger c("c"); + + { + Ref<RefLogger> ptr(a); + ASSERT_EQ(&a, ptr.ptr()); + log() << "| "; + ptr = b; + ASSERT_EQ(&b, ptr.ptr()); + log() << "| "; + } + ASSERT_STREQ("ref(a) | ref(b) deref(a) | deref(b) ", takeLogStr().c_str()); + + { + Ref<RefLogger> ptr(a); + ASSERT_EQ(&a, ptr.ptr()); + log() << "| "; + ptr = c; + ASSERT_EQ(&c, ptr.ptr()); + log() << "| "; + } + ASSERT_STREQ("ref(a) | ref(c) deref(a) | deref(c) ", takeLogStr().c_str()); + + { + Ref<RefLogger> ptr(a); + ASSERT_EQ(&a, ptr.ptr()); + log() << "| "; + ptr = adoptRef(b); + ASSERT_EQ(&b, ptr.ptr()); + log() << "| "; + } + ASSERT_STREQ("ref(a) | deref(a) | deref(b) ", takeLogStr().c_str()); + + { + Ref<RefLogger> ptr(a); + ASSERT_EQ(&a, ptr.ptr()); + log() << "| "; + ptr = adoptRef(c); + ASSERT_EQ(&c, ptr.ptr()); + log() << "| "; + } + ASSERT_STREQ("ref(a) | deref(a) | deref(c) ", takeLogStr().c_str()); +} + +static Ref<RefLogger> passWithRef(Ref<RefLogger>&& reference) +{ + return WTF::move(reference); +} + +static RefPtr<RefLogger> passWithPassRefPtr(PassRefPtr<RefLogger> reference) +{ + return reference; +} + +TEST(WTF_Ref, ReturnValue) +{ + DerivedRefLogger a("a"); + RefLogger b("b"); + DerivedRefLogger c("c"); + + { + Ref<RefLogger> ptr(passWithRef(Ref<RefLogger>(a))); + ASSERT_EQ(&a, ptr.ptr()); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + + { + Ref<RefLogger> ptr(a); + ASSERT_EQ(&a, ptr.ptr()); + log() << "| "; + ptr = passWithRef(b); + ASSERT_EQ(&b, ptr.ptr()); + log() << "| "; + } + ASSERT_STREQ("ref(a) | ref(b) deref(a) | deref(b) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr(passWithRef(a)); + ASSERT_EQ(&a, ptr.get()); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr(passWithPassRefPtr(passWithRef(a))); + ASSERT_EQ(&a, ptr.get()); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<DerivedRefLogger> ptr(&a); + RefPtr<RefLogger> ptr2(WTF::move(ptr)); + ASSERT_EQ(nullptr, ptr.get()); + ASSERT_EQ(&a, ptr2.get()); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + + { + Ref<DerivedRefLogger> derivedReference(a); + Ref<RefLogger> baseReference(passWithRef(derivedReference.copyRef())); + ASSERT_EQ(&a, derivedReference.ptr()); + ASSERT_EQ(&a, baseReference.ptr()); + } + ASSERT_STREQ("ref(a) ref(a) deref(a) deref(a) ", takeLogStr().c_str()); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/RefCounter.cpp b/Tools/TestWebKitAPI/Tests/WTF/RefCounter.cpp new file mode 100644 index 000000000..f8771e0c2 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/RefCounter.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2011 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 <wtf/Ref.h> +#include <wtf/RefCounter.h> +#include <wtf/text/WTFString.h> + +namespace TestWebKitAPI { + +static const int CallbackExpected = 0xC0FFEE; +static const int CallbackNotExpected = 0xDECAF; + +enum CounterType { }; +typedef RefCounter::Token<CounterType> TokenType; + +TEST(WTF, RefCounter) +{ + // RefCounter API is pretty simple, containing the following 4 methods to test: + // + // 1) RefCounter(std::function<void()>); + // 2) ~RefCounter(); + // 3) Ref<Count> token() const; + // 4) unsigned value() const; + // + // We'll test: + // 1) Construction: + // 1a) with a callback + // 1b) without a callback + // 2) Destruction where the RefCounter::Count has: + // 2a) a non-zero reference count (Count outlives RefCounter). + // 2b) a zero reference count (Count is deleted by RefCounter's destructor). + // 3) Call count to ref/deref the Count object, where: + // 3a) ref with callback from 0 -> 1. + // 3b) ref with callback from 1 -> >1. + // 3c) deref with callback from >1 -> 1. + // 3d) deref with callback from 1 -> 0. + // 3d) deref with callback from 1 -> 0. + // 3e) ref with callback from 1 -> >1 AFTER RefCounter has been destroyed. + // 3f) deref with callback from >1 -> 1 AFTER RefCounter has been destroyed. + // 3g) deref with callback from 1 -> 0 AFTER RefCounter has been destroyed. + // 3h) ref without callback + // 3i) deref without callback + // 3j) ref using a Ref rather than a RefPtr (make sure there is no unnecessary reference count churn). + // 3k) deref using a Ref rather than a RefPtr (make sure there is no unnecessary reference count churn). + // 4) Test the value of the counter: + // 4a) at construction. + // 4b) as read within the callback. + // 4c) as read after the ref/deref. + + // These values will outlive the following block. + int callbackValue = CallbackNotExpected; + TokenType incTo1Again; + + { + // Testing (1a) - Construction with a callback. + RefCounter* counterPtr = nullptr; + RefCounter counter([&](bool value) { + // Check that the callback is called at the expected times, and the correct number of times. + EXPECT_EQ(callbackValue, CallbackExpected); + // Value provided should be equal to the counter value. + EXPECT_EQ(value, counterPtr->value()); + // return the value of the counter in the callback. + callbackValue = value; + }); + counterPtr = &counter; + // Testing (4a) - after construction value() is 0. + EXPECT_EQ(0, static_cast<int>(counter.value())); + + // Testing (3a) - ref with callback from 0 -> 1. + callbackValue = CallbackExpected; + TokenType incTo1(counter.token<CounterType>()); + // Testing (4b) & (4c) - values within & after callback. + EXPECT_EQ(true, callbackValue); + EXPECT_EQ(1, static_cast<int>(counter.value())); + + // Testing (3b) - ref with callback from 1 -> 2. + TokenType incTo2(incTo1); + // Testing (4b) & (4c) - values within & after callback. + EXPECT_EQ(2, static_cast<int>(counter.value())); + + // Testing (3c) - deref with callback from >1 -> 1. + incTo1 = nullptr; + // Testing (4b) & (4c) - values within & after callback. + EXPECT_EQ(1, static_cast<int>(counter.value())); + + { + // Testing (3j) - ref using a Ref rather than a RefPtr. + TokenType incTo2Again(counter.token<CounterType>()); + // Testing (4b) & (4c) - values within & after callback. + EXPECT_EQ(2, static_cast<int>(counter.value())); + // Testing (3k) - deref using a Ref rather than a RefPtr. + } + EXPECT_EQ(1, static_cast<int>(counter.value())); + // Testing (4b) & (4c) - values within & after callback. + + // Testing (3d) - deref with callback from 1 -> 0. + callbackValue = CallbackExpected; + incTo2 = nullptr; + // Testing (4b) & (4c) - values within & after callback. + EXPECT_EQ(0, callbackValue); + EXPECT_EQ(0, static_cast<int>(counter.value())); + + // Testing (2a) - Destruction where the RefCounter::Count has a non-zero reference count. + callbackValue = CallbackExpected; + incTo1Again = counter.token<CounterType>(); + EXPECT_EQ(1, callbackValue); + EXPECT_EQ(1, static_cast<int>(counter.value())); + callbackValue = CallbackNotExpected; + } + + // Testing (3e) - ref with callback from 1 -> >1 AFTER RefCounter has been destroyed. + TokenType incTo2Again = incTo1Again; + // Testing (3f) - deref with callback from >1 -> 1 AFTER RefCounter has been destroyed. + incTo1Again = nullptr; + // Testing (3g) - deref with callback from 1 -> 0 AFTER RefCounter has been destroyed. + incTo2Again = nullptr; + + // Testing (1b) - Construction without a callback. + RefCounter counter; + // Testing (4a) - after construction value() is 0. + EXPECT_EQ(0, static_cast<int>(counter.value())); + // Testing (3h) - ref without callback + TokenType incTo1(counter.token<CounterType>()); + // Testing (4c) - value as read after the ref. + EXPECT_EQ(1, static_cast<int>(counter.value())); + // Testing (3i) - deref without callback + incTo1 = nullptr; + // Testing (4c) - value as read after the deref. + EXPECT_EQ(0, static_cast<int>(counter.value())); + // Testing (2b) - Destruction where the RefCounter::Count has a zero reference count. + // ... not a lot to test here! - we can at least ensure this code path is run & we don't crash! +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/RefLogger.h b/Tools/TestWebKitAPI/Tests/WTF/RefLogger.h new file mode 100644 index 000000000..c5cfb071f --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/RefLogger.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef RefLogger_h + +namespace TestWebKitAPI { + +inline std::ostringstream& log() +{ + static std::ostringstream log; + return log; +} + +inline std::string takeLogStr() +{ + std::string string = log().str(); + log().str(""); + return string; +} + +struct RefLogger { + RefLogger(const char* name) : name(*name) { } + void ref() { log() << "ref(" << &name << ") "; } + void deref() { log() << "deref(" << &name << ") "; } + const char& name; +}; + +struct DerivedRefLogger : RefLogger { + DerivedRefLogger(const char* name) : RefLogger(name) { log().str(""); } +}; + +} + +#endif diff --git a/Tools/TestWebKitAPI/Tests/WTF/RefPtr.cpp b/Tools/TestWebKitAPI/Tests/WTF/RefPtr.cpp new file mode 100644 index 000000000..b726d7ddf --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/RefPtr.cpp @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2013 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 "RefLogger.h" +#include <wtf/RefPtr.h> + +namespace TestWebKitAPI { + +TEST(WTF_RefPtr, Basic) +{ + DerivedRefLogger a("a"); + + RefPtr<RefLogger> empty; + ASSERT_EQ(nullptr, empty.get()); + + { + RefPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + ASSERT_EQ(&a, &*ptr); + ASSERT_EQ(&a.name, &ptr->name); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr = &a; + ASSERT_EQ(&a, ptr.get()); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> p1 = &a; + RefPtr<RefLogger> p2(p1); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + ASSERT_STREQ("ref(a) ref(a) deref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> p1 = &a; + RefPtr<RefLogger> p2 = p1; + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + ASSERT_STREQ("ref(a) ref(a) deref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> p1 = &a; + RefPtr<RefLogger> p2 = WTF::move(p1); + ASSERT_EQ(nullptr, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> p1 = &a; + RefPtr<RefLogger> p2(WTF::move(p1)); + ASSERT_EQ(nullptr, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<DerivedRefLogger> p1 = &a; + RefPtr<RefLogger> p2 = p1; + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + ASSERT_STREQ("ref(a) ref(a) deref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<DerivedRefLogger> p1 = &a; + RefPtr<RefLogger> p2 = WTF::move(p1); + ASSERT_EQ(nullptr, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + ptr = nullptr; + ASSERT_EQ(nullptr, ptr.get()); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + ptr.release(); + ASSERT_EQ(nullptr, ptr.get()); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_RefPtr, AssignPassRefToRefPtr) +{ + DerivedRefLogger a("a"); + { + Ref<RefLogger> passRef(a); + RefPtr<RefLogger> ptr = WTF::move(passRef); + ASSERT_EQ(&a, ptr.get()); + ptr.release(); + ASSERT_EQ(nullptr, ptr.get()); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_RefPtr, Adopt) +{ + DerivedRefLogger a("a"); + + RefPtr<RefLogger> empty; + ASSERT_EQ(nullptr, empty.get()); + + { + RefPtr<RefLogger> ptr(adoptRef(&a)); + ASSERT_EQ(&a, ptr.get()); + ASSERT_EQ(&a, &*ptr); + ASSERT_EQ(&a.name, &ptr->name); + } + ASSERT_STREQ("deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr = adoptRef(&a); + ASSERT_EQ(&a, ptr.get()); + } + ASSERT_STREQ("deref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_RefPtr, Assignment) +{ + DerivedRefLogger a("a"); + RefLogger b("b"); + DerivedRefLogger c("c"); + + { + RefPtr<RefLogger> p1(&a); + RefPtr<RefLogger> p2(&b); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&b, p2.get()); + log() << "| "; + p1 = p2; + ASSERT_EQ(&b, p1.get()); + ASSERT_EQ(&b, p2.get()); + log() << "| "; + } + ASSERT_STREQ("ref(a) ref(b) | ref(b) deref(a) | deref(b) deref(b) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + log() << "| "; + ptr = &b; + ASSERT_EQ(&b, ptr.get()); + log() << "| "; + } + ASSERT_STREQ("ref(a) | ref(b) deref(a) | deref(b) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + log() << "| "; + ptr = adoptRef(&b); + ASSERT_EQ(&b, ptr.get()); + log() << "| "; + } + ASSERT_STREQ("ref(a) | deref(a) | deref(b) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + ptr = nullptr; + ASSERT_EQ(nullptr, ptr.get()); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> p1(&a); + RefPtr<RefLogger> p2(&b); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&b, p2.get()); + log() << "| "; + p1 = WTF::move(p2); + ASSERT_EQ(&b, p1.get()); + ASSERT_EQ(nullptr, p2.get()); + log() << "| "; + } + ASSERT_STREQ("ref(a) ref(b) | deref(a) | deref(b) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> p1(&a); + RefPtr<DerivedRefLogger> p2(&c); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&c, p2.get()); + log() << "| "; + p1 = p2; + ASSERT_EQ(&c, p1.get()); + ASSERT_EQ(&c, p2.get()); + log() << "| "; + } + ASSERT_STREQ("ref(a) ref(c) | ref(c) deref(a) | deref(c) deref(c) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + log() << "| "; + ptr = &c; + ASSERT_EQ(&c, ptr.get()); + log() << "| "; + } + ASSERT_STREQ("ref(a) | ref(c) deref(a) | deref(c) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + log() << "| "; + ptr = adoptRef(&c); + ASSERT_EQ(&c, ptr.get()); + log() << "| "; + } + ASSERT_STREQ("ref(a) | deref(a) | deref(c) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> p1(&a); + RefPtr<DerivedRefLogger> p2(&c); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&c, p2.get()); + log() << "| "; + p1 = WTF::move(p2); + ASSERT_EQ(&c, p1.get()); + ASSERT_EQ(nullptr, p2.get()); + log() << "| "; + } + ASSERT_STREQ("ref(a) ref(c) | deref(a) | deref(c) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + log() << "| "; + ptr = ptr; + ASSERT_EQ(&a, ptr.get()); + log() << "| "; + } + ASSERT_STREQ("ref(a) | ref(a) deref(a) | deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr(&a); + ASSERT_EQ(&a, ptr.get()); + ptr = WTF::move(ptr); + ASSERT_EQ(&a, ptr.get()); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_RefPtr, Swap) +{ + RefLogger a("a"); + RefLogger b("b"); + + { + RefPtr<RefLogger> p1(&a); + RefPtr<RefLogger> p2(&b); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&b, p2.get()); + p1.swap(p2); + ASSERT_EQ(&b, p1.get()); + ASSERT_EQ(&a, p2.get()); + log() << "| "; + } + ASSERT_STREQ("ref(a) ref(b) | deref(a) deref(b) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> p1(&a); + RefPtr<RefLogger> p2(&b); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&b, p2.get()); + std::swap(p1, p2); + ASSERT_EQ(&b, p1.get()); + ASSERT_EQ(&a, p2.get()); + log() << "| "; + } + ASSERT_STREQ("ref(a) ref(b) | deref(a) deref(b) ", takeLogStr().c_str()); +} + +TEST(WTF_RefPtr, ReleaseNonNull) +{ + RefLogger a("a"); + + { + RefPtr<RefLogger> refPtr = &a; + RefPtr<RefLogger> ref = refPtr.releaseNonNull(); + } + + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_RefPtr, Release) +{ + DerivedRefLogger a("a"); + RefLogger b("b"); + DerivedRefLogger c("c"); + + { + RefPtr<RefLogger> p1 = &a; + RefPtr<RefLogger> p2 = p1.release(); + ASSERT_EQ(nullptr, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> p1 = &a; + RefPtr<RefLogger> p2(p1.release()); + ASSERT_EQ(nullptr, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<DerivedRefLogger> p1 = &a; + RefPtr<RefLogger> p2 = p1.release(); + ASSERT_EQ(nullptr, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> p1(&a); + RefPtr<RefLogger> p2(&b); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&b, p2.get()); + log() << "| "; + p1 = p2.release(); + ASSERT_EQ(&b, p1.get()); + ASSERT_EQ(nullptr, p2.get()); + log() << "| "; + } + ASSERT_STREQ("ref(a) ref(b) | deref(a) | deref(b) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> p1(&a); + RefPtr<DerivedRefLogger> p2(&c); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&c, p2.get()); + log() << "| "; + p1 = p2.release(); + ASSERT_EQ(&c, p1.get()); + ASSERT_EQ(nullptr, p2.get()); + log() << "| "; + } + ASSERT_STREQ("ref(a) ref(c) | deref(a) | deref(c) ", takeLogStr().c_str()); +} + +RefPtr<RefLogger> f1(RefLogger& logger) +{ + return RefPtr<RefLogger>(&logger); +} + +TEST(WTF_RefPtr, ReturnValue) +{ + DerivedRefLogger a("a"); + + { + f1(a); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + + { + auto ptr = f1(a); + } + ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/RunLoop.cpp b/Tools/TestWebKitAPI/Tests/WTF/RunLoop.cpp new file mode 100644 index 000000000..e7e3d9f99 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/RunLoop.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 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 "PlatformUtilities.h" +#include <wtf/RunLoop.h> + +namespace TestWebKitAPI { + +static bool testFinished; +static int count = 100; + +TEST(WTF_RunLoop, Deadlock) +{ + RunLoop::initializeMainRunLoop(); + + struct DispatchFromDestructorTester { + ~DispatchFromDestructorTester() { + RunLoop::main().dispatch([] { + if (!(--count)) + testFinished = true; + }); + } + }; + + for (int i = 0; i < count; ++i) { + auto capture = std::make_shared<DispatchFromDestructorTester>(); + RunLoop::main().dispatch([capture] { }); + } + + Util::run(&testFinished); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/SHA1.cpp b/Tools/TestWebKitAPI/Tests/WTF/SHA1.cpp new file mode 100644 index 000000000..dcf9d27b3 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/SHA1.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER OR 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 <wtf/SHA1.h> +#include <wtf/text/CString.h> + +namespace TestWebKitAPI { + +static void expectSHA1(CString input, int repeat, CString expected) +{ + SHA1 sha1; + for (int i = 0; i < repeat; ++i) + sha1.addBytes(input); + CString actual = sha1.computeHexDigest(); + + ASSERT_EQ(expected.length(), actual.length()); + ASSERT_STREQ(expected.data(), actual.data()); +} + +TEST(WTF_SHA1, Computation) +{ + // Examples taken from sample code in RFC 3174. + expectSHA1("abc", 1, "A9993E364706816ABA3E25717850C26C9CD0D89D"); + expectSHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 1, "84983E441C3BD26EBAAE4AA1F95129E5E54670F1"); + expectSHA1("a", 1000000, "34AA973CD4C4DAA4F61EEB2BDBAD27316534016F"); + expectSHA1("0123456701234567012345670123456701234567012345670123456701234567", 10, "DEA356A2CDDD90C7A7ECEDC5EBB563934F460452"); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/SaturatedArithmeticOperations.cpp b/Tools/TestWebKitAPI/Tests/WTF/SaturatedArithmeticOperations.cpp new file mode 100644 index 000000000..e854e256e --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/SaturatedArithmeticOperations.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2012, Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER OR 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 "limits.h" +#include <wtf/SaturatedArithmetic.h> + +namespace TestWebKitAPI { + +TEST(WTF, SaturatedArithmeticAddition) +{ + ASSERT_EQ(saturatedAddition(0, 0), 0); + ASSERT_EQ(saturatedAddition(0, 1), 1); + ASSERT_EQ(saturatedAddition(0, 100), 100); + ASSERT_EQ(saturatedAddition(100, 50), 150); + + ASSERT_EQ(saturatedAddition(0, -1), -1); + ASSERT_EQ(saturatedAddition(1, -1), 0); + ASSERT_EQ(saturatedAddition(100, -50), 50); + ASSERT_EQ(saturatedAddition(50, -100), -50); + + ASSERT_EQ(saturatedAddition(INT_MAX - 1, 0), INT_MAX - 1); + ASSERT_EQ(saturatedAddition(INT_MAX - 1, 1), INT_MAX); + ASSERT_EQ(saturatedAddition(INT_MAX - 1, 2), INT_MAX); + ASSERT_EQ(saturatedAddition(0, INT_MAX - 1), INT_MAX - 1); + ASSERT_EQ(saturatedAddition(1, INT_MAX - 1), INT_MAX); + ASSERT_EQ(saturatedAddition(2, INT_MAX - 1), INT_MAX); + ASSERT_EQ(saturatedAddition(INT_MAX - 1, INT_MAX - 1), INT_MAX); + ASSERT_EQ(saturatedAddition(INT_MAX, INT_MAX), INT_MAX); + + ASSERT_EQ(saturatedAddition(INT_MIN, 0), INT_MIN); + ASSERT_EQ(saturatedAddition(INT_MIN + 1, 0), INT_MIN + 1); + ASSERT_EQ(saturatedAddition(INT_MIN + 1, 1), INT_MIN + 2); + ASSERT_EQ(saturatedAddition(INT_MIN + 1, 2), INT_MIN + 3); + ASSERT_EQ(saturatedAddition(INT_MIN + 1, -1), INT_MIN); + ASSERT_EQ(saturatedAddition(INT_MIN + 1, -2), INT_MIN); + ASSERT_EQ(saturatedAddition(0, INT_MIN + 1), INT_MIN + 1); + ASSERT_EQ(saturatedAddition(-1, INT_MIN + 1), INT_MIN); + ASSERT_EQ(saturatedAddition(-2, INT_MIN + 1), INT_MIN); + + ASSERT_EQ(saturatedAddition(INT_MAX / 2, 10000), INT_MAX / 2 + 10000); + ASSERT_EQ(saturatedAddition(INT_MAX / 2 + 1, INT_MAX / 2 + 1), INT_MAX); + ASSERT_EQ(saturatedAddition(INT_MIN, INT_MAX), -1); +} + +TEST(WTF, SaturatedArithmeticSubtraction) +{ + ASSERT_EQ(saturatedSubtraction(0, 0), 0); + ASSERT_EQ(saturatedSubtraction(0, 1), -1); + ASSERT_EQ(saturatedSubtraction(0, 100), -100); + ASSERT_EQ(saturatedSubtraction(100, 50), 50); + + ASSERT_EQ(saturatedSubtraction(0, -1), 1); + ASSERT_EQ(saturatedSubtraction(1, -1), 2); + ASSERT_EQ(saturatedSubtraction(100, -50), 150); + ASSERT_EQ(saturatedSubtraction(50, -100), 150); + + ASSERT_EQ(saturatedSubtraction(INT_MAX, 0), INT_MAX); + ASSERT_EQ(saturatedSubtraction(INT_MAX, 1), INT_MAX - 1); + ASSERT_EQ(saturatedSubtraction(INT_MAX - 1, 0), INT_MAX - 1); + ASSERT_EQ(saturatedSubtraction(INT_MAX - 1, -1), INT_MAX); + ASSERT_EQ(saturatedSubtraction(INT_MAX - 1, -2), INT_MAX); + ASSERT_EQ(saturatedSubtraction(0, INT_MAX - 1), -INT_MAX + 1); + ASSERT_EQ(saturatedSubtraction(-1, INT_MAX - 1), -INT_MAX); + ASSERT_EQ(saturatedSubtraction(-2, INT_MAX - 1), -INT_MAX - 1); + ASSERT_EQ(saturatedSubtraction(-3, INT_MAX - 1), -INT_MAX - 1); + + ASSERT_EQ(saturatedSubtraction(INT_MIN, 0), INT_MIN); + ASSERT_EQ(saturatedSubtraction(INT_MIN + 1, 0), INT_MIN + 1); + ASSERT_EQ(saturatedSubtraction(INT_MIN + 1, 1), INT_MIN); + ASSERT_EQ(saturatedSubtraction(INT_MIN + 1, 2), INT_MIN); + + ASSERT_EQ(saturatedSubtraction(INT_MIN, INT_MIN), 0); + ASSERT_EQ(saturatedSubtraction(INT_MAX, INT_MAX), 0); + ASSERT_EQ(saturatedSubtraction(INT_MAX, INT_MIN), INT_MAX); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/StringBuilder.cpp b/Tools/TestWebKitAPI/Tests/WTF/StringBuilder.cpp new file mode 100644 index 000000000..11bf3590e --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/StringBuilder.cpp @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2013 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER OR 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 "WTFStringUtilities.h" + +namespace TestWebKitAPI { + +static void expectBuilderContent(const String& expected, const StringBuilder& builder) +{ + // Not using builder.toString() or builder.toStringPreserveCapacity() because they all + // change internal state of builder. + if (builder.is8Bit()) + EXPECT_EQ(expected, String(builder.characters8(), builder.length())); + else + EXPECT_EQ(expected, String(builder.characters16(), builder.length())); +} + +void expectEmpty(const StringBuilder& builder) +{ + EXPECT_EQ(0U, builder.length()); + EXPECT_TRUE(builder.isEmpty()); + EXPECT_EQ(0, builder.characters8()); +} + +TEST(StringBuilderTest, DefaultConstructor) +{ + StringBuilder builder; + expectEmpty(builder); +} + +TEST(StringBuilderTest, Append) +{ + StringBuilder builder; + builder.append(String("0123456789")); + expectBuilderContent("0123456789", builder); + builder.append("abcd"); + expectBuilderContent("0123456789abcd", builder); + builder.append("efgh", 3); + expectBuilderContent("0123456789abcdefg", builder); + builder.append(""); + expectBuilderContent("0123456789abcdefg", builder); + builder.append('#'); + expectBuilderContent("0123456789abcdefg#", builder); + + builder.toString(); // Test after reifyString(). + StringBuilder builder1; + builder.append("", 0); + expectBuilderContent("0123456789abcdefg#", builder); + builder1.append(builder.characters8(), builder.length()); + builder1.append("XYZ"); + builder.append(builder1.characters8(), builder1.length()); + expectBuilderContent("0123456789abcdefg#0123456789abcdefg#XYZ", builder); + + StringBuilder builder2; + builder2.reserveCapacity(100); + builder2.append("xyz"); + const LChar* characters = builder2.characters8(); + builder2.append("0123456789"); + ASSERT_EQ(characters, builder2.characters8()); + builder2.toStringPreserveCapacity(); // Test after reifyString with buffer preserved. + builder2.append("abcd"); + ASSERT_EQ(characters, builder2.characters8()); + + // Test appending UChar32 characters to StringBuilder. + StringBuilder builderForUChar32Append; + UChar32 frakturAChar = 0x1D504; + builderForUChar32Append.append(frakturAChar); // The fraktur A is not in the BMP, so it's two UTF-16 code units long. + ASSERT_EQ(2U, builderForUChar32Append.length()); + builderForUChar32Append.append(static_cast<UChar32>('A')); + ASSERT_EQ(3U, builderForUChar32Append.length()); + const UChar resultArray[] = { U16_LEAD(frakturAChar), U16_TRAIL(frakturAChar), 'A' }; + expectBuilderContent(String(resultArray, WTF_ARRAY_LENGTH(resultArray)), builderForUChar32Append); +} + +TEST(StringBuilderTest, ToString) +{ + StringBuilder builder; + builder.append("0123456789"); + String string = builder.toString(); + ASSERT_EQ(String("0123456789"), string); + ASSERT_EQ(string.impl(), builder.toString().impl()); + + // Changing the StringBuilder should not affect the original result of toString(). + builder.append("abcdefghijklmnopqrstuvwxyz"); + ASSERT_EQ(String("0123456789"), string); + + // Changing the StringBuilder should not affect the original result of toString() in case the capacity is not changed. + builder.reserveCapacity(200); + string = builder.toString(); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string); + builder.append("ABC"); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string); + + // Changing the original result of toString() should not affect the content of the StringBuilder. + String string1 = builder.toString(); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1); + string1.append("DEF"); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), builder.toString()); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABCDEF"), string1); + + // Resizing the StringBuilder should not affect the original result of toString(). + string1 = builder.toString(); + builder.resize(10); + builder.append("###"); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1); +} + +TEST(StringBuilderTest, ToStringPreserveCapacity) +{ + StringBuilder builder; + builder.append("0123456789"); + unsigned capacity = builder.capacity(); + String string = builder.toStringPreserveCapacity(); + ASSERT_EQ(capacity, builder.capacity()); + ASSERT_EQ(String("0123456789"), string); + ASSERT_EQ(string.impl(), builder.toStringPreserveCapacity().impl()); + ASSERT_EQ(string.characters8(), builder.characters8()); + + // Changing the StringBuilder should not affect the original result of toStringPreserveCapacity(). + builder.append("abcdefghijklmnopqrstuvwxyz"); + ASSERT_EQ(String("0123456789"), string); + + // Changing the StringBuilder should not affect the original result of toStringPreserveCapacity() in case the capacity is not changed. + builder.reserveCapacity(200); + capacity = builder.capacity(); + string = builder.toStringPreserveCapacity(); + ASSERT_EQ(capacity, builder.capacity()); + ASSERT_EQ(string.characters8(), builder.characters8()); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string); + builder.append("ABC"); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string); + + // Changing the original result of toStringPreserveCapacity() should not affect the content of the StringBuilder. + capacity = builder.capacity(); + String string1 = builder.toStringPreserveCapacity(); + ASSERT_EQ(capacity, builder.capacity()); + ASSERT_EQ(string1.characters8(), builder.characters8()); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1); + string1.append("DEF"); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), builder.toStringPreserveCapacity()); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABCDEF"), string1); + + // Resizing the StringBuilder should not affect the original result of toStringPreserveCapacity(). + capacity = builder.capacity(); + string1 = builder.toStringPreserveCapacity(); + ASSERT_EQ(capacity, builder.capacity()); + ASSERT_EQ(string.characters8(), builder.characters8()); + builder.resize(10); + builder.append("###"); + ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1); +} + +TEST(StringBuilderTest, Clear) +{ + StringBuilder builder; + builder.append("0123456789"); + builder.clear(); + expectEmpty(builder); +} + +TEST(StringBuilderTest, Array) +{ + StringBuilder builder; + builder.append("0123456789"); + EXPECT_EQ('0', static_cast<char>(builder[0])); + EXPECT_EQ('9', static_cast<char>(builder[9])); + builder.toString(); // Test after reifyString(). + EXPECT_EQ('0', static_cast<char>(builder[0])); + EXPECT_EQ('9', static_cast<char>(builder[9])); +} + +TEST(StringBuilderTest, Resize) +{ + StringBuilder builder; + builder.append("0123456789"); + builder.resize(10); + EXPECT_EQ(10U, builder.length()); + expectBuilderContent("0123456789", builder); + builder.resize(8); + EXPECT_EQ(8U, builder.length()); + expectBuilderContent("01234567", builder); + + builder.toString(); + builder.resize(7); + EXPECT_EQ(7U, builder.length()); + expectBuilderContent("0123456", builder); + builder.resize(0); + expectEmpty(builder); +} + +TEST(StringBuilderTest, Equal) +{ + StringBuilder builder1; + StringBuilder builder2; + ASSERT_TRUE(builder1 == builder2); + ASSERT_TRUE(equal(builder1, static_cast<LChar*>(0), 0)); + ASSERT_TRUE(builder1 == String()); + ASSERT_TRUE(String() == builder1); + ASSERT_TRUE(builder1 != String("abc")); + + builder1.append("123"); + builder1.reserveCapacity(32); + builder2.append("123"); + builder1.reserveCapacity(64); + ASSERT_TRUE(builder1 == builder2); + ASSERT_TRUE(builder1 == String("123")); + ASSERT_TRUE(String("123") == builder1); + + builder2.append("456"); + ASSERT_TRUE(builder1 != builder2); + ASSERT_TRUE(builder2 != builder1); + ASSERT_TRUE(String("123") != builder2); + ASSERT_TRUE(builder2 != String("123")); + builder2.toString(); // Test after reifyString(). + ASSERT_TRUE(builder1 != builder2); + + builder2.resize(3); + ASSERT_TRUE(builder1 == builder2); + + builder1.toString(); // Test after reifyString(). + ASSERT_TRUE(builder1 == builder2); +} + +TEST(StringBuilderTest, CanShrink) +{ + StringBuilder builder; + builder.reserveCapacity(256); + ASSERT_TRUE(builder.canShrink()); + for (int i = 0; i < 256; i++) + builder.append('x'); + ASSERT_EQ(builder.length(), builder.capacity()); + ASSERT_FALSE(builder.canShrink()); +} + +TEST(StringBuilderTest, ToAtomicString) +{ + StringBuilder builder; + builder.append("123"); + AtomicString atomicString = builder.toAtomicString(); + ASSERT_EQ(String("123"), atomicString); + + builder.reserveCapacity(256); + ASSERT_TRUE(builder.canShrink()); + for (int i = builder.length(); i < 128; i++) + builder.append('x'); + AtomicString atomicString1 = builder.toAtomicString(); + ASSERT_EQ(128u, atomicString1.length()); + ASSERT_EQ('x', atomicString1[127]); + + // Later change of builder should not affect the atomic string. + for (int i = builder.length(); i < 256; i++) + builder.append('x'); + ASSERT_EQ(128u, atomicString1.length()); + + ASSERT_FALSE(builder.canShrink()); + String string = builder.toString(); + AtomicString atomicString2 = builder.toAtomicString(); + // They should share the same StringImpl. + ASSERT_EQ(atomicString2.impl(), string.impl()); +} + +TEST(StringBuilderTest, ToAtomicStringOnEmpty) +{ + { // Default constructed. + StringBuilder builder; + AtomicString atomicString = builder.toAtomicString(); + ASSERT_EQ(emptyAtom, atomicString); + } + { // With capacity. + StringBuilder builder; + builder.reserveCapacity(64); + AtomicString atomicString = builder.toAtomicString(); + ASSERT_EQ(emptyAtom, atomicString); + } + { // AtomicString constructed from a null string. + StringBuilder builder; + builder.append(String()); + AtomicString atomicString = builder.toAtomicString(); + ASSERT_EQ(emptyAtom, atomicString); + } + { // AtomicString constructed from an empty string. + StringBuilder builder; + builder.append(emptyString()); + AtomicString atomicString = builder.toAtomicString(); + ASSERT_EQ(emptyAtom, atomicString); + } + { // AtomicString constructed from an empty StringBuilder. + StringBuilder builder; + StringBuilder emptyBuilder; + builder.append(emptyBuilder); + AtomicString atomicString = builder.toAtomicString(); + ASSERT_EQ(emptyAtom, atomicString); + } + { // AtomicString constructed from an empty char* string. + StringBuilder builder; + builder.append("", 0); + AtomicString atomicString = builder.toAtomicString(); + ASSERT_EQ(emptyAtom, atomicString); + } + { // Cleared StringBuilder. + StringBuilder builder; + builder.appendLiteral("WebKit"); + builder.clear(); + AtomicString atomicString = builder.toAtomicString(); + ASSERT_EQ(emptyAtom, atomicString); + } +} + +} // namespace diff --git a/Tools/TestWebKitAPI/Tests/WTF/StringHasher.cpp b/Tools/TestWebKitAPI/Tests/WTF/StringHasher.cpp new file mode 100644 index 000000000..a4d223c99 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/StringHasher.cpp @@ -0,0 +1,444 @@ +/* + * Copyright (C) 2013 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 <wtf/StringHasher.h> + +namespace TestWebKitAPI { + +static const LChar nullLChars[2] = { 0, 0 }; +static const UChar nullUChars[2] = { 0, 0 }; + +static const unsigned emptyStringHash = 0x4EC889EU; +static const unsigned singleNullCharacterHash = 0x3D3ABF44U; + +static const LChar testALChars[6] = { 0x41, 0x95, 0xFF, 0x50, 0x01, 0 }; +static const UChar testAUChars[6] = { 0x41, 0x95, 0xFF, 0x50, 0x01, 0 }; +static const UChar testBUChars[6] = { 0x41, 0x95, 0xFFFF, 0x1080, 0x01, 0 }; + +static const unsigned testAHash1 = 0xEA32B004; +static const unsigned testAHash2 = 0x93F0F71E; +static const unsigned testAHash3 = 0xCB609EB1; +static const unsigned testAHash4 = 0x7984A706; +static const unsigned testAHash5 = 0x0427561F; + +static const unsigned testBHash1 = 0xEA32B004; +static const unsigned testBHash2 = 0x93F0F71E; +static const unsigned testBHash3 = 0x59EB1B2C; +static const unsigned testBHash4 = 0xA7BCCC0A; +static const unsigned testBHash5 = 0x79201649; + +TEST(WTF, StringHasher) +{ + StringHasher hasher; + + // The initial state of the hasher. + ASSERT_EQ(emptyStringHash, hasher.hash()); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); +} + +TEST(WTF, StringHasher_addCharacter) +{ + StringHasher hasher; + + // Hashing a single character. + hasher = StringHasher(); + hasher.addCharacter(0); + ASSERT_EQ(singleNullCharacterHash, hasher.hash()); + ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing five characters, checking the intermediate state after each is added. + hasher = StringHasher(); + hasher.addCharacter(testAUChars[0]); + ASSERT_EQ(testAHash1, hasher.hash()); + ASSERT_EQ(testAHash1 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testAUChars[1]); + ASSERT_EQ(testAHash2, hasher.hash()); + ASSERT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testAUChars[2]); + ASSERT_EQ(testAHash3, hasher.hash()); + ASSERT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testAUChars[3]); + ASSERT_EQ(testAHash4, hasher.hash()); + ASSERT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testAUChars[4]); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing a second set of five characters, including non-Latin-1 characters. + hasher = StringHasher(); + hasher.addCharacter(testBUChars[0]); + ASSERT_EQ(testBHash1, hasher.hash()); + ASSERT_EQ(testBHash1 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testBUChars[1]); + ASSERT_EQ(testBHash2, hasher.hash()); + ASSERT_EQ(testBHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testBUChars[2]); + ASSERT_EQ(testBHash3, hasher.hash()); + ASSERT_EQ(testBHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testBUChars[3]); + ASSERT_EQ(testBHash4, hasher.hash()); + ASSERT_EQ(testBHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testBUChars[4]); + ASSERT_EQ(testBHash5, hasher.hash()); + ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); +} + +TEST(WTF, StringHasher_addCharacters) +{ + StringHasher hasher; + + // Hashing zero characters. + hasher = StringHasher(); + hasher.addCharacters(static_cast<LChar*>(0), 0); + ASSERT_EQ(emptyStringHash, hasher.hash()); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(nullLChars, 0); + ASSERT_EQ(emptyStringHash, hasher.hash()); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(nullLChars); + ASSERT_EQ(emptyStringHash, hasher.hash()); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(static_cast<UChar*>(0), 0); + ASSERT_EQ(emptyStringHash, hasher.hash()); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(nullUChars, 0); + ASSERT_EQ(emptyStringHash, hasher.hash()); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(nullUChars); + ASSERT_EQ(emptyStringHash, hasher.hash()); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing one character. + hasher = StringHasher(); + hasher.addCharacters(nullLChars, 1); + ASSERT_EQ(singleNullCharacterHash, hasher.hash()); + ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(nullUChars, 1); + ASSERT_EQ(singleNullCharacterHash, hasher.hash()); + ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing five characters, all at once. + hasher = StringHasher(); + hasher.addCharacters(testALChars, 5); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testALChars); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testAUChars, 5); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testAUChars); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testBUChars, 5); + ASSERT_EQ(testBHash5, hasher.hash()); + ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testBUChars); + ASSERT_EQ(testBHash5, hasher.hash()); + ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing five characters, in groups of two, then the last one. + hasher = StringHasher(); + hasher.addCharacters(testALChars, 2); + ASSERT_EQ(testAHash2, hasher.hash()); + ASSERT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testALChars + 2, 2); + ASSERT_EQ(testAHash4, hasher.hash()); + ASSERT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testALChars + 4, 1); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testALChars, 2); + hasher.addCharacters(testALChars + 2, 2); + hasher.addCharacters(testALChars + 4); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testAUChars, 2); + ASSERT_EQ(testAHash2, hasher.hash()); + ASSERT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testAUChars + 2, 2); + ASSERT_EQ(testAHash4, hasher.hash()); + ASSERT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testAUChars + 4, 1); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testAUChars, 2); + hasher.addCharacters(testAUChars + 2, 2); + hasher.addCharacters(testAUChars + 4); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testBUChars, 2); + ASSERT_EQ(testBHash2, hasher.hash()); + ASSERT_EQ(testBHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testBUChars + 2, 2); + ASSERT_EQ(testBHash4, hasher.hash()); + ASSERT_EQ(testBHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testBUChars + 4, 1); + ASSERT_EQ(testBHash5, hasher.hash()); + ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testBUChars, 2); + hasher.addCharacters(testBUChars + 2, 2); + hasher.addCharacters(testBUChars + 4); + ASSERT_EQ(testBHash5, hasher.hash()); + ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing five characters, the first three, then the last two. + hasher = StringHasher(); + hasher.addCharacters(testALChars, 3); + ASSERT_EQ(testAHash3, hasher.hash()); + ASSERT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testALChars + 3, 2); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testALChars, 3); + ASSERT_EQ(testAHash3, hasher.hash()); + ASSERT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testALChars + 3); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testAUChars, 3); + ASSERT_EQ(testAHash3, hasher.hash()); + ASSERT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testAUChars + 3, 2); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testAUChars, 3); + ASSERT_EQ(testAHash3, hasher.hash()); + ASSERT_EQ(testAHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testAUChars + 3, 2); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testBUChars, 3); + ASSERT_EQ(testBHash3, hasher.hash()); + ASSERT_EQ(testBHash3 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacters(testBUChars + 3, 2); + ASSERT_EQ(testBHash5, hasher.hash()); + ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharacters(testBUChars, 3); + hasher.addCharacters(testBUChars + 3); + ASSERT_EQ(testBHash5, hasher.hash()); + ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); +} + +TEST(WTF, StringHasher_addCharactersAssumingAligned) +{ + StringHasher hasher; + + // Hashing zero characters. + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(static_cast<LChar*>(0), 0); + ASSERT_EQ(emptyStringHash, hasher.hash()); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(nullLChars, 0); + ASSERT_EQ(emptyStringHash, hasher.hash()); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(static_cast<UChar*>(0), 0); + ASSERT_EQ(emptyStringHash, hasher.hash()); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(nullUChars, 0); + ASSERT_EQ(emptyStringHash, hasher.hash()); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(nullUChars); + ASSERT_EQ(emptyStringHash, hasher.hash()); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing one character. + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(nullLChars, 1); + ASSERT_EQ(singleNullCharacterHash, hasher.hash()); + ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(nullUChars, 1); + ASSERT_EQ(singleNullCharacterHash, hasher.hash()); + ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing five characters, all at once. + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testALChars, 5); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testALChars); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testAUChars, 5); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testAUChars); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testBUChars, 5); + ASSERT_EQ(testBHash5, hasher.hash()); + ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testBUChars); + ASSERT_EQ(testBHash5, hasher.hash()); + ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing five characters, in groups of two, then the last one. + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testALChars, 2); + ASSERT_EQ(testAHash2, hasher.hash()); + ASSERT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharactersAssumingAligned(testALChars + 2, 2); + ASSERT_EQ(testAHash4, hasher.hash()); + ASSERT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharactersAssumingAligned(testALChars + 4, 1); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testALChars, 2); + hasher.addCharactersAssumingAligned(testALChars + 2, 2); + hasher.addCharactersAssumingAligned(testALChars + 4); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testAUChars, 2); + ASSERT_EQ(testAHash2, hasher.hash()); + ASSERT_EQ(testAHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharactersAssumingAligned(testAUChars + 2, 2); + ASSERT_EQ(testAHash4, hasher.hash()); + ASSERT_EQ(testAHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharactersAssumingAligned(testAUChars + 4, 1); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testAUChars, 2); + hasher.addCharactersAssumingAligned(testAUChars + 2, 2); + hasher.addCharactersAssumingAligned(testAUChars + 4); + ASSERT_EQ(testAHash5, hasher.hash()); + ASSERT_EQ(testAHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testBUChars, 2); + ASSERT_EQ(testBHash2, hasher.hash()); + ASSERT_EQ(testBHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharactersAssumingAligned(testBUChars + 2, 2); + ASSERT_EQ(testBHash4, hasher.hash()); + ASSERT_EQ(testBHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharactersAssumingAligned(testBUChars + 4, 1); + ASSERT_EQ(testBHash5, hasher.hash()); + ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher = StringHasher(); + hasher.addCharactersAssumingAligned(testBUChars, 2); + hasher.addCharactersAssumingAligned(testBUChars + 2, 2); + hasher.addCharactersAssumingAligned(testBUChars + 4); + ASSERT_EQ(testBHash5, hasher.hash()); + ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + + // Hashing five characters, first two characters one at a time, + // then two more, then the last one. + hasher = StringHasher(); + hasher.addCharacter(testBUChars[0]); + ASSERT_EQ(testBHash1, hasher.hash()); + ASSERT_EQ(testBHash1 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharacter(testBUChars[1]); + ASSERT_EQ(testBHash2, hasher.hash()); + ASSERT_EQ(testBHash2 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharactersAssumingAligned(testBUChars[2], testBUChars[3]); + ASSERT_EQ(testBHash4, hasher.hash()); + ASSERT_EQ(testBHash4 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); + hasher.addCharactersAssumingAligned(testBUChars + 4); + ASSERT_EQ(testBHash5, hasher.hash()); + ASSERT_EQ(testBHash5 & 0xFFFFFF, hasher.hashWithTop8BitsMasked()); +} + +TEST(WTF, StringHasher_computeHash) +{ + ASSERT_EQ(emptyStringHash, StringHasher::computeHash(static_cast<LChar*>(0), 0)); + ASSERT_EQ(emptyStringHash, StringHasher::computeHash(nullLChars, 0)); + ASSERT_EQ(emptyStringHash, StringHasher::computeHash(static_cast<UChar*>(0), 0)); + ASSERT_EQ(emptyStringHash, StringHasher::computeHash(nullUChars, 0)); + + ASSERT_EQ(singleNullCharacterHash, StringHasher::computeHash(nullLChars, 1)); + ASSERT_EQ(singleNullCharacterHash, StringHasher::computeHash(nullUChars, 1)); + + ASSERT_EQ(testAHash5, StringHasher::computeHash(testALChars, 5)); + ASSERT_EQ(testAHash5, StringHasher::computeHash(testAUChars, 5)); + ASSERT_EQ(testBHash5, StringHasher::computeHash(testBUChars, 5)); +} + +TEST(WTF, StringHasher_computeHashAndMaskTop8Bits) +{ + ASSERT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(static_cast<LChar*>(0), 0)); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(nullLChars, 0)); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(static_cast<UChar*>(0), 0)); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(nullUChars, 0)); + + ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(nullLChars, 1)); + ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(nullUChars, 1)); + + ASSERT_EQ(testAHash5 & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(testALChars, 5)); + ASSERT_EQ(testAHash5 & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(testAUChars, 5)); + ASSERT_EQ(testBHash5 & 0xFFFFFF, StringHasher::computeHashAndMaskTop8Bits(testBUChars, 5)); +} + +TEST(WTF, StringHasher_hashMemory) +{ + ASSERT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::hashMemory(0, 0)); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::hashMemory(nullUChars, 0)); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::hashMemory<0>(0)); + ASSERT_EQ(emptyStringHash & 0xFFFFFF, StringHasher::hashMemory<0>(nullUChars)); + + ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, StringHasher::hashMemory(nullUChars, 2)); + ASSERT_EQ(singleNullCharacterHash & 0xFFFFFF, StringHasher::hashMemory<2>(nullUChars)); + + ASSERT_EQ(testAHash5 & 0xFFFFFF, StringHasher::hashMemory(testAUChars, 10)); + ASSERT_EQ(testAHash5 & 0xFFFFFF, StringHasher::hashMemory<10>(testAUChars)); + ASSERT_EQ(testBHash5 & 0xFFFFFF, StringHasher::hashMemory(testBUChars, 10)); + ASSERT_EQ(testBHash5 & 0xFFFFFF, StringHasher::hashMemory<10>(testBUChars)); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp b/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp new file mode 100644 index 000000000..75fadf8c4 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp @@ -0,0 +1,548 @@ +/* + * Copyright (C) 2012 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 <wtf/text/SymbolImpl.h> +#include <wtf/text/WTFString.h> + +namespace TestWebKitAPI { + +TEST(WTF, StringImplCreationFromLiteral) +{ + // Constructor using the template to determine the size. + RefPtr<StringImpl> stringWithTemplate = StringImpl::createFromLiteral("Template Literal"); + ASSERT_EQ(strlen("Template Literal"), stringWithTemplate->length()); + ASSERT_TRUE(equal(stringWithTemplate.get(), "Template Literal")); + ASSERT_TRUE(stringWithTemplate->is8Bit()); + + // Constructor taking the size explicitely. + const char* programmaticStringData = "Explicit Size Literal"; + RefPtr<StringImpl> programmaticString = StringImpl::createFromLiteral(programmaticStringData, strlen(programmaticStringData)); + ASSERT_EQ(strlen(programmaticStringData), programmaticString->length()); + ASSERT_TRUE(equal(programmaticString.get(), programmaticStringData)); + ASSERT_EQ(programmaticStringData, reinterpret_cast<const char*>(programmaticString->characters8())); + ASSERT_TRUE(programmaticString->is8Bit()); + + // Constructor without explicit size. + const char* stringWithoutLengthLiteral = "No Size Literal"; + RefPtr<StringImpl> programmaticStringNoLength = StringImpl::createFromLiteral(stringWithoutLengthLiteral); + ASSERT_EQ(strlen(stringWithoutLengthLiteral), programmaticStringNoLength->length()); + ASSERT_TRUE(equal(programmaticStringNoLength.get(), stringWithoutLengthLiteral)); + ASSERT_EQ(stringWithoutLengthLiteral, reinterpret_cast<const char*>(programmaticStringNoLength->characters8())); + ASSERT_TRUE(programmaticStringNoLength->is8Bit()); +} + +TEST(WTF, StringImplReplaceWithLiteral) +{ + RefPtr<StringImpl> testStringImpl = StringImpl::createFromLiteral("1224"); + ASSERT_TRUE(testStringImpl->is8Bit()); + + // Cases for 8Bit source. + testStringImpl = testStringImpl->replace('2', "", 0); + ASSERT_TRUE(equal(testStringImpl.get(), "14")); + + testStringImpl = StringImpl::createFromLiteral("1224"); + ASSERT_TRUE(testStringImpl->is8Bit()); + + testStringImpl = testStringImpl->replace('3', "NotFound", 8); + ASSERT_TRUE(equal(testStringImpl.get(), "1224")); + + testStringImpl = testStringImpl->replace('2', "3", 1); + ASSERT_TRUE(equal(testStringImpl.get(), "1334")); + + testStringImpl = StringImpl::createFromLiteral("1224"); + ASSERT_TRUE(testStringImpl->is8Bit()); + testStringImpl = testStringImpl->replace('2', "555", 3); + ASSERT_TRUE(equal(testStringImpl.get(), "15555554")); + + // Cases for 16Bit source. + String testString = String::fromUTF8("résumé"); + ASSERT_FALSE(testString.impl()->is8Bit()); + + testStringImpl = testString.impl()->replace('2', "NotFound", 8); + ASSERT_TRUE(equal(testStringImpl.get(), String::fromUTF8("résumé").impl())); + + testStringImpl = testString.impl()->replace(UChar(0x00E9 /*U+00E9 is 'é'*/), "e", 1); + ASSERT_TRUE(equal(testStringImpl.get(), "resume")); + + testString = String::fromUTF8("résumé"); + ASSERT_FALSE(testString.impl()->is8Bit()); + testStringImpl = testString.impl()->replace(UChar(0x00E9 /*U+00E9 is 'é'*/), "", 0); + ASSERT_TRUE(equal(testStringImpl.get(), "rsum")); + + testString = String::fromUTF8("résumé"); + ASSERT_FALSE(testString.impl()->is8Bit()); + testStringImpl = testString.impl()->replace(UChar(0x00E9 /*U+00E9 is 'é'*/), "555", 3); + ASSERT_TRUE(equal(testStringImpl.get(), "r555sum555")); +} + +TEST(WTF, StringImplEqualIgnoringASCIICaseBasic) +{ + RefPtr<StringImpl> a = StringImpl::createFromLiteral("aBcDeFG"); + RefPtr<StringImpl> b = StringImpl::createFromLiteral("ABCDEFG"); + RefPtr<StringImpl> c = StringImpl::createFromLiteral("abcdefg"); + const char d[] = "aBcDeFG"; + RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>("")); + RefPtr<StringImpl> shorter = StringImpl::createFromLiteral("abcdef"); + RefPtr<StringImpl> different = StringImpl::createFromLiteral("abcrefg"); + + // Identity. + ASSERT_TRUE(equalIgnoringASCIICase(a.get(), a.get())); + ASSERT_TRUE(equalIgnoringASCIICase(b.get(), b.get())); + ASSERT_TRUE(equalIgnoringASCIICase(c.get(), c.get())); + ASSERT_TRUE(equalIgnoringASCIICase(a.get(), d)); + ASSERT_TRUE(equalIgnoringASCIICase(b.get(), d)); + ASSERT_TRUE(equalIgnoringASCIICase(c.get(), d)); + + // Transitivity. + ASSERT_TRUE(equalIgnoringASCIICase(a.get(), b.get())); + ASSERT_TRUE(equalIgnoringASCIICase(b.get(), c.get())); + ASSERT_TRUE(equalIgnoringASCIICase(a.get(), c.get())); + + // Negative cases. + ASSERT_FALSE(equalIgnoringASCIICase(a.get(), empty.get())); + ASSERT_FALSE(equalIgnoringASCIICase(b.get(), empty.get())); + ASSERT_FALSE(equalIgnoringASCIICase(c.get(), empty.get())); + ASSERT_FALSE(equalIgnoringASCIICase(a.get(), shorter.get())); + ASSERT_FALSE(equalIgnoringASCIICase(b.get(), shorter.get())); + ASSERT_FALSE(equalIgnoringASCIICase(c.get(), shorter.get())); + ASSERT_FALSE(equalIgnoringASCIICase(a.get(), different.get())); + ASSERT_FALSE(equalIgnoringASCIICase(b.get(), different.get())); + ASSERT_FALSE(equalIgnoringASCIICase(c.get(), different.get())); + ASSERT_FALSE(equalIgnoringASCIICase(empty.get(), d)); + ASSERT_FALSE(equalIgnoringASCIICase(shorter.get(), d)); + ASSERT_FALSE(equalIgnoringASCIICase(different.get(), d)); +} + +TEST(WTF, StringImplEqualIgnoringASCIICaseWithNull) +{ + RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG"); + ASSERT_FALSE(equalIgnoringASCIICase(nullptr, reference.get())); + ASSERT_FALSE(equalIgnoringASCIICase(reference.get(), nullptr)); + ASSERT_TRUE(equalIgnoringASCIICase(nullptr, nullptr)); +} + +TEST(WTF, StringImplEqualIgnoringASCIICaseWithEmpty) +{ + RefPtr<StringImpl> a = StringImpl::create(reinterpret_cast<const LChar*>("")); + RefPtr<StringImpl> b = StringImpl::create(reinterpret_cast<const LChar*>("")); + ASSERT_TRUE(equalIgnoringASCIICase(a.get(), b.get())); + ASSERT_TRUE(equalIgnoringASCIICase(b.get(), a.get())); +} + +static RefPtr<StringImpl> stringFromUTF8(const char* characters) +{ + return String::fromUTF8(characters).impl(); +} + +TEST(WTF, StringImplEqualIgnoringASCIICaseWithLatin1Characters) +{ + RefPtr<StringImpl> a = stringFromUTF8("aBcéeFG"); + RefPtr<StringImpl> b = stringFromUTF8("ABCÉEFG"); + RefPtr<StringImpl> c = stringFromUTF8("ABCéEFG"); + RefPtr<StringImpl> d = stringFromUTF8("abcéefg"); + const char e[] = "aBcéeFG"; + + // Identity. + ASSERT_TRUE(equalIgnoringASCIICase(a.get(), a.get())); + ASSERT_TRUE(equalIgnoringASCIICase(b.get(), b.get())); + ASSERT_TRUE(equalIgnoringASCIICase(c.get(), c.get())); + ASSERT_TRUE(equalIgnoringASCIICase(d.get(), d.get())); + + // All combination. + ASSERT_FALSE(equalIgnoringASCIICase(a.get(), b.get())); + ASSERT_TRUE(equalIgnoringASCIICase(a.get(), c.get())); + ASSERT_TRUE(equalIgnoringASCIICase(a.get(), d.get())); + ASSERT_FALSE(equalIgnoringASCIICase(b.get(), c.get())); + ASSERT_FALSE(equalIgnoringASCIICase(b.get(), d.get())); + ASSERT_TRUE(equalIgnoringASCIICase(c.get(), d.get())); + ASSERT_FALSE(equalIgnoringASCIICase(a.get(), e)); + ASSERT_FALSE(equalIgnoringASCIICase(b.get(), e)); + ASSERT_FALSE(equalIgnoringASCIICase(c.get(), e)); + ASSERT_FALSE(equalIgnoringASCIICase(d.get(), e)); +} + +TEST(WTF, StringImplFindIgnoringASCIICaseBasic) +{ + RefPtr<StringImpl> referenceA = stringFromUTF8("aBcéeFG"); + RefPtr<StringImpl> referenceB = stringFromUTF8("ABCÉEFG"); + + // Search the exact string. + EXPECT_EQ(static_cast<size_t>(0), referenceA->findIgnoringASCIICase(referenceA.get())); + EXPECT_EQ(static_cast<size_t>(0), referenceB->findIgnoringASCIICase(referenceB.get())); + + // A and B are distinct by the non-ascii character é/É. + EXPECT_EQ(static_cast<size_t>(notFound), referenceA->findIgnoringASCIICase(referenceB.get())); + EXPECT_EQ(static_cast<size_t>(notFound), referenceB->findIgnoringASCIICase(referenceA.get())); + + // Find the prefix. + EXPECT_EQ(static_cast<size_t>(0), referenceA->findIgnoringASCIICase(StringImpl::createFromLiteral("a").get())); + EXPECT_EQ(static_cast<size_t>(0), referenceA->findIgnoringASCIICase(stringFromUTF8("abcé").get())); + EXPECT_EQ(static_cast<size_t>(0), referenceA->findIgnoringASCIICase(StringImpl::createFromLiteral("A").get())); + EXPECT_EQ(static_cast<size_t>(0), referenceA->findIgnoringASCIICase(stringFromUTF8("ABCé").get())); + EXPECT_EQ(static_cast<size_t>(0), referenceB->findIgnoringASCIICase(StringImpl::createFromLiteral("a").get())); + EXPECT_EQ(static_cast<size_t>(0), referenceB->findIgnoringASCIICase(stringFromUTF8("abcÉ").get())); + EXPECT_EQ(static_cast<size_t>(0), referenceB->findIgnoringASCIICase(StringImpl::createFromLiteral("A").get())); + EXPECT_EQ(static_cast<size_t>(0), referenceB->findIgnoringASCIICase(stringFromUTF8("ABCÉ").get())); + + // Not a prefix. + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(StringImpl::createFromLiteral("x").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("accé").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("abcÉ").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(StringImpl::createFromLiteral("X").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("ABDé").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("ABCÉ").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(StringImpl::createFromLiteral("y").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("accÉ").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("abcé").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(StringImpl::createFromLiteral("Y").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("ABdÉ").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("ABCé").get())); + + // Find the infix. + EXPECT_EQ(static_cast<size_t>(2), referenceA->findIgnoringASCIICase(stringFromUTF8("cée").get())); + EXPECT_EQ(static_cast<size_t>(3), referenceA->findIgnoringASCIICase(stringFromUTF8("ée").get())); + EXPECT_EQ(static_cast<size_t>(2), referenceA->findIgnoringASCIICase(stringFromUTF8("cé").get())); + EXPECT_EQ(static_cast<size_t>(2), referenceA->findIgnoringASCIICase(stringFromUTF8("c").get())); + EXPECT_EQ(static_cast<size_t>(3), referenceA->findIgnoringASCIICase(stringFromUTF8("é").get())); + EXPECT_EQ(static_cast<size_t>(2), referenceA->findIgnoringASCIICase(stringFromUTF8("Cée").get())); + EXPECT_EQ(static_cast<size_t>(3), referenceA->findIgnoringASCIICase(stringFromUTF8("éE").get())); + EXPECT_EQ(static_cast<size_t>(2), referenceA->findIgnoringASCIICase(stringFromUTF8("Cé").get())); + EXPECT_EQ(static_cast<size_t>(2), referenceA->findIgnoringASCIICase(stringFromUTF8("C").get())); + + EXPECT_EQ(static_cast<size_t>(2), referenceB->findIgnoringASCIICase(stringFromUTF8("cÉe").get())); + EXPECT_EQ(static_cast<size_t>(3), referenceB->findIgnoringASCIICase(stringFromUTF8("Ée").get())); + EXPECT_EQ(static_cast<size_t>(2), referenceB->findIgnoringASCIICase(stringFromUTF8("cÉ").get())); + EXPECT_EQ(static_cast<size_t>(2), referenceB->findIgnoringASCIICase(stringFromUTF8("c").get())); + EXPECT_EQ(static_cast<size_t>(3), referenceB->findIgnoringASCIICase(stringFromUTF8("É").get())); + EXPECT_EQ(static_cast<size_t>(2), referenceB->findIgnoringASCIICase(stringFromUTF8("CÉe").get())); + EXPECT_EQ(static_cast<size_t>(3), referenceB->findIgnoringASCIICase(stringFromUTF8("ÉE").get())); + EXPECT_EQ(static_cast<size_t>(2), referenceB->findIgnoringASCIICase(stringFromUTF8("CÉ").get())); + EXPECT_EQ(static_cast<size_t>(2), referenceB->findIgnoringASCIICase(stringFromUTF8("C").get())); + + // Not an infix. + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("céd").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("Ée").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("bé").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("x").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("É").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("CÉe").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("éd").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("CÉ").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("Y").get())); + + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("cée").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("Éc").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("cé").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("W").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("é").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("bÉe").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("éE").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("BÉ").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("z").get())); + + // Find the suffix. + EXPECT_EQ(static_cast<size_t>(6), referenceA->findIgnoringASCIICase(StringImpl::createFromLiteral("g").get())); + EXPECT_EQ(static_cast<size_t>(4), referenceA->findIgnoringASCIICase(stringFromUTF8("efg").get())); + EXPECT_EQ(static_cast<size_t>(3), referenceA->findIgnoringASCIICase(stringFromUTF8("éefg").get())); + EXPECT_EQ(static_cast<size_t>(6), referenceA->findIgnoringASCIICase(StringImpl::createFromLiteral("G").get())); + EXPECT_EQ(static_cast<size_t>(4), referenceA->findIgnoringASCIICase(stringFromUTF8("EFG").get())); + EXPECT_EQ(static_cast<size_t>(3), referenceA->findIgnoringASCIICase(stringFromUTF8("éEFG").get())); + + EXPECT_EQ(static_cast<size_t>(6), referenceB->findIgnoringASCIICase(StringImpl::createFromLiteral("g").get())); + EXPECT_EQ(static_cast<size_t>(4), referenceB->findIgnoringASCIICase(stringFromUTF8("efg").get())); + EXPECT_EQ(static_cast<size_t>(3), referenceB->findIgnoringASCIICase(stringFromUTF8("Éefg").get())); + EXPECT_EQ(static_cast<size_t>(6), referenceB->findIgnoringASCIICase(StringImpl::createFromLiteral("G").get())); + EXPECT_EQ(static_cast<size_t>(4), referenceB->findIgnoringASCIICase(stringFromUTF8("EFG").get())); + EXPECT_EQ(static_cast<size_t>(3), referenceB->findIgnoringASCIICase(stringFromUTF8("ÉEFG").get())); + + // Not a suffix. + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(StringImpl::createFromLiteral("X").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("edg").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("Éefg").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(StringImpl::createFromLiteral("w").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("dFG").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA->findIgnoringASCIICase(stringFromUTF8("ÉEFG").get())); + + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(StringImpl::createFromLiteral("Z").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("ffg").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("éefg").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(StringImpl::createFromLiteral("r").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("EgG").get())); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB->findIgnoringASCIICase(stringFromUTF8("éEFG").get())); +} + +TEST(WTF, StringImplFindIgnoringASCIICaseWithValidOffset) +{ + RefPtr<StringImpl> reference = stringFromUTF8("ABCÉEFGaBcéeFG"); + EXPECT_EQ(static_cast<size_t>(0), reference->findIgnoringASCIICase(stringFromUTF8("ABC").get(), 0)); + EXPECT_EQ(static_cast<size_t>(7), reference->findIgnoringASCIICase(stringFromUTF8("ABC").get(), 1)); + EXPECT_EQ(static_cast<size_t>(0), reference->findIgnoringASCIICase(stringFromUTF8("ABCÉ").get(), 0)); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(stringFromUTF8("ABCÉ").get(), 1)); + EXPECT_EQ(static_cast<size_t>(7), reference->findIgnoringASCIICase(stringFromUTF8("ABCé").get(), 0)); + EXPECT_EQ(static_cast<size_t>(7), reference->findIgnoringASCIICase(stringFromUTF8("ABCé").get(), 1)); +} + +TEST(WTF, StringImplFindIgnoringASCIICaseWithInvalidOffset) +{ + RefPtr<StringImpl> reference = stringFromUTF8("ABCÉEFGaBcéeFG"); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(stringFromUTF8("ABC").get(), 15)); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(stringFromUTF8("ABC").get(), 16)); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(stringFromUTF8("ABCÉ").get(), 17)); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(stringFromUTF8("ABCÉ").get(), 42)); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(stringFromUTF8("ABCÉ").get(), std::numeric_limits<unsigned>::max())); +} + +TEST(WTF, StringImplFindIgnoringASCIICaseOnNull) +{ + RefPtr<StringImpl> reference = stringFromUTF8("ABCÉEFG"); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(nullptr)); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(nullptr, 0)); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(nullptr, 3)); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(nullptr, 7)); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(nullptr, 8)); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(nullptr, 42)); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(nullptr, std::numeric_limits<unsigned>::max())); +} + +TEST(WTF, StringImplFindIgnoringASCIICaseOnEmpty) +{ + RefPtr<StringImpl> reference = stringFromUTF8("ABCÉEFG"); + RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>("")); + EXPECT_EQ(static_cast<size_t>(0), reference->findIgnoringASCIICase(empty.get())); + EXPECT_EQ(static_cast<size_t>(0), reference->findIgnoringASCIICase(empty.get(), 0)); + EXPECT_EQ(static_cast<size_t>(3), reference->findIgnoringASCIICase(empty.get(), 3)); + EXPECT_EQ(static_cast<size_t>(7), reference->findIgnoringASCIICase(empty.get(), 7)); + EXPECT_EQ(static_cast<size_t>(7), reference->findIgnoringASCIICase(empty.get(), 8)); + EXPECT_EQ(static_cast<size_t>(7), reference->findIgnoringASCIICase(empty.get(), 42)); + EXPECT_EQ(static_cast<size_t>(7), reference->findIgnoringASCIICase(empty.get(), std::numeric_limits<unsigned>::max())); +} + +TEST(WTF, StringImplFindIgnoringASCIICaseWithPatternLongerThanReference) +{ + RefPtr<StringImpl> reference = stringFromUTF8("ABCÉEFG"); + RefPtr<StringImpl> pattern = stringFromUTF8("XABCÉEFG"); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference->findIgnoringASCIICase(pattern.get())); + EXPECT_EQ(static_cast<size_t>(1), pattern->findIgnoringASCIICase(reference.get())); +} + +TEST(WTF, StringImplStartsWithIgnoringASCIICaseBasic) +{ + RefPtr<StringImpl> reference = stringFromUTF8("aBcéX"); + RefPtr<StringImpl> referenceEquivalent = stringFromUTF8("AbCéx"); + + // Identity. + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(reference.get())); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*reference.get())); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(referenceEquivalent.get())); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*referenceEquivalent.get())); + ASSERT_TRUE(referenceEquivalent->startsWithIgnoringASCIICase(reference.get())); + ASSERT_TRUE(referenceEquivalent->startsWithIgnoringASCIICase(*reference.get())); + ASSERT_TRUE(referenceEquivalent->startsWithIgnoringASCIICase(referenceEquivalent.get())); + ASSERT_TRUE(referenceEquivalent->startsWithIgnoringASCIICase(*referenceEquivalent.get())); + + // Proper prefixes. + RefPtr<StringImpl> aLower = StringImpl::createFromLiteral("a"); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(aLower.get())); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*aLower.get())); + RefPtr<StringImpl> aUpper = StringImpl::createFromLiteral("A"); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(aUpper.get())); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*aUpper.get())); + + RefPtr<StringImpl> abcLower = StringImpl::createFromLiteral("abc"); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(abcLower.get())); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*abcLower.get())); + RefPtr<StringImpl> abcUpper = StringImpl::createFromLiteral("ABC"); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(abcUpper.get())); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*abcUpper.get())); + + RefPtr<StringImpl> abcAccentLower = stringFromUTF8("abcé"); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(abcAccentLower.get())); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*abcAccentLower.get())); + RefPtr<StringImpl> abcAccentUpper = stringFromUTF8("ABCé"); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(abcAccentUpper.get())); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*abcAccentUpper.get())); + + // Negative cases. + RefPtr<StringImpl> differentFirstChar = stringFromUTF8("bBcéX"); + RefPtr<StringImpl> differentFirstCharProperPrefix = stringFromUTF8("CBcé"); + ASSERT_FALSE(reference->startsWithIgnoringASCIICase(differentFirstChar.get())); + ASSERT_FALSE(reference->startsWithIgnoringASCIICase(*differentFirstChar.get())); + ASSERT_FALSE(reference->startsWithIgnoringASCIICase(differentFirstCharProperPrefix.get())); + ASSERT_FALSE(reference->startsWithIgnoringASCIICase(*differentFirstCharProperPrefix.get())); + + RefPtr<StringImpl> uppercaseAccent = stringFromUTF8("aBcÉX"); + RefPtr<StringImpl> uppercaseAccentProperPrefix = stringFromUTF8("aBcÉX"); + ASSERT_FALSE(reference->startsWithIgnoringASCIICase(uppercaseAccent.get())); + ASSERT_FALSE(reference->startsWithIgnoringASCIICase(*uppercaseAccent.get())); + ASSERT_FALSE(reference->startsWithIgnoringASCIICase(uppercaseAccentProperPrefix.get())); + ASSERT_FALSE(reference->startsWithIgnoringASCIICase(*uppercaseAccentProperPrefix.get())); +} + +TEST(WTF, StringImplStartsWithIgnoringASCIICaseWithNull) +{ + RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG"); + ASSERT_FALSE(reference->startsWithIgnoringASCIICase(nullptr)); + + RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>("")); + ASSERT_FALSE(empty->startsWithIgnoringASCIICase(nullptr)); +} + +TEST(WTF, StringImplStartsWithIgnoringASCIICaseWithEmpty) +{ + RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG"); + RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>("")); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(empty.get())); + ASSERT_TRUE(reference->startsWithIgnoringASCIICase(*empty.get())); + ASSERT_TRUE(empty->startsWithIgnoringASCIICase(empty.get())); + ASSERT_TRUE(empty->startsWithIgnoringASCIICase(*empty.get())); + ASSERT_FALSE(empty->startsWithIgnoringASCIICase(reference.get())); + ASSERT_FALSE(empty->startsWithIgnoringASCIICase(*reference.get())); +} + +TEST(WTF, StringImplEndsWithIgnoringASCIICaseBasic) +{ + RefPtr<StringImpl> reference = stringFromUTF8("XÉCbA"); + RefPtr<StringImpl> referenceEquivalent = stringFromUTF8("xÉcBa"); + + // Identity. + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(reference.get())); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*reference.get())); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(referenceEquivalent.get())); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*referenceEquivalent.get())); + ASSERT_TRUE(referenceEquivalent->endsWithIgnoringASCIICase(reference.get())); + ASSERT_TRUE(referenceEquivalent->endsWithIgnoringASCIICase(*reference.get())); + ASSERT_TRUE(referenceEquivalent->endsWithIgnoringASCIICase(referenceEquivalent.get())); + ASSERT_TRUE(referenceEquivalent->endsWithIgnoringASCIICase(*referenceEquivalent.get())); + + // Proper suffixes. + RefPtr<StringImpl> aLower = StringImpl::createFromLiteral("a"); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(aLower.get())); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*aLower.get())); + RefPtr<StringImpl> aUpper = StringImpl::createFromLiteral("a"); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(aUpper.get())); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*aUpper.get())); + + RefPtr<StringImpl> abcLower = StringImpl::createFromLiteral("cba"); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(abcLower.get())); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*abcLower.get())); + RefPtr<StringImpl> abcUpper = StringImpl::createFromLiteral("CBA"); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(abcUpper.get())); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*abcUpper.get())); + + RefPtr<StringImpl> abcAccentLower = stringFromUTF8("Écba"); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(abcAccentLower.get())); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*abcAccentLower.get())); + RefPtr<StringImpl> abcAccentUpper = stringFromUTF8("ÉCBA"); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(abcAccentUpper.get())); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*abcAccentUpper.get())); + + // Negative cases. + RefPtr<StringImpl> differentLastChar = stringFromUTF8("XÉCbB"); + RefPtr<StringImpl> differentLastCharProperSuffix = stringFromUTF8("ÉCbb"); + ASSERT_FALSE(reference->endsWithIgnoringASCIICase(differentLastChar.get())); + ASSERT_FALSE(reference->endsWithIgnoringASCIICase(*differentLastChar.get())); + ASSERT_FALSE(reference->endsWithIgnoringASCIICase(differentLastCharProperSuffix.get())); + ASSERT_FALSE(reference->endsWithIgnoringASCIICase(*differentLastCharProperSuffix.get())); + + RefPtr<StringImpl> lowercaseAccent = stringFromUTF8("aBcéX"); + RefPtr<StringImpl> loweraseAccentProperSuffix = stringFromUTF8("aBcéX"); + ASSERT_FALSE(reference->endsWithIgnoringASCIICase(lowercaseAccent.get())); + ASSERT_FALSE(reference->endsWithIgnoringASCIICase(*lowercaseAccent.get())); + ASSERT_FALSE(reference->endsWithIgnoringASCIICase(loweraseAccentProperSuffix.get())); + ASSERT_FALSE(reference->endsWithIgnoringASCIICase(*loweraseAccentProperSuffix.get())); +} + +TEST(WTF, StringImplEndsWithIgnoringASCIICaseWithNull) +{ + RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG"); + ASSERT_FALSE(reference->endsWithIgnoringASCIICase(nullptr)); + + RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>("")); + ASSERT_FALSE(empty->endsWithIgnoringASCIICase(nullptr)); +} + +TEST(WTF, StringImplEndsWithIgnoringASCIICaseWithEmpty) +{ + RefPtr<StringImpl> reference = StringImpl::createFromLiteral("aBcDeFG"); + RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>("")); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(empty.get())); + ASSERT_TRUE(reference->endsWithIgnoringASCIICase(*empty.get())); + ASSERT_TRUE(empty->endsWithIgnoringASCIICase(empty.get())); + ASSERT_TRUE(empty->endsWithIgnoringASCIICase(*empty.get())); + ASSERT_FALSE(empty->endsWithIgnoringASCIICase(reference.get())); + ASSERT_FALSE(empty->endsWithIgnoringASCIICase(*reference.get())); +} + +TEST(WTF, StringImplCreateSymbolEmpty) +{ + RefPtr<StringImpl> reference = StringImpl::createSymbolEmpty(); + ASSERT_TRUE(reference->isSymbol()); + ASSERT_FALSE(reference->isAtomic()); + ASSERT_EQ(0u, reference->length()); + ASSERT_TRUE(equal(reference.get(), "")); +} + +TEST(WTF, StringImplCreateSymbol) +{ + RefPtr<StringImpl> original = stringFromUTF8("original"); + RefPtr<StringImpl> reference = StringImpl::createSymbol(original); + ASSERT_TRUE(reference->isSymbol()); + ASSERT_FALSE(reference->isAtomic()); + ASSERT_FALSE(original->isSymbol()); + ASSERT_FALSE(original->isAtomic()); + ASSERT_EQ(original->length(), reference->length()); + ASSERT_TRUE(equal(reference.get(), "original")); +} + +TEST(WTF, StringImplSymbolToAtomicString) +{ + RefPtr<StringImpl> original = stringFromUTF8("original"); + RefPtr<StringImpl> reference = StringImpl::createSymbol(original); + ASSERT_TRUE(reference->isSymbol()); + ASSERT_FALSE(reference->isAtomic()); + + RefPtr<StringImpl> atomic = AtomicStringImpl::add(reference.get()); + ASSERT_TRUE(atomic->isAtomic()); + ASSERT_FALSE(atomic->isSymbol()); + ASSERT_TRUE(reference->isSymbol()); + ASSERT_FALSE(reference->isAtomic()); +} + +TEST(WTF, StringImplSymbolEmptyToAtomicString) +{ + RefPtr<StringImpl> reference = StringImpl::createSymbolEmpty(); + ASSERT_TRUE(reference->isSymbol()); + ASSERT_FALSE(reference->isAtomic()); + + RefPtr<StringImpl> atomic = AtomicStringImpl::add(reference.get()); + ASSERT_TRUE(atomic->isAtomic()); + ASSERT_FALSE(atomic->isSymbol()); + ASSERT_TRUE(reference->isSymbol()); + ASSERT_FALSE(reference->isAtomic()); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/StringOperators.cpp b/Tools/TestWebKitAPI/Tests/WTF/StringOperators.cpp new file mode 100644 index 000000000..149b85b21 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/StringOperators.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2011 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" + +#define WTF_STRINGTYPEADAPTER_COPIED_WTF_STRING() (++wtfStringCopyCount) + +static int wtfStringCopyCount; + +#include <wtf/text/WTFString.h> + +namespace TestWebKitAPI { + +#define EXPECT_N_WTF_STRING_COPIES(count, expr) \ + do { \ + wtfStringCopyCount = 0; \ + String __testString = expr; \ + (void)__testString; \ + EXPECT_EQ(count, wtfStringCopyCount) << #expr; \ + } while (false) + +TEST(WTF, StringOperators) +{ + String string("String"); + AtomicString atomicString("AtomicString"); + ASCIILiteral literal("ASCIILiteral"); + + EXPECT_EQ(0, wtfStringCopyCount); + + EXPECT_N_WTF_STRING_COPIES(2, string + string); + EXPECT_N_WTF_STRING_COPIES(2, string + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + string); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + atomicString); + + EXPECT_N_WTF_STRING_COPIES(1, "C string" + string); + EXPECT_N_WTF_STRING_COPIES(1, string + "C string"); + EXPECT_N_WTF_STRING_COPIES(1, "C string" + atomicString); + EXPECT_N_WTF_STRING_COPIES(1, atomicString + "C string"); + + EXPECT_N_WTF_STRING_COPIES(1, literal + string); + EXPECT_N_WTF_STRING_COPIES(1, string + literal); + EXPECT_N_WTF_STRING_COPIES(1, literal + atomicString); + EXPECT_N_WTF_STRING_COPIES(1, atomicString + literal); + + EXPECT_N_WTF_STRING_COPIES(2, "C string" + string + "C string" + string); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + (string + "C string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, ("C string" + string) + ("C string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, string + "C string" + string + "C string"); + EXPECT_N_WTF_STRING_COPIES(2, string + ("C string" + string + "C string")); + EXPECT_N_WTF_STRING_COPIES(2, (string + "C string") + (string + "C string")); + + EXPECT_N_WTF_STRING_COPIES(2, literal + string + literal + string); + EXPECT_N_WTF_STRING_COPIES(2, literal + (string + literal + string)); + EXPECT_N_WTF_STRING_COPIES(2, (literal + string) + (literal + string)); + EXPECT_N_WTF_STRING_COPIES(2, string + literal + string + literal); + EXPECT_N_WTF_STRING_COPIES(2, string + (literal + string + literal)); + EXPECT_N_WTF_STRING_COPIES(2, (string + literal) + (string + literal)); + + EXPECT_N_WTF_STRING_COPIES(2, literal + string + "C string" + string); + EXPECT_N_WTF_STRING_COPIES(2, literal + (string + "C string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, (literal + string) + ("C string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + string + literal + string); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + (string + literal + string)); + EXPECT_N_WTF_STRING_COPIES(2, ("C string" + string) + (literal + string)); + + EXPECT_N_WTF_STRING_COPIES(2, literal + atomicString + "C string" + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, literal + (atomicString + "C string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, (literal + atomicString) + ("C string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + atomicString + literal + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + (atomicString + literal + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, ("C string" + atomicString) + (literal + atomicString)); + + EXPECT_N_WTF_STRING_COPIES(2, literal + atomicString + "C string" + string); + EXPECT_N_WTF_STRING_COPIES(2, literal + (atomicString + "C string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, (literal + atomicString) + ("C string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + atomicString + literal + string); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + (atomicString + literal + string)); + EXPECT_N_WTF_STRING_COPIES(2, ("C string" + atomicString) + (literal + string)); + + EXPECT_N_WTF_STRING_COPIES(2, literal + string + "C string" + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, literal + (string + "C string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, (literal + string) + ("C string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + string + literal + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + (string + literal + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, ("C string" + string) + (literal + atomicString)); + + EXPECT_N_WTF_STRING_COPIES(2, "C string" + atomicString + "C string" + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + (atomicString + "C string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, ("C string" + atomicString) + ("C string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + "C string" + atomicString + "C string"); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + ("C string" + atomicString + "C string")); + EXPECT_N_WTF_STRING_COPIES(2, (atomicString + "C string") + (atomicString + "C string")); + + EXPECT_N_WTF_STRING_COPIES(2, literal + atomicString + literal + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, literal + (atomicString + literal + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, (literal + atomicString) + (literal + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + literal + atomicString + literal); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + (literal + atomicString + literal)); + EXPECT_N_WTF_STRING_COPIES(2, (atomicString + literal) + (atomicString + literal)); + + EXPECT_N_WTF_STRING_COPIES(2, "C string" + string + "C string" + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + (string + "C string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, ("C string" + string) + ("C string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, string + "C string" + atomicString + "C string"); + EXPECT_N_WTF_STRING_COPIES(2, string + ("C string" + atomicString + "C string")); + EXPECT_N_WTF_STRING_COPIES(2, (string + "C string") + (atomicString + "C string")); + + EXPECT_N_WTF_STRING_COPIES(2, literal + string + literal + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, literal + (string + literal + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, (literal + string) + (literal + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, string + literal + atomicString + literal); + EXPECT_N_WTF_STRING_COPIES(2, string + (literal + atomicString + literal)); + EXPECT_N_WTF_STRING_COPIES(2, (string + literal) + (atomicString + literal)); + + EXPECT_N_WTF_STRING_COPIES(2, "C string" + atomicString + "C string" + string); + EXPECT_N_WTF_STRING_COPIES(2, "C string" + (atomicString + "C string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, ("C string" + atomicString) + ("C string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + "C string" + string + "C string"); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + ("C string" + string + "C string")); + EXPECT_N_WTF_STRING_COPIES(2, (atomicString + "C string") + (string + "C string")); + + EXPECT_N_WTF_STRING_COPIES(2, literal + atomicString + literal + string); + EXPECT_N_WTF_STRING_COPIES(2, literal + (atomicString + literal + string)); + EXPECT_N_WTF_STRING_COPIES(2, (literal + atomicString) + (literal + string)); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + literal + string + literal); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + (literal + string + literal)); + EXPECT_N_WTF_STRING_COPIES(2, (atomicString + literal) + (string + literal)); + +#if COMPILER(MSVC) + EXPECT_N_WTF_STRING_COPIES(1, L"wide string" + string); + EXPECT_N_WTF_STRING_COPIES(1, string + L"wide string"); + EXPECT_N_WTF_STRING_COPIES(1, L"wide string" + atomicString); + EXPECT_N_WTF_STRING_COPIES(1, atomicString + L"wide string"); + + EXPECT_N_WTF_STRING_COPIES(2, L"wide string" + string + L"wide string" + string); + EXPECT_N_WTF_STRING_COPIES(2, L"wide string" + (string + L"wide string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, (L"wide string" + string) + (L"wide string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, string + L"wide string" + string + L"wide string"); + EXPECT_N_WTF_STRING_COPIES(2, string + (L"wide string" + string + L"wide string")); + EXPECT_N_WTF_STRING_COPIES(2, (string + L"wide string") + (string + L"wide string")); + + EXPECT_N_WTF_STRING_COPIES(2, L"wide string" + atomicString + L"wide string" + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, L"wide string" + (atomicString + L"wide string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, (L"wide string" + atomicString) + (L"wide string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + L"wide string" + atomicString + L"wide string"); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + (L"wide string" + atomicString + L"wide string")); + EXPECT_N_WTF_STRING_COPIES(2, (atomicString + L"wide string") + (atomicString + L"wide string")); + + EXPECT_N_WTF_STRING_COPIES(2, L"wide string" + string + L"wide string" + atomicString); + EXPECT_N_WTF_STRING_COPIES(2, L"wide string" + (string + L"wide string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, (L"wide string" + string) + (L"wide string" + atomicString)); + EXPECT_N_WTF_STRING_COPIES(2, string + L"wide string" + atomicString + L"wide string"); + EXPECT_N_WTF_STRING_COPIES(2, string + (L"wide string" + atomicString + L"wide string")); + EXPECT_N_WTF_STRING_COPIES(2, (string + L"wide string") + (atomicString + L"wide string")); + + EXPECT_N_WTF_STRING_COPIES(2, L"wide string" + atomicString + L"wide string" + string); + EXPECT_N_WTF_STRING_COPIES(2, L"wide string" + (atomicString + L"wide string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, (L"wide string" + atomicString) + (L"wide string" + string)); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + L"wide string" + string + L"wide string"); + EXPECT_N_WTF_STRING_COPIES(2, atomicString + (L"wide string" + string + L"wide string")); + EXPECT_N_WTF_STRING_COPIES(2, (atomicString + L"wide string") + (string + L"wide string")); +#endif +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/StringView.cpp b/Tools/TestWebKitAPI/Tests/WTF/StringView.cpp new file mode 100644 index 000000000..408cc642e --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/StringView.cpp @@ -0,0 +1,736 @@ +/* + * Copyright (C) 2014 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 <wtf/text/StringBuilder.h> +#include <wtf/text/StringView.h> + +namespace TestWebKitAPI { + +TEST(WTF, StringViewEmptyVsNull) +{ + StringView nullView; + EXPECT_TRUE(nullView.isNull()); + EXPECT_TRUE(nullView.isEmpty()); + + // Test in a boolean context to test operator bool(). + if (nullView) + FAIL(); + else + SUCCEED(); + + if (!nullView) + SUCCEED(); + else + FAIL(); + + StringView emptyView = StringView::empty(); + EXPECT_FALSE(emptyView.isNull()); + EXPECT_TRUE(emptyView.isEmpty()); + + // Test in a boolean context to test operator bool(). + if (emptyView) + SUCCEED(); + else + FAIL(); + + if (!emptyView) + FAIL(); + else + SUCCEED(); + + StringView viewWithCharacters(String("hello")); + EXPECT_FALSE(viewWithCharacters.isNull()); + EXPECT_FALSE(viewWithCharacters.isEmpty()); + + // Test in a boolean context to test operator bool(). + if (viewWithCharacters) + SUCCEED(); + else + FAIL(); + + if (!viewWithCharacters) + FAIL(); + else + SUCCEED(); +} + +bool compareLoopIterations(StringView::CodePoints codePoints, std::vector<UChar32> expected) +{ + std::vector<UChar32> actual; + for (auto codePoint : codePoints) + actual.push_back(codePoint); + return actual == expected; +} + +static bool compareLoopIterations(StringView::CodeUnits codeUnits, std::vector<UChar> expected) +{ + std::vector<UChar> actual; + for (auto codeUnit : codeUnits) + actual.push_back(codeUnit); + return actual == expected; +} + +static void build(StringBuilder& builder, std::vector<UChar> input) +{ + builder.clear(); + for (auto codeUnit : input) + builder.append(codeUnit); +} + +TEST(WTF, StringViewIterators) +{ + compareLoopIterations(StringView().codePoints(), { }); + compareLoopIterations(StringView().codeUnits(), { }); + + compareLoopIterations(StringView::empty().codePoints(), { }); + compareLoopIterations(StringView::empty().codeUnits(), { }); + + compareLoopIterations(StringView(String("hello")).codePoints(), {'h', 'e', 'l', 'l', 'o'}); + compareLoopIterations(StringView(String("hello")).codeUnits(), {'h', 'e', 'l', 'l', 'o'}); + + StringBuilder b; + build(b, {0xD800, 0xDD55}); // Surrogates for unicode code point U+10155 + EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0x10155})); + EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0xD800, 0xDD55})); + + build(b, {0xD800}); // Leading surrogate only + EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0xD800})); + EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0xD800})); + + build(b, {0xD800, 0xD801}); // Two leading surrogates + EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0xD800, 0xD801})); + EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0xD800, 0xD801})); + + build(b, {0xDD55}); // Trailing surrogate only + EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0xDD55})); + EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0xDD55})); + + build(b, {0xD800, 'h'}); // Leading surrogate followed by non-surrogate + EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0xD800, 'h'})); + EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0xD800, 'h'})); + + build(b, {0x0306}); // "COMBINING BREVE" + EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0x0306})); + EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0x0306})); + + build(b, {0x0306, 0xD800, 0xDD55, 'h', 'e', 'l', 'o'}); // Mix of single code unit and multi code unit code points + EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codePoints(), {0x0306, 0x10155, 'h', 'e', 'l', 'o'})); + EXPECT_TRUE(compareLoopIterations(StringView(b.toString()).codeUnits(), {0x0306, 0xD800, 0xDD55, 'h', 'e', 'l', 'o'})); +} + +TEST(WTF, StringViewEqualIgnoringASCIICaseBasic) +{ + RefPtr<StringImpl> a = StringImpl::createFromLiteral("aBcDeFG"); + RefPtr<StringImpl> b = StringImpl::createFromLiteral("ABCDEFG"); + RefPtr<StringImpl> c = StringImpl::createFromLiteral("abcdefg"); + const char d[] = "aBcDeFG"; + RefPtr<StringImpl> empty = StringImpl::create(reinterpret_cast<const LChar*>("")); + RefPtr<StringImpl> shorter = StringImpl::createFromLiteral("abcdef"); + RefPtr<StringImpl> different = StringImpl::createFromLiteral("abcrefg"); + + StringView stringViewA(*a.get()); + StringView stringViewB(*b.get()); + StringView stringViewC(*c.get()); + StringView emptyStringView(*empty.get()); + StringView shorterStringView(*shorter.get()); + StringView differentStringView(*different.get()); + + ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewB)); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewC)); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewC)); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, d)); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, d)); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewC, d)); + + // Identity. + ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewA)); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewB)); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewC, stringViewC)); + + // Transitivity. + ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewB)); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewC)); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewC)); + + // Negative cases. + ASSERT_FALSE(equalIgnoringASCIICase(stringViewA, emptyStringView)); + ASSERT_FALSE(equalIgnoringASCIICase(stringViewB, emptyStringView)); + ASSERT_FALSE(equalIgnoringASCIICase(stringViewC, emptyStringView)); + ASSERT_FALSE(equalIgnoringASCIICase(stringViewA, shorterStringView)); + ASSERT_FALSE(equalIgnoringASCIICase(stringViewB, shorterStringView)); + ASSERT_FALSE(equalIgnoringASCIICase(stringViewC, shorterStringView)); + ASSERT_FALSE(equalIgnoringASCIICase(stringViewA, differentStringView)); + ASSERT_FALSE(equalIgnoringASCIICase(stringViewB, differentStringView)); + ASSERT_FALSE(equalIgnoringASCIICase(stringViewC, differentStringView)); + ASSERT_FALSE(equalIgnoringASCIICase(emptyStringView, d)); + ASSERT_FALSE(equalIgnoringASCIICase(shorterStringView, d)); + ASSERT_FALSE(equalIgnoringASCIICase(differentStringView, d)); +} + +TEST(WTF, StringViewEqualIgnoringASCIICaseWithEmpty) +{ + RefPtr<StringImpl> a = StringImpl::create(reinterpret_cast<const LChar*>("")); + RefPtr<StringImpl> b = StringImpl::create(reinterpret_cast<const LChar*>("")); + StringView stringViewA(*a.get()); + StringView stringViewB(*b.get()); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewB)); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewA)); +} + +TEST(WTF, StringViewEqualIgnoringASCIICaseWithLatin1Characters) +{ + RefPtr<StringImpl> a = StringImpl::create(reinterpret_cast<const LChar*>("aBcéeFG")); + RefPtr<StringImpl> b = StringImpl::create(reinterpret_cast<const LChar*>("ABCÉEFG")); + RefPtr<StringImpl> c = StringImpl::create(reinterpret_cast<const LChar*>("ABCéEFG")); + RefPtr<StringImpl> d = StringImpl::create(reinterpret_cast<const LChar*>("abcéefg")); + const char e[] = "aBcéeFG"; + StringView stringViewA(*a.get()); + StringView stringViewB(*b.get()); + StringView stringViewC(*c.get()); + StringView stringViewD(*d.get()); + + // Identity. + ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewA)); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewB, stringViewB)); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewC, stringViewC)); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewD, stringViewD)); + + // All combination. + ASSERT_FALSE(equalIgnoringASCIICase(stringViewA, stringViewB)); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewC)); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewA, stringViewD)); + ASSERT_FALSE(equalIgnoringASCIICase(stringViewB, stringViewC)); + ASSERT_FALSE(equalIgnoringASCIICase(stringViewB, stringViewD)); + ASSERT_TRUE(equalIgnoringASCIICase(stringViewC, stringViewD)); + ASSERT_FALSE(equalIgnoringASCIICase(stringViewA, e)); + ASSERT_FALSE(equalIgnoringASCIICase(stringViewB, e)); + ASSERT_FALSE(equalIgnoringASCIICase(stringViewC, e)); + ASSERT_FALSE(equalIgnoringASCIICase(stringViewD, e)); +} + +StringView stringViewFromLiteral(const char* characters) +{ + return StringView(reinterpret_cast<const LChar*>(characters), strlen(characters)); +} + +StringView stringViewFromUTF8(String &ref, const char* characters) +{ + ref = String::fromUTF8(characters); + return ref; +} + +TEST(WTF, StringViewFindIgnoringASCIICaseBasic) +{ + String referenceAHolder; + StringView referenceA = stringViewFromUTF8(referenceAHolder, "aBcéeFG"); + String referenceBHolder; + StringView referenceB = stringViewFromUTF8(referenceBHolder, "ABCÉEFG"); + + // Search the exact string. + EXPECT_EQ(static_cast<size_t>(0), referenceA.findIgnoringASCIICase(referenceA)); + EXPECT_EQ(static_cast<size_t>(0), referenceB.findIgnoringASCIICase(referenceB)); + + // A and B are distinct by the non-ascii character é/É. + EXPECT_EQ(static_cast<size_t>(notFound), referenceA.findIgnoringASCIICase(referenceB)); + EXPECT_EQ(static_cast<size_t>(notFound), referenceB.findIgnoringASCIICase(referenceA)); + + String tempStringHolder; + // Find the prefix. + EXPECT_EQ(static_cast<size_t>(0), referenceA.findIgnoringASCIICase(stringViewFromLiteral("a"))); + EXPECT_EQ(static_cast<size_t>(0), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "abcé"))); + EXPECT_EQ(static_cast<size_t>(0), referenceA.findIgnoringASCIICase(stringViewFromLiteral("A"))); + EXPECT_EQ(static_cast<size_t>(0), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCé"))); + EXPECT_EQ(static_cast<size_t>(0), referenceB.findIgnoringASCIICase(stringViewFromLiteral("a"))); + EXPECT_EQ(static_cast<size_t>(0), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "abcÉ"))); + EXPECT_EQ(static_cast<size_t>(0), referenceB.findIgnoringASCIICase(stringViewFromLiteral("A"))); + EXPECT_EQ(static_cast<size_t>(0), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCÉ"))); + + // Not a prefix. + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromLiteral("x"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "accé"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "abcÉ"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromLiteral("X"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABDé"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCÉ"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromLiteral("y"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "accÉ"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "abcé"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromLiteral("Y"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABdÉ"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCé"))); + + // Find the infix. + EXPECT_EQ(static_cast<size_t>(2), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "cée"))); + EXPECT_EQ(static_cast<size_t>(3), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ée"))); + EXPECT_EQ(static_cast<size_t>(2), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "cé"))); + EXPECT_EQ(static_cast<size_t>(2), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "c"))); + EXPECT_EQ(static_cast<size_t>(3), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "é"))); + EXPECT_EQ(static_cast<size_t>(2), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "Cée"))); + EXPECT_EQ(static_cast<size_t>(3), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "éE"))); + EXPECT_EQ(static_cast<size_t>(2), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "Cé"))); + EXPECT_EQ(static_cast<size_t>(2), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "C"))); + + EXPECT_EQ(static_cast<size_t>(2), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "cÉe"))); + EXPECT_EQ(static_cast<size_t>(3), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "Ée"))); + EXPECT_EQ(static_cast<size_t>(2), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "cÉ"))); + EXPECT_EQ(static_cast<size_t>(2), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "c"))); + EXPECT_EQ(static_cast<size_t>(3), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "É"))); + EXPECT_EQ(static_cast<size_t>(2), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "CÉe"))); + EXPECT_EQ(static_cast<size_t>(3), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ÉE"))); + EXPECT_EQ(static_cast<size_t>(2), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "CÉ"))); + EXPECT_EQ(static_cast<size_t>(2), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "C"))); + + // Not an infix. + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "céd"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "Ée"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "bé"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "x"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "É"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "CÉe"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "éd"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "CÉ"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "Y"))); + + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "cée"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "Éc"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "cé"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "W"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "é"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "bÉe"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "éE"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "BÉ"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "z"))); + + // Find the suffix. + EXPECT_EQ(static_cast<size_t>(6), referenceA.findIgnoringASCIICase(stringViewFromLiteral("g"))); + EXPECT_EQ(static_cast<size_t>(4), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "efg"))); + EXPECT_EQ(static_cast<size_t>(3), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "éefg"))); + EXPECT_EQ(static_cast<size_t>(6), referenceA.findIgnoringASCIICase(stringViewFromLiteral("G"))); + EXPECT_EQ(static_cast<size_t>(4), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "EFG"))); + EXPECT_EQ(static_cast<size_t>(3), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "éEFG"))); + + EXPECT_EQ(static_cast<size_t>(6), referenceB.findIgnoringASCIICase(stringViewFromLiteral("g"))); + EXPECT_EQ(static_cast<size_t>(4), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "efg"))); + EXPECT_EQ(static_cast<size_t>(3), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "Éefg"))); + EXPECT_EQ(static_cast<size_t>(6), referenceB.findIgnoringASCIICase(stringViewFromLiteral("G"))); + EXPECT_EQ(static_cast<size_t>(4), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "EFG"))); + EXPECT_EQ(static_cast<size_t>(3), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ÉEFG"))); + + // Not a suffix. + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromLiteral("X"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "edg"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "Éefg"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromLiteral("w"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "dFG"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceA.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ÉEFG"))); + + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromLiteral("Z"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ffg"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "éefg"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromLiteral("r"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "EgG"))); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), referenceB.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "éEFG"))); +} + +TEST(WTF, StringViewFindIgnoringASCIICaseWithValidOffset) +{ + String referenceHolder; + StringView reference = stringViewFromUTF8(referenceHolder, "ABCÉEFGaBcéeFG"); + String tempStringHolder; + + EXPECT_EQ(static_cast<size_t>(0), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABC"), 0)); + EXPECT_EQ(static_cast<size_t>(7), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABC"), 1)); + EXPECT_EQ(static_cast<size_t>(0), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCÉ"), 0)); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCÉ"), 1)); + EXPECT_EQ(static_cast<size_t>(7), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCé"), 0)); + EXPECT_EQ(static_cast<size_t>(7), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCé"), 1)); +} + +TEST(WTF, StringViewFindIgnoringASCIICaseWithInvalidOffset) +{ + String referenceHolder; + StringView reference = stringViewFromUTF8(referenceHolder, "ABCÉEFGaBcéeFG"); + String tempStringHolder; + + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABC"), 15)); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABC"), 16)); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCÉ"), 17)); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCÉ"), 42)); + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference.findIgnoringASCIICase(stringViewFromUTF8(tempStringHolder, "ABCÉ"), std::numeric_limits<unsigned>::max())); +} + +TEST(WTF, StringViewFindIgnoringASCIICaseOnEmpty) +{ + String referenceHolder; + StringView reference = stringViewFromUTF8(referenceHolder, "ABCÉEFG"); + StringView empty = stringViewFromLiteral(""); + EXPECT_EQ(static_cast<size_t>(0), reference.findIgnoringASCIICase(empty)); + EXPECT_EQ(static_cast<size_t>(0), reference.findIgnoringASCIICase(empty, 0)); + EXPECT_EQ(static_cast<size_t>(3), reference.findIgnoringASCIICase(empty, 3)); + EXPECT_EQ(static_cast<size_t>(7), reference.findIgnoringASCIICase(empty, 7)); + EXPECT_EQ(static_cast<size_t>(7), reference.findIgnoringASCIICase(empty, 8)); + EXPECT_EQ(static_cast<size_t>(7), reference.findIgnoringASCIICase(empty, 42)); + EXPECT_EQ(static_cast<size_t>(7), reference.findIgnoringASCIICase(empty, std::numeric_limits<unsigned>::max())); +} + +TEST(WTF, StringViewFindIgnoringASCIICaseWithPatternLongerThanReference) +{ + String referenceHolder; + StringView reference = stringViewFromUTF8(referenceHolder, "ABCÉEFG"); + String patternHolder; + StringView pattern = stringViewFromUTF8(patternHolder, "ABCÉEFGA"); + + EXPECT_EQ(static_cast<size_t>(WTF::notFound), reference.findIgnoringASCIICase(pattern)); + EXPECT_EQ(static_cast<size_t>(0), pattern.findIgnoringASCIICase(reference)); +} + +TEST(WTF, StringViewStartsWithBasic) +{ + StringView reference = stringViewFromLiteral("abcdefg"); + String referenceUTF8Ref; + StringView referenceUTF8 = stringViewFromUTF8(referenceUTF8Ref, "à îûèô"); + + StringView oneLetterPrefix = stringViewFromLiteral("a"); + StringView shortPrefix = stringViewFromLiteral("abc"); + StringView longPrefix = stringViewFromLiteral("abcdef"); + StringView upperCasePrefix = stringViewFromLiteral("ABC"); + StringView empty = stringViewFromLiteral(""); + StringView notPrefix = stringViewFromLiteral("bc"); + + String oneLetterPrefixUTF8Ref; + StringView oneLetterPrefixUTF8 = stringViewFromUTF8(oneLetterPrefixUTF8Ref, "à "); + String shortPrefixUTF8Ref; + StringView shortPrefixUTF8 = stringViewFromUTF8(shortPrefixUTF8Ref, "à î"); + String longPrefixUTF8Ref; + StringView longPrefixUTF8 = stringViewFromUTF8(longPrefixUTF8Ref, "à îûè"); + String upperCasePrefixUTF8Ref; + StringView upperCasePrefixUTF8 = stringViewFromUTF8(upperCasePrefixUTF8Ref, "ÀÎ"); + String notPrefixUTF8Ref; + StringView notPrefixUTF8 = stringViewFromUTF8(notPrefixUTF8Ref, "îû"); + + EXPECT_TRUE(reference.startsWith(reference)); + EXPECT_TRUE(reference.startsWith(oneLetterPrefix)); + EXPECT_TRUE(reference.startsWith(shortPrefix)); + EXPECT_TRUE(reference.startsWith(longPrefix)); + EXPECT_TRUE(reference.startsWith(empty)); + + EXPECT_TRUE(referenceUTF8.startsWith(referenceUTF8)); + EXPECT_TRUE(referenceUTF8.startsWith(oneLetterPrefixUTF8)); + EXPECT_TRUE(referenceUTF8.startsWith(shortPrefixUTF8)); + EXPECT_TRUE(referenceUTF8.startsWith(longPrefixUTF8)); + EXPECT_TRUE(referenceUTF8.startsWith(empty)); + + EXPECT_FALSE(reference.startsWith(notPrefix)); + EXPECT_FALSE(reference.startsWith(upperCasePrefix)); + EXPECT_FALSE(reference.startsWith(notPrefixUTF8)); + EXPECT_FALSE(reference.startsWith(upperCasePrefixUTF8)); + EXPECT_FALSE(referenceUTF8.startsWith(notPrefix)); + EXPECT_FALSE(referenceUTF8.startsWith(upperCasePrefix)); + EXPECT_FALSE(referenceUTF8.startsWith(notPrefixUTF8)); + EXPECT_FALSE(referenceUTF8.startsWith(upperCasePrefixUTF8)); +} + +TEST(WTF, StringViewStartsWithEmpty) +{ + StringView a = stringViewFromLiteral(""); + String refB; + StringView b = stringViewFromUTF8(refB, ""); + + EXPECT_TRUE(a.startsWith(a)); + EXPECT_TRUE(a.startsWith(b)); + EXPECT_TRUE(b.startsWith(a)); + EXPECT_TRUE(b.startsWith(b)); +} + +TEST(WTF, StringViewStartsWithIgnoringASCIICaseBasic) +{ + StringView reference = stringViewFromLiteral("abcdefg"); + + String referenceUTF8Ref; + StringView referenceUTF8 = stringViewFromUTF8(referenceUTF8Ref, "à îûèô"); + + StringView oneLetterPrefix = stringViewFromLiteral("a"); + StringView shortPrefix = stringViewFromLiteral("abc"); + StringView longPrefix = stringViewFromLiteral("abcdef"); + StringView upperCasePrefix = stringViewFromLiteral("ABC"); + StringView mixedCasePrefix = stringViewFromLiteral("aBcDe"); + StringView empty = stringViewFromLiteral(""); + StringView notPrefix = stringViewFromLiteral("bc"); + + String oneLetterPrefixUTF8Ref; + StringView oneLetterPrefixUTF8 = stringViewFromUTF8(oneLetterPrefixUTF8Ref, "à "); + String shortPrefixUTF8Ref; + StringView shortPrefixUTF8 = stringViewFromUTF8(shortPrefixUTF8Ref, "à î"); + String longPrefixUTF8Ref; + StringView longPrefixUTF8 = stringViewFromUTF8(longPrefixUTF8Ref, "à îûè"); + String upperCasePrefixUTF8Ref; + StringView upperCasePrefixUTF8 = stringViewFromUTF8(upperCasePrefixUTF8Ref, "ÀÎ"); + String notPrefixUTF8Ref; + StringView notPrefixUTF8 = stringViewFromUTF8(notPrefixUTF8Ref, "îû"); + + EXPECT_TRUE(reference.startsWithIgnoringASCIICase(reference)); + EXPECT_TRUE(reference.startsWithIgnoringASCIICase(oneLetterPrefix)); + EXPECT_TRUE(reference.startsWithIgnoringASCIICase(shortPrefix)); + EXPECT_TRUE(reference.startsWithIgnoringASCIICase(longPrefix)); + EXPECT_TRUE(reference.startsWithIgnoringASCIICase(empty)); + EXPECT_TRUE(reference.startsWithIgnoringASCIICase(upperCasePrefix)); + EXPECT_TRUE(reference.startsWithIgnoringASCIICase(mixedCasePrefix)); + + EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(referenceUTF8)); + EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(oneLetterPrefixUTF8)); + EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(shortPrefixUTF8)); + EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(longPrefixUTF8)); + EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(empty)); + + EXPECT_FALSE(reference.startsWithIgnoringASCIICase(notPrefix)); + EXPECT_FALSE(reference.startsWithIgnoringASCIICase(notPrefixUTF8)); + EXPECT_FALSE(reference.startsWithIgnoringASCIICase(upperCasePrefixUTF8)); + EXPECT_FALSE(referenceUTF8.startsWithIgnoringASCIICase(notPrefix)); + EXPECT_FALSE(referenceUTF8.startsWithIgnoringASCIICase(notPrefixUTF8)); + EXPECT_FALSE(referenceUTF8.startsWithIgnoringASCIICase(upperCasePrefix)); + EXPECT_FALSE(referenceUTF8.startsWithIgnoringASCIICase(upperCasePrefixUTF8)); +} + + +TEST(WTF, StringViewStartsWithIgnoringASCIICaseEmpty) +{ + StringView a = stringViewFromLiteral(""); + String refB; + StringView b = stringViewFromUTF8(refB, ""); + + EXPECT_TRUE(a.startsWithIgnoringASCIICase(a)); + EXPECT_TRUE(a.startsWithIgnoringASCIICase(b)); + EXPECT_TRUE(b.startsWithIgnoringASCIICase(a)); + EXPECT_TRUE(b.startsWithIgnoringASCIICase(b)); +} + +TEST(WTF, StringViewStartsWithIgnoringASCIICaseWithLatin1Characters) +{ + StringView reference = stringViewFromLiteral("aBcéeFG"); + String referenceUTF8Ref; + StringView referenceUTF8 = stringViewFromUTF8(referenceUTF8Ref, "aBcéeFG"); + + StringView a = stringViewFromLiteral("aBcéeF"); + StringView b = stringViewFromLiteral("ABCéEF"); + StringView c = stringViewFromLiteral("abcéef"); + StringView d = stringViewFromLiteral("Abcéef"); + + String refE; + StringView e = stringViewFromUTF8(refE, "aBcéeF"); + String refF; + StringView f = stringViewFromUTF8(refF, "ABCéEF"); + String refG; + StringView g = stringViewFromUTF8(refG, "abcéef"); + String refH; + StringView h = stringViewFromUTF8(refH, "Abcéef"); + + EXPECT_TRUE(reference.startsWithIgnoringASCIICase(a)); + EXPECT_TRUE(reference.startsWithIgnoringASCIICase(b)); + EXPECT_TRUE(reference.startsWithIgnoringASCIICase(c)); + EXPECT_TRUE(reference.startsWithIgnoringASCIICase(d)); + + EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(e)); + EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(f)); + EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(g)); + EXPECT_TRUE(referenceUTF8.startsWithIgnoringASCIICase(h)); + + EXPECT_FALSE(reference.endsWithIgnoringASCIICase(referenceUTF8)); + EXPECT_FALSE(referenceUTF8.endsWithIgnoringASCIICase(reference)); +} + +TEST(WTF, StringViewEndsWithBasic) +{ + StringView reference = stringViewFromLiteral("abcdefg"); + String referenceUTF8Ref; + StringView referenceUTF8 = stringViewFromUTF8(referenceUTF8Ref, "à îûèô"); + + StringView oneLetterSuffix = stringViewFromLiteral("g"); + StringView shortSuffix = stringViewFromLiteral("efg"); + StringView longSuffix = stringViewFromLiteral("cdefg"); + StringView upperCaseSuffix = stringViewFromLiteral("EFG"); + StringView empty = stringViewFromLiteral(""); + StringView notSuffix = stringViewFromLiteral("bc"); + + String oneLetterSuffixUTF8Ref; + StringView oneLetterSuffixUTF8 = stringViewFromUTF8(oneLetterSuffixUTF8Ref, "ô"); + String shortSuffixUTF8Ref; + StringView shortSuffixUTF8 = stringViewFromUTF8(shortSuffixUTF8Ref, "èô"); + String longSuffixUTF8Ref; + StringView longSuffixUTF8 = stringViewFromUTF8(longSuffixUTF8Ref, "îûèô"); + String upperCaseSuffixUTF8Ref; + StringView upperCaseSuffixUTF8 = stringViewFromUTF8(upperCaseSuffixUTF8Ref, "ÈÔ"); + String notSuffixUTF8Ref; + StringView notSuffixUTF8 = stringViewFromUTF8(notSuffixUTF8Ref, "îû"); + + EXPECT_TRUE(reference.endsWith(reference)); + EXPECT_TRUE(reference.endsWith(oneLetterSuffix)); + EXPECT_TRUE(reference.endsWith(shortSuffix)); + EXPECT_TRUE(reference.endsWith(longSuffix)); + EXPECT_TRUE(reference.endsWith(empty)); + + EXPECT_TRUE(referenceUTF8.endsWith(referenceUTF8)); + EXPECT_TRUE(referenceUTF8.endsWith(oneLetterSuffixUTF8)); + EXPECT_TRUE(referenceUTF8.endsWith(shortSuffixUTF8)); + EXPECT_TRUE(referenceUTF8.endsWith(longSuffixUTF8)); + EXPECT_TRUE(referenceUTF8.endsWith(empty)); + + EXPECT_FALSE(reference.endsWith(notSuffix)); + EXPECT_FALSE(reference.endsWith(upperCaseSuffix)); + EXPECT_FALSE(reference.endsWith(notSuffixUTF8)); + EXPECT_FALSE(reference.endsWith(upperCaseSuffixUTF8)); + EXPECT_FALSE(referenceUTF8.endsWith(notSuffix)); + EXPECT_FALSE(referenceUTF8.endsWith(upperCaseSuffix)); + EXPECT_FALSE(referenceUTF8.endsWith(notSuffixUTF8)); + EXPECT_FALSE(referenceUTF8.endsWith(upperCaseSuffixUTF8)); +} + +TEST(WTF, StringViewEndsWithEmpty) +{ + StringView a = stringViewFromLiteral(""); + String refB; + StringView b = stringViewFromUTF8(refB, ""); + + EXPECT_TRUE(a.endsWith(a)); + EXPECT_TRUE(a.endsWith(b)); + EXPECT_TRUE(b.endsWith(a)); + EXPECT_TRUE(b.endsWith(b)); +} + +TEST(WTF, StringViewEndsWithIgnoringASCIICaseBasic) +{ + StringView reference = stringViewFromLiteral("abcdefg"); + String referenceUTF8Ref; + StringView referenceUTF8 = stringViewFromUTF8(referenceUTF8Ref, "à îûèô"); + + StringView oneLetterSuffix = stringViewFromLiteral("g"); + StringView shortSuffix = stringViewFromLiteral("efg"); + StringView longSuffix = stringViewFromLiteral("bcdefg"); + StringView upperCaseSuffix = stringViewFromLiteral("EFG"); + StringView mixedCaseSuffix = stringViewFromLiteral("bCdeFg"); + StringView empty = stringViewFromLiteral(""); + StringView notSuffix = stringViewFromLiteral("bc"); + + String oneLetterSuffixUTF8Ref; + StringView oneLetterSuffixUTF8 = stringViewFromUTF8(oneLetterSuffixUTF8Ref, "ô"); + String shortSuffixUTF8Ref; + StringView shortSuffixUTF8 = stringViewFromUTF8(shortSuffixUTF8Ref, "èô"); + String longSuffixUTF8Ref; + StringView longSuffixUTF8 = stringViewFromUTF8(longSuffixUTF8Ref, "îûèô"); + String upperCaseSuffixUTF8Ref; + StringView upperCaseSuffixUTF8 = stringViewFromUTF8(upperCaseSuffixUTF8Ref, "ÈÔ"); + String notSuffixUTF8Ref; + StringView notSuffixUTF8 = stringViewFromUTF8(notSuffixUTF8Ref, "îû"); + + EXPECT_TRUE(reference.endsWithIgnoringASCIICase(reference)); + EXPECT_TRUE(reference.endsWithIgnoringASCIICase(oneLetterSuffix)); + EXPECT_TRUE(reference.endsWithIgnoringASCIICase(shortSuffix)); + EXPECT_TRUE(reference.endsWithIgnoringASCIICase(longSuffix)); + EXPECT_TRUE(reference.endsWithIgnoringASCIICase(empty)); + EXPECT_TRUE(reference.endsWithIgnoringASCIICase(upperCaseSuffix)); + EXPECT_TRUE(reference.endsWithIgnoringASCIICase(mixedCaseSuffix)); + + EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(referenceUTF8)); + EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(oneLetterSuffixUTF8)); + EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(shortSuffixUTF8)); + EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(longSuffixUTF8)); + EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(empty)); + + EXPECT_FALSE(reference.endsWithIgnoringASCIICase(notSuffix)); + EXPECT_FALSE(reference.endsWithIgnoringASCIICase(notSuffixUTF8)); + EXPECT_FALSE(reference.endsWithIgnoringASCIICase(upperCaseSuffixUTF8)); + EXPECT_FALSE(referenceUTF8.endsWithIgnoringASCIICase(notSuffix)); + EXPECT_FALSE(referenceUTF8.endsWithIgnoringASCIICase(notSuffixUTF8)); + EXPECT_FALSE(referenceUTF8.endsWithIgnoringASCIICase(upperCaseSuffix)); + EXPECT_FALSE(referenceUTF8.endsWithIgnoringASCIICase(upperCaseSuffixUTF8)); +} + +TEST(WTF, StringViewEndsWithIgnoringASCIICaseEmpty) +{ + StringView a = stringViewFromLiteral(""); + String refB; + StringView b = stringViewFromUTF8(refB, ""); + + EXPECT_TRUE(a.endsWithIgnoringASCIICase(a)); + EXPECT_TRUE(a.endsWithIgnoringASCIICase(b)); + EXPECT_TRUE(b.endsWithIgnoringASCIICase(a)); + EXPECT_TRUE(b.endsWithIgnoringASCIICase(b)); +} + +TEST(WTF, StringViewEndsWithIgnoringASCIICaseWithLatin1Characters) +{ + StringView reference = stringViewFromLiteral("aBcéeFG"); + String referenceUTF8Ref; + StringView referenceUTF8 = stringViewFromUTF8(referenceUTF8Ref, "aBcéeFG"); + + StringView a = stringViewFromLiteral("BcéeFG"); + StringView b = stringViewFromLiteral("BCéEFG"); + StringView c = stringViewFromLiteral("bcéefG"); + StringView d = stringViewFromLiteral("bcéefg"); + + String refE; + StringView e = stringViewFromUTF8(refE, "bcéefG"); + String refF; + StringView f = stringViewFromUTF8(refF, "BCéEFG"); + String refG; + StringView g = stringViewFromUTF8(refG, "bcéefG"); + String refH; + StringView h = stringViewFromUTF8(refH, "bcéefg"); + + EXPECT_TRUE(reference.endsWithIgnoringASCIICase(a)); + EXPECT_TRUE(reference.endsWithIgnoringASCIICase(b)); + EXPECT_TRUE(reference.endsWithIgnoringASCIICase(c)); + EXPECT_TRUE(reference.endsWithIgnoringASCIICase(d)); + + EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(e)); + EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(f)); + EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(g)); + EXPECT_TRUE(referenceUTF8.endsWithIgnoringASCIICase(h)); + + EXPECT_FALSE(reference.endsWithIgnoringASCIICase(referenceUTF8)); + EXPECT_FALSE(referenceUTF8.endsWithIgnoringASCIICase(reference)); +} + +TEST(WTF, StringView8Bit) +{ + StringView nullView; + StringView emptyView = StringView::empty(); + EXPECT_TRUE(StringView().is8Bit()); + EXPECT_TRUE(StringView::empty().is8Bit()); + + LChar* lcharPtr = nullptr; + UChar* ucharPtr = nullptr; + EXPECT_TRUE(StringView(lcharPtr, 0).is8Bit()); + EXPECT_FALSE(StringView(ucharPtr, 0).is8Bit()); + + EXPECT_TRUE(StringView(String(lcharPtr, 0)).is8Bit()); + EXPECT_TRUE(StringView(String(ucharPtr, 0)).is8Bit()); + + EXPECT_TRUE(StringView(String().impl()).is8Bit()); + EXPECT_TRUE(StringView(emptyString().impl()).is8Bit()); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/TemporaryChange.cpp b/Tools/TestWebKitAPI/Tests/WTF/TemporaryChange.cpp new file mode 100644 index 000000000..3ba0f15bd --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/TemporaryChange.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2011 Google 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 <wtf/TemporaryChange.h> + +namespace TestWebKitAPI { + +TEST(WTF, TemporaryChangeNested) +{ + bool originallyFalse = false; + { + TemporaryChange<bool> change1OriginallyFalse(originallyFalse, true); + EXPECT_TRUE(originallyFalse); + { + TemporaryChange<bool> change2OriginallyFalse(originallyFalse, false); + EXPECT_FALSE(originallyFalse); + } + EXPECT_TRUE(originallyFalse); + } + EXPECT_FALSE(originallyFalse); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp b/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp new file mode 100644 index 000000000..bec224141 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp @@ -0,0 +1,617 @@ +/* + * Copyright (C) 2011 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 "MoveOnly.h" +#include <wtf/Vector.h> +#include <wtf/text/CString.h> + +namespace TestWebKitAPI { + +TEST(WTF_Vector, Basic) +{ + Vector<int> intVector; + EXPECT_TRUE(intVector.isEmpty()); + EXPECT_EQ(0U, intVector.size()); + EXPECT_EQ(0U, intVector.capacity()); +} + +TEST(WTF_Vector, Iterator) +{ + Vector<int> intVector; + intVector.append(10); + intVector.append(11); + intVector.append(12); + intVector.append(13); + + Vector<int>::iterator it = intVector.begin(); + Vector<int>::iterator end = intVector.end(); + EXPECT_TRUE(end != it); + + EXPECT_EQ(10, *it); + ++it; + EXPECT_EQ(11, *it); + ++it; + EXPECT_EQ(12, *it); + ++it; + EXPECT_EQ(13, *it); + ++it; + + EXPECT_TRUE(end == it); +} + +TEST(WTF_Vector, OverloadedOperatorAmpersand) +{ + struct Test { + private: + Test* operator&(); + }; + + Vector<Test> vector; + vector.append(Test()); +} + +TEST(WTF_Vector, AppendLast) +{ + Vector<unsigned> vector; + vector.append(0); + + // FIXME: This test needs to be run with GuardMalloc to show the bug. + for (size_t i = 0; i < 100; ++i) + vector.append(const_cast<const unsigned&>(vector.last())); +} + +TEST(WTF_Vector, InitializerList) +{ + Vector<int> vector = { 1, 2, 3, 4 }; + EXPECT_EQ(4U, vector.size()); + + EXPECT_EQ(1, vector[0]); + EXPECT_EQ(2, vector[1]); + EXPECT_EQ(3, vector[2]); + EXPECT_EQ(4, vector[3]); +} + +TEST(WTF_Vector, InitializeFromOtherInitialCapacity) +{ + Vector<int, 3> vector = { 1, 3, 2, 4 }; + Vector<int, 5> vectorCopy(vector); + EXPECT_EQ(4U, vector.size()); + EXPECT_EQ(4U, vectorCopy.size()); + EXPECT_EQ(5U, vectorCopy.capacity()); + + EXPECT_EQ(1, vectorCopy[0]); + EXPECT_EQ(3, vectorCopy[1]); + EXPECT_EQ(2, vectorCopy[2]); + EXPECT_EQ(4, vectorCopy[3]); +} + +TEST(WTF_Vector, CopyFromOtherInitialCapacity) +{ + Vector<int, 3> vector = { 1, 3, 2, 4 }; + Vector<int, 5> vectorCopy { 0 }; + EXPECT_EQ(4U, vector.size()); + EXPECT_EQ(1U, vectorCopy.size()); + + vectorCopy = vector; + + EXPECT_EQ(4U, vector.size()); + EXPECT_EQ(4U, vectorCopy.size()); + EXPECT_EQ(5U, vectorCopy.capacity()); + + EXPECT_EQ(1, vectorCopy[0]); + EXPECT_EQ(3, vectorCopy[1]); + EXPECT_EQ(2, vectorCopy[2]); + EXPECT_EQ(4, vectorCopy[3]); +} + +TEST(WTF_Vector, InitializeFromOtherOverflowBehavior) +{ + Vector<int, 7, WTF::CrashOnOverflow> vector = { 4, 3, 2, 1 }; + Vector<int, 7, UnsafeVectorOverflow> vectorCopy(vector); + EXPECT_EQ(4U, vector.size()); + EXPECT_EQ(4U, vectorCopy.size()); + + EXPECT_EQ(4, vectorCopy[0]); + EXPECT_EQ(3, vectorCopy[1]); + EXPECT_EQ(2, vectorCopy[2]); + EXPECT_EQ(1, vectorCopy[3]); +} + +TEST(WTF_Vector, CopyFromOtherOverflowBehavior) +{ + Vector<int, 7, WTF::CrashOnOverflow> vector = { 4, 3, 2, 1 }; + Vector<int, 7, UnsafeVectorOverflow> vectorCopy = { 0, 0, 0 }; + + EXPECT_EQ(4U, vector.size()); + EXPECT_EQ(3U, vectorCopy.size()); + + vectorCopy = vector; + + EXPECT_EQ(4U, vector.size()); + EXPECT_EQ(4U, vectorCopy.size()); + + EXPECT_EQ(4, vectorCopy[0]); + EXPECT_EQ(3, vectorCopy[1]); + EXPECT_EQ(2, vectorCopy[2]); + EXPECT_EQ(1, vectorCopy[3]); +} + +TEST(WTF_Vector, InitializeFromOtherMinCapacity) +{ + Vector<int, 7, WTF::CrashOnOverflow, 1> vector = { 3, 4, 2, 1 }; + Vector<int, 7, WTF::CrashOnOverflow, 50> vectorCopy(vector); + EXPECT_EQ(4U, vector.size()); + EXPECT_EQ(4U, vectorCopy.size()); + + EXPECT_EQ(3, vectorCopy[0]); + EXPECT_EQ(4, vectorCopy[1]); + EXPECT_EQ(2, vectorCopy[2]); + EXPECT_EQ(1, vectorCopy[3]); +} + +TEST(WTF_Vector, CopyFromOtherMinCapacity) +{ + Vector<int, 7, WTF::CrashOnOverflow, 1> vector = { 3, 4, 2, 1 }; + Vector<int, 7, WTF::CrashOnOverflow, 50> vectorCopy; + + EXPECT_EQ(4U, vector.size()); + EXPECT_EQ(0U, vectorCopy.size()); + + vectorCopy = vector; + + EXPECT_EQ(4U, vector.size()); + EXPECT_EQ(4U, vectorCopy.size()); + + EXPECT_EQ(3, vectorCopy[0]); + EXPECT_EQ(4, vectorCopy[1]); + EXPECT_EQ(2, vectorCopy[2]); + EXPECT_EQ(1, vectorCopy[3]); +} + +TEST(WTF_Vector, Reverse) +{ + Vector<int> intVector; + intVector.append(10); + intVector.append(11); + intVector.append(12); + intVector.append(13); + intVector.reverse(); + + EXPECT_EQ(13, intVector[0]); + EXPECT_EQ(12, intVector[1]); + EXPECT_EQ(11, intVector[2]); + EXPECT_EQ(10, intVector[3]); + + intVector.append(9); + intVector.reverse(); + + EXPECT_EQ(9, intVector[0]); + EXPECT_EQ(10, intVector[1]); + EXPECT_EQ(11, intVector[2]); + EXPECT_EQ(12, intVector[3]); + EXPECT_EQ(13, intVector[4]); +} + +TEST(WTF_Vector, ReverseIterator) +{ + Vector<int> intVector; + intVector.append(10); + intVector.append(11); + intVector.append(12); + intVector.append(13); + + Vector<int>::reverse_iterator it = intVector.rbegin(); + Vector<int>::reverse_iterator end = intVector.rend(); + EXPECT_TRUE(end != it); + + EXPECT_EQ(13, *it); + ++it; + EXPECT_EQ(12, *it); + ++it; + EXPECT_EQ(11, *it); + ++it; + EXPECT_EQ(10, *it); + ++it; + + EXPECT_TRUE(end == it); +} + +TEST(WTF_Vector, MoveOnly_UncheckedAppend) +{ + Vector<MoveOnly> vector; + + vector.reserveInitialCapacity(100); + for (size_t i = 0; i < 100; ++i) { + MoveOnly moveOnly(i); + vector.uncheckedAppend(WTF::move(moveOnly)); + EXPECT_EQ(0U, moveOnly.value()); + } + + for (size_t i = 0; i < 100; ++i) + EXPECT_EQ(i, vector[i].value()); +} + +TEST(WTF_Vector, MoveOnly_Append) +{ + Vector<MoveOnly> vector; + + for (size_t i = 0; i < 100; ++i) { + MoveOnly moveOnly(i); + vector.append(WTF::move(moveOnly)); + EXPECT_EQ(0U, moveOnly.value()); + } + + for (size_t i = 0; i < 100; ++i) + EXPECT_EQ(i, vector[i].value()); + + for (size_t i = 0; i < 16; ++i) { + Vector<MoveOnly> vector; + + vector.append(i); + + for (size_t j = 0; j < i; ++j) + vector.append(j); + vector.append(WTF::move(vector[0])); + + EXPECT_EQ(0U, vector[0].value()); + + for (size_t j = 0; j < i; ++j) + EXPECT_EQ(j, vector[j + 1].value()); + EXPECT_EQ(i, vector.last().value()); + } +} + +TEST(WTF_Vector, MoveOnly_Insert) +{ + Vector<MoveOnly> vector; + + for (size_t i = 0; i < 100; ++i) { + MoveOnly moveOnly(i); + vector.insert(0, WTF::move(moveOnly)); + EXPECT_EQ(0U, moveOnly.value()); + } + + EXPECT_EQ(vector.size(), 100U); + for (size_t i = 0; i < 100; ++i) + EXPECT_EQ(99 - i, vector[i].value()); + + for (size_t i = 0; i < 200; i += 2) { + MoveOnly moveOnly(1000 + i); + vector.insert(i, WTF::move(moveOnly)); + EXPECT_EQ(0U, moveOnly.value()); + } + + EXPECT_EQ(200U, vector.size()); + for (size_t i = 0; i < 200; ++i) { + if (i % 2) + EXPECT_EQ(99 - i / 2, vector[i].value()); + else + EXPECT_EQ(1000 + i, vector[i].value()); + } +} + +TEST(WTF_Vector, MoveOnly_TakeLast) +{ + Vector<MoveOnly> vector; + + for (size_t i = 0; i < 100; ++i) { + MoveOnly moveOnly(i); + vector.append(WTF::move(moveOnly)); + EXPECT_EQ(0U, moveOnly.value()); + } + + EXPECT_EQ(100U, vector.size()); + for (size_t i = 0; i < 100; ++i) + EXPECT_EQ(99 - i, vector.takeLast().value()); + + EXPECT_EQ(0U, vector.size()); +} + +TEST(WTF_Vector, VectorOfVectorsOfVectorsInlineCapacitySwap) +{ + Vector<Vector<Vector<int, 1>, 1>, 1> a; + Vector<Vector<Vector<int, 1>, 1>, 1> b; + Vector<Vector<Vector<int, 1>, 1>, 1> c; + + EXPECT_EQ(0U, a.size()); + EXPECT_EQ(0U, b.size()); + EXPECT_EQ(0U, c.size()); + + Vector<int, 1> x; + x.append(42); + + EXPECT_EQ(1U, x.size()); + EXPECT_EQ(42, x[0]); + + Vector<Vector<int, 1>, 1> y; + y.append(x); + + EXPECT_EQ(1U, x.size()); + EXPECT_EQ(42, x[0]); + EXPECT_EQ(1U, y.size()); + EXPECT_EQ(1U, y[0].size()); + EXPECT_EQ(42, y[0][0]); + + a.append(y); + + EXPECT_EQ(1U, x.size()); + EXPECT_EQ(42, x[0]); + EXPECT_EQ(1U, y.size()); + EXPECT_EQ(1U, y[0].size()); + EXPECT_EQ(42, y[0][0]); + EXPECT_EQ(1U, a.size()); + EXPECT_EQ(1U, a[0].size()); + EXPECT_EQ(1U, a[0][0].size()); + EXPECT_EQ(42, a[0][0][0]); + + a.swap(b); + + EXPECT_EQ(0U, a.size()); + EXPECT_EQ(1U, x.size()); + EXPECT_EQ(42, x[0]); + EXPECT_EQ(1U, y.size()); + EXPECT_EQ(1U, y[0].size()); + EXPECT_EQ(42, y[0][0]); + EXPECT_EQ(1U, b.size()); + EXPECT_EQ(1U, b[0].size()); + EXPECT_EQ(1U, b[0][0].size()); + EXPECT_EQ(42, b[0][0][0]); + + b.swap(c); + + EXPECT_EQ(0U, a.size()); + EXPECT_EQ(0U, b.size()); + EXPECT_EQ(1U, x.size()); + EXPECT_EQ(42, x[0]); + EXPECT_EQ(1U, y.size()); + EXPECT_EQ(1U, y[0].size()); + EXPECT_EQ(42, y[0][0]); + EXPECT_EQ(1U, c.size()); + EXPECT_EQ(1U, c[0].size()); + EXPECT_EQ(1U, c[0][0].size()); + EXPECT_EQ(42, c[0][0][0]); + + y[0][0] = 24; + + EXPECT_EQ(1U, x.size()); + EXPECT_EQ(42, x[0]); + EXPECT_EQ(1U, y.size()); + EXPECT_EQ(1U, y[0].size()); + EXPECT_EQ(24, y[0][0]); + + a.append(y); + + EXPECT_EQ(1U, x.size()); + EXPECT_EQ(42, x[0]); + EXPECT_EQ(1U, y.size()); + EXPECT_EQ(1U, y[0].size()); + EXPECT_EQ(24, y[0][0]); + EXPECT_EQ(1U, a.size()); + EXPECT_EQ(1U, a[0].size()); + EXPECT_EQ(1U, a[0][0].size()); + EXPECT_EQ(24, a[0][0][0]); + EXPECT_EQ(1U, c.size()); + EXPECT_EQ(1U, c[0].size()); + EXPECT_EQ(1U, c[0][0].size()); + EXPECT_EQ(42, c[0][0][0]); + EXPECT_EQ(0U, b.size()); +} + +TEST(WTF_Vector, RemoveFirst) +{ + Vector<int> v; + EXPECT_TRUE(v.isEmpty()); + EXPECT_FALSE(v.removeFirst(1)); + EXPECT_FALSE(v.removeFirst(-1)); + EXPECT_TRUE(v.isEmpty()); + + v.fill(2, 10); + EXPECT_EQ(10U, v.size()); + EXPECT_FALSE(v.removeFirst(1)); + EXPECT_EQ(10U, v.size()); + v.clear(); + + v.fill(1, 10); + EXPECT_EQ(10U, v.size()); + EXPECT_TRUE(v.removeFirst(1)); + EXPECT_TRUE(v == Vector<int>({1, 1, 1, 1, 1, 1, 1, 1, 1})); + EXPECT_EQ(9U, v.size()); + EXPECT_FALSE(v.removeFirst(2)); + EXPECT_EQ(9U, v.size()); + EXPECT_TRUE(v == Vector<int>({1, 1, 1, 1, 1, 1, 1, 1, 1})); + + unsigned removed = 0; + while (v.removeFirst(1)) + ++removed; + EXPECT_EQ(9U, removed); + EXPECT_TRUE(v.isEmpty()); + + v.resize(1); + EXPECT_EQ(1U, v.size()); + EXPECT_TRUE(v.removeFirst(1)); + EXPECT_EQ(0U, v.size()); + EXPECT_TRUE(v.isEmpty()); +} + +TEST(WTF_Vector, RemoveAll) +{ + // Using a memcpy-able type. + static_assert(VectorTraits<int>::canMoveWithMemcpy, "Should use a memcpy-able type"); + Vector<int> v; + EXPECT_TRUE(v.isEmpty()); + EXPECT_FALSE(v.removeAll(1)); + EXPECT_FALSE(v.removeAll(-1)); + EXPECT_TRUE(v.isEmpty()); + + v.fill(1, 10); + EXPECT_EQ(10U, v.size()); + EXPECT_EQ(10U, v.removeAll(1)); + EXPECT_TRUE(v.isEmpty()); + + v.fill(2, 10); + EXPECT_EQ(10U, v.size()); + EXPECT_EQ(0U, v.removeAll(1)); + EXPECT_EQ(10U, v.size()); + + v = {1, 2, 1, 2, 1, 2, 2, 1, 1, 1}; + EXPECT_EQ(10U, v.size()); + EXPECT_EQ(6U, v.removeAll(1)); + EXPECT_EQ(4U, v.size()); + EXPECT_TRUE(v == Vector<int>({2, 2, 2, 2})); + EXPECT_TRUE(v.find(1) == notFound); + EXPECT_EQ(4U, v.removeAll(2)); + EXPECT_TRUE(v.isEmpty()); + + v = {3, 1, 2, 1, 2, 1, 2, 2, 1, 1, 1, 3}; + EXPECT_EQ(12U, v.size()); + EXPECT_EQ(6U, v.removeAll(1)); + EXPECT_EQ(6U, v.size()); + EXPECT_TRUE(v.find(1) == notFound); + EXPECT_TRUE(v == Vector<int>({3, 2, 2, 2, 2, 3})); + + EXPECT_EQ(4U, v.removeAll(2)); + EXPECT_EQ(2U, v.size()); + EXPECT_TRUE(v.find(2) == notFound); + EXPECT_TRUE(v == Vector<int>({3, 3})); + + EXPECT_EQ(2U, v.removeAll(3)); + EXPECT_TRUE(v.isEmpty()); + + v = {1, 1, 1, 3, 2, 4, 2, 2, 2, 4, 4, 3}; + EXPECT_EQ(12U, v.size()); + EXPECT_EQ(3U, v.removeAll(1)); + EXPECT_EQ(9U, v.size()); + EXPECT_TRUE(v.find(1) == notFound); + EXPECT_TRUE(v == Vector<int>({3, 2, 4, 2, 2, 2, 4, 4, 3})); + + // Using a non memcpy-able type. + static_assert(!VectorTraits<CString>::canMoveWithMemcpy, "Should use a non memcpy-able type"); + Vector<CString> vExpected; + Vector<CString> v2; + EXPECT_TRUE(v2.isEmpty()); + EXPECT_FALSE(v2.removeAll("1")); + EXPECT_TRUE(v2.isEmpty()); + + v2.fill("1", 10); + EXPECT_EQ(10U, v2.size()); + EXPECT_EQ(10U, v2.removeAll("1")); + EXPECT_TRUE(v2.isEmpty()); + + v2.fill("2", 10); + EXPECT_EQ(10U, v2.size()); + EXPECT_EQ(0U, v2.removeAll("1")); + EXPECT_EQ(10U, v2.size()); + + v2 = {"1", "2", "1", "2", "1", "2", "2", "1", "1", "1"}; + EXPECT_EQ(10U, v2.size()); + EXPECT_EQ(6U, v2.removeAll("1")); + EXPECT_EQ(4U, v2.size()); + EXPECT_TRUE(v2.find("1") == notFound); + EXPECT_EQ(4U, v2.removeAll("2")); + EXPECT_TRUE(v2.isEmpty()); + + v2 = {"3", "1", "2", "1", "2", "1", "2", "2", "1", "1", "1", "3"}; + EXPECT_EQ(12U, v2.size()); + EXPECT_EQ(6U, v2.removeAll("1")); + EXPECT_EQ(6U, v2.size()); + EXPECT_TRUE(v2.find("1") == notFound); + vExpected = {"3", "2", "2", "2", "2", "3"}; + EXPECT_TRUE(v2 == vExpected); + + EXPECT_EQ(4U, v2.removeAll("2")); + EXPECT_EQ(2U, v2.size()); + EXPECT_TRUE(v2.find("2") == notFound); + vExpected = {"3", "3"}; + EXPECT_TRUE(v2 == vExpected); + + EXPECT_EQ(2U, v2.removeAll("3")); + EXPECT_TRUE(v2.isEmpty()); + + v2 = {"1", "1", "1", "3", "2", "4", "2", "2", "2", "4", "4", "3"}; + EXPECT_EQ(12U, v2.size()); + EXPECT_EQ(3U, v2.removeAll("1")); + EXPECT_EQ(9U, v2.size()); + EXPECT_TRUE(v2.find("1") == notFound); + vExpected = {"3", "2", "4", "2", "2", "2", "4", "4", "3"}; + EXPECT_TRUE(v2 == vExpected); +} + +TEST(WTF_Vector, RemoveFirstMatching) +{ + Vector<int> v; + EXPECT_TRUE(v.isEmpty()); + EXPECT_FALSE(v.removeFirstMatching([] (int value) { return value > 0; })); + EXPECT_FALSE(v.removeFirstMatching([] (int) { return true; })); + EXPECT_FALSE(v.removeFirstMatching([] (int) { return false; })); + + v = {3, 1, 2, 1, 2, 1, 2, 2, 1, 1, 1, 3}; + EXPECT_EQ(12U, v.size()); + EXPECT_FALSE(v.removeFirstMatching([] (int) { return false; })); + EXPECT_EQ(12U, v.size()); + EXPECT_FALSE(v.removeFirstMatching([] (int value) { return value < 0; })); + EXPECT_EQ(12U, v.size()); + EXPECT_TRUE(v.removeFirstMatching([] (int value) { return value < 3; })); + EXPECT_EQ(11U, v.size()); + EXPECT_TRUE(v == Vector<int>({3, 2, 1, 2, 1, 2, 2, 1, 1, 1, 3})); + EXPECT_TRUE(v.removeFirstMatching([] (int value) { return value > 2; })); + EXPECT_EQ(10U, v.size()); + EXPECT_TRUE(v == Vector<int>({2, 1, 2, 1, 2, 2, 1, 1, 1, 3})); + EXPECT_TRUE(v.removeFirstMatching([] (int value) { return value > 2; })); + EXPECT_EQ(9U, v.size()); + EXPECT_TRUE(v == Vector<int>({2, 1, 2, 1, 2, 2, 1, 1, 1})); +} + +TEST(WTF_Vector, RemoveAllMatching) +{ + Vector<int> v; + EXPECT_TRUE(v.isEmpty()); + EXPECT_FALSE(v.removeAllMatching([] (int value) { return value > 0; })); + EXPECT_FALSE(v.removeAllMatching([] (int) { return true; })); + EXPECT_FALSE(v.removeAllMatching([] (int) { return false; })); + + v = {3, 1, 2, 1, 2, 1, 2, 2, 1, 1, 1, 3}; + EXPECT_EQ(12U, v.size()); + EXPECT_EQ(0U, v.removeAllMatching([] (int) { return false; })); + EXPECT_EQ(12U, v.size()); + EXPECT_EQ(0U, v.removeAllMatching([] (int value) { return value < 0; })); + EXPECT_EQ(12U, v.size()); + EXPECT_EQ(12U, v.removeAllMatching([] (int value) { return value > 0; })); + EXPECT_TRUE(v.isEmpty()); + + v = {3, 1, 2, 1, 2, 1, 3, 2, 2, 1, 1, 1, 3}; + EXPECT_EQ(13U, v.size()); + EXPECT_EQ(3U, v.removeAllMatching([] (int value) { return value > 2; })); + EXPECT_EQ(10U, v.size()); + EXPECT_TRUE(v == Vector<int>({1, 2, 1, 2, 1, 2, 2, 1, 1, 1})); + EXPECT_EQ(6U, v.removeAllMatching([] (int value) { return value != 2; })); + EXPECT_EQ(4U, v.size()); + EXPECT_TRUE(v == Vector<int>({2, 2, 2, 2})); + EXPECT_EQ(4U, v.removeAllMatching([] (int value) { return value == 2; })); + EXPECT_TRUE(v.isEmpty()); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/WTFString.cpp b/Tools/TestWebKitAPI/Tests/WTF/WTFString.cpp new file mode 100644 index 000000000..a13ca0aa0 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/WTFString.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2012 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 <limits> +#include <wtf/MathExtras.h> +#include <wtf/text/CString.h> +#include <wtf/text/WTFString.h> + +namespace TestWebKitAPI { + +TEST(WTF, StringCreationFromLiteral) +{ + String stringFromLiteral(ASCIILiteral("Explicit construction syntax")); + ASSERT_EQ(strlen("Explicit construction syntax"), stringFromLiteral.length()); + ASSERT_TRUE(stringFromLiteral == "Explicit construction syntax"); + ASSERT_TRUE(stringFromLiteral.is8Bit()); + ASSERT_TRUE(String("Explicit construction syntax") == stringFromLiteral); + + String stringWithTemplate("Template Literal", String::ConstructFromLiteral); + ASSERT_EQ(strlen("Template Literal"), stringWithTemplate.length()); + ASSERT_TRUE(stringWithTemplate == "Template Literal"); + ASSERT_TRUE(stringWithTemplate.is8Bit()); + ASSERT_TRUE(String("Template Literal") == stringWithTemplate); +} + +TEST(WTF, StringASCII) +{ + CString output; + + // Null String. + output = String().ascii(); + ASSERT_STREQ("", output.data()); + + // Empty String. + output = emptyString().ascii(); + ASSERT_STREQ("", output.data()); + + // Regular String. + output = String(ASCIILiteral("foobar")).ascii(); + ASSERT_STREQ("foobar", output.data()); +} + +static void testNumberToStringECMAScript(double number, const char* reference) +{ + CString numberString = String::numberToStringECMAScript(number).latin1(); + ASSERT_STREQ(reference, numberString.data()); +} + +TEST(WTF, StringNumberToStringECMAScriptBoundaries) +{ + typedef std::numeric_limits<double> Limits; + + // Infinity. + testNumberToStringECMAScript(Limits::infinity(), "Infinity"); + testNumberToStringECMAScript(-Limits::infinity(), "-Infinity"); + + // NaN. + testNumberToStringECMAScript(-Limits::quiet_NaN(), "NaN"); + + // Zeros. + testNumberToStringECMAScript(0, "0"); + testNumberToStringECMAScript(-0, "0"); + + // Min-Max. + testNumberToStringECMAScript(Limits::min(), "2.2250738585072014e-308"); + testNumberToStringECMAScript(Limits::max(), "1.7976931348623157e+308"); +} + +TEST(WTF, StringNumberToStringECMAScriptRegularNumbers) +{ + // Pi. + testNumberToStringECMAScript(piDouble, "3.141592653589793"); + testNumberToStringECMAScript(piFloat, "3.1415927410125732"); + testNumberToStringECMAScript(piOverTwoDouble, "1.5707963267948966"); + testNumberToStringECMAScript(piOverTwoFloat, "1.5707963705062866"); + testNumberToStringECMAScript(piOverFourDouble, "0.7853981633974483"); + testNumberToStringECMAScript(piOverFourFloat, "0.7853981852531433"); + + // e. + const double e = 2.71828182845904523536028747135266249775724709369995; + testNumberToStringECMAScript(e, "2.718281828459045"); + + // c, speed of light in m/s. + const double c = 299792458; + testNumberToStringECMAScript(c, "299792458"); + + // Golen ratio. + const double phi = 1.6180339887498948482; + testNumberToStringECMAScript(phi, "1.618033988749895"); +} + +TEST(WTF, StringReplaceWithLiteral) +{ + // Cases for 8Bit source. + String testString = "1224"; + ASSERT_TRUE(testString.is8Bit()); + testString.replaceWithLiteral('2', ""); + ASSERT_STREQ("14", testString.utf8().data()); + + testString = "1224"; + ASSERT_TRUE(testString.is8Bit()); + testString.replaceWithLiteral('2', "3"); + ASSERT_STREQ("1334", testString.utf8().data()); + + testString = "1224"; + ASSERT_TRUE(testString.is8Bit()); + testString.replaceWithLiteral('2', "555"); + ASSERT_STREQ("15555554", testString.utf8().data()); + + testString = "1224"; + ASSERT_TRUE(testString.is8Bit()); + testString.replaceWithLiteral('3', "NotFound"); + ASSERT_STREQ("1224", testString.utf8().data()); + + // Cases for 16Bit source. + testString = String::fromUTF8("résumé"); + ASSERT_FALSE(testString.is8Bit()); + testString.replaceWithLiteral(UChar(0x00E9 /*U+00E9 is 'é'*/), "e"); + ASSERT_STREQ("resume", testString.utf8().data()); + + testString = String::fromUTF8("résumé"); + ASSERT_FALSE(testString.is8Bit()); + testString.replaceWithLiteral(UChar(0x00E9 /*U+00E9 is 'é'*/), ""); + ASSERT_STREQ("rsum", testString.utf8().data()); + + testString = String::fromUTF8("résumé"); + ASSERT_FALSE(testString.is8Bit()); + testString.replaceWithLiteral('3', "NotFound"); + ASSERT_STREQ("résumé", testString.utf8().data()); +} + +TEST(WTF, StringIsolatedCopy) +{ + String original = "1234"; + auto copy = WTF::move(original).isolatedCopy(); + ASSERT_FALSE(original.impl() == copy.impl()); +} + +TEST(WTF, StringToInt) +{ + bool ok; + + EXPECT_EQ(0, String().toInt()); + EXPECT_EQ(0, String().toInt(&ok)); + EXPECT_FALSE(ok); + + EXPECT_EQ(0, emptyString().toInt()); + EXPECT_EQ(0, emptyString().toInt(&ok)); + EXPECT_FALSE(ok); + + EXPECT_EQ(0, String("0").toInt()); + EXPECT_EQ(0, String("0").toInt(&ok)); + EXPECT_TRUE(ok); + + EXPECT_EQ(1, String("1").toInt()); + EXPECT_EQ(1, String("1").toInt(&ok)); + EXPECT_TRUE(ok); + + EXPECT_EQ(2147483647, String("2147483647").toInt()); + EXPECT_EQ(2147483647, String("2147483647").toInt(&ok)); + EXPECT_TRUE(ok); + + EXPECT_EQ(0, String("2147483648").toInt()); + EXPECT_EQ(0, String("2147483648").toInt(&ok)); + EXPECT_FALSE(ok); + + EXPECT_EQ(-2147483648, String("-2147483648").toInt()); + EXPECT_EQ(-2147483648, String("-2147483648").toInt(&ok)); + EXPECT_TRUE(ok); + + EXPECT_EQ(0, String("-2147483649").toInt()); + EXPECT_EQ(0, String("-2147483649").toInt(&ok)); + EXPECT_FALSE(ok); + + // fail if we see leading junk + EXPECT_EQ(0, String("x1").toInt()); + EXPECT_EQ(0, String("x1").toInt(&ok)); + EXPECT_FALSE(ok); + + // succeed if we see leading spaces + EXPECT_EQ(1, String(" 1").toInt()); + EXPECT_EQ(1, String(" 1").toInt(&ok)); + EXPECT_TRUE(ok); + + // silently ignore trailing junk + EXPECT_EQ(1, String("1x").toInt()); + EXPECT_EQ(1, String("1x").toInt(&ok)); + EXPECT_TRUE(ok); +} + +TEST(WTF, StringToDouble) +{ + bool ok; + + EXPECT_EQ(0.0, String().toDouble()); + EXPECT_EQ(0.0, String().toDouble(&ok)); + EXPECT_FALSE(ok); + + EXPECT_EQ(0.0, emptyString().toDouble()); + EXPECT_EQ(0.0, emptyString().toDouble(&ok)); + EXPECT_FALSE(ok); + + EXPECT_EQ(0.0, String("0").toDouble()); + EXPECT_EQ(0.0, String("0").toDouble(&ok)); + EXPECT_TRUE(ok); + + EXPECT_EQ(1.0, String("1").toDouble()); + EXPECT_EQ(1.0, String("1").toDouble(&ok)); + EXPECT_TRUE(ok); + + // fail if we see leading junk + EXPECT_EQ(0.0, String("x1").toDouble()); + EXPECT_EQ(0.0, String("x1").toDouble(&ok)); + EXPECT_FALSE(ok); + + // succeed if we see leading spaces + EXPECT_EQ(1.0, String(" 1").toDouble()); + EXPECT_EQ(1.0, String(" 1").toDouble(&ok)); + EXPECT_TRUE(ok); + + // ignore trailing junk, but return false for "ok" + // FIXME: This is an inconsistency with toInt, which always guarantees + // it will return 0 if it's also going to return false for ok. + EXPECT_EQ(1.0, String("1x").toDouble()); + EXPECT_EQ(1.0, String("1x").toDouble(&ok)); + EXPECT_FALSE(ok); + + // parse only numbers, not special values such as "infinity" + EXPECT_EQ(0.0, String("infinity").toDouble()); + EXPECT_EQ(0.0, String("infinity").toDouble(&ok)); + EXPECT_FALSE(ok); + + // parse only numbers, not special values such as "nan" + EXPECT_EQ(0.0, String("nan").toDouble()); + EXPECT_EQ(0.0, String("nan").toDouble(&ok)); + EXPECT_FALSE(ok); +} + +TEST(WTF, StringhasInfixStartingAt) +{ + EXPECT_TRUE(String("Test").is8Bit()); + EXPECT_TRUE(String("Te").is8Bit()); + EXPECT_TRUE(String("st").is8Bit()); + EXPECT_TRUE(String("Test").hasInfixStartingAt(String("Te"), 0)); + EXPECT_FALSE(String("Test").hasInfixStartingAt(String("Te"), 2)); + EXPECT_TRUE(String("Test").hasInfixStartingAt(String("st"), 2)); + EXPECT_FALSE(String("Test").hasInfixStartingAt(String("ST"), 2)); + + EXPECT_FALSE(String::fromUTF8("ä¸å›½").is8Bit()); + EXPECT_FALSE(String::fromUTF8("ä¸").is8Bit()); + EXPECT_FALSE(String::fromUTF8("国").is8Bit()); + EXPECT_TRUE(String::fromUTF8("ä¸å›½").hasInfixStartingAt(String::fromUTF8("ä¸"), 0)); + EXPECT_FALSE(String::fromUTF8("ä¸å›½").hasInfixStartingAt(String::fromUTF8("ä¸"), 1)); + EXPECT_TRUE(String::fromUTF8("ä¸å›½").hasInfixStartingAt(String::fromUTF8("国"), 1)); + + EXPECT_FALSE(String::fromUTF8("ä¸å›½").hasInfixStartingAt(String("Te"), 0)); + EXPECT_FALSE(String("Test").hasInfixStartingAt(String::fromUTF8("ä¸"), 2)); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/WeakPtr.cpp b/Tools/TestWebKitAPI/Tests/WTF/WeakPtr.cpp new file mode 100644 index 000000000..5b900007a --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/WeakPtr.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2015 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 "test.h" + +#include <wtf/WeakPtr.h> + +namespace TestWebKitAPI { + +TEST(WTF_WeakPtr, Basic) +{ + int dummy = 5; + WeakPtrFactory<int>* factory = new WeakPtrFactory<int>(&dummy); + WeakPtr<int> weakPtr1 = factory->createWeakPtr(); + WeakPtr<int> weakPtr2 = factory->createWeakPtr(); + WeakPtr<int> weakPtr3 = factory->createWeakPtr(); + EXPECT_EQ(weakPtr1.get(), &dummy); + EXPECT_EQ(weakPtr2.get(), &dummy); + EXPECT_EQ(weakPtr3.get(), &dummy); + EXPECT_TRUE(weakPtr1); + EXPECT_TRUE(weakPtr2); + EXPECT_TRUE(weakPtr3); + delete factory; + EXPECT_NULL(weakPtr1.get()); + EXPECT_NULL(weakPtr2.get()); + EXPECT_NULL(weakPtr3.get()); + EXPECT_FALSE(weakPtr1); + EXPECT_FALSE(weakPtr2); + EXPECT_FALSE(weakPtr3); +} + +TEST(WTF_WeakPtr, Assignment) +{ + int dummy = 5; + WeakPtr<int> weakPtr; + { + WeakPtrFactory<int> factory(&dummy); + EXPECT_NULL(weakPtr.get()); + weakPtr = factory.createWeakPtr(); + EXPECT_EQ(weakPtr.get(), &dummy); + } + EXPECT_NULL(weakPtr.get()); +} + +TEST(WTF_WeakPtr, MultipleFactories) +{ + int dummy1 = 5; + int dummy2 = 7; + WeakPtrFactory<int>* factory1 = new WeakPtrFactory<int>(&dummy1); + WeakPtrFactory<int>* factory2 = new WeakPtrFactory<int>(&dummy2); + WeakPtr<int> weakPtr1 = factory1->createWeakPtr(); + WeakPtr<int> weakPtr2 = factory2->createWeakPtr(); + EXPECT_EQ(weakPtr1.get(), &dummy1); + EXPECT_EQ(weakPtr2.get(), &dummy2); + delete factory1; + EXPECT_NULL(weakPtr1.get()); + EXPECT_EQ(weakPtr2.get(), &dummy2); + delete factory2; + EXPECT_NULL(weakPtr2.get()); +} + +TEST(WTF_WeakPtr, RevokeAll) +{ + int dummy = 5; + WeakPtrFactory<int> factory(&dummy); + WeakPtr<int> weakPtr1 = factory.createWeakPtr(); + WeakPtr<int> weakPtr2 = factory.createWeakPtr(); + WeakPtr<int> weakPtr3 = factory.createWeakPtr(); + EXPECT_EQ(weakPtr1.get(), &dummy); + EXPECT_EQ(weakPtr2.get(), &dummy); + EXPECT_EQ(weakPtr3.get(), &dummy); + factory.revokeAll(); + EXPECT_NULL(weakPtr1.get()); + EXPECT_NULL(weakPtr2.get()); + EXPECT_NULL(weakPtr3.get()); +} + +TEST(WTF_WeakPtr, NullFactory) +{ + WeakPtrFactory<int> factory(nullptr); + WeakPtr<int> weakPtr = factory.createWeakPtr(); + EXPECT_NULL(weakPtr.get()); + factory.revokeAll(); + EXPECT_NULL(weakPtr.get()); +} + +struct Foo { + void bar() { }; +}; + +TEST(WTF_WeakPtr, Dereference) +{ + Foo f; + WeakPtrFactory<Foo> factory(&f); + WeakPtr<Foo> weakPtr = factory.createWeakPtr(); + weakPtr->bar(); +} + +TEST(WTF_WeakPtr, Forget) +{ + int dummy = 5; + int dummy2 = 7; + + WeakPtrFactory<int> outerFactory(&dummy2); + WeakPtr<int> weakPtr1, weakPtr2, weakPtr3, weakPtr4; + { + WeakPtrFactory<int> innerFactory(&dummy); + weakPtr1 = innerFactory.createWeakPtr(); + weakPtr2 = innerFactory.createWeakPtr(); + weakPtr3 = innerFactory.createWeakPtr(); + EXPECT_EQ(weakPtr1.get(), &dummy); + EXPECT_EQ(weakPtr2.get(), &dummy); + EXPECT_EQ(weakPtr3.get(), &dummy); + weakPtr1.clear(); + weakPtr3 = nullptr; + EXPECT_NULL(weakPtr1.get()); + EXPECT_EQ(weakPtr2.get(), &dummy); + EXPECT_NULL(weakPtr3.get()); + weakPtr1.clear(); + weakPtr3.clear(); + EXPECT_NULL(weakPtr1.get()); + EXPECT_EQ(weakPtr2.get(), &dummy); + EXPECT_NULL(weakPtr3.get()); + weakPtr3 = nullptr; + EXPECT_NULL(weakPtr1.get()); + EXPECT_EQ(weakPtr2.get(), &dummy); + EXPECT_NULL(weakPtr3.get()); + + weakPtr4 = weakPtr2; + EXPECT_EQ(weakPtr2.get(), &dummy); + EXPECT_EQ(weakPtr4.get(), &dummy); + + WeakPtr<int> weakPtr5 = weakPtr2; + EXPECT_EQ(weakPtr2.get(), &dummy); + EXPECT_EQ(weakPtr5.get(), &dummy); + weakPtr5.clear(); + EXPECT_NULL(weakPtr5.get()); + EXPECT_EQ(weakPtr2.get(), &dummy); + + weakPtr4 = outerFactory.createWeakPtr(); + EXPECT_EQ(weakPtr2.get(), &dummy); + EXPECT_EQ(weakPtr4.get(), &dummy2); + } + + EXPECT_NULL(weakPtr1.get()); + EXPECT_NULL(weakPtr2.get()); + EXPECT_EQ(weakPtr4.get(), &dummy2); + + WeakPtr<int> weakPtr5 = weakPtr4; + EXPECT_EQ(weakPtr4.get(), &dummy2); + EXPECT_EQ(weakPtr5.get(), &dummy2); + weakPtr5.clear(); + EXPECT_NULL(weakPtr5.get()); + WeakPtr<int> weakPtr6 = weakPtr5; + EXPECT_NULL(weakPtr6.get()); + EXPECT_EQ(weakPtr5.get(), weakPtr6.get()); + + WeakPtr<int> weakPtr7 = outerFactory.createWeakPtr(); + EXPECT_EQ(weakPtr7.get(), &dummy2); + weakPtr7 = nullptr; + EXPECT_NULL(weakPtr7.get()); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/WorkQueue.cpp b/Tools/TestWebKitAPI/Tests/WTF/WorkQueue.cpp new file mode 100644 index 000000000..74f5dc294 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/WorkQueue.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2015 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 "Test.h" +#include <wtf/Condition.h> +#include <wtf/Lock.h> +#include <wtf/Vector.h> +#include <wtf/WorkQueue.h> +#include <string> +#include <thread> + +namespace TestWebKitAPI { + +static const char* simpleTestLabel = "simpleTest"; +static const char* longTestLabel = "longTest"; +static const char* thirdTestLabel = "thirdTest"; +static const char* dispatchAfterLabel = "dispatchAfter"; + +TEST(WTF_WorkQueue, Simple) +{ + Lock m_lock; + Condition m_testCompleted; + Vector<std::string> m_functionCallOrder; + + bool calledSimpleTest = false; + bool calledLongTest = false; + bool calledThirdTest = false; + + static const char* simpleTestLabel = "simpleTest"; + static const char* longTestLabel = "longTest"; + static const char* thirdTestLabel = "thirdTest"; + + auto queue = WorkQueue::create("com.apple.WebKit.Test.simple"); + int initialRefCount = queue->refCount(); + EXPECT_EQ(1, initialRefCount); + + LockHolder locker(m_lock); + queue->dispatch([&](void) { + m_functionCallOrder.append(simpleTestLabel); + calledSimpleTest = true; + }); + + queue->dispatch([&](void) { + m_functionCallOrder.append(longTestLabel); + std::this_thread::sleep_for(std::chrono::nanoseconds(100)); + calledLongTest = true; + }); + + queue->dispatch([&](void) { + LockHolder locker(m_lock); + m_functionCallOrder.append(thirdTestLabel); + calledThirdTest = true; + + EXPECT_TRUE(calledSimpleTest); + EXPECT_TRUE(calledLongTest); + EXPECT_TRUE(calledThirdTest); + + m_testCompleted.notifyOne(); + }); + + EXPECT_GT(queue->refCount(), 1); + + m_testCompleted.wait(m_lock); + + EXPECT_TRUE(calledSimpleTest); + EXPECT_TRUE(calledLongTest); + EXPECT_TRUE(calledThirdTest); + + EXPECT_EQ(static_cast<size_t>(3), m_functionCallOrder.size()); + EXPECT_STREQ(simpleTestLabel, m_functionCallOrder[0].c_str()); + EXPECT_STREQ(longTestLabel, m_functionCallOrder[1].c_str()); + EXPECT_STREQ(thirdTestLabel, m_functionCallOrder[2].c_str()); +} + +TEST(WTF_WorkQueue, TwoQueues) +{ + Lock m_lock; + Condition m_testQueue1Completed, m_testQueue2Completed; + Vector<std::string> m_functionCallOrder; + + bool calledSimpleTest = false; + bool calledLongTest = false; + bool calledThirdTest = false; + + auto queue1 = WorkQueue::create("com.apple.WebKit.Test.twoQueues1"); + auto queue2 = WorkQueue::create("com.apple.WebKit.Test.twoQueues2"); + + EXPECT_EQ(1, queue1->refCount()); + EXPECT_EQ(1, queue2->refCount()); + + LockHolder locker(m_lock); + + queue1->dispatch([&](void) { + m_functionCallOrder.append(simpleTestLabel); + calledSimpleTest = true; + }); + + queue2->dispatch([&](void) { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + LockHolder locker(m_lock); + + // Will fail if queue2 took the mutex before queue1. + EXPECT_TRUE(calledThirdTest); + + m_functionCallOrder.append(longTestLabel); + calledLongTest = true; + m_testQueue2Completed.notifyOne(); + }); + + queue1->dispatch([&](void) { + LockHolder locker(m_lock); + m_functionCallOrder.append(thirdTestLabel); + calledThirdTest = true; + + m_testQueue1Completed.notifyOne(); + }); + + m_testQueue1Completed.wait(m_lock); + + EXPECT_TRUE(calledSimpleTest); + EXPECT_FALSE(calledLongTest); + EXPECT_TRUE(calledThirdTest); + + m_testQueue2Completed.wait(m_lock); + + EXPECT_TRUE(calledSimpleTest); + EXPECT_TRUE(calledLongTest); + EXPECT_TRUE(calledThirdTest); + + EXPECT_EQ(static_cast<size_t>(3), m_functionCallOrder.size()); + EXPECT_STREQ(simpleTestLabel, m_functionCallOrder[0].c_str()); + EXPECT_STREQ(thirdTestLabel, m_functionCallOrder[1].c_str()); + EXPECT_STREQ(longTestLabel, m_functionCallOrder[2].c_str()); +} + +TEST(WTF_WorkQueue, DispatchAfter) +{ + Lock m_lock; + Condition m_testCompleted, m_dispatchAfterTestCompleted; + Vector<std::string> m_functionCallOrder; + + bool calledSimpleTest = false; + bool calledDispatchAfterTest = false; + + auto queue = WorkQueue::create("com.apple.WebKit.Test.dispatchAfter"); + + LockHolder locker(m_lock); + + queue->dispatch([&](void) { + LockHolder locker(m_lock); + m_functionCallOrder.append(simpleTestLabel); + calledSimpleTest = true; + m_testCompleted.notifyOne(); + }); + + queue->dispatchAfter(std::chrono::milliseconds(500), [&](void) { + LockHolder locker(m_lock); + m_functionCallOrder.append(dispatchAfterLabel); + calledDispatchAfterTest = true; + m_dispatchAfterTestCompleted.notifyOne(); + }); + + m_testCompleted.wait(m_lock); + + EXPECT_TRUE(calledSimpleTest); + EXPECT_FALSE(calledDispatchAfterTest); + + m_dispatchAfterTestCompleted.wait(m_lock); + + EXPECT_TRUE(calledSimpleTest); + EXPECT_TRUE(calledDispatchAfterTest); + + EXPECT_EQ(static_cast<size_t>(2), m_functionCallOrder.size()); + EXPECT_STREQ(simpleTestLabel, m_functionCallOrder[0].c_str()); + EXPECT_STREQ(dispatchAfterLabel, m_functionCallOrder[1].c_str()); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/darwin/OSObjectPtr.cpp b/Tools/TestWebKitAPI/Tests/WTF/darwin/OSObjectPtr.cpp new file mode 100644 index 000000000..5389dcbc8 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/darwin/OSObjectPtr.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2014 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 <wtf/OSObjectPtr.h> + +#include <dispatch/dispatch.h> +#include <CoreFoundation/CoreFoundation.h> + +namespace TestWebKitAPI { + +TEST(OSObjectPtr, AdoptOSObject) +{ + OSObjectPtr<dispatch_queue_t> foo = adoptOSObject(dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL)); + + EXPECT_EQ(1, CFGetRetainCount(foo.get())); +} + +TEST(OSObjectPtr, RetainRelease) +{ + dispatch_queue_t foo = dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL); + EXPECT_EQ(1, CFGetRetainCount(foo)); + + WTF::retainOSObject(foo); + EXPECT_EQ(2, CFGetRetainCount(foo)); + + WTF::releaseOSObject(foo); + EXPECT_EQ(1, CFGetRetainCount(foo)); +} + +TEST(OSObjectPtr, LeakRef) +{ + OSObjectPtr<dispatch_queue_t> foo = adoptOSObject(dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL)); + EXPECT_EQ(1, CFGetRetainCount(foo.get())); + + dispatch_queue_t queue = foo.leakRef(); + EXPECT_EQ(nullptr, foo.get()); + EXPECT_EQ(1, CFGetRetainCount(queue)); + + WTF::releaseOSObject(queue); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/glib/GMainLoopSource.cpp b/Tools/TestWebKitAPI/Tests/WTF/glib/GMainLoopSource.cpp new file mode 100644 index 000000000..54991eeeb --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/glib/GMainLoopSource.cpp @@ -0,0 +1,547 @@ +/* + * Copyright (C) 2014 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" + +#include <wtf/glib/GThreadSafeMainLoopSource.h> +#include <stdio.h> + +namespace TestWebKitAPI { + +template <typename T> +class GMainLoopSourceTest { +public: + GMainLoopSourceTest() + : m_mainLoop(g_main_loop_new(nullptr, TRUE)) + { + } + + ~GMainLoopSourceTest() + { + g_main_loop_unref(m_mainLoop); + } + + void runLoop() + { + g_main_loop_run(m_mainLoop); + } + + void delayedFinish() + { + g_timeout_add(250, + [](gpointer data) { + GMainLoopSourceTest& test = *static_cast<GMainLoopSourceTest*>(data); + test.finish(); + return G_SOURCE_REMOVE; + }, this); + } + + void finish() + { + g_main_loop_quit(m_mainLoop); + } + + T& source() { return m_source; } + +private: + GMainLoop* m_mainLoop; + T m_source; +}; + +template <typename T> +static void basicRescheduling(T& context) +{ + EXPECT_TRUE(!context.test.source().isActive()); + + context.test.source().schedule("[Test] FirstTask", [&] { + // This should never be called. That's why we assert + // that the variable is false a few lines later. + context.finishedFirstTask = true; + }); + EXPECT_TRUE(context.test.source().isScheduled()); + + context.test.source().schedule("[Test] SecondTask", [&] { + EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled()); + context.finishedSecondTask = true; + context.test.finish(); + }); + EXPECT_TRUE(context.test.source().isScheduled()); + + context.test.runLoop(); + + EXPECT_TRUE(!context.test.source().isActive()); + EXPECT_FALSE(context.finishedFirstTask); + EXPECT_TRUE(context.finishedSecondTask); +} + +TEST(WTF_GMainLoopSource, BasicRescheduling) +{ + struct TestingContext { + GMainLoopSourceTest<GMainLoopSource> test; + bool finishedFirstTask = false; + bool finishedSecondTask = false; + } context; + basicRescheduling<TestingContext>(context); + + struct ThreadSafeTestingContext { + GMainLoopSourceTest<GThreadSafeMainLoopSource> test; + bool finishedFirstTask = false; + bool finishedSecondTask = false; + } threadSafeContext; + basicRescheduling<ThreadSafeTestingContext>(threadSafeContext); +} + +template <typename T> +static void reentrantRescheduling(T& context) +{ + EXPECT_TRUE(!context.test.source().isActive()); + + context.test.source().schedule("[Test] FirstTask", [&] { + EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled()); + + context.test.source().schedule("[Test] SecondTask", [&] { + EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled()); + EXPECT_TRUE(context.finishedFirstTask); + + context.finishedSecondTask = true; + context.test.finish(); + }); + EXPECT_TRUE(context.test.source().isScheduled()); + + context.finishedFirstTask = true; + }); + EXPECT_TRUE(context.test.source().isScheduled()); + + context.test.runLoop(); + + EXPECT_TRUE(!context.test.source().isActive()); + EXPECT_TRUE(context.finishedFirstTask); + EXPECT_TRUE(context.finishedSecondTask); +} + +TEST(WTF_GMainLoopSource, ReentrantRescheduling) +{ + struct TestingContext { + GMainLoopSourceTest<GMainLoopSource> test; + bool finishedFirstTask = false; + bool finishedSecondTask = false; + } context; + reentrantRescheduling<TestingContext>(context); + + struct ThreadSafeTestingContext { + GMainLoopSourceTest<GThreadSafeMainLoopSource> test; + bool finishedFirstTask = false; + bool finishedSecondTask = false; + } threadSafeContext; + reentrantRescheduling<ThreadSafeTestingContext>(threadSafeContext); +} + +TEST(WTF_GMainLoopSource, ReschedulingFromDifferentThread) +{ + struct TestingContext { + GMainLoopSourceTest<GThreadSafeMainLoopSource> test; + bool finishedFirstTask; + bool finishedSecondTask; + } context; + + EXPECT_TRUE(!context.test.source().isActive()); + + context.test.source().schedule("[Test] FirstTask", [&] { + EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled()); + + g_usleep(1 * G_USEC_PER_SEC); + context.finishedFirstTask = true; + }); + EXPECT_TRUE(context.test.source().isScheduled()); + + GThread* helperThread = g_thread_new(nullptr, [](gpointer data) -> gpointer { + g_usleep(0.25 * G_USEC_PER_SEC); + + TestingContext& context = *static_cast<TestingContext*>(data); + EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled()); + EXPECT_FALSE(context.finishedFirstTask); + + context.test.source().schedule("[Test] SecondTask", [&] { + EXPECT_TRUE(context.finishedFirstTask); + + context.finishedSecondTask = true; + context.test.finish(); + }); + EXPECT_TRUE(context.test.source().isScheduled()); + + g_thread_exit(nullptr); + return nullptr; + }, &context); + + context.test.runLoop(); + g_thread_unref(helperThread); + + EXPECT_TRUE(!context.test.source().isActive()); + EXPECT_TRUE(context.finishedFirstTask); + EXPECT_TRUE(context.finishedSecondTask); +} + +TEST(WTF_GMainLoopSource, DestructionDuringDispatch) +{ + // This is just a raw test that ensures deleting the GMainLoopSource object during + // dispatch does not cause problems. This test succeeds if it doesn't crash. + + GMainLoopSource* source; + GMainLoop* loop = g_main_loop_new(nullptr, TRUE); + + source = new GMainLoopSource; + source->schedule("[Test] DestroySourceTask", [&] { + delete source; + g_main_loop_quit(loop); + }); + g_main_loop_run(loop); + + source = new GMainLoopSource; + source->schedule("[Test] DestroySourceTask", std::function<bool ()>([&] { + delete source; + g_main_loop_quit(loop); + return false; + })); + g_main_loop_run(loop); + + g_main_loop_unref(loop); +} + +template <typename T> +static void cancelRepeatingSourceDuringDispatch(T& context) +{ + EXPECT_TRUE(!context.test.source().isActive()); + + context.test.source().schedule("[Test] RepeatingTask", + std::function<bool ()>([&] { + EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled()); + + context.callCount++; + if (context.callCount == 3) + context.test.source().cancel(); + return true; + })); + EXPECT_TRUE(context.test.source().isScheduled()); + + context.test.delayedFinish(); + context.test.runLoop(); + + EXPECT_TRUE(!context.test.source().isActive()); + EXPECT_EQ(3, context.callCount); +} + +TEST(WTF_GMainLoopSource, CancelRepeatingSourceDuringDispatch) +{ + struct TestingContext { + GMainLoopSourceTest<GMainLoopSource> test; + unsigned callCount = 0; + } context; + cancelRepeatingSourceDuringDispatch<TestingContext>(context); + + struct ThreadSafeTestingContext { + GMainLoopSourceTest<GThreadSafeMainLoopSource> test; + unsigned callCount = 0; + } threadSafeContext; + cancelRepeatingSourceDuringDispatch<ThreadSafeTestingContext>(threadSafeContext); +} + +template <typename T> +static void basicDestroyCallbacks() +{ + { + T context; + EXPECT_TRUE(!context.test.source().isActive()); + context.test.source().schedule("[Test] DestroyCallback", + [&] { + EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled()); + context.callbackCalled = true; + }, G_PRIORITY_DEFAULT, + [&] { + EXPECT_TRUE(!context.test.source().isActive()); + context.destroyCallbackCalled = true; + context.test.finish(); + }); + EXPECT_TRUE(context.test.source().isScheduled()); + + context.test.runLoop(); + + EXPECT_TRUE(!context.test.source().isActive()); + EXPECT_TRUE(context.callbackCalled); + EXPECT_TRUE(context.destroyCallbackCalled); + } + + { + T context; + EXPECT_TRUE(!context.test.source().isActive()); + context.test.source().schedule("[Test] DestroyCallback", + std::function<bool ()>([&] { + EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled()); + context.callbackCalled = true; + return false; + }), G_PRIORITY_DEFAULT, + [&] { + EXPECT_TRUE(!context.test.source().isActive()); + context.destroyCallbackCalled = true; + context.test.finish(); + }); + EXPECT_TRUE(context.test.source().isScheduled()); + + context.test.runLoop(); + + EXPECT_TRUE(!context.test.source().isActive()); + EXPECT_TRUE(context.callbackCalled); + EXPECT_TRUE(context.destroyCallbackCalled); + } +} + +TEST(WTF_GMainLoopSource, BasicDestroyCallbacks) +{ + struct TestingContext { + GMainLoopSourceTest<GMainLoopSource> test; + bool callbackCalled = false; + bool destroyCallbackCalled = false; + }; + basicDestroyCallbacks<TestingContext>(); + + struct ThreadSafeTestingContext { + GMainLoopSourceTest<GThreadSafeMainLoopSource> test; + bool callbackCalled = false; + bool destroyCallbackCalled = false; + }; + basicDestroyCallbacks<ThreadSafeTestingContext>(); +} + +template <typename T> +static void destroyCallbacksAfterCancellingDuringDispatch() +{ + { + T context; + EXPECT_TRUE(!context.test.source().isActive()); + context.test.source().schedule("[Test] DestroyCallback", + [&] { + EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled()); + context.callbackCallCount++; + context.test.source().cancel(); + }, G_PRIORITY_DEFAULT, + [&] { + EXPECT_TRUE(!context.test.source().isActive()); + context.destroyCallbackCalled = true; + context.test.finish(); + }); + EXPECT_TRUE(context.test.source().isScheduled()); + + context.test.delayedFinish(); + context.test.runLoop(); + + EXPECT_TRUE(!context.test.source().isActive()); + EXPECT_EQ(1, context.callbackCallCount); + EXPECT_TRUE(context.destroyCallbackCalled); + } + + { + T context; + EXPECT_TRUE(!context.test.source().isActive()); + context.test.source().schedule("[Test] DestroyCallback", + std::function<bool ()>([&] { + EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled()); + context.callbackCallCount++; + if (context.callbackCallCount == 3) + context.test.source().cancel(); + return true; + }), G_PRIORITY_DEFAULT, + [&] { + EXPECT_TRUE(!context.test.source().isActive()); + context.destroyCallbackCalled = true; + }); + EXPECT_TRUE(context.test.source().isScheduled()); + + context.test.delayedFinish(); + context.test.runLoop(); + + EXPECT_TRUE(!context.test.source().isActive()); + EXPECT_EQ(3, context.callbackCallCount); + EXPECT_TRUE(context.destroyCallbackCalled); + } +} + +TEST(WTF_GMainLoopSource, DestroyCallbacksAfterCancellingDuringDispatch) +{ + struct TestingContext { + GMainLoopSourceTest<GMainLoopSource> test; + unsigned callbackCallCount= 0; + bool destroyCallbackCalled = false; + }; + destroyCallbacksAfterCancellingDuringDispatch<TestingContext>(); + + struct ThreadSafeTestingContext { + GMainLoopSourceTest<GThreadSafeMainLoopSource> test; + unsigned callbackCallCount= 0; + bool destroyCallbackCalled = false; + }; + destroyCallbacksAfterCancellingDuringDispatch<ThreadSafeTestingContext>(); +} + +template <typename T> +static void destroyCallbacksAfterReschedulingDuringDispatch() +{ + { + T context; + EXPECT_TRUE(!context.test.source().isActive()); + context.test.source().schedule("[Test] BaseCallback", + [&] { + EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled()); + context.firstCallbackCallCount++; + context.test.source().schedule("[Test] ReschedulingCallback", + [&] { + EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled()); + context.secondCallbackCallCount++; + }, G_PRIORITY_DEFAULT, + [&] { + EXPECT_TRUE(!context.test.source().isActive()); + context.secondDestroyCallbackCalled = true; + }); + EXPECT_TRUE(context.test.source().isScheduled()); + }, G_PRIORITY_DEFAULT, + [&] { + // At this point the GMainLoopSource has been rescheduled, ergo the Scheduled status. + EXPECT_TRUE(context.test.source().isScheduled()); + context.firstDestroyCallbackCalled = true; + }); + EXPECT_TRUE(context.test.source().isScheduled()); + + context.test.delayedFinish(); + context.test.runLoop(); + + EXPECT_TRUE(!context.test.source().isActive()); + EXPECT_EQ(1, context.firstCallbackCallCount); + EXPECT_TRUE(context.firstDestroyCallbackCalled); + EXPECT_EQ(1, context.secondCallbackCallCount); + EXPECT_TRUE(context.secondDestroyCallbackCalled); + } + + { + T context; + EXPECT_TRUE(!context.test.source().isActive()); + context.test.source().schedule("[Test] BaseCallback", + std::function<bool ()>([&] { + EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled()); + context.firstCallbackCallCount++; + context.test.source().schedule("[Test] ReschedulingCallback", + std::function<bool ()>([&] { + EXPECT_TRUE(context.test.source().isActive() && !context.test.source().isScheduled()); + context.secondCallbackCallCount++; + return context.secondCallbackCallCount != 3; + }), G_PRIORITY_DEFAULT, + [&] { + EXPECT_TRUE(!context.test.source().isActive()); + context.secondDestroyCallbackCalled = true; + }); + EXPECT_TRUE(context.test.source().isScheduled()); + return true; + }), G_PRIORITY_DEFAULT, + [&] { + // At this point the GMainLoopSource has been rescheduled, ergo the Scheduled status. + EXPECT_TRUE(context.test.source().isScheduled()); + context.firstDestroyCallbackCalled = true; + }); + EXPECT_TRUE(context.test.source().isScheduled()); + + context.test.delayedFinish(); + context.test.runLoop(); + + EXPECT_TRUE(!context.test.source().isActive()); + EXPECT_EQ(1, context.firstCallbackCallCount); + EXPECT_TRUE(context.firstDestroyCallbackCalled); + EXPECT_EQ(3, context.secondCallbackCallCount); + EXPECT_TRUE(context.secondDestroyCallbackCalled); + } +} + +TEST(WTF_GMainLoopSource, DestroyCallbacksAfterReschedulingDuringDispatch) +{ + struct TestingContext { + GMainLoopSourceTest<GMainLoopSource> test; + unsigned firstCallbackCallCount = 0; + bool firstDestroyCallbackCalled = false; + unsigned secondCallbackCallCount = 0; + bool secondDestroyCallbackCalled = false; + }; + destroyCallbacksAfterReschedulingDuringDispatch<TestingContext>(); + + struct ThreadSafeTestingContext { + GMainLoopSourceTest<GThreadSafeMainLoopSource> test; + unsigned firstCallbackCallCount = 0; + bool firstDestroyCallbackCalled = false; + unsigned secondCallbackCallCount = 0; + bool secondDestroyCallbackCalled = false; + }; + destroyCallbacksAfterReschedulingDuringDispatch<ThreadSafeTestingContext>(); +} + +TEST(WTF_GMainLoopSource, DeleteOnDestroySources) +{ + // Testing the delete-on-destroy sources is very limited. There's no good way + // of testing that the GMainLoopSource objects are deleted when their GSource + // is destroyed. + + struct TestingContext { + GMainLoopSourceTest<GMainLoopSource> test; + unsigned callbackCallCount = 0; + bool destroyCallbackCalled = false; + } context; + + { + TestingContext context; + + GMainLoopSource::scheduleAndDeleteOnDestroy("[Test] DeleteOnDestroy", + [&] { + context.callbackCallCount++; + }, G_PRIORITY_DEFAULT, + [&] { + EXPECT_FALSE(context.destroyCallbackCalled); + context.destroyCallbackCalled = true; + }); + + context.test.delayedFinish(); + context.test.runLoop(); + EXPECT_EQ(1, context.callbackCallCount); + EXPECT_TRUE(context.destroyCallbackCalled); + } + + { + TestingContext context; + + GMainLoopSource::scheduleAndDeleteOnDestroy("[Test] DeleteOnDestroy", + std::function<bool ()>([&] { + context.callbackCallCount++; + return context.callbackCallCount != 3; + }), G_PRIORITY_DEFAULT, + [&] { + EXPECT_FALSE(context.destroyCallbackCalled); + context.destroyCallbackCalled = true; + }); + + context.test.delayedFinish(); + context.test.runLoop(); + EXPECT_EQ(3, context.callbackCallCount); + EXPECT_TRUE(context.destroyCallbackCalled); + } +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/glib/GUniquePtr.cpp b/Tools/TestWebKitAPI/Tests/WTF/glib/GUniquePtr.cpp new file mode 100644 index 000000000..11f02c7b0 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/glib/GUniquePtr.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2014 Igalia S.L. + * + * 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 <gio/gio.h> + +inline std::ostringstream& log() +{ + static std::ostringstream log; + return log; +} + +inline std::string takeLogStr() +{ + std::string string = log().str(); + log().str(""); + return string; +} + +static void (* _g_free)(void*) = g_free; +#define g_free(x) \ + log() << "g_free(" << ptr << ");"; \ + _g_free(x); + +static void (* _g_error_free)(GError*) = g_error_free; +#define g_error_free(x) \ + log() << "g_error_free(" << ptr << ");"; \ + _g_error_free(x); + +static void (* _g_list_free)(GList*) = g_list_free; +#define g_list_free(x) \ + log() << "g_list_free(" << ptr << ");"; \ + _g_list_free(x); + +static void (* _g_slist_free)(GSList*) = g_slist_free; +#define g_slist_free(x) \ + log() << "g_slist_free(" << ptr << ");"; \ + _g_slist_free(x); + +static void (* _g_pattern_spec_free)(GPatternSpec*) = g_pattern_spec_free; +#define g_pattern_spec_free(x) \ + log() << "g_pattern_spec_free(" << ptr << ");"; \ + _g_pattern_spec_free(x); + +static void (* _g_dir_close)(GDir*) = g_dir_close; +#define g_dir_close(x) \ + log() << "g_dir_close(" << ptr << ");"; \ + _g_dir_close(x); + +static void (* _g_timer_destroy)(GTimer*) = g_timer_destroy; +#define g_timer_destroy(x) \ + log() << "g_timer_destroy(" << ptr << ");"; \ + _g_timer_destroy(x); + +static void (* _g_key_file_free)(GKeyFile*) = g_key_file_free; +#define g_key_file_free(x) \ + log() << "g_key_file_free(" << ptr << ");"; \ + _g_key_file_free(x); + +#include <wtf/glib/GUniquePtr.h> + +namespace TestWebKitAPI { + +TEST(WTF_GUniquePtr, Basic) +{ + std::ostringstream actual; + + { + GUniquePtr<char> a(g_strdup("a")); + actual << "g_free(" << a.get() << ");"; + } + ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str()); + actual.str(""); + + { + GUniquePtr<GError> a(g_error_new_literal(G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "a")); + actual << "g_error_free(" << a.get() << ");"; + } + ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str()); + actual.str(""); + + { + GUniquePtr<GList> a(g_list_prepend(nullptr, g_strdup("a"))); + actual << "g_list_free(" << a.get() << ");"; + } + ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str()); + actual.str(""); + + { + GUniquePtr<GSList> a(g_slist_prepend(nullptr, g_strdup("a"))); + actual << "g_slist_free(" << a.get() << ");"; + } + ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str()); + actual.str(""); + + { + GUniquePtr<GPatternSpec> a(g_pattern_spec_new("a")); + actual << "g_pattern_spec_free(" << a.get() << ");"; + } + ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str()); + actual.str(""); + + { + GUniquePtr<GDir> a(g_dir_open("/tmp", 0, nullptr)); + actual << "g_dir_close(" << a.get() << ");"; + } + ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str()); + actual.str(""); + + { + GUniquePtr<GTimer> a(g_timer_new()); + actual << "g_timer_destroy(" << a.get() << ");"; + } + ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str()); + actual.str(""); + + { + GUniquePtr<GKeyFile> a(g_key_file_new()); + actual << "g_key_file_free(" << a.get() << ");"; + } + ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str()); + actual.str(""); +} + +static void returnOutChar(char** outChar) +{ + *outChar = g_strdup("a"); +} + +TEST(WTF_GUniquePtr, OutPtr) +{ + std::ostringstream actual; + + { + GUniqueOutPtr<char> a; + ASSERT_EQ(nullptr, a.get()); + } + ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str()); + actual.str(""); + + { + GUniqueOutPtr<char> a; + returnOutChar(&a.outPtr()); + actual << "g_free(" << a.get() << ");"; + } + ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str()); + actual.str(""); + + { + GUniqueOutPtr<char> a; + returnOutChar(&a.outPtr()); + actual << "g_free(" << a.get() << ");"; + returnOutChar(&a.outPtr()); + ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str()); + actual.str(""); + actual << "g_free(" << a.get() << ");"; + } + ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str()); + actual.str(""); + + { + GUniqueOutPtr<char> a; + returnOutChar(&a.outPtr()); + GUniquePtr<char> b = a.release(); + ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str()); + actual << "g_free(" << b.get() << ");"; + } + ASSERT_STREQ(actual.str().c_str(), takeLogStr().c_str()); + actual.str(""); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/glib/WorkQueueGLib.cpp b/Tools/TestWebKitAPI/Tests/WTF/glib/WorkQueueGLib.cpp new file mode 100644 index 000000000..9923d6580 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/glib/WorkQueueGLib.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015 Igalia S.L. + * + * 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 "Test.h" +#include <gio/gio.h> +#include <thread> +#include <wtf/Condition.h> +#include <wtf/Lock.h> +#include <wtf/WorkQueue.h> +#include <wtf/glib/GRefPtr.h> +#include <wtf/glib/GUniquePtr.h> + +namespace TestWebKitAPI { + +TEST(WTF_WorkQueue, AsyncIO) +{ + struct TestingContext { + Lock m_lock; + Condition m_testCompleted; + GMainContext* m_mainContext; + } context; + + auto queue = WorkQueue::create("com.apple.WebKit.Test.AsyncIO"); + context.m_mainContext = g_main_context_default(); + EXPECT_FALSE(g_main_context_get_thread_default()); + + GUniquePtr<char> currentDirectory(g_get_current_dir()); + GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(currentDirectory.get())); + + LockHolder locker(context.m_lock); + queue->dispatch([&](void) { + EXPECT_TRUE(g_main_context_get_thread_default()); + EXPECT_TRUE(g_main_context_get_thread_default() != context.m_mainContext); + context.m_mainContext = g_main_context_get_thread_default(); + g_file_query_info_async(file.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, nullptr, + [](GObject*, GAsyncResult*, gpointer userData) { + TestingContext* context = static_cast<TestingContext*>(userData); + LockHolder locker(context->m_lock); + EXPECT_EQ(g_main_context_get_thread_default(), context->m_mainContext); + context->m_testCompleted.notifyOne(); + }, &context); + }); + + context.m_testCompleted.wait(context.m_lock); +} + +} // namespace TestWebKitAPI |