diff options
Diffstat (limited to 'Tools/TestWebKitAPI/Tests/WTF')
35 files changed, 6517 insertions, 217 deletions
diff --git a/Tools/TestWebKitAPI/Tests/WTF/AtomicString.cpp b/Tools/TestWebKitAPI/Tests/WTF/AtomicString.cpp index 9df71ce44..de962ec42 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/AtomicString.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/AtomicString.cpp @@ -39,7 +39,6 @@ TEST(WTF, AtomicStringCreationFromLiteral) const char* programmaticStringData = "Explicit Size Literal"; AtomicString programmaticString(programmaticStringData, strlen(programmaticStringData), AtomicString::ConstructFromLiteral); ASSERT_EQ(strlen(programmaticStringData), programmaticString.length()); - ASSERT_TRUE(programmaticStringData == programmaticStringData); ASSERT_TRUE(programmaticString.string().is8Bit()); ASSERT_EQ(programmaticStringData, reinterpret_cast<const char*>(programmaticString.string().characters8())); } @@ -54,4 +53,12 @@ TEST(WTF, AtomicStringCreationFromLiteralUniqueness) ASSERT_EQ(string1.impl(), string3.impl()); } +TEST(WTF, AtomicStringExistingHash) +{ + AtomicString string1("Template Literal", AtomicString::ConstructFromLiteral); + ASSERT_EQ(string1.existingHash(), string1.impl()->existingHash()); + AtomicString string2; + ASSERT_EQ(string2.existingHash(), 0u); +} + } // 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/CheckedArithmeticOperations.cpp b/Tools/TestWebKitAPI/Tests/WTF/CheckedArithmeticOperations.cpp index 77b8ff458..d6b548316 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/CheckedArithmeticOperations.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/CheckedArithmeticOperations.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Apple Inc. All rights reserved. + * 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 @@ -28,117 +28,397 @@ namespace TestWebKitAPI { -#define CheckedArithmeticTest(type, coerceLiteral, MixedSignednessTest) \ +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) \ { \ - 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()); \ - MixedSignednessTest(EXPECT_EQ(coerceLiteral(0), (value + -10).unsafeGet())); \ - MixedSignednessTest(EXPECT_EQ(0U, (value - 10U).unsafeGet())); \ - MixedSignednessTest(EXPECT_EQ(coerceLiteral(0), (-10 + value).unsafeGet())); \ - MixedSignednessTest(EXPECT_EQ(0U, (10U - value).unsafeGet())); \ - value = std::numeric_limits<type>::min(); \ - MixedSignednessTest(EXPECT_EQ(true, (Checked<type, RecordOverflow>(value - 1)).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, !(value--).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ - value = std::numeric_limits<type>::max(); \ - MixedSignednessTest(EXPECT_EQ(true, !value.hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, (Checked<type, RecordOverflow>(value + 1)).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, !(value++).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ - value = std::numeric_limits<type>::max(); \ - MixedSignednessTest(EXPECT_EQ(true, (value += 1).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ - value = std::numeric_limits<type>::min(); \ - MixedSignednessTest(EXPECT_EQ(true, (value - 1U).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, !(value--).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ - value = std::numeric_limits<type>::max(); \ - MixedSignednessTest(EXPECT_EQ(true, !value.hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, (Checked<type, RecordOverflow>(value + 1U)).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, !(value++).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ - value = std::numeric_limits<type>::max(); \ - MixedSignednessTest(EXPECT_EQ(true, (value += 1U).hasOverflowed())); \ - MixedSignednessTest(EXPECT_EQ(true, value.hasOverflowed())); \ + 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; } +}; -#define CoerceLiteralToUnsigned(x) x##U -#define CoerceLiteralNop(x) x -#define AllowMixedSignednessTest(x) x -#define IgnoreMixedSignednessTest(x) CheckedArithmeticTest(int8_t, CoerceLiteralNop, IgnoreMixedSignednessTest) CheckedArithmeticTest(int16_t, CoerceLiteralNop, IgnoreMixedSignednessTest) CheckedArithmeticTest(int32_t, CoerceLiteralNop, AllowMixedSignednessTest) @@ -146,4 +426,62 @@ CheckedArithmeticTest(uint32_t, CoerceLiteralToUnsigned, AllowMixedSignednessTes 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..83f1f82ee --- /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 = WTFMove(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 = WTFMove(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/HashCountedSet.cpp b/Tools/TestWebKitAPI/Tests/WTF/HashCountedSet.cpp new file mode 100644 index 000000000..4c1afd105 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/HashCountedSet.cpp @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * 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/HashCountedSet.h> +#include <wtf/text/StringHash.h> + +namespace TestWebKitAPI { + +typedef WTF::HashCountedSet<int> IntHashCountedSet; + +TEST(WTF_HashCountedSet, HashTableIteratorComparison) +{ + IntHashCountedSet hashCountedSet; + hashCountedSet.add(1); + ASSERT_TRUE(hashCountedSet.begin() != hashCountedSet.end()); + ASSERT_FALSE(hashCountedSet.begin() == hashCountedSet.end()); + + IntHashCountedSet::const_iterator begin = hashCountedSet.begin(); + ASSERT_TRUE(begin == hashCountedSet.begin()); + ASSERT_TRUE(hashCountedSet.begin() == begin); + ASSERT_TRUE(begin != hashCountedSet.end()); + ASSERT_TRUE(hashCountedSet.end() != begin); + ASSERT_FALSE(begin != hashCountedSet.begin()); + ASSERT_FALSE(hashCountedSet.begin() != begin); + ASSERT_FALSE(begin == hashCountedSet.end()); + ASSERT_FALSE(hashCountedSet.end() == begin); +} + +struct TestDoubleHashTraits : HashTraits<double> { + static const int minimumTableSize = 8; +}; + +typedef HashCountedSet<double, DefaultHash<double>::Hash, TestDoubleHashTraits> DoubleHashCountedSet; + +static int bucketForKey(double key) +{ + return DefaultHash<double>::Hash::hash(key) & (TestDoubleHashTraits::minimumTableSize - 1); +} + +TEST(WTF_HashCountedSet, DoubleHashCollisions) +{ + // The "clobber" key here is one that ends up stealing the bucket that the -0 key + // originally wants to be in. This makes the 0 and -0 keys collide and the test then + // fails unless the FloatHash::equals() implementation can distinguish them. + const double clobberKey = 6; + const double zeroKey = 0; + const double negativeZeroKey = -zeroKey; + + DoubleHashCountedSet hashCountedSet; + + hashCountedSet.add(clobberKey); + + ASSERT_EQ(hashCountedSet.count(clobberKey), 1u); + ASSERT_EQ(hashCountedSet.count(zeroKey), 0u); + ASSERT_EQ(hashCountedSet.count(negativeZeroKey), 0u); + + hashCountedSet.add(zeroKey); + hashCountedSet.add(negativeZeroKey); + + ASSERT_EQ(bucketForKey(clobberKey), bucketForKey(negativeZeroKey)); + ASSERT_EQ(hashCountedSet.count(clobberKey), 1u); + ASSERT_EQ(hashCountedSet.count(zeroKey), 1u); + ASSERT_EQ(hashCountedSet.count(negativeZeroKey), 1u); + + hashCountedSet.add(clobberKey); + hashCountedSet.add(zeroKey); + hashCountedSet.add(negativeZeroKey); + + ASSERT_EQ(hashCountedSet.count(clobberKey), 2u); + ASSERT_EQ(hashCountedSet.count(zeroKey), 2u); + ASSERT_EQ(hashCountedSet.count(negativeZeroKey), 2u); + + hashCountedSet.add(clobberKey, 12); + hashCountedSet.add(zeroKey, 15); + hashCountedSet.add(negativeZeroKey, 17); + + ASSERT_EQ(hashCountedSet.count(clobberKey), 14u); + ASSERT_EQ(hashCountedSet.count(zeroKey), 17u); + ASSERT_EQ(hashCountedSet.count(negativeZeroKey), 19u); +} + +TEST(WTF_HashCountedSet, DoubleHashCollisionsInitialCount) +{ + // The "clobber" key here is one that ends up stealing the bucket that the -0 key + // originally wants to be in. This makes the 0 and -0 keys collide and the test then + // fails unless the FloatHash::equals() implementation can distinguish them. + const double clobberKey = 6; + const double zeroKey = 0; + const double negativeZeroKey = -zeroKey; + + DoubleHashCountedSet hashCountedSet; + + hashCountedSet.add(clobberKey, 5); + + ASSERT_EQ(hashCountedSet.count(clobberKey), 5u); + ASSERT_EQ(hashCountedSet.count(zeroKey), 0u); + ASSERT_EQ(hashCountedSet.count(negativeZeroKey), 0u); + + hashCountedSet.add(zeroKey, 22); + hashCountedSet.add(negativeZeroKey, 0); + + ASSERT_EQ(bucketForKey(clobberKey), bucketForKey(negativeZeroKey)); + ASSERT_EQ(hashCountedSet.count(clobberKey), 5u); + ASSERT_EQ(hashCountedSet.count(zeroKey), 22u); + ASSERT_EQ(hashCountedSet.count(negativeZeroKey), 0u); + + hashCountedSet.add(clobberKey); + hashCountedSet.add(zeroKey); + hashCountedSet.add(negativeZeroKey); + + ASSERT_EQ(hashCountedSet.count(clobberKey), 6u); + ASSERT_EQ(hashCountedSet.count(zeroKey), 23u); + ASSERT_EQ(hashCountedSet.count(negativeZeroKey), 1u); +} + + +TEST(WTF_HashCountedSet, MoveOnlyKeys) +{ + HashCountedSet<MoveOnly> moveOnlyKeys; + + for (size_t i = 0; i < 100; ++i) { + MoveOnly moveOnly(i + 1); + moveOnlyKeys.add(WTFMove(moveOnly)); + } + + for (size_t i = 0; i < 100; ++i) { + auto it = moveOnlyKeys.find(MoveOnly(i + 1)); + ASSERT_FALSE(it == moveOnlyKeys.end()); + ASSERT_EQ(it->value, 1u); + } + + for (size_t i = 0; i < 100; ++i) + ASSERT_FALSE(moveOnlyKeys.add(MoveOnly(i + 1)).isNewEntry); + + for (size_t i = 0; i < 100; ++i) + ASSERT_FALSE(moveOnlyKeys.remove(MoveOnly(i + 1))); + + for (size_t i = 0; i < 100; ++i) + ASSERT_TRUE(moveOnlyKeys.remove(MoveOnly(i + 1))); + + ASSERT_TRUE(moveOnlyKeys.isEmpty()); +} + +TEST(WTF_HashCountedSet, MoveOnlyKeysInitialCount) +{ + HashCountedSet<MoveOnly> moveOnlyKeys; + + for (size_t i = 0; i < 100; ++i) { + MoveOnly moveOnly(i + 1); + moveOnlyKeys.add(WTFMove(moveOnly), i + 1); + } + + for (size_t i = 0; i < 100; ++i) { + auto it = moveOnlyKeys.find(MoveOnly(i + 1)); + ASSERT_FALSE(it == moveOnlyKeys.end()); + ASSERT_EQ(it->value, i + 1); + } + + for (size_t i = 0; i < 100; ++i) + ASSERT_FALSE(moveOnlyKeys.add(MoveOnly(i + 1)).isNewEntry); + + for (size_t i = 0; i < 100; ++i) + ASSERT_EQ(moveOnlyKeys.count(MoveOnly(i + 1)), i + 2); + + for (size_t i = 0; i < 100; ++i) + ASSERT_FALSE(moveOnlyKeys.remove(MoveOnly(i + 1))); + + for (size_t i = 0; i < 100; ++i) + ASSERT_EQ(moveOnlyKeys.count(MoveOnly(i + 1)), i + 1); +} + +TEST(WTF_HashCountedSet, InitializerList) +{ + HashCountedSet<String> hashCountedSet = { + "one", + "two", + "three", + "four", + "four", + "four", + "four", + }; + + EXPECT_EQ(4u, hashCountedSet.size()); + + EXPECT_EQ(hashCountedSet.count("one"), 1u); + EXPECT_EQ(hashCountedSet.count("two"), 1u); + EXPECT_EQ(hashCountedSet.count("three"), 1u); + EXPECT_EQ(hashCountedSet.count("four"), 4u); +} + +TEST(WTF_HashCountedSet, InitializerListInitialCount) +{ + HashCountedSet<String> hashCountedSet = { + { String("one"), 1u }, + { String("two"), 2u }, + { String("three"), 3u }, + { String("four"), 4u }, + }; + + EXPECT_EQ(4u, hashCountedSet.size()); + + EXPECT_EQ(hashCountedSet.count("one"), 1u); + EXPECT_EQ(hashCountedSet.count("two"), 2u); + EXPECT_EQ(hashCountedSet.count("three"), 3u); + EXPECT_EQ(hashCountedSet.count("four"), 4u); +} + +TEST(WTF_HashCountedSet, UniquePtrKey) +{ + ConstructorDestructorCounter::TestingScope scope; + + HashCountedSet<std::unique_ptr<ConstructorDestructorCounter>> hashCountedSet; + + auto uniquePtr = std::make_unique<ConstructorDestructorCounter>(); + hashCountedSet.add(WTFMove(uniquePtr)); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount); + + hashCountedSet.clear(); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount); +} + +TEST(WTF_HashCountedSet, UniquePtrKeyInitialCount) +{ + ConstructorDestructorCounter::TestingScope scope; + + HashCountedSet<std::unique_ptr<ConstructorDestructorCounter>> hashCountedSet; + + auto uniquePtr = std::make_unique<ConstructorDestructorCounter>(); + hashCountedSet.add(WTFMove(uniquePtr), 12); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount); + + hashCountedSet.clear(); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount); +} + +TEST(WTF_HashCountedSet, UniquePtrKey_CustomDeleter) +{ + ConstructorDestructorCounter::TestingScope constructorDestructorCounterScope; + DeleterCounter<ConstructorDestructorCounter>::TestingScope deleterCounterScope; + + HashCountedSet<std::unique_ptr<ConstructorDestructorCounter, DeleterCounter<ConstructorDestructorCounter>>> hashCountedSet; + + std::unique_ptr<ConstructorDestructorCounter, DeleterCounter<ConstructorDestructorCounter>> uniquePtr(new ConstructorDestructorCounter(), DeleterCounter<ConstructorDestructorCounter>()); + hashCountedSet.add(WTFMove(uniquePtr)); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount); + + EXPECT_EQ(0u, DeleterCounter<ConstructorDestructorCounter>::deleterCount); + + hashCountedSet.clear(); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount); + + EXPECT_EQ(1u, DeleterCounter<ConstructorDestructorCounter>::deleterCount); +} + +TEST(WTF_HashCountedSet, UniquePtrKey_FindUsingRawPointer) +{ + HashCountedSet<std::unique_ptr<int>> hashCountedSet; + + auto uniquePtr = std::make_unique<int>(5); + int* ptr = uniquePtr.get(); + hashCountedSet.add(WTFMove(uniquePtr)); + + auto it = hashCountedSet.find(ptr); + ASSERT_TRUE(it != hashCountedSet.end()); + EXPECT_EQ(ptr, it->key.get()); + EXPECT_EQ(1u, it->value); +} + +TEST(WTF_HashCountedSet, UniquePtrKey_ContainsUsingRawPointer) +{ + HashCountedSet<std::unique_ptr<int>> hashCountedSet; + + auto uniquePtr = std::make_unique<int>(5); + int* ptr = uniquePtr.get(); + hashCountedSet.add(WTFMove(uniquePtr)); + + EXPECT_EQ(true, hashCountedSet.contains(ptr)); +} + +TEST(WTF_HashCountedSet, UniquePtrKey_GetUsingRawPointer) +{ + HashCountedSet<std::unique_ptr<int>> hashCountedSet; + + auto uniquePtr = std::make_unique<int>(5); + int* ptr = uniquePtr.get(); + hashCountedSet.add(WTFMove(uniquePtr)); + + int value = hashCountedSet.count(ptr); + EXPECT_EQ(1, value); +} + +TEST(WTF_HashCountedSet, UniquePtrKey_RemoveUsingRawPointer) +{ + ConstructorDestructorCounter::TestingScope scope; + + HashCountedSet<std::unique_ptr<ConstructorDestructorCounter>> hashCountedSet; + + auto uniquePtr = std::make_unique<ConstructorDestructorCounter>(); + ConstructorDestructorCounter* ptr = uniquePtr.get(); + hashCountedSet.add(WTFMove(uniquePtr)); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(0u, ConstructorDestructorCounter::destructionCount); + + bool result = hashCountedSet.remove(ptr); + EXPECT_EQ(true, result); + + EXPECT_EQ(1u, ConstructorDestructorCounter::constructionCount); + EXPECT_EQ(1u, ConstructorDestructorCounter::destructionCount); +} + +TEST(WTF_HashCountedSet, RefPtrKey_Add) +{ + HashCountedSet<RefPtr<RefLogger>> hashCountedSet; + + DerivedRefLogger a("a"); + RefPtr<RefLogger> ptr(&a); + hashCountedSet.add(ptr); + + ASSERT_STREQ("ref(a) ref(a) ", takeLogStr().c_str()); + EXPECT_EQ(1U, hashCountedSet.count(ptr)); + EXPECT_EQ(1U, hashCountedSet.count(ptr.get())); +} + +TEST(WTF_HashCountedSet, RefPtrKey_AddUsingRelease) +{ + HashCountedSet<RefPtr<RefLogger>> hashCountedSet; + + DerivedRefLogger a("a"); + RefPtr<RefLogger> ptr(&a); + hashCountedSet.add(ptr.release()); + + EXPECT_STREQ("ref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_HashCountedSet, RefPtrKey_AddUsingMove) +{ + HashCountedSet<RefPtr<RefLogger>> hashCountedSet; + + DerivedRefLogger a("a"); + RefPtr<RefLogger> ptr(&a); + hashCountedSet.add(WTFMove(ptr)); + + EXPECT_STREQ("ref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_HashCountedSet, RefPtrKey_AddUsingRaw) +{ + HashCountedSet<RefPtr<RefLogger>> hashCountedSet; + + DerivedRefLogger a("a"); + RefPtr<RefLogger> ptr(&a); + hashCountedSet.add(ptr.get()); + + EXPECT_STREQ("ref(a) ref(a) ", takeLogStr().c_str()); + EXPECT_EQ(1U, hashCountedSet.count(ptr)); + EXPECT_EQ(1U, hashCountedSet.count(ptr.get())); +} + +TEST(WTF_HashCountedSet, RefPtrKey_AddKeyAlreadyPresent) +{ + HashCountedSet<RefPtr<RefLogger>> hashCountedSet; + + DerivedRefLogger a("a"); + + { + RefPtr<RefLogger> ptr(&a); + hashCountedSet.add(ptr); + } + + EXPECT_STREQ("ref(a) ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr2(&a); + auto addResult = hashCountedSet.add(ptr2); + EXPECT_FALSE(addResult.isNewEntry); + } + + EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_HashCountedSet, RefPtrKey_AddUsingReleaseKeyAlreadyPresent) +{ + HashCountedSet<RefPtr<RefLogger>> hashCountedSet; + + DerivedRefLogger a("a"); + + { + RefPtr<RefLogger> ptr(&a); + hashCountedSet.add(ptr); + } + + EXPECT_STREQ("ref(a) ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr2(&a); + auto addResult = hashCountedSet.add(ptr2.release()); + EXPECT_FALSE(addResult.isNewEntry); + } + + EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); +} + +TEST(WTF_HashCountedSet, RefPtrKey_AddUsingMoveKeyAlreadyPresent) +{ + HashCountedSet<RefPtr<RefLogger>> hashCountedSet; + + DerivedRefLogger a("a"); + + { + RefPtr<RefLogger> ptr(&a); + hashCountedSet.add(ptr); + } + + EXPECT_STREQ("ref(a) ref(a) deref(a) ", takeLogStr().c_str()); + + { + RefPtr<RefLogger> ptr2(&a); + auto addResult = hashCountedSet.add(WTFMove(ptr2)); + EXPECT_FALSE(addResult.isNewEntry); + } + + EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/HashMap.cpp b/Tools/TestWebKitAPI/Tests/WTF/HashMap.cpp index d03fb9594..145edae1a 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/HashMap.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/HashMap.cpp @@ -25,7 +25,9 @@ #include "config.h" +#include "Counters.h" #include "MoveOnly.h" +#include "RefLogger.h" #include <string> #include <wtf/HashMap.h> #include <wtf/text/StringHash.h> @@ -90,7 +92,7 @@ TEST(WTF_HashMap, MoveOnlyValues) for (size_t i = 0; i < 100; ++i) { MoveOnly moveOnly(i + 1); - moveOnlyValues.set(i + 1, std::move(moveOnly)); + moveOnlyValues.set(i + 1, WTFMove(moveOnly)); } for (size_t i = 0; i < 100; ++i) { @@ -113,7 +115,7 @@ TEST(WTF_HashMap, MoveOnlyKeys) for (size_t i = 0; i < 100; ++i) { MoveOnly moveOnly(i + 1); - moveOnlyKeys.set(std::move(moveOnly), i + 1); + moveOnlyKeys.set(WTFMove(moveOnly), i + 1); } for (size_t i = 0; i < 100; ++i) { @@ -139,7 +141,7 @@ TEST(WTF_HashMap, InitializerList) { 4, "four" }, }; - EXPECT_EQ(4, map.size()); + EXPECT_EQ(4u, map.size()); EXPECT_EQ("one", map.get(1)); EXPECT_EQ("two", map.get(2)); @@ -148,4 +150,553 @@ TEST(WTF_HashMap, InitializerList) 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(WTFMove(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(WTFMove(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(WTFMove(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(WTFMove(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(WTFMove(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(WTFMove(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(WTFMove(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(WTFMove(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(WTFMove(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(WTFMove(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(WTFMove(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, Ensure) +{ + HashMap<unsigned, unsigned> map; + { + auto addResult = map.ensure(1, [] { return 1; }); + EXPECT_EQ(1u, addResult.iterator->value); + EXPECT_EQ(1u, addResult.iterator->key); + EXPECT_TRUE(addResult.isNewEntry); + auto addResult2 = map.ensure(1, [] { return 2; }); + EXPECT_EQ(1u, addResult2.iterator->value); + EXPECT_EQ(1u, addResult2.iterator->key); + EXPECT_FALSE(addResult2.isNewEntry); + } +} + +TEST(WTF_HashMap, Ensure_MoveOnlyValues) +{ + HashMap<unsigned, MoveOnly> moveOnlyValues; + { + auto addResult = moveOnlyValues.ensure(1, [] { return MoveOnly(1); }); + EXPECT_EQ(1u, addResult.iterator->value.value()); + EXPECT_EQ(1u, addResult.iterator->key); + EXPECT_TRUE(addResult.isNewEntry); + auto addResult2 = moveOnlyValues.ensure(1, [] { return MoveOnly(2); }); + EXPECT_EQ(1u, addResult2.iterator->value.value()); + EXPECT_EQ(1u, addResult2.iterator->key); + EXPECT_FALSE(addResult2.isNewEntry); + } +} + +TEST(WTF_HashMap, Ensure_UniquePointer) +{ + HashMap<unsigned, std::unique_ptr<unsigned>> map; + { + auto addResult = map.ensure(1, [] { return std::make_unique<unsigned>(1); }); + EXPECT_EQ(1u, *map.get(1)); + EXPECT_EQ(1u, *addResult.iterator->value.get()); + EXPECT_EQ(1u, addResult.iterator->key); + EXPECT_TRUE(addResult.isNewEntry); + auto addResult2 = map.ensure(1, [] { return std::make_unique<unsigned>(2); }); + EXPECT_EQ(1u, *map.get(1)); + EXPECT_EQ(1u, *addResult2.iterator->value.get()); + EXPECT_EQ(1u, addResult2.iterator->key); + EXPECT_FALSE(addResult2.isNewEntry); + } +} + +TEST(WTF_HashMap, Ensure_RefPtr) +{ + HashMap<unsigned, RefPtr<RefLogger>> map; + + { + DerivedRefLogger a("a"); + + map.ensure(1, [&] { return RefPtr<RefLogger>(&a); }); + EXPECT_STREQ("ref(a) ", takeLogStr().c_str()); + + map.ensure(1, [&] { return RefPtr<RefLogger>(&a); }); + EXPECT_STREQ("", takeLogStr().c_str()); + } +} + +class ObjectWithRefLogger { +public: + ObjectWithRefLogger(Ref<RefLogger>&& logger) + : m_logger(WTFMove(logger)) + { + } + + Ref<RefLogger> m_logger; +}; + + +void testMovingUsingEnsure(Ref<RefLogger>&& logger) +{ + HashMap<unsigned, std::unique_ptr<ObjectWithRefLogger>> map; + + map.ensure(1, [&] { return std::make_unique<ObjectWithRefLogger>(WTFMove(logger)); }); +} + +void testMovingUsingAdd(Ref<RefLogger>&& logger) +{ + HashMap<unsigned, std::unique_ptr<ObjectWithRefLogger>> map; + + auto& slot = map.add(1, nullptr).iterator->value; + slot = std::make_unique<ObjectWithRefLogger>(WTFMove(logger)); +} + +TEST(WTF_HashMap, Ensure_LambdasCapturingByReference) +{ + { + DerivedRefLogger a("a"); + Ref<RefLogger> ref(a); + testMovingUsingEnsure(WTFMove(ref)); + + EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + } + + { + DerivedRefLogger a("a"); + Ref<RefLogger> ref(a); + testMovingUsingAdd(WTFMove(ref)); + + EXPECT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); + } +} + + +TEST(WTF_HashMap, ValueIsDestructedOnRemove) +{ + struct DestructorObserver { + DestructorObserver() = default; + + DestructorObserver(bool* destructed) + : destructed(destructed) + { + } + + ~DestructorObserver() + { + if (destructed) + *destructed = true; + } + + DestructorObserver(DestructorObserver&& other) + : destructed(other.destructed) + { + other.destructed = nullptr; + } + + DestructorObserver& operator=(DestructorObserver&& other) + { + destructed = other.destructed; + other.destructed = nullptr; + return *this; + } + + bool* destructed { nullptr }; + }; + + HashMap<int, DestructorObserver> map; + + bool destructed = false; + map.add(5, DestructorObserver { &destructed }); + + EXPECT_FALSE(destructed); + + bool removeResult = map.remove(5); + + EXPECT_TRUE(removeResult); + EXPECT_TRUE(destructed); +} + +TEST(WTF_HashMap, RefPtrNotZeroedBeforeDeref) +{ + struct DerefObserver { + NEVER_INLINE void ref() + { + ++count; + } + NEVER_INLINE void deref() + { + --count; + observedBucket = bucketAddress->get(); + } + unsigned count { 1 }; + const RefPtr<DerefObserver>* bucketAddress { nullptr }; + const DerefObserver* observedBucket { nullptr }; + }; + + auto observer = std::make_unique<DerefObserver>(); + + HashMap<RefPtr<DerefObserver>, int> map; + map.add(adoptRef(observer.get()), 5); + + auto iterator = map.find(observer.get()); + EXPECT_TRUE(iterator != map.end()); + + observer->bucketAddress = &iterator->key; + + EXPECT_TRUE(observer->observedBucket == nullptr); + EXPECT_TRUE(map.remove(observer.get())); + + // It if fine to either leave the old value intact at deletion or already set it to the deleted + // value. + // A zero would be a incorrect outcome as it would mean we nulled the bucket before an opaque + // call. + EXPECT_TRUE(observer->observedBucket == observer.get() || observer->observedBucket == RefPtr<DerefObserver>::hashTableDeletedValue()); + EXPECT_EQ(observer->count, 0u); +} + } // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/HashSet.cpp b/Tools/TestWebKitAPI/Tests/WTF/HashSet.cpp index 88e7d075f..1d6ffc039 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/HashSet.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/HashSet.cpp @@ -25,9 +25,10 @@ #include "config.h" +#include "Counters.h" #include "MoveOnly.h" #include <wtf/HashSet.h> - +#include <wtf/RefPtr.h> namespace TestWebKitAPI { @@ -43,7 +44,7 @@ void testInitialCapacity() HashSet<int, DefaultHash<int>::Hash, InitialCapacityTestHashTraits<initialCapacity> > testSet; // Initial capacity is null. - ASSERT_EQ(0, testSet.capacity()); + ASSERT_EQ(0u, testSet.capacity()); // Adding items up to size should never change the capacity. for (size_t i = 0; i < size; ++i) { @@ -84,7 +85,7 @@ TEST(WTF_HashSet, MoveOnly) for (size_t i = 0; i < 100; ++i) { MoveOnly moveOnly(i + 1); - hashSet.add(std::move(moveOnly)); + hashSet.add(WTFMove(moveOnly)); } for (size_t i = 0; i < 100; ++i) @@ -96,11 +97,256 @@ TEST(WTF_HashSet, MoveOnly) EXPECT_TRUE(hashSet.isEmpty()); for (size_t i = 0; i < 100; ++i) - hashSet.add(std::move(MoveOnly(i + 1))); + 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(WTFMove(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(WTFMove(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(WTFMove(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(WTFMove(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(WTFMove(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)); + } +} + +TEST(WTF_HashSet, RefPtrNotZeroedBeforeDeref) +{ + struct DerefObserver { + NEVER_INLINE void ref() + { + ++count; + } + NEVER_INLINE void deref() + { + --count; + observedBucket = bucketAddress->get(); + } + unsigned count { 1 }; + const RefPtr<DerefObserver>* bucketAddress { nullptr }; + const DerefObserver* observedBucket { nullptr }; + }; + + auto observer = std::make_unique<DerefObserver>(); + + HashSet<RefPtr<DerefObserver>> set; + set.add(adoptRef(observer.get())); + + auto iterator = set.find(observer.get()); + EXPECT_TRUE(iterator != set.end()); + + observer->bucketAddress = iterator.get(); + + EXPECT_TRUE(observer->observedBucket == nullptr); + EXPECT_TRUE(set.remove(observer.get())); + + // It if fine to either leave the old value intact at deletion or already set it to the deleted + // value. + // A zero would be a incorrect outcome as it would mean we nulled the bucket before an opaque + // call. + EXPECT_TRUE(observer->observedBucket == observer.get() || observer->observedBucket == RefPtr<DerefObserver>::hashTableDeletedValue()); + EXPECT_EQ(observer->count, 0u); +} + + +TEST(WTF_HashSet, UniquePtrNotZeroedBeforeDestructor) +{ + struct DestructorObserver { + ~DestructorObserver() + { + observe(); + } + std::function<void()> observe; + }; + + const std::unique_ptr<DestructorObserver>* bucketAddress = nullptr; + const DestructorObserver* observedBucket = nullptr; + std::unique_ptr<DestructorObserver> observer(new DestructorObserver { [&]() { + observedBucket = bucketAddress->get(); + }}); + + const DestructorObserver* observerAddress = observer.get(); + + HashSet<std::unique_ptr<DestructorObserver>> set; + auto addResult = set.add(WTFMove(observer)); + + EXPECT_TRUE(addResult.isNewEntry); + EXPECT_TRUE(observedBucket == nullptr); + + bucketAddress = addResult.iterator.get(); + + EXPECT_TRUE(observedBucket == nullptr); + EXPECT_TRUE(set.remove(*addResult.iterator)); + + EXPECT_TRUE(observedBucket == observerAddress || observedBucket == reinterpret_cast<const DestructorObserver*>(-1)); +} + + } // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/ListHashSet.cpp b/Tools/TestWebKitAPI/Tests/WTF/ListHashSet.cpp index a550a845b..d81dcfcfe 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/ListHashSet.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/ListHashSet.cpp @@ -78,7 +78,7 @@ TEST(WTF_ListHashSet, AppendOrMoveToLastNewItems) result = list.appendOrMoveToLast(3); ASSERT_TRUE(result.isNewEntry); - ASSERT_EQ(list.size(), 3); + ASSERT_EQ(list.size(), 3u); // The list should be in order 1, 2, 3. ListHashSet<int>::iterator iterator = list.begin(); @@ -99,11 +99,11 @@ TEST(WTF_ListHashSet, AppendOrMoveToLastWithDuplicates) ASSERT_TRUE(result.isNewEntry); result = list.appendOrMoveToLast(1); ASSERT_FALSE(result.isNewEntry); - ASSERT_EQ(1, list.size()); + ASSERT_EQ(1u, list.size()); list.add(2); list.add(3); - ASSERT_EQ(3, list.size()); + ASSERT_EQ(3u, list.size()); // Appending 2 move it to the end. ASSERT_EQ(3, list.last()); @@ -118,7 +118,7 @@ TEST(WTF_ListHashSet, AppendOrMoveToLastWithDuplicates) ASSERT_FALSE(result.isNewEntry); result = list.appendOrMoveToLast(1); ASSERT_FALSE(result.isNewEntry); - ASSERT_EQ(3, list.size()); + ASSERT_EQ(3u, list.size()); ListHashSet<int>::iterator iterator = list.begin(); ASSERT_EQ(3, *iterator); @@ -139,7 +139,7 @@ TEST(WTF_ListHashSet, PrependOrMoveToLastNewItems) result = list.prependOrMoveToFirst(3); ASSERT_TRUE(result.isNewEntry); - ASSERT_EQ(list.size(), 3); + ASSERT_EQ(list.size(), 3u); // The list should be in order 3, 1, 2. ListHashSet<int>::iterator iterator = list.begin(); @@ -160,11 +160,11 @@ TEST(WTF_ListHashSet, PrependOrMoveToLastWithDuplicates) ASSERT_TRUE(result.isNewEntry); result = list.prependOrMoveToFirst(1); ASSERT_FALSE(result.isNewEntry); - ASSERT_EQ(1, list.size()); + ASSERT_EQ(1u, list.size()); list.add(2); list.add(3); - ASSERT_EQ(3, list.size()); + ASSERT_EQ(3u, list.size()); // Prepending 2 move it to the beginning. ASSERT_EQ(1, list.first()); @@ -179,7 +179,7 @@ TEST(WTF_ListHashSet, PrependOrMoveToLastWithDuplicates) ASSERT_FALSE(result.isNewEntry); result = list.prependOrMoveToFirst(3); ASSERT_FALSE(result.isNewEntry); - ASSERT_EQ(3, list.size()); + ASSERT_EQ(3u, list.size()); ListHashSet<int>::iterator iterator = list.begin(); ASSERT_EQ(3, *iterator); diff --git a/Tools/TestWebKitAPI/Tests/WTF/Lock.cpp b/Tools/TestWebKitAPI/Tests/WTF/Lock.cpp new file mode 100644 index 000000000..5576302d6 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/Lock.cpp @@ -0,0 +1,189 @@ +/* + * 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])); + } +} + +bool skipSlow() +{ +#if PLATFORM(WIN) && !defined(NDEBUG) + return true; +#else + return false; +#endif +} + +TEST(WTF_WordLock, UncontendedShortSection) +{ + runLockTest<WordLock>(1, 1, 1, 10000000); +} + +TEST(WTF_WordLock, UncontendedLongSection) +{ + runLockTest<WordLock>(1, 1, 10000, 1000); +} + +TEST(WTF_WordLock, ContendedShortSection) +{ + if (skipSlow()) + return; + runLockTest<WordLock>(1, 10, 1, 5000000); +} + +TEST(WTF_WordLock, ContendedLongSection) +{ + if (skipSlow()) + return; + runLockTest<WordLock>(1, 10, 10000, 10000); +} + +TEST(WTF_WordLock, ManyContendedShortSections) +{ + if (skipSlow()) + return; + runLockTest<WordLock>(10, 10, 1, 500000); +} + +TEST(WTF_WordLock, ManyContendedLongSections) +{ + if (skipSlow()) + return; + 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) +{ + if (skipSlow()) + return; + runLockTest<Lock>(1, 10, 1, 10000000); +} + +TEST(WTF_Lock, ContendedLongSection) +{ + if (skipSlow()) + return; + runLockTest<Lock>(1, 10, 10000, 10000); +} + +TEST(WTF_Lock, ManyContendedShortSections) +{ + if (skipSlow()) + return; + runLockTest<Lock>(10, 10, 1, 500000); +} + +TEST(WTF_Lock, ManyContendedLongSections) +{ + if (skipSlow()) + return; + runLockTest<Lock>(10, 10, 10000, 1000); +} + +TEST(WTF_Lock, ManyContendedLongerSections) +{ + if (skipSlow()) + return; + runLockTest<Lock>(10, 10, 100000, 1); +} + +TEST(WTF_Lock, SectionAddressCollision) +{ + if (skipSlow()) + return; + runLockTest<Lock>(4, 2, 10000, 2000); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/MediaTime.cpp b/Tools/TestWebKitAPI/Tests/WTF/MediaTime.cpp index 8e9b178af..2386143e3 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/MediaTime.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/MediaTime.cpp @@ -10,7 +10,7 @@ * 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 Computer, Inc. ("Apple") nor the names of + * 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. * @@ -26,24 +26,14 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define _USE_MATH_DEFINES 1 #include "config.h" +#include <limits> +#include <wtf/MathExtras.h> #include <wtf/MediaTime.h> using namespace std; -#if COMPILER(MSVC) -// Work around Visual Studio 2008's lack of an INFINITY or NAN definition. -#include <limits> -#if !defined(INFINITY) -#define INFINITY (numeric_limits<double>::infinity()) -#endif -#if !defined(NAN) -#define NAN (numeric_limits<double>::quiet_NaN()) -#endif -#endif - namespace WTF { std::ostream& operator<<(std::ostream& out, const MediaTime& val) @@ -55,6 +45,8 @@ std::ostream& operator<<(std::ostream& out, const MediaTime& val) 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 << " }"; @@ -180,13 +172,29 @@ TEST(WTF, MediaTime) 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()); + EXPECT_EQ(MediaTime::createWithDouble(piDouble, 1 << 30), MediaTime(3373259426U, 1 << 30)); + + EXPECT_EQ(MediaTime::createWithFloat(std::numeric_limits<float>::infinity()), MediaTime::positiveInfiniteTime()); + EXPECT_EQ(MediaTime::createWithFloat(-std::numeric_limits<float>::infinity()), MediaTime::negativeInfiniteTime()); + EXPECT_EQ(MediaTime::createWithFloat(std::numeric_limits<float>::quiet_NaN()), MediaTime::invalidTime()); + + EXPECT_EQ(MediaTime::createWithDouble(std::numeric_limits<double>::infinity()), MediaTime::positiveInfiniteTime()); + EXPECT_EQ(MediaTime::createWithDouble(-std::numeric_limits<double>::infinity()), MediaTime::negativeInfiniteTime()); + EXPECT_EQ(MediaTime::createWithDouble(std::numeric_limits<double>::quiet_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()); diff --git a/Tools/TestWebKitAPI/Tests/WTF/MetaAllocator.cpp b/Tools/TestWebKitAPI/Tests/WTF/MetaAllocator.cpp index 61c16d1ab..59af4f849 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/MetaAllocator.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/MetaAllocator.cpp @@ -10,7 +10,7 @@ * 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 Computer, Inc. ("Apple") nor the names of + * 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. * @@ -31,6 +31,10 @@ #include <wtf/MetaAllocator.h> #include <wtf/Vector.h> +#if OS(WINDOWS) +#undef small +#endif + using namespace WTF; namespace TestWebKitAPI { diff --git a/Tools/TestWebKitAPI/Tests/WTF/NakedPtr.cpp b/Tools/TestWebKitAPI/Tests/WTF/NakedPtr.cpp new file mode 100644 index 000000000..f4b4836cd --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/NakedPtr.cpp @@ -0,0 +1,238 @@ +/* + * 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 = WTFMove(p1); + ASSERT_EQ(&a, p1.get()); + ASSERT_EQ(&a, p2.get()); + } + + { + NakedPtr<RefLogger> p1 = &a; + NakedPtr<RefLogger> p2(WTFMove(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 = WTFMove(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 = WTFMove(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 = WTFMove(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()); +#if COMPILER(CLANG) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wself-move" +#endif + ptr = WTFMove(ptr); +#if COMPILER(CLANG) +#pragma clang diagnostic pop +#endif + 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/OptionSet.cpp b/Tools/TestWebKitAPI/Tests/WTF/OptionSet.cpp new file mode 100644 index 000000000..37efc9939 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/OptionSet.cpp @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2016 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/OptionSet.h> + +namespace TestWebKitAPI { + +enum class ExampleFlags : uint64_t { + A = 1 << 0, + B = 1 << 1, + C = 1 << 2, + D = 1ULL << 31, + E = 1ULL << 63, +}; + +TEST(WTF_OptionSet, EmptySet) +{ + OptionSet<ExampleFlags> set; + EXPECT_TRUE(set.isEmpty()); + EXPECT_FALSE(set.contains(ExampleFlags::A)); + EXPECT_FALSE(set.contains(ExampleFlags::B)); + EXPECT_FALSE(set.contains(ExampleFlags::C)); + EXPECT_FALSE(set.contains(ExampleFlags::D)); + EXPECT_FALSE(set.contains(ExampleFlags::E)); +} + +TEST(WTF_OptionSet, ContainsOneFlag) +{ + OptionSet<ExampleFlags> set = ExampleFlags::A; + EXPECT_FALSE(set.isEmpty()); + EXPECT_TRUE(set.contains(ExampleFlags::A)); + EXPECT_FALSE(set.contains(ExampleFlags::B)); + EXPECT_FALSE(set.contains(ExampleFlags::C)); + EXPECT_FALSE(set.contains(ExampleFlags::D)); + EXPECT_FALSE(set.contains(ExampleFlags::E)); +} + +TEST(WTF_OptionSet, ContainsTwoFlags) +{ + OptionSet<ExampleFlags> set { ExampleFlags::A, ExampleFlags::B }; + EXPECT_FALSE(set.isEmpty()); + EXPECT_TRUE(set.contains(ExampleFlags::A)); + EXPECT_TRUE(set.contains(ExampleFlags::B)); + EXPECT_FALSE(set.contains(ExampleFlags::C)); + EXPECT_FALSE(set.contains(ExampleFlags::D)); + EXPECT_FALSE(set.contains(ExampleFlags::E)); +} + +TEST(WTF_OptionSet, ContainsTwoFlags2) +{ + OptionSet<ExampleFlags> set { ExampleFlags::A, ExampleFlags::D }; + EXPECT_FALSE(set.isEmpty()); + EXPECT_TRUE(set.contains(ExampleFlags::A)); + EXPECT_TRUE(set.contains(ExampleFlags::D)); + EXPECT_FALSE(set.contains(ExampleFlags::B)); + EXPECT_FALSE(set.contains(ExampleFlags::C)); + EXPECT_FALSE(set.contains(ExampleFlags::E)); +} + +TEST(WTF_OptionSet, ContainsTwoFlags3) +{ + OptionSet<ExampleFlags> set { ExampleFlags::D, ExampleFlags::E }; + EXPECT_FALSE(set.isEmpty()); + EXPECT_TRUE(set.contains(ExampleFlags::D)); + EXPECT_TRUE(set.contains(ExampleFlags::E)); + EXPECT_FALSE(set.contains(ExampleFlags::A)); + EXPECT_FALSE(set.contains(ExampleFlags::B)); + EXPECT_FALSE(set.contains(ExampleFlags::C)); +} + +TEST(WTF_OptionSet, OperatorBitwiseOr) +{ + OptionSet<ExampleFlags> set = ExampleFlags::A; + set |= ExampleFlags::C; + EXPECT_TRUE(set.contains(ExampleFlags::A)); + EXPECT_FALSE(set.contains(ExampleFlags::B)); + EXPECT_TRUE(set.contains(ExampleFlags::C)); +} + +TEST(WTF_OptionSet, EmptyOptionSetToRawValueToOptionSet) +{ + OptionSet<ExampleFlags> set; + EXPECT_TRUE(set.isEmpty()); + EXPECT_FALSE(set.contains(ExampleFlags::A)); + EXPECT_FALSE(set.contains(ExampleFlags::B)); + EXPECT_FALSE(set.contains(ExampleFlags::C)); + + auto set2 = OptionSet<ExampleFlags>::fromRaw(set.toRaw()); + EXPECT_TRUE(set2.isEmpty()); + EXPECT_FALSE(set2.contains(ExampleFlags::A)); + EXPECT_FALSE(set2.contains(ExampleFlags::B)); + EXPECT_FALSE(set2.contains(ExampleFlags::C)); +} + +TEST(WTF_OptionSet, OptionSetThatContainsOneFlagToRawValueToOptionSet) +{ + OptionSet<ExampleFlags> set = ExampleFlags::A; + EXPECT_FALSE(set.isEmpty()); + EXPECT_TRUE(set.contains(ExampleFlags::A)); + EXPECT_FALSE(set.contains(ExampleFlags::B)); + EXPECT_FALSE(set.contains(ExampleFlags::C)); + EXPECT_FALSE(set.contains(ExampleFlags::D)); + EXPECT_FALSE(set.contains(ExampleFlags::E)); + + auto set2 = OptionSet<ExampleFlags>::fromRaw(set.toRaw()); + EXPECT_FALSE(set2.isEmpty()); + EXPECT_TRUE(set2.contains(ExampleFlags::A)); + EXPECT_FALSE(set2.contains(ExampleFlags::B)); + EXPECT_FALSE(set2.contains(ExampleFlags::C)); + EXPECT_FALSE(set2.contains(ExampleFlags::D)); + EXPECT_FALSE(set2.contains(ExampleFlags::E)); +} + +TEST(WTF_OptionSet, OptionSetThatContainsOneFlagToRawValueToOptionSet2) +{ + OptionSet<ExampleFlags> set = ExampleFlags::E; + EXPECT_FALSE(set.isEmpty()); + EXPECT_TRUE(set.contains(ExampleFlags::E)); + EXPECT_FALSE(set.contains(ExampleFlags::A)); + EXPECT_FALSE(set.contains(ExampleFlags::B)); + EXPECT_FALSE(set.contains(ExampleFlags::C)); + EXPECT_FALSE(set.contains(ExampleFlags::D)); + + auto set2 = OptionSet<ExampleFlags>::fromRaw(set.toRaw()); + EXPECT_FALSE(set2.isEmpty()); + EXPECT_TRUE(set2.contains(ExampleFlags::E)); + EXPECT_FALSE(set2.contains(ExampleFlags::A)); + EXPECT_FALSE(set2.contains(ExampleFlags::B)); + EXPECT_FALSE(set2.contains(ExampleFlags::C)); + EXPECT_FALSE(set2.contains(ExampleFlags::D)); +} + +TEST(WTF_OptionSet, OptionSetThatContainsTwoFlagsToRawValueToOptionSet) +{ + OptionSet<ExampleFlags> set { ExampleFlags::A, ExampleFlags::C }; + EXPECT_FALSE(set.isEmpty()); + EXPECT_TRUE(set.contains(ExampleFlags::A)); + EXPECT_TRUE(set.contains(ExampleFlags::C)); + EXPECT_FALSE(set.contains(ExampleFlags::B)); + + auto set2 = OptionSet<ExampleFlags>::fromRaw(set.toRaw()); + EXPECT_FALSE(set2.isEmpty()); + EXPECT_TRUE(set2.contains(ExampleFlags::A)); + EXPECT_TRUE(set2.contains(ExampleFlags::C)); + EXPECT_FALSE(set2.contains(ExampleFlags::B)); +} + +TEST(WTF_OptionSet, OptionSetThatContainsTwoFlagsToRawValueToOptionSet2) +{ + OptionSet<ExampleFlags> set { ExampleFlags::D, ExampleFlags::E }; + EXPECT_FALSE(set.isEmpty()); + EXPECT_TRUE(set.contains(ExampleFlags::D)); + EXPECT_TRUE(set.contains(ExampleFlags::E)); + EXPECT_FALSE(set.contains(ExampleFlags::A)); + EXPECT_FALSE(set.contains(ExampleFlags::B)); + EXPECT_FALSE(set.contains(ExampleFlags::C)); + + auto set2 = OptionSet<ExampleFlags>::fromRaw(set.toRaw()); + EXPECT_FALSE(set2.isEmpty()); + EXPECT_TRUE(set2.contains(ExampleFlags::D)); + EXPECT_TRUE(set2.contains(ExampleFlags::E)); + EXPECT_FALSE(set2.contains(ExampleFlags::A)); + EXPECT_FALSE(set2.contains(ExampleFlags::B)); + EXPECT_FALSE(set2.contains(ExampleFlags::C)); +} + +TEST(WTF_OptionSet, TwoIteratorsIntoSameOptionSet) +{ + OptionSet<ExampleFlags> set { ExampleFlags::C, ExampleFlags::B }; + OptionSet<ExampleFlags>::iterator it1 = set.begin(); + OptionSet<ExampleFlags>::iterator it2 = it1; + ++it1; + EXPECT_STRONG_ENUM_EQ(ExampleFlags::C, *it1); + EXPECT_STRONG_ENUM_EQ(ExampleFlags::B, *it2); +} + +TEST(WTF_OptionSet, IterateOverOptionSetThatContainsTwoFlags) +{ + OptionSet<ExampleFlags> set { ExampleFlags::A, ExampleFlags::C }; + OptionSet<ExampleFlags>::iterator it = set.begin(); + OptionSet<ExampleFlags>::iterator end = set.end(); + EXPECT_TRUE(it != end); + EXPECT_STRONG_ENUM_EQ(ExampleFlags::A, *it); + ++it; + EXPECT_STRONG_ENUM_EQ(ExampleFlags::C, *it); + ++it; + EXPECT_TRUE(it == end); +} + +TEST(WTF_OptionSet, IterateOverOptionSetThatContainsFlags2) +{ + OptionSet<ExampleFlags> set { ExampleFlags::D, ExampleFlags::E }; + OptionSet<ExampleFlags>::iterator it = set.begin(); + OptionSet<ExampleFlags>::iterator end = set.end(); + EXPECT_TRUE(it != end); + EXPECT_STRONG_ENUM_EQ(ExampleFlags::D, *it); + ++it; + EXPECT_STRONG_ENUM_EQ(ExampleFlags::E, *it); + ++it; + EXPECT_TRUE(it == end); +} + +TEST(WTF_OptionSet, NextItemAfterLargestIn32BitFlagSet) +{ + enum class ThirtyTwoBitFlags : uint32_t { + A = 1UL << 31, + }; + OptionSet<ThirtyTwoBitFlags> set { ThirtyTwoBitFlags::A }; + OptionSet<ThirtyTwoBitFlags>::iterator it = set.begin(); + OptionSet<ThirtyTwoBitFlags>::iterator end = set.end(); + EXPECT_TRUE(it != end); + ++it; + EXPECT_TRUE(it == end); +} + +TEST(WTF_OptionSet, NextItemAfterLargestIn64BitFlagSet) +{ + enum class SixtyFourBitFlags : uint64_t { + A = 1ULL << 63, + }; + OptionSet<SixtyFourBitFlags> set { SixtyFourBitFlags::A }; + OptionSet<SixtyFourBitFlags>::iterator it = set.begin(); + OptionSet<SixtyFourBitFlags>::iterator end = set.end(); + EXPECT_TRUE(it != end); + ++it; + EXPECT_TRUE(it == end); +} + +TEST(WTF_OptionSet, IterationOrderTheSameRegardlessOfInsertionOrder) +{ + OptionSet<ExampleFlags> set1 = ExampleFlags::C; + set1 |= ExampleFlags::A; + + OptionSet<ExampleFlags> set2 = ExampleFlags::A; + set2 |= ExampleFlags::C; + + OptionSet<ExampleFlags>::iterator it1 = set1.begin(); + OptionSet<ExampleFlags>::iterator it2 = set2.begin(); + + EXPECT_TRUE(*it1 == *it2); + ++it1; + ++it2; + EXPECT_TRUE(*it1 == *it2); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/Optional.cpp b/Tools/TestWebKitAPI/Tests/WTF/Optional.cpp new file mode 100644 index 000000000..a0715fd62 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/Optional.cpp @@ -0,0 +1,150 @@ +/* + * 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); +} + +TEST(WTF_Optional, Equality) +{ + Optional<int> unengaged1; + Optional<int> unengaged2; + + Optional<int> engaged1 { 1 }; + Optional<int> engaged2 { 2 }; + Optional<int> engagedx2 { 2 }; + + EXPECT_TRUE(unengaged1 == unengaged2); + EXPECT_FALSE(engaged1 == engaged2); + EXPECT_FALSE(engaged1 == unengaged1); + EXPECT_TRUE(engaged2 == engagedx2); + + EXPECT_TRUE(unengaged1 == Nullopt); + EXPECT_FALSE(engaged1 == Nullopt); + EXPECT_TRUE(Nullopt == unengaged1); + EXPECT_FALSE(Nullopt == engaged1); + + EXPECT_TRUE(engaged1 == 1); + EXPECT_TRUE(1 == engaged1); + EXPECT_FALSE(unengaged1 == 1); + EXPECT_FALSE(1 == unengaged1); +} + +TEST(WTF_Optional, Inequality) +{ + Optional<int> unengaged1; + Optional<int> unengaged2; + + Optional<int> engaged1 { 1 }; + Optional<int> engaged2 { 2 }; + Optional<int> engagedx2 { 2 }; + + EXPECT_FALSE(unengaged1 != unengaged2); + EXPECT_TRUE(engaged1 != engaged2); + EXPECT_TRUE(engaged1 != unengaged1); + EXPECT_FALSE(engaged2 != engagedx2); + + EXPECT_FALSE(unengaged1 != Nullopt); + EXPECT_TRUE(engaged1 != Nullopt); + EXPECT_FALSE(Nullopt != unengaged1); + EXPECT_TRUE(Nullopt != engaged1); + + EXPECT_FALSE(engaged1 != 1); + EXPECT_TRUE(engaged1 != 2); + EXPECT_FALSE(1 != engaged1); + EXPECT_TRUE(2 != engaged1); + + EXPECT_TRUE(unengaged1 != 1); + EXPECT_TRUE(1 != unengaged1); +} + + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WTF/ParkingLot.cpp b/Tools/TestWebKitAPI/Tests/WTF/ParkingLot.cpp new file mode 100644 index 000000000..b6a881fe0 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/ParkingLot.cpp @@ -0,0 +1,275 @@ +/* + * 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); +} + +#if !PLATFORM(IOS) +TEST(WTF_ParkingLot, UnparkOneFiftyThenFiftyAll) +{ + repeatParkingTest(2, 1, 10000, 100, 50); +} +#endif + +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 index 1a1c9fdfc..5e5c8adc4 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/RedBlackTree.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/RedBlackTree.cpp @@ -10,7 +10,7 @@ * 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 Computer, Inc. ("Apple") nor the names of + * 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. * diff --git a/Tools/TestWebKitAPI/Tests/WTF/Ref.cpp b/Tools/TestWebKitAPI/Tests/WTF/Ref.cpp index 1b9eef67a..c35606e23 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/Ref.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/Ref.cpp @@ -26,7 +26,6 @@ #include "config.h" #include "RefLogger.h" -#include <wtf/PassRef.h> #include <wtf/Ref.h> #include <wtf/RefPtr.h> @@ -38,14 +37,14 @@ TEST(WTF_Ref, Basic) { Ref<RefLogger> ptr(a); - ASSERT_EQ(&a, &ptr.get()); + 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.get()); + ASSERT_EQ(&a, ptr.ptr()); ASSERT_EQ(&a.name, &ptr->name); } ASSERT_STREQ("deref(a) ", takeLogStr().c_str()); @@ -59,51 +58,51 @@ TEST(WTF_Ref, Assignment) { Ref<RefLogger> ptr(a); - ASSERT_EQ(&a, &ptr.get()); + ASSERT_EQ(&a, ptr.ptr()); log() << "| "; ptr = b; - ASSERT_EQ(&b, &ptr.get()); + 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.get()); + ASSERT_EQ(&a, ptr.ptr()); log() << "| "; ptr = c; - ASSERT_EQ(&c, &ptr.get()); + 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.get()); + ASSERT_EQ(&a, ptr.ptr()); log() << "| "; ptr = adoptRef(b); - ASSERT_EQ(&b, &ptr.get()); + 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.get()); + ASSERT_EQ(&a, ptr.ptr()); log() << "| "; ptr = adoptRef(c); - ASSERT_EQ(&c, &ptr.get()); + ASSERT_EQ(&c, ptr.ptr()); log() << "| "; } ASSERT_STREQ("ref(a) | deref(a) | deref(c) ", takeLogStr().c_str()); } -PassRef<RefLogger> passWithPassRef(PassRef<RefLogger> reference) +static Ref<RefLogger> passWithRef(Ref<RefLogger>&& reference) { - return reference; + return WTFMove(reference); } -RefPtr<RefLogger> passWithPassRefPtr(PassRefPtr<RefLogger> reference) +static RefPtr<RefLogger> passWithPassRefPtr(PassRefPtr<RefLogger> reference) { return reference; } @@ -115,32 +114,48 @@ TEST(WTF_Ref, ReturnValue) DerivedRefLogger c("c"); { - Ref<RefLogger> ptr(passWithPassRef(a)); - ASSERT_EQ(&a, &ptr.get()); + 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.get()); + ASSERT_EQ(&a, ptr.ptr()); log() << "| "; - ptr = passWithPassRef(b); - ASSERT_EQ(&b, &ptr.get()); + 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(passWithPassRef(a)); + RefPtr<RefLogger> ptr(passWithRef(a)); ASSERT_EQ(&a, ptr.get()); } ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); { - RefPtr<RefLogger> ptr(passWithPassRefPtr(passWithPassRef(a))); + 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(WTFMove(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 index 2bb5c36fa..c5cfb071f 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/RefLogger.h +++ b/Tools/TestWebKitAPI/Tests/WTF/RefLogger.h @@ -48,7 +48,7 @@ struct RefLogger { }; struct DerivedRefLogger : RefLogger { - DerivedRefLogger(const char* name) : RefLogger(name) { } + DerivedRefLogger(const char* name) : RefLogger(name) { log().str(""); } }; } diff --git a/Tools/TestWebKitAPI/Tests/WTF/RefPtr.cpp b/Tools/TestWebKitAPI/Tests/WTF/RefPtr.cpp index bc1e1e484..aebe98c48 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/RefPtr.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/RefPtr.cpp @@ -69,7 +69,7 @@ TEST(WTF_RefPtr, Basic) { RefPtr<RefLogger> p1 = &a; - RefPtr<RefLogger> p2 = std::move(p1); + RefPtr<RefLogger> p2 = WTFMove(p1); ASSERT_EQ(nullptr, p1.get()); ASSERT_EQ(&a, p2.get()); } @@ -77,7 +77,7 @@ TEST(WTF_RefPtr, Basic) { RefPtr<RefLogger> p1 = &a; - RefPtr<RefLogger> p2(std::move(p1)); + RefPtr<RefLogger> p2(WTFMove(p1)); ASSERT_EQ(nullptr, p1.get()); ASSERT_EQ(&a, p2.get()); } @@ -93,7 +93,7 @@ TEST(WTF_RefPtr, Basic) { RefPtr<DerivedRefLogger> p1 = &a; - RefPtr<RefLogger> p2 = std::move(p1); + RefPtr<RefLogger> p2 = WTFMove(p1); ASSERT_EQ(nullptr, p1.get()); ASSERT_EQ(&a, p2.get()); } @@ -102,7 +102,7 @@ TEST(WTF_RefPtr, Basic) { RefPtr<RefLogger> ptr(&a); ASSERT_EQ(&a, ptr.get()); - ptr.clear(); + ptr = nullptr; ASSERT_EQ(nullptr, ptr.get()); } ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); @@ -120,8 +120,8 @@ TEST(WTF_RefPtr, AssignPassRefToRefPtr) { DerivedRefLogger a("a"); { - PassRef<RefLogger> passRef(a); - RefPtr<RefLogger> ptr = std::move(passRef); + Ref<RefLogger> passRef(a); + RefPtr<RefLogger> ptr = WTFMove(passRef); ASSERT_EQ(&a, ptr.get()); ptr.release(); ASSERT_EQ(nullptr, ptr.get()); @@ -204,7 +204,7 @@ TEST(WTF_RefPtr, Assignment) ASSERT_EQ(&a, p1.get()); ASSERT_EQ(&b, p2.get()); log() << "| "; - p1 = std::move(p2); + p1 = WTFMove(p2); ASSERT_EQ(&b, p1.get()); ASSERT_EQ(nullptr, p2.get()); log() << "| "; @@ -250,7 +250,7 @@ TEST(WTF_RefPtr, Assignment) ASSERT_EQ(&a, p1.get()); ASSERT_EQ(&c, p2.get()); log() << "| "; - p1 = std::move(p2); + p1 = WTFMove(p2); ASSERT_EQ(&c, p1.get()); ASSERT_EQ(nullptr, p2.get()); log() << "| "; @@ -270,7 +270,15 @@ TEST(WTF_RefPtr, Assignment) { RefPtr<RefLogger> ptr(&a); ASSERT_EQ(&a, ptr.get()); - ptr = std::move(ptr); +#if COMPILER(CLANG) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wself-move" +#endif + ptr = WTFMove(ptr); +#if COMPILER(CLANG) +#pragma clang diagnostic pop +#endif ASSERT_EQ(&a, ptr.get()); } ASSERT_STREQ("ref(a) deref(a) ", takeLogStr().c_str()); 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/StringBuilder.cpp b/Tools/TestWebKitAPI/Tests/WTF/StringBuilder.cpp index 9ca5d6ee8..11bf3590e 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/StringBuilder.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/StringBuilder.cpp @@ -38,14 +38,17 @@ static void expectBuilderContent(const String& expected, const StringBuilder& bu { // Not using builder.toString() or builder.toStringPreserveCapacity() because they all // change internal state of builder. - EXPECT_EQ(expected, String(builder.deprecatedCharacters(), builder.length())); + 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.deprecatedCharacters()); + EXPECT_EQ(0, builder.characters8()); } TEST(StringBuilderTest, DefaultConstructor) @@ -72,20 +75,20 @@ TEST(StringBuilderTest, Append) StringBuilder builder1; builder.append("", 0); expectBuilderContent("0123456789abcdefg#", builder); - builder1.append(builder.deprecatedCharacters(), builder.length()); + builder1.append(builder.characters8(), builder.length()); builder1.append("XYZ"); - builder.append(builder1.deprecatedCharacters(), builder1.length()); + builder.append(builder1.characters8(), builder1.length()); expectBuilderContent("0123456789abcdefg#0123456789abcdefg#XYZ", builder); StringBuilder builder2; builder2.reserveCapacity(100); builder2.append("xyz"); - const UChar* characters = builder2.deprecatedCharacters(); + const LChar* characters = builder2.characters8(); builder2.append("0123456789"); - ASSERT_EQ(characters, builder2.deprecatedCharacters()); + ASSERT_EQ(characters, builder2.characters8()); builder2.toStringPreserveCapacity(); // Test after reifyString with buffer preserved. builder2.append("abcd"); - ASSERT_EQ(characters, builder2.deprecatedCharacters()); + ASSERT_EQ(characters, builder2.characters8()); // Test appending UChar32 characters to StringBuilder. StringBuilder builderForUChar32Append; @@ -140,7 +143,7 @@ TEST(StringBuilderTest, ToStringPreserveCapacity) ASSERT_EQ(capacity, builder.capacity()); ASSERT_EQ(String("0123456789"), string); ASSERT_EQ(string.impl(), builder.toStringPreserveCapacity().impl()); - ASSERT_EQ(string.deprecatedCharacters(), builder.deprecatedCharacters()); + ASSERT_EQ(string.characters8(), builder.characters8()); // Changing the StringBuilder should not affect the original result of toStringPreserveCapacity(). builder.append("abcdefghijklmnopqrstuvwxyz"); @@ -151,7 +154,7 @@ TEST(StringBuilderTest, ToStringPreserveCapacity) capacity = builder.capacity(); string = builder.toStringPreserveCapacity(); ASSERT_EQ(capacity, builder.capacity()); - ASSERT_EQ(string.deprecatedCharacters(), builder.deprecatedCharacters()); + ASSERT_EQ(string.characters8(), builder.characters8()); ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string); builder.append("ABC"); ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string); @@ -160,7 +163,7 @@ TEST(StringBuilderTest, ToStringPreserveCapacity) capacity = builder.capacity(); String string1 = builder.toStringPreserveCapacity(); ASSERT_EQ(capacity, builder.capacity()); - ASSERT_EQ(string1.deprecatedCharacters(), builder.deprecatedCharacters()); + ASSERT_EQ(string1.characters8(), builder.characters8()); ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1); string1.append("DEF"); ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), builder.toStringPreserveCapacity()); @@ -170,7 +173,7 @@ TEST(StringBuilderTest, ToStringPreserveCapacity) capacity = builder.capacity(); string1 = builder.toStringPreserveCapacity(); ASSERT_EQ(capacity, builder.capacity()); - ASSERT_EQ(string.deprecatedCharacters(), builder.deprecatedCharacters()); + ASSERT_EQ(string.characters8(), builder.characters8()); builder.resize(10); builder.append("###"); ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1); diff --git a/Tools/TestWebKitAPI/Tests/WTF/StringHasher.cpp b/Tools/TestWebKitAPI/Tests/WTF/StringHasher.cpp index a4d223c99..739a8190e 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/StringHasher.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/StringHasher.cpp @@ -25,7 +25,7 @@ #include "config.h" -#include <wtf/StringHasher.h> +#include <wtf/Hasher.h> namespace TestWebKitAPI { diff --git a/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp b/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp index d5804f6b4..01f36f7b6 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp @@ -25,7 +25,7 @@ #include "config.h" -#include <wtf/text/StringImpl.h> +#include <wtf/text/SymbolImpl.h> #include <wtf/text/WTFString.h> namespace TestWebKitAPI { @@ -55,17 +55,6 @@ TEST(WTF, StringImplCreationFromLiteral) ASSERT_TRUE(programmaticStringNoLength->is8Bit()); } -TEST(WTF, StringImplFromLiteralLoop16BitConversion) -{ - RefPtr<StringImpl> controlString = StringImpl::create("Template Literal"); - for (size_t i = 0; i < 10; ++i) { - RefPtr<StringImpl> string = StringImpl::createFromLiteral("Template Literal"); - - ASSERT_EQ(0, memcmp(controlString->deprecatedCharacters(), string->deprecatedCharacters(), controlString->length() * sizeof(UChar))); - ASSERT_TRUE(string->has16BitShadow()); - } -} - TEST(WTF, StringImplReplaceWithLiteral) { RefPtr<StringImpl> testStringImpl = StringImpl::createFromLiteral("1224"); @@ -110,4 +99,451 @@ TEST(WTF, StringImplReplaceWithLiteral) 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"); + StringImpl* nullStringImpl = nullptr; + ASSERT_FALSE(equalIgnoringASCIICase(nullStringImpl, reference.get())); + ASSERT_FALSE(equalIgnoringASCIICase(reference.get(), nullStringImpl)); + ASSERT_TRUE(equalIgnoringASCIICase(nullStringImpl, nullStringImpl)); +} + +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 index 149b85b21..7a1d9297f 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/StringOperators.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/StringOperators.cpp @@ -184,4 +184,20 @@ TEST(WTF, StringOperators) #endif } +TEST(WTF, ConcatenateCharacterArrayAndEmptyString) +{ + String emptyString; + EXPECT_EQ(static_cast<unsigned>(0), emptyString.length()); + + UChar ucharArray[] = { 't', 'e', 's', 't', '\0' }; + String concatenation16 = ucharArray + emptyString; + ASSERT_EQ(static_cast<unsigned>(4), concatenation16.length()); + ASSERT_TRUE(concatenation16 == String(ucharArray)); + + LChar lcharArray[] = { 't', 'e', 's', 't', '\0' }; + String concatenation8 = lcharArray + emptyString; + ASSERT_EQ(static_cast<unsigned>(4), concatenation8.length()); + ASSERT_TRUE(concatenation8 == String(lcharArray)); +} + } // 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/Vector.cpp b/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp index 2278784c4..6567ac559 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp @@ -27,6 +27,7 @@ #include "MoveOnly.h" #include <wtf/Vector.h> +#include <wtf/text/CString.h> namespace TestWebKitAPI { @@ -34,8 +35,8 @@ TEST(WTF_Vector, Basic) { Vector<int> intVector; EXPECT_TRUE(intVector.isEmpty()); - EXPECT_EQ(0ul, intVector.size()); - EXPECT_EQ(0ul, intVector.capacity()); + EXPECT_EQ(0U, intVector.size()); + EXPECT_EQ(0U, intVector.capacity()); } TEST(WTF_Vector, Iterator) @@ -94,6 +95,103 @@ TEST(WTF_Vector, InitializerList) 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; @@ -149,12 +247,12 @@ TEST(WTF_Vector, MoveOnly_UncheckedAppend) vector.reserveInitialCapacity(100); for (size_t i = 0; i < 100; ++i) { MoveOnly moveOnly(i); - vector.uncheckedAppend(std::move(moveOnly)); - EXPECT_EQ(moveOnly.value(), 0U); + vector.uncheckedAppend(WTFMove(moveOnly)); + EXPECT_EQ(0U, moveOnly.value()); } for (size_t i = 0; i < 100; ++i) - EXPECT_EQ(vector[i].value(), i); + EXPECT_EQ(i, vector[i].value()); } TEST(WTF_Vector, MoveOnly_Append) @@ -163,12 +261,12 @@ TEST(WTF_Vector, MoveOnly_Append) for (size_t i = 0; i < 100; ++i) { MoveOnly moveOnly(i); - vector.append(std::move(moveOnly)); - EXPECT_EQ(moveOnly.value(), 0U); + vector.append(WTFMove(moveOnly)); + EXPECT_EQ(0U, moveOnly.value()); } for (size_t i = 0; i < 100; ++i) - EXPECT_EQ(vector[i].value(), i); + EXPECT_EQ(i, vector[i].value()); for (size_t i = 0; i < 16; ++i) { Vector<MoveOnly> vector; @@ -177,13 +275,13 @@ TEST(WTF_Vector, MoveOnly_Append) for (size_t j = 0; j < i; ++j) vector.append(j); - vector.append(std::move(vector[0])); + vector.append(WTFMove(vector[0])); - EXPECT_EQ(vector[0].value(), 0U); + EXPECT_EQ(0U, vector[0].value()); for (size_t j = 0; j < i; ++j) - EXPECT_EQ(vector[j + 1].value(), j); - EXPECT_EQ(vector.last().value(), i); + EXPECT_EQ(j, vector[j + 1].value()); + EXPECT_EQ(i, vector.last().value()); } } @@ -193,7 +291,7 @@ TEST(WTF_Vector, MoveOnly_Insert) for (size_t i = 0; i < 100; ++i) { MoveOnly moveOnly(i); - vector.insert(0, std::move(moveOnly)); + vector.insert(0, WTFMove(moveOnly)); EXPECT_EQ(0U, moveOnly.value()); } @@ -203,11 +301,11 @@ TEST(WTF_Vector, MoveOnly_Insert) for (size_t i = 0; i < 200; i += 2) { MoveOnly moveOnly(1000 + i); - vector.insert(i, std::move(moveOnly)); + vector.insert(i, WTFMove(moveOnly)); EXPECT_EQ(0U, moveOnly.value()); } - EXPECT_EQ(vector.size(), 200U); + EXPECT_EQ(200U, vector.size()); for (size_t i = 0; i < 200; ++i) { if (i % 2) EXPECT_EQ(99 - i / 2, vector[i].value()); @@ -216,4 +314,304 @@ TEST(WTF_Vector, MoveOnly_Insert) } } +TEST(WTF_Vector, MoveOnly_TakeLast) +{ + Vector<MoveOnly> vector; + + for (size_t i = 0; i < 100; ++i) { + MoveOnly moveOnly(i); + vector.append(WTFMove(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..e809ddac4 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WTF/WTFString.cpp @@ -0,0 +1,295 @@ +/* + * 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 = WTFMove(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)); +} + +TEST(WTF, StringExistingHash) +{ + String string1("Template Literal"); + ASSERT_FALSE(string1.isNull()); + ASSERT_FALSE(string1.impl()->hasHash()); + string1.impl()->hash(); + ASSERT_EQ(string1.existingHash(), string1.impl()->existingHash()); + String string2; + ASSERT_EQ(string2.existingHash(), 0u); +} + +} // 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/gobject/GUniquePtr.cpp b/Tools/TestWebKitAPI/Tests/WTF/glib/GUniquePtr.cpp index 8942f8657..11f02c7b0 100644 --- a/Tools/TestWebKitAPI/Tests/WTF/gobject/GUniquePtr.cpp +++ b/Tools/TestWebKitAPI/Tests/WTF/glib/GUniquePtr.cpp @@ -80,7 +80,7 @@ static void (* _g_key_file_free)(GKeyFile*) = g_key_file_free; log() << "g_key_file_free(" << ptr << ");"; \ _g_key_file_free(x); -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GUniquePtr.h> 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 |