diff options
Diffstat (limited to 'mfbt/tests')
47 files changed, 12388 insertions, 0 deletions
diff --git a/mfbt/tests/TestArray.cpp b/mfbt/tests/TestArray.cpp new file mode 100644 index 000000000..bdaeed825 --- /dev/null +++ b/mfbt/tests/TestArray.cpp @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Array.h" + +void TestInitialValueByConstructor() +{ + using namespace mozilla; + // Style 1 + Array<int32_t, 3> arr1(1, 2, 3); + MOZ_RELEASE_ASSERT(arr1[0] == 1); + MOZ_RELEASE_ASSERT(arr1[1] == 2); + MOZ_RELEASE_ASSERT(arr1[2] == 3); + // Style 2 + Array<int32_t, 3> arr2{5, 6, 7}; + MOZ_RELEASE_ASSERT(arr2[0] == 5); + MOZ_RELEASE_ASSERT(arr2[1] == 6); + MOZ_RELEASE_ASSERT(arr2[2] == 7); + // Style 3 + Array<int32_t, 3> arr3({8, 9, 10}); + MOZ_RELEASE_ASSERT(arr3[0] == 8); + MOZ_RELEASE_ASSERT(arr3[1] == 9); + MOZ_RELEASE_ASSERT(arr3[2] == 10); +} + +int +main() +{ + TestInitialValueByConstructor(); + return 0; +}
\ No newline at end of file diff --git a/mfbt/tests/TestArrayUtils.cpp b/mfbt/tests/TestArrayUtils.cpp new file mode 100644 index 000000000..6566a3cd8 --- /dev/null +++ b/mfbt/tests/TestArrayUtils.cpp @@ -0,0 +1,313 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Assertions.h" + +using mozilla::IsInRange; + +static void +TestIsInRangeNonClass() +{ + void* nul = nullptr; + int* intBegin = nullptr; + int* intEnd = intBegin + 1; + int* intEnd2 = intBegin + 2; + + MOZ_RELEASE_ASSERT(IsInRange(nul, intBegin, intEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(nul, intEnd, intEnd2)); + + MOZ_RELEASE_ASSERT(IsInRange(intBegin, intBegin, intEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(intEnd, intBegin, intEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(intBegin, intBegin, intEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(intEnd, intBegin, intEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(intEnd2, intBegin, intEnd2)); + + uintptr_t uintBegin = uintptr_t(intBegin); + uintptr_t uintEnd = uintptr_t(intEnd); + uintptr_t uintEnd2 = uintptr_t(intEnd2); + + MOZ_RELEASE_ASSERT(IsInRange(nul, uintBegin, uintEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(nul, uintEnd, uintEnd2)); + + MOZ_RELEASE_ASSERT(IsInRange(intBegin, uintBegin, uintEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(intEnd, uintBegin, uintEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(intBegin, uintBegin, uintEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(intEnd, uintBegin, uintEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(intEnd2, uintBegin, uintEnd2)); +} + +static void +TestIsInRangeVoid() +{ + int* intBegin = nullptr; + int* intEnd = intBegin + 1; + int* intEnd2 = intBegin + 2; + + void* voidBegin = intBegin; + void* voidEnd = intEnd; + void* voidEnd2 = intEnd2; + + MOZ_RELEASE_ASSERT(IsInRange(voidBegin, intBegin, intEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(voidEnd, intBegin, intEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(voidBegin, voidBegin, voidEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(voidEnd, voidBegin, voidEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(voidBegin, intBegin, intEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(voidEnd, intBegin, intEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(voidEnd2, intBegin, intEnd2)); + + MOZ_RELEASE_ASSERT(IsInRange(voidBegin, voidBegin, voidEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(voidEnd, voidBegin, voidEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(voidEnd2, voidBegin, voidEnd2)); + + uintptr_t uintBegin = uintptr_t(intBegin); + uintptr_t uintEnd = uintptr_t(intEnd); + uintptr_t uintEnd2 = uintptr_t(intEnd2); + + MOZ_RELEASE_ASSERT(IsInRange(voidBegin, uintBegin, uintEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(voidEnd, uintBegin, uintEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(voidBegin, uintBegin, uintEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(voidEnd, uintBegin, uintEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(voidEnd2, uintBegin, uintEnd2)); +} + +struct Base { int mX; }; + +static void +TestIsInRangeClass() +{ + void* nul = nullptr; + Base* baseBegin = nullptr; + Base* baseEnd = baseBegin + 1; + Base* baseEnd2 = baseBegin + 2; + + MOZ_RELEASE_ASSERT(IsInRange(nul, baseBegin, baseEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(nul, baseEnd, baseEnd2)); + + MOZ_RELEASE_ASSERT(IsInRange(baseBegin, baseBegin, baseEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, baseBegin, baseEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(baseBegin, baseBegin, baseEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(baseEnd, baseBegin, baseEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, baseBegin, baseEnd2)); + + uintptr_t ubaseBegin = uintptr_t(baseBegin); + uintptr_t ubaseEnd = uintptr_t(baseEnd); + uintptr_t ubaseEnd2 = uintptr_t(baseEnd2); + + MOZ_RELEASE_ASSERT(IsInRange(nul, ubaseBegin, ubaseEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(nul, ubaseEnd, ubaseEnd2)); + + MOZ_RELEASE_ASSERT(IsInRange(baseBegin, ubaseBegin, ubaseEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, ubaseBegin, ubaseEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(baseBegin, ubaseBegin, ubaseEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(baseEnd, ubaseBegin, ubaseEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, ubaseBegin, ubaseEnd2)); +} + +struct EmptyBase {}; + +static void +TestIsInRangeEmptyClass() +{ + void* nul = nullptr; + EmptyBase* baseBegin = nullptr; + EmptyBase* baseEnd = baseBegin + 1; + EmptyBase* baseEnd2 = baseBegin + 2; + + MOZ_RELEASE_ASSERT(IsInRange(nul, baseBegin, baseEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(nul, baseEnd, baseEnd2)); + + MOZ_RELEASE_ASSERT(IsInRange(baseBegin, baseBegin, baseEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, baseBegin, baseEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(baseBegin, baseBegin, baseEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(baseEnd, baseBegin, baseEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, baseBegin, baseEnd2)); + + uintptr_t ubaseBegin = uintptr_t(baseBegin); + uintptr_t ubaseEnd = uintptr_t(baseEnd); + uintptr_t ubaseEnd2 = uintptr_t(baseEnd2); + + MOZ_RELEASE_ASSERT(IsInRange(nul, ubaseBegin, ubaseEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(nul, ubaseEnd, ubaseEnd2)); + + MOZ_RELEASE_ASSERT(IsInRange(baseBegin, ubaseBegin, ubaseEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, ubaseBegin, ubaseEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(baseBegin, ubaseBegin, ubaseEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(baseEnd, ubaseBegin, ubaseEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, ubaseBegin, ubaseEnd2)); +} + +struct Derived : Base {}; + +static void +TestIsInRangeClassDerived() +{ + void* nul = nullptr; + Derived* derivedBegin = nullptr; + Derived* derivedEnd = derivedBegin + 1; + Derived* derivedEnd2 = derivedBegin + 2; + + Base* baseBegin = static_cast<Base*>(derivedBegin); + Base* baseEnd = static_cast<Base*>(derivedEnd); + Base* baseEnd2 = static_cast<Base*>(derivedEnd2); + + MOZ_RELEASE_ASSERT(IsInRange(nul, derivedBegin, derivedEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(nul, derivedEnd, derivedEnd2)); + + MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedBegin, derivedEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, derivedBegin, derivedEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedBegin, derivedEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(baseEnd, derivedBegin, derivedEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, derivedBegin, derivedEnd2)); + + uintptr_t uderivedBegin = uintptr_t(derivedBegin); + uintptr_t uderivedEnd = uintptr_t(derivedEnd); + uintptr_t uderivedEnd2 = uintptr_t(derivedEnd2); + + MOZ_RELEASE_ASSERT(IsInRange(derivedBegin, uderivedBegin, uderivedEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(derivedEnd, uderivedBegin, uderivedEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(derivedBegin, uderivedBegin, uderivedEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(derivedEnd, uderivedBegin, uderivedEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(derivedEnd2, uderivedBegin, uderivedEnd2)); +} + +struct DerivedEmpty : EmptyBase {}; + +static void +TestIsInRangeClassDerivedEmpty() +{ + void* nul = nullptr; + DerivedEmpty* derivedEmptyBegin = nullptr; + DerivedEmpty* derivedEmptyEnd = derivedEmptyBegin + 1; + DerivedEmpty* derivedEmptyEnd2 = derivedEmptyBegin + 2; + + EmptyBase* baseBegin = static_cast<EmptyBase*>(derivedEmptyBegin); + EmptyBase* baseEnd = static_cast<EmptyBase*>(derivedEmptyEnd); + EmptyBase* baseEnd2 = static_cast<EmptyBase*>(derivedEmptyEnd2); + + MOZ_RELEASE_ASSERT(IsInRange(nul, derivedEmptyBegin, derivedEmptyEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(nul, derivedEmptyEnd, derivedEmptyEnd2)); + + MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedEmptyBegin, derivedEmptyEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, derivedEmptyBegin, derivedEmptyEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedEmptyBegin, derivedEmptyEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(baseEnd, derivedEmptyBegin, derivedEmptyEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, derivedEmptyBegin, derivedEmptyEnd2)); + + uintptr_t uderivedEmptyBegin = uintptr_t(derivedEmptyBegin); + uintptr_t uderivedEmptyEnd = uintptr_t(derivedEmptyEnd); + uintptr_t uderivedEmptyEnd2 = uintptr_t(derivedEmptyEnd2); + + MOZ_RELEASE_ASSERT(IsInRange(derivedEmptyBegin, uderivedEmptyBegin, + uderivedEmptyEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(derivedEmptyEnd, uderivedEmptyBegin, + uderivedEmptyEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(derivedEmptyBegin, uderivedEmptyBegin, + uderivedEmptyEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(derivedEmptyEnd, uderivedEmptyBegin, + uderivedEmptyEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(derivedEmptyEnd2, uderivedEmptyBegin, + uderivedEmptyEnd2)); +} + +struct ExtraDerived : Base { int y; }; + +static void +TestIsInRangeClassExtraDerived() +{ + void* nul = nullptr; + ExtraDerived* derivedBegin = nullptr; + ExtraDerived* derivedEnd = derivedBegin + 1; + ExtraDerived* derivedEnd2 = derivedBegin + 2; + + Base* baseBegin = static_cast<Base*>(derivedBegin); + Base* baseEnd = static_cast<Base*>(derivedEnd); + Base* baseEnd2 = static_cast<Base*>(derivedEnd2); + + MOZ_RELEASE_ASSERT(IsInRange(nul, derivedBegin, derivedEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(nul, derivedEnd, derivedEnd2)); + + MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedBegin, derivedEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, derivedBegin, derivedEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedBegin, derivedEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(baseEnd, derivedBegin, derivedEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, derivedBegin, derivedEnd2)); + + uintptr_t uderivedBegin = uintptr_t(derivedBegin); + uintptr_t uderivedEnd = uintptr_t(derivedEnd); + uintptr_t uderivedEnd2 = uintptr_t(derivedEnd2); + + MOZ_RELEASE_ASSERT(IsInRange(derivedBegin, uderivedBegin, uderivedEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(derivedEnd, uderivedBegin, uderivedEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(derivedBegin, uderivedBegin, uderivedEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(derivedEnd, uderivedBegin, uderivedEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(derivedEnd2, uderivedBegin, uderivedEnd2)); +} + +struct ExtraDerivedEmpty : EmptyBase { int y; }; + +static void +TestIsInRangeClassExtraDerivedEmpty() +{ + void* nul = nullptr; + ExtraDerivedEmpty* derivedBegin = nullptr; + ExtraDerivedEmpty* derivedEnd = derivedBegin + 1; + ExtraDerivedEmpty* derivedEnd2 = derivedBegin + 2; + + EmptyBase* baseBegin = static_cast<EmptyBase*>(derivedBegin); + EmptyBase* baseEnd = static_cast<EmptyBase*>(derivedEnd); + EmptyBase* baseEnd2 = static_cast<EmptyBase*>(derivedEnd2); + + MOZ_RELEASE_ASSERT(IsInRange(nul, derivedBegin, derivedEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(nul, derivedEnd, derivedEnd2)); + + MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedBegin, derivedEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, derivedBegin, derivedEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedBegin, derivedEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(baseEnd, derivedBegin, derivedEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, derivedBegin, derivedEnd2)); + + uintptr_t uderivedBegin = uintptr_t(derivedBegin); + uintptr_t uderivedEnd = uintptr_t(derivedEnd); + uintptr_t uderivedEnd2 = uintptr_t(derivedEnd2); + + MOZ_RELEASE_ASSERT(IsInRange(derivedBegin, uderivedBegin, uderivedEnd)); + MOZ_RELEASE_ASSERT(!IsInRange(derivedEnd, uderivedBegin, uderivedEnd)); + + MOZ_RELEASE_ASSERT(IsInRange(derivedBegin, uderivedBegin, uderivedEnd2)); + MOZ_RELEASE_ASSERT(IsInRange(derivedEnd, uderivedBegin, uderivedEnd2)); + MOZ_RELEASE_ASSERT(!IsInRange(derivedEnd2, uderivedBegin, uderivedEnd2)); +} + +int +main() +{ + TestIsInRangeNonClass(); + TestIsInRangeVoid(); + TestIsInRangeClass(); + TestIsInRangeEmptyClass(); + TestIsInRangeClassDerived(); + TestIsInRangeClassDerivedEmpty(); + TestIsInRangeClassExtraDerived(); + TestIsInRangeClassExtraDerivedEmpty(); + return 0; +} diff --git a/mfbt/tests/TestAtomics.cpp b/mfbt/tests/TestAtomics.cpp new file mode 100644 index 000000000..e23b3d517 --- /dev/null +++ b/mfbt/tests/TestAtomics.cpp @@ -0,0 +1,295 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/Atomics.h" + +#include <stdint.h> + +using mozilla::Atomic; +using mozilla::MemoryOrdering; +using mozilla::Relaxed; +using mozilla::ReleaseAcquire; +using mozilla::SequentiallyConsistent; + +#define A(a,b) MOZ_RELEASE_ASSERT(a,b) + +template <typename T, MemoryOrdering Order> +static void +TestTypeWithOrdering() +{ + Atomic<T, Order> atomic(5); + A(atomic == 5, "Atomic variable did not initialize"); + + // Test atomic increment + A(++atomic == T(6), "Atomic increment did not work"); + A(atomic++ == T(6), "Atomic post-increment did not work"); + A(atomic == T(7), "Atomic post-increment did not work"); + + // Test atomic decrement + A(--atomic == 6, "Atomic decrement did not work"); + A(atomic-- == 6, "Atomic post-decrement did not work"); + A(atomic == 5, "Atomic post-decrement did not work"); + + // Test other arithmetic. + T result; + result = (atomic += T(5)); + A(atomic == T(10), "Atomic += did not work"); + A(result == T(10), "Atomic += returned the wrong value"); + result = (atomic -= T(3)); + A(atomic == T(7), "Atomic -= did not work"); + A(result == T(7), "Atomic -= returned the wrong value"); + + // Test assignment + result = (atomic = T(5)); + A(atomic == T(5), "Atomic assignment failed"); + A(result == T(5), "Atomic assignment returned the wrong value"); + + // Test logical operations. + result = (atomic ^= T(2)); + A(atomic == T(7), "Atomic ^= did not work"); + A(result == T(7), "Atomic ^= returned the wrong value"); + result = (atomic ^= T(4)); + A(atomic == T(3), "Atomic ^= did not work"); + A(result == T(3), "Atomic ^= returned the wrong value"); + result = (atomic |= T(8)); + A(atomic == T(11), "Atomic |= did not work"); + A(result == T(11), "Atomic |= returned the wrong value"); + result = (atomic |= T(8)); + A(atomic == T(11), "Atomic |= did not work"); + A(result == T(11), "Atomic |= returned the wrong value"); + result = (atomic &= T(12)); + A(atomic == T(8), "Atomic &= did not work"); + A(result == T(8), "Atomic &= returned the wrong value"); + + // Test exchange. + atomic = T(30); + result = atomic.exchange(42); + A(atomic == T(42), "Atomic exchange did not work"); + A(result == T(30), "Atomic exchange returned the wrong value"); + + // Test CAS. + atomic = T(1); + bool boolResult = atomic.compareExchange(0, 2); + A(!boolResult, "CAS should have returned false."); + A(atomic == T(1), "CAS shouldn't have done anything."); + + boolResult = atomic.compareExchange(1, 42); + A(boolResult, "CAS should have succeeded."); + A(atomic == T(42), "CAS should have changed atomic's value."); +} + +template<typename T, MemoryOrdering Order> +static void +TestPointerWithOrdering() +{ + T array1[10]; + Atomic<T*, Order> atomic(array1); + A(atomic == array1, "Atomic variable did not initialize"); + + // Test atomic increment + A(++atomic == array1 + 1, "Atomic increment did not work"); + A(atomic++ == array1 + 1, "Atomic post-increment did not work"); + A(atomic == array1 + 2, "Atomic post-increment did not work"); + + // Test atomic decrement + A(--atomic == array1 + 1, "Atomic decrement did not work"); + A(atomic-- == array1 + 1, "Atomic post-decrement did not work"); + A(atomic == array1, "Atomic post-decrement did not work"); + + // Test other arithmetic operations + T* result; + result = (atomic += 2); + A(atomic == array1 + 2, "Atomic += did not work"); + A(result == array1 + 2, "Atomic += returned the wrong value"); + result = (atomic -= 1); + A(atomic == array1 + 1, "Atomic -= did not work"); + A(result == array1 + 1, "Atomic -= returned the wrong value"); + + // Test stores + result = (atomic = array1); + A(atomic == array1, "Atomic assignment did not work"); + A(result == array1, "Atomic assignment returned the wrong value"); + + // Test exchange + atomic = array1 + 2; + result = atomic.exchange(array1); + A(atomic == array1, "Atomic exchange did not work"); + A(result == array1 + 2, "Atomic exchange returned the wrong value"); + + atomic = array1; + bool boolResult = atomic.compareExchange(array1 + 1, array1 + 2); + A(!boolResult, "CAS should have returned false."); + A(atomic == array1, "CAS shouldn't have done anything."); + + boolResult = atomic.compareExchange(array1, array1 + 3); + A(boolResult, "CAS should have succeeded."); + A(atomic == array1 + 3, "CAS should have changed atomic's value."); +} + +enum EnumType +{ + EnumType_0 = 0, + EnumType_1 = 1, + EnumType_2 = 2, + EnumType_3 = 3 +}; + +template<MemoryOrdering Order> +static void +TestEnumWithOrdering() +{ + Atomic<EnumType, Order> atomic(EnumType_2); + A(atomic == EnumType_2, "Atomic variable did not initialize"); + + // Test assignment + EnumType result; + result = (atomic = EnumType_3); + A(atomic == EnumType_3, "Atomic assignment failed"); + A(result == EnumType_3, "Atomic assignment returned the wrong value"); + + // Test exchange. + atomic = EnumType_1; + result = atomic.exchange(EnumType_2); + A(atomic == EnumType_2, "Atomic exchange did not work"); + A(result == EnumType_1, "Atomic exchange returned the wrong value"); + + // Test CAS. + atomic = EnumType_1; + bool boolResult = atomic.compareExchange(EnumType_0, EnumType_2); + A(!boolResult, "CAS should have returned false."); + A(atomic == EnumType_1, "CAS shouldn't have done anything."); + + boolResult = atomic.compareExchange(EnumType_1, EnumType_3); + A(boolResult, "CAS should have succeeded."); + A(atomic == EnumType_3, "CAS should have changed atomic's value."); +} + +enum class EnumClass : uint32_t +{ + Value0 = 0, + Value1 = 1, + Value2 = 2, + Value3 = 3 +}; + +template<MemoryOrdering Order> +static void +TestEnumClassWithOrdering() +{ + Atomic<EnumClass, Order> atomic(EnumClass::Value2); + A(atomic == EnumClass::Value2, "Atomic variable did not initialize"); + + // Test assignment + EnumClass result; + result = (atomic = EnumClass::Value3); + A(atomic == EnumClass::Value3, "Atomic assignment failed"); + A(result == EnumClass::Value3, "Atomic assignment returned the wrong value"); + + // Test exchange. + atomic = EnumClass::Value1; + result = atomic.exchange(EnumClass::Value2); + A(atomic == EnumClass::Value2, "Atomic exchange did not work"); + A(result == EnumClass::Value1, "Atomic exchange returned the wrong value"); + + // Test CAS. + atomic = EnumClass::Value1; + bool boolResult = atomic.compareExchange(EnumClass::Value0, EnumClass::Value2); + A(!boolResult, "CAS should have returned false."); + A(atomic == EnumClass::Value1, "CAS shouldn't have done anything."); + + boolResult = atomic.compareExchange(EnumClass::Value1, EnumClass::Value3); + A(boolResult, "CAS should have succeeded."); + A(atomic == EnumClass::Value3, "CAS should have changed atomic's value."); +} + +template <MemoryOrdering Order> +static void +TestBoolWithOrdering() +{ + Atomic<bool, Order> atomic(false); + A(atomic == false, "Atomic variable did not initialize"); + + // Test assignment + bool result; + result = (atomic = true); + A(atomic == true, "Atomic assignment failed"); + A(result == true, "Atomic assignment returned the wrong value"); + + // Test exchange. + atomic = false; + result = atomic.exchange(true); + A(atomic == true, "Atomic exchange did not work"); + A(result == false, "Atomic exchange returned the wrong value"); + + // Test CAS. + atomic = false; + bool boolResult = atomic.compareExchange(true, false); + A(!boolResult, "CAS should have returned false."); + A(atomic == false, "CAS shouldn't have done anything."); + + boolResult = atomic.compareExchange(false, true); + A(boolResult, "CAS should have succeeded."); + A(atomic == true, "CAS should have changed atomic's value."); +} + +template <typename T> +static void +TestType() +{ + TestTypeWithOrdering<T, SequentiallyConsistent>(); + TestTypeWithOrdering<T, ReleaseAcquire>(); + TestTypeWithOrdering<T, Relaxed>(); +} + +template<typename T> +static void +TestPointer() +{ + TestPointerWithOrdering<T, SequentiallyConsistent>(); + TestPointerWithOrdering<T, ReleaseAcquire>(); + TestPointerWithOrdering<T, Relaxed>(); +} + +static void +TestEnum() +{ + TestEnumWithOrdering<SequentiallyConsistent>(); + TestEnumWithOrdering<ReleaseAcquire>(); + TestEnumWithOrdering<Relaxed>(); + + TestEnumClassWithOrdering<SequentiallyConsistent>(); + TestEnumClassWithOrdering<ReleaseAcquire>(); + TestEnumClassWithOrdering<Relaxed>(); +} + +static void +TestBool() +{ + TestBoolWithOrdering<SequentiallyConsistent>(); + TestBoolWithOrdering<ReleaseAcquire>(); + TestBoolWithOrdering<Relaxed>(); +} + +#undef A + +int +main() +{ + TestType<uint32_t>(); + TestType<int32_t>(); + TestType<uint64_t>(); + TestType<int64_t>(); + TestType<intptr_t>(); + TestType<uintptr_t>(); + TestPointer<int>(); + TestPointer<float>(); + TestPointer<uint16_t*>(); + TestPointer<uint32_t*>(); + TestEnum(); + TestBool(); + return 0; +} diff --git a/mfbt/tests/TestBinarySearch.cpp b/mfbt/tests/TestBinarySearch.cpp new file mode 100644 index 000000000..44644bd3a --- /dev/null +++ b/mfbt/tests/TestBinarySearch.cpp @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/BinarySearch.h" +#include "mozilla/Vector.h" + +using mozilla::ArrayLength; +using mozilla::BinarySearch; +using mozilla::BinarySearchIf; +using mozilla::Vector; + +#define A(a) MOZ_RELEASE_ASSERT(a) + +struct Person +{ + int mAge; + int mId; + Person(int aAge, int aId) : mAge(aAge), mId(aId) {} +}; + +struct GetAge +{ + Vector<Person>& mV; + explicit GetAge(Vector<Person>& aV) : mV(aV) {} + int operator[](size_t index) const { return mV[index].mAge; } +}; + +struct RangeFinder { + const int mLower, mUpper; + RangeFinder(int lower, int upper) : mLower(lower), mUpper(upper) {} + int operator()(int val) const { + if (val >= mUpper) return -1; + if (val < mLower) return 1; + return 0; + } +}; + +static void +TestBinarySearch() +{ + size_t m; + + Vector<int> v1; + MOZ_RELEASE_ASSERT(v1.append(2)); + MOZ_RELEASE_ASSERT(v1.append(4)); + MOZ_RELEASE_ASSERT(v1.append(6)); + MOZ_RELEASE_ASSERT(v1.append(8)); + + MOZ_RELEASE_ASSERT(!BinarySearch(v1, 0, v1.length(), 1, &m) && m == 0); + MOZ_RELEASE_ASSERT( BinarySearch(v1, 0, v1.length(), 2, &m) && m == 0); + MOZ_RELEASE_ASSERT(!BinarySearch(v1, 0, v1.length(), 3, &m) && m == 1); + MOZ_RELEASE_ASSERT( BinarySearch(v1, 0, v1.length(), 4, &m) && m == 1); + MOZ_RELEASE_ASSERT(!BinarySearch(v1, 0, v1.length(), 5, &m) && m == 2); + MOZ_RELEASE_ASSERT( BinarySearch(v1, 0, v1.length(), 6, &m) && m == 2); + MOZ_RELEASE_ASSERT(!BinarySearch(v1, 0, v1.length(), 7, &m) && m == 3); + MOZ_RELEASE_ASSERT( BinarySearch(v1, 0, v1.length(), 8, &m) && m == 3); + MOZ_RELEASE_ASSERT(!BinarySearch(v1, 0, v1.length(), 9, &m) && m == 4); + + MOZ_RELEASE_ASSERT(!BinarySearch(v1, 1, 3, 1, &m) && m == 1); + MOZ_RELEASE_ASSERT(!BinarySearch(v1, 1, 3, 2, &m) && m == 1); + MOZ_RELEASE_ASSERT(!BinarySearch(v1, 1, 3, 3, &m) && m == 1); + MOZ_RELEASE_ASSERT( BinarySearch(v1, 1, 3, 4, &m) && m == 1); + MOZ_RELEASE_ASSERT(!BinarySearch(v1, 1, 3, 5, &m) && m == 2); + MOZ_RELEASE_ASSERT( BinarySearch(v1, 1, 3, 6, &m) && m == 2); + MOZ_RELEASE_ASSERT(!BinarySearch(v1, 1, 3, 7, &m) && m == 3); + MOZ_RELEASE_ASSERT(!BinarySearch(v1, 1, 3, 8, &m) && m == 3); + MOZ_RELEASE_ASSERT(!BinarySearch(v1, 1, 3, 9, &m) && m == 3); + + MOZ_RELEASE_ASSERT(!BinarySearch(v1, 0, 0, 0, &m) && m == 0); + MOZ_RELEASE_ASSERT(!BinarySearch(v1, 0, 0, 9, &m) && m == 0); + + Vector<int> v2; + MOZ_RELEASE_ASSERT(!BinarySearch(v2, 0, 0, 0, &m) && m == 0); + MOZ_RELEASE_ASSERT(!BinarySearch(v2, 0, 0, 9, &m) && m == 0); + + Vector<Person> v3; + MOZ_RELEASE_ASSERT(v3.append(Person(2, 42))); + MOZ_RELEASE_ASSERT(v3.append(Person(4, 13))); + MOZ_RELEASE_ASSERT(v3.append(Person(6, 360))); + + A(!BinarySearch(GetAge(v3), 0, v3.length(), 1, &m) && m == 0); + A( BinarySearch(GetAge(v3), 0, v3.length(), 2, &m) && m == 0); + A(!BinarySearch(GetAge(v3), 0, v3.length(), 3, &m) && m == 1); + A( BinarySearch(GetAge(v3), 0, v3.length(), 4, &m) && m == 1); + A(!BinarySearch(GetAge(v3), 0, v3.length(), 5, &m) && m == 2); + A( BinarySearch(GetAge(v3), 0, v3.length(), 6, &m) && m == 2); + A(!BinarySearch(GetAge(v3), 0, v3.length(), 7, &m) && m == 3); +} + +static void +TestBinarySearchIf() +{ + const int v1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + const size_t len = ArrayLength(v1); + size_t m; + + A( BinarySearchIf(v1, 0, len, RangeFinder( 2, 3), &m) && m == 2); + A(!BinarySearchIf(v1, 0, len, RangeFinder(-5, -2), &m) && m == 0); + A( BinarySearchIf(v1, 0, len, RangeFinder( 3, 5), &m) && m >= 3 && m < 5); + A(!BinarySearchIf(v1, 0, len, RangeFinder(10, 12), &m) && m == 10); +} + +int +main() +{ + TestBinarySearch(); + TestBinarySearchIf(); + return 0; +} diff --git a/mfbt/tests/TestBloomFilter.cpp b/mfbt/tests/TestBloomFilter.cpp new file mode 100644 index 000000000..42e8b821e --- /dev/null +++ b/mfbt/tests/TestBloomFilter.cpp @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/BloomFilter.h" + +#include <stddef.h> +#include <stdio.h> + +using mozilla::BloomFilter; + +class FilterChecker +{ +public: + explicit FilterChecker(uint32_t aHash) : mHash(aHash) { } + + uint32_t hash() const { return mHash; } + +private: + uint32_t mHash; +}; + +int +main() +{ + BloomFilter<12, FilterChecker>* filter = new BloomFilter<12, FilterChecker>(); + MOZ_RELEASE_ASSERT(filter); + + FilterChecker one(1); + FilterChecker two(0x20000); + FilterChecker many(0x10000); + FilterChecker multiple(0x20001); + + filter->add(&one); + MOZ_RELEASE_ASSERT(filter->mightContain(&one), + "Filter should contain 'one'"); + + MOZ_RELEASE_ASSERT(!filter->mightContain(&multiple), + "Filter claims to contain 'multiple' when it should not"); + + MOZ_RELEASE_ASSERT(filter->mightContain(&many), + "Filter should contain 'many' (false positive)"); + + filter->add(&two); + MOZ_RELEASE_ASSERT(filter->mightContain(&multiple), + "Filter should contain 'multiple' (false positive)"); + + // Test basic removals + filter->remove(&two); + MOZ_RELEASE_ASSERT(!filter->mightContain(&multiple), + "Filter claims to contain 'multiple' when it should not after two " + "was removed"); + + // Test multiple addition/removal + const size_t FILTER_SIZE = 255; + for (size_t i = 0; i < FILTER_SIZE - 1; ++i) { + filter->add(&two); + } + MOZ_RELEASE_ASSERT(filter->mightContain(&multiple), + "Filter should contain 'multiple' after 'two' added lots of times " + "(false positive)"); + + for (size_t i = 0; i < FILTER_SIZE - 1; ++i) { + filter->remove(&two); + } + MOZ_RELEASE_ASSERT(!filter->mightContain(&multiple), + "Filter claims to contain 'multiple' when it should not after two " + "was removed lots of times"); + + // Test overflowing the filter buckets + for (size_t i = 0; i < FILTER_SIZE + 1; ++i) { + filter->add(&two); + } + MOZ_RELEASE_ASSERT(filter->mightContain(&multiple), + "Filter should contain 'multiple' after 'two' added lots more " + "times (false positive)"); + + for (size_t i = 0; i < FILTER_SIZE + 1; ++i) { + filter->remove(&two); + } + MOZ_RELEASE_ASSERT(filter->mightContain(&multiple), + "Filter claims to not contain 'multiple' even though we should " + "have run out of space in the buckets (false positive)"); + MOZ_RELEASE_ASSERT(filter->mightContain(&two), + "Filter claims to not contain 'two' even though we should have " + "run out of space in the buckets (false positive)"); + + filter->remove(&one); + + MOZ_RELEASE_ASSERT(!filter->mightContain(&one), + "Filter should not contain 'one', because we didn't overflow its " + "bucket"); + + filter->clear(); + + MOZ_RELEASE_ASSERT(!filter->mightContain(&multiple), + "clear() failed to work"); + + return 0; +} diff --git a/mfbt/tests/TestBufferList.cpp b/mfbt/tests/TestBufferList.cpp new file mode 100644 index 000000000..cccaac021 --- /dev/null +++ b/mfbt/tests/TestBufferList.cpp @@ -0,0 +1,256 @@ +/* -*- Mode: C++; tab-width: 9; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This is included first to ensure it doesn't implicitly depend on anything +// else. +#include "mozilla/BufferList.h" + +// It would be nice if we could use the InfallibleAllocPolicy from mozalloc, +// but MFBT cannot use mozalloc. +class InfallibleAllocPolicy +{ +public: + template <typename T> + T* pod_malloc(size_t aNumElems) + { + if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) { + MOZ_CRASH("TestBufferList.cpp: overflow"); + } + T* rv = static_cast<T*>(malloc(aNumElems * sizeof(T))); + if (!rv) { + MOZ_CRASH("TestBufferList.cpp: out of memory"); + } + return rv; + } + + void free_(void* aPtr) { free(aPtr); } + + void reportAllocOverflow() const {} + + bool checkSimulatedOOM() const { return true; } +}; + +typedef mozilla::BufferList<InfallibleAllocPolicy> BufferList; + +int main(void) +{ + const size_t kInitialSize = 16; + const size_t kInitialCapacity = 24; + const size_t kStandardCapacity = 32; + + BufferList bl(kInitialSize, kInitialCapacity, kStandardCapacity); + + memset(bl.Start(), 0x0c, kInitialSize); + MOZ_RELEASE_ASSERT(bl.Size() == kInitialSize); + + // Simple iteration and access. + + BufferList::IterImpl iter(bl.Iter()); + MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize); + MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize)); + MOZ_RELEASE_ASSERT(!iter.HasRoomFor(kInitialSize + 1)); + MOZ_RELEASE_ASSERT(!iter.HasRoomFor(size_t(-1))); + MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c); + MOZ_RELEASE_ASSERT(!iter.Done()); + + iter.Advance(bl, 4); + MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize - 4); + MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize - 4)); + MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c); + MOZ_RELEASE_ASSERT(!iter.Done()); + + iter.Advance(bl, 11); + MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialSize - 4 - 11); + MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialSize - 4 - 11)); + MOZ_RELEASE_ASSERT(!iter.HasRoomFor(kInitialSize - 4 - 11 + 1)); + MOZ_RELEASE_ASSERT(*iter.Data() == 0x0c); + MOZ_RELEASE_ASSERT(!iter.Done()); + + iter.Advance(bl, kInitialSize - 4 - 11); + MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 0); + MOZ_RELEASE_ASSERT(!iter.HasRoomFor(1)); + MOZ_RELEASE_ASSERT(iter.Done()); + + // Writing to the buffer. + + const size_t kSmallWrite = 16; + + char toWrite[kSmallWrite]; + memset(toWrite, 0x0a, kSmallWrite); + bl.WriteBytes(toWrite, kSmallWrite); + + MOZ_RELEASE_ASSERT(bl.Size() == kInitialSize + kSmallWrite); + + iter = bl.Iter(); + iter.Advance(bl, kInitialSize); + MOZ_RELEASE_ASSERT(!iter.Done()); + MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialCapacity - kInitialSize); + MOZ_RELEASE_ASSERT(iter.HasRoomFor(kInitialCapacity - kInitialSize)); + MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a); + + // AdvanceAcrossSegments. + + iter = bl.Iter(); + MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialCapacity - 4)); + MOZ_RELEASE_ASSERT(!iter.Done()); + MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 4); + MOZ_RELEASE_ASSERT(iter.HasRoomFor(4)); + MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a); + + iter = bl.Iter(); + MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite - 4)); + MOZ_RELEASE_ASSERT(!iter.Done()); + MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == 4); + MOZ_RELEASE_ASSERT(iter.HasRoomFor(4)); + MOZ_RELEASE_ASSERT(*iter.Data() == 0x0a); + + MOZ_RELEASE_ASSERT(bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite - 1)); + MOZ_RELEASE_ASSERT(bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite)); + MOZ_RELEASE_ASSERT(!bl.Iter().AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite + 1)); + MOZ_RELEASE_ASSERT(!bl.Iter().AdvanceAcrossSegments(bl, size_t(-1))); + + // Reading non-contiguous bytes. + + char toRead[kSmallWrite]; + iter = bl.Iter(); + iter.Advance(bl, kInitialSize); + bl.ReadBytes(iter, toRead, kSmallWrite); + MOZ_RELEASE_ASSERT(memcmp(toRead, toWrite, kSmallWrite) == 0); + MOZ_RELEASE_ASSERT(iter.Done()); + + // Make sure reading up to the end of a segment advances the iter to the next + // segment. + iter = bl.Iter(); + bl.ReadBytes(iter, toRead, kInitialSize); + MOZ_RELEASE_ASSERT(!iter.Done()); + MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kInitialCapacity - kInitialSize); + + const size_t kBigWrite = 1024; + + char* toWriteBig = static_cast<char*>(malloc(kBigWrite)); + for (unsigned i = 0; i < kBigWrite; i++) { + toWriteBig[i] = i % 37; + } + bl.WriteBytes(toWriteBig, kBigWrite); + + char* toReadBig = static_cast<char*>(malloc(kBigWrite)); + iter = bl.Iter(); + MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kInitialSize + kSmallWrite)); + bl.ReadBytes(iter, toReadBig, kBigWrite); + MOZ_RELEASE_ASSERT(memcmp(toReadBig, toWriteBig, kBigWrite) == 0); + MOZ_RELEASE_ASSERT(iter.Done()); + + free(toReadBig); + free(toWriteBig); + + // Currently bl contains these segments: + // #0: offset 0, [0x0c]*16 + [0x0a]*8, size 24 + // #1: offset 24, [0x0a]*8 + [i%37 for i in 0..24], size 32 + // #2: offset 56, [i%37 for i in 24..56, size 32 + // ... + // #32: offset 1016, [i%37 for i in 984..1016], size 32 + // #33: offset 1048, [i%37 for i in 1016..1024], size 8 + + static size_t kTotalSize = kInitialSize + kSmallWrite + kBigWrite; + + MOZ_RELEASE_ASSERT(bl.Size() == kTotalSize); + + static size_t kLastSegmentSize = (kTotalSize - kInitialCapacity) % kStandardCapacity; + + iter = bl.Iter(); + MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kTotalSize - kLastSegmentSize - kStandardCapacity)); + MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kStandardCapacity); + iter.Advance(bl, kStandardCapacity); + MOZ_RELEASE_ASSERT(iter.RemainingInSegment() == kLastSegmentSize); + MOZ_RELEASE_ASSERT(unsigned(*iter.Data()) == (kTotalSize - kLastSegmentSize - kInitialSize - kSmallWrite) % 37); + + // Clear. + + bl.Clear(); + MOZ_RELEASE_ASSERT(bl.Size() == 0); + MOZ_RELEASE_ASSERT(bl.Iter().Done()); + + // Move assignment. + + const size_t kSmallCapacity = 8; + + BufferList bl2(0, kSmallCapacity, kSmallCapacity); + bl2.WriteBytes(toWrite, kSmallWrite); + bl2.WriteBytes(toWrite, kSmallWrite); + bl2.WriteBytes(toWrite, kSmallWrite); + + bl = mozilla::Move(bl2); + MOZ_RELEASE_ASSERT(bl2.Size() == 0); + MOZ_RELEASE_ASSERT(bl2.Iter().Done()); + + iter = bl.Iter(); + MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3)); + MOZ_RELEASE_ASSERT(iter.Done()); + + // MoveFallible + + bool success; + bl2 = bl.MoveFallible<InfallibleAllocPolicy>(&success); + MOZ_RELEASE_ASSERT(success); + MOZ_RELEASE_ASSERT(bl.Size() == 0); + MOZ_RELEASE_ASSERT(bl.Iter().Done()); + MOZ_RELEASE_ASSERT(bl2.Size() == kSmallWrite * 3); + + iter = bl2.Iter(); + MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kSmallWrite * 3)); + MOZ_RELEASE_ASSERT(iter.Done()); + + bl = bl2.MoveFallible<InfallibleAllocPolicy>(&success); + + // Borrowing. + + const size_t kBorrowStart = 4; + const size_t kBorrowSize = 24; + + iter = bl.Iter(); + iter.Advance(bl, kBorrowStart); + bl2 = bl.Borrow<InfallibleAllocPolicy>(iter, kBorrowSize, &success); + MOZ_RELEASE_ASSERT(success); + MOZ_RELEASE_ASSERT(bl2.Size() == kBorrowSize); + + MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3 - kBorrowSize - kBorrowStart)); + MOZ_RELEASE_ASSERT(iter.Done()); + + iter = bl2.Iter(); + MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kBorrowSize)); + MOZ_RELEASE_ASSERT(iter.Done()); + + BufferList::IterImpl iter1(bl.Iter()), iter2(bl2.Iter()); + iter1.Advance(bl, kBorrowStart); + MOZ_RELEASE_ASSERT(iter1.Data() == iter2.Data()); + MOZ_RELEASE_ASSERT(iter1.AdvanceAcrossSegments(bl, kBorrowSize - 5)); + MOZ_RELEASE_ASSERT(iter2.AdvanceAcrossSegments(bl2, kBorrowSize - 5)); + MOZ_RELEASE_ASSERT(iter1.Data() == iter2.Data()); + + // Extracting. + + const size_t kExtractStart = 8; + const size_t kExtractSize = 24; + const size_t kExtractOverSize = 1000; + + iter = bl.Iter(); + iter.Advance(bl, kExtractStart); + bl2 = bl.Extract(iter, kExtractSize, &success); + MOZ_RELEASE_ASSERT(success); + MOZ_RELEASE_ASSERT(bl2.Size() == kExtractSize); + + BufferList bl3 = bl.Extract(iter, kExtractOverSize, &success); + MOZ_RELEASE_ASSERT(!success); + + MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3 - kExtractSize - kExtractStart)); + MOZ_RELEASE_ASSERT(iter.Done()); + + iter = bl2.Iter(); + MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kExtractSize)); + MOZ_RELEASE_ASSERT(iter.Done()); + + return 0; +} diff --git a/mfbt/tests/TestCasting.cpp b/mfbt/tests/TestCasting.cpp new file mode 100644 index 000000000..c8bd12488 --- /dev/null +++ b/mfbt/tests/TestCasting.cpp @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Casting.h" + +#include <stdint.h> + +using mozilla::BitwiseCast; +using mozilla::detail::IsInBounds; + +template<typename Uint, typename Ulong, bool = (sizeof(Uint) == sizeof(Ulong))> +struct UintUlongBitwiseCast; + +template<typename Uint, typename Ulong> +struct UintUlongBitwiseCast<Uint, Ulong, true> +{ + static void test() + { + MOZ_RELEASE_ASSERT(BitwiseCast<Ulong>(Uint(8675309)) == Ulong(8675309)); + } +}; + +template<typename Uint, typename Ulong> +struct UintUlongBitwiseCast<Uint, Ulong, false> +{ + static void test() { } +}; + +static void +TestBitwiseCast() +{ + MOZ_RELEASE_ASSERT(BitwiseCast<int>(int(8675309)) == int(8675309)); + UintUlongBitwiseCast<unsigned int, unsigned long>::test(); +} + +static void +TestSameSize() +{ + MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int16_t>(int16_t(0)))); + MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int16_t>(int16_t(INT16_MIN)))); + MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int16_t>(int16_t(INT16_MAX)))); + MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, uint16_t>(uint16_t(UINT16_MAX)))); + MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, int16_t>(uint16_t(0)))); + MOZ_RELEASE_ASSERT((!IsInBounds<uint16_t, int16_t>(uint16_t(-1)))); + MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint16_t>(int16_t(-1)))); + MOZ_RELEASE_ASSERT((IsInBounds<int16_t, uint16_t>(int16_t(INT16_MAX)))); + MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint16_t>(int16_t(INT16_MIN)))); + MOZ_RELEASE_ASSERT((IsInBounds<int32_t, uint32_t>(int32_t(INT32_MAX)))); + MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, uint32_t>(int32_t(INT32_MIN)))); +} + +static void +TestToBiggerSize() +{ + MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int32_t>(int16_t(0)))); + MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int32_t>(int16_t(INT16_MIN)))); + MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int32_t>(int16_t(INT16_MAX)))); + MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, uint32_t>(uint16_t(UINT16_MAX)))); + MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, int32_t>(uint16_t(0)))); + MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, int32_t>(uint16_t(-1)))); + MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint32_t>(int16_t(-1)))); + MOZ_RELEASE_ASSERT((IsInBounds<int16_t, uint32_t>(int16_t(INT16_MAX)))); + MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint32_t>(int16_t(INT16_MIN)))); + MOZ_RELEASE_ASSERT((IsInBounds<int32_t, uint64_t>(int32_t(INT32_MAX)))); + MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, uint64_t>(int32_t(INT32_MIN)))); +} + +static void +TestToSmallerSize() +{ + MOZ_RELEASE_ASSERT((IsInBounds<int16_t, int8_t>(int16_t(0)))); + MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, int8_t>(int16_t(INT16_MIN)))); + MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, int8_t>(int16_t(INT16_MAX)))); + MOZ_RELEASE_ASSERT((!IsInBounds<uint16_t, uint8_t>(uint16_t(UINT16_MAX)))); + MOZ_RELEASE_ASSERT((IsInBounds<uint16_t, int8_t>(uint16_t(0)))); + MOZ_RELEASE_ASSERT((!IsInBounds<uint16_t, int8_t>(uint16_t(-1)))); + MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint8_t>(int16_t(-1)))); + MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint8_t>(int16_t(INT16_MAX)))); + MOZ_RELEASE_ASSERT((!IsInBounds<int16_t, uint8_t>(int16_t(INT16_MIN)))); + MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, uint16_t>(int32_t(INT32_MAX)))); + MOZ_RELEASE_ASSERT((!IsInBounds<int32_t, uint16_t>(int32_t(INT32_MIN)))); + + // Boundary cases + MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, int32_t>(int64_t(INT32_MIN) - 1))); + MOZ_RELEASE_ASSERT((IsInBounds<int64_t, int32_t>(int64_t(INT32_MIN)))); + MOZ_RELEASE_ASSERT((IsInBounds<int64_t, int32_t>(int64_t(INT32_MIN) + 1))); + MOZ_RELEASE_ASSERT((IsInBounds<int64_t, int32_t>(int64_t(INT32_MAX) - 1))); + MOZ_RELEASE_ASSERT((IsInBounds<int64_t, int32_t>(int64_t(INT32_MAX)))); + MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, int32_t>(int64_t(INT32_MAX) + 1))); + + MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, uint32_t>(int64_t(-1)))); + MOZ_RELEASE_ASSERT((IsInBounds<int64_t, uint32_t>(int64_t(0)))); + MOZ_RELEASE_ASSERT((IsInBounds<int64_t, uint32_t>(int64_t(1)))); + MOZ_RELEASE_ASSERT((IsInBounds<int64_t, uint32_t>(int64_t(UINT32_MAX) - 1))); + MOZ_RELEASE_ASSERT((IsInBounds<int64_t, uint32_t>(int64_t(UINT32_MAX)))); + MOZ_RELEASE_ASSERT((!IsInBounds<int64_t, uint32_t>(int64_t(UINT32_MAX) + 1))); +} + +int +main() +{ + TestBitwiseCast(); + + TestSameSize(); + TestToBiggerSize(); + TestToSmallerSize(); + + return 0; +} diff --git a/mfbt/tests/TestCeilingFloor.cpp b/mfbt/tests/TestCeilingFloor.cpp new file mode 100644 index 000000000..f1c50dec8 --- /dev/null +++ b/mfbt/tests/TestCeilingFloor.cpp @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/MathAlgorithms.h" + +using mozilla::CeilingLog2; +using mozilla::FloorLog2; +using mozilla::RoundUpPow2; + +static void +TestCeiling() +{ + for (uint32_t i = 0; i <= 1; i++) { + MOZ_RELEASE_ASSERT(CeilingLog2(i) == 0); + } + for (uint32_t i = 2; i <= 2; i++) { + MOZ_RELEASE_ASSERT(CeilingLog2(i) == 1); + } + for (uint32_t i = 3; i <= 4; i++) { + MOZ_RELEASE_ASSERT(CeilingLog2(i) == 2); + } + for (uint32_t i = 5; i <= 8; i++) { + MOZ_RELEASE_ASSERT(CeilingLog2(i) == 3); + } + for (uint32_t i = 9; i <= 16; i++) { + MOZ_RELEASE_ASSERT(CeilingLog2(i) == 4); + } +} + +static void +TestFloor() +{ + for (uint32_t i = 0; i <= 1; i++) { + MOZ_RELEASE_ASSERT(FloorLog2(i) == 0); + } + for (uint32_t i = 2; i <= 3; i++) { + MOZ_RELEASE_ASSERT(FloorLog2(i) == 1); + } + for (uint32_t i = 4; i <= 7; i++) { + MOZ_RELEASE_ASSERT(FloorLog2(i) == 2); + } + for (uint32_t i = 8; i <= 15; i++) { + MOZ_RELEASE_ASSERT(FloorLog2(i) == 3); + } + for (uint32_t i = 16; i <= 31; i++) { + MOZ_RELEASE_ASSERT(FloorLog2(i) == 4); + } +} + +static void +TestRoundUpPow2() +{ + MOZ_RELEASE_ASSERT(RoundUpPow2(0) == 1); + MOZ_RELEASE_ASSERT(RoundUpPow2(1) == 1); + MOZ_RELEASE_ASSERT(RoundUpPow2(2) == 2); + MOZ_RELEASE_ASSERT(RoundUpPow2(3) == 4); + MOZ_RELEASE_ASSERT(RoundUpPow2(4) == 4); + MOZ_RELEASE_ASSERT(RoundUpPow2(5) == 8); + MOZ_RELEASE_ASSERT(RoundUpPow2(6) == 8); + MOZ_RELEASE_ASSERT(RoundUpPow2(7) == 8); + MOZ_RELEASE_ASSERT(RoundUpPow2(8) == 8); + MOZ_RELEASE_ASSERT(RoundUpPow2(9) == 16); + + MOZ_RELEASE_ASSERT(RoundUpPow2(15) == 16); + MOZ_RELEASE_ASSERT(RoundUpPow2(16) == 16); + MOZ_RELEASE_ASSERT(RoundUpPow2(17) == 32); + + MOZ_RELEASE_ASSERT(RoundUpPow2(31) == 32); + MOZ_RELEASE_ASSERT(RoundUpPow2(32) == 32); + MOZ_RELEASE_ASSERT(RoundUpPow2(33) == 64); + + size_t MaxPow2 = size_t(1) << (sizeof(size_t) * CHAR_BIT - 1); + MOZ_RELEASE_ASSERT(RoundUpPow2(MaxPow2 - 1) == MaxPow2); + MOZ_RELEASE_ASSERT(RoundUpPow2(MaxPow2) == MaxPow2); + // not valid to round up when past the max power of two +} + +int +main() +{ + TestCeiling(); + TestFloor(); + + TestRoundUpPow2(); + return 0; +} diff --git a/mfbt/tests/TestCheckedInt.cpp b/mfbt/tests/TestCheckedInt.cpp new file mode 100644 index 000000000..7ded8e350 --- /dev/null +++ b/mfbt/tests/TestCheckedInt.cpp @@ -0,0 +1,626 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/CheckedInt.h" + +#include <iostream> +#include <climits> + +using namespace mozilla; + +int gIntegerTypesTested = 0; +int gTestsPassed = 0; +int gTestsFailed = 0; + +void verifyImplFunction(bool aX, bool aExpected, + const char* aFile, int aLine, + int aSize, bool aIsTSigned) +{ + if (aX == aExpected) { + gTestsPassed++; + } else { + gTestsFailed++; + std::cerr << "Test failed at " << aFile << ":" << aLine; + std::cerr << " with T a "; + if (aIsTSigned) { + std::cerr << "signed"; + } else { + std::cerr << "unsigned"; + } + std::cerr << " " << CHAR_BIT * aSize << "-bit integer type" << std::endl; + } +} + +#define VERIFY_IMPL(x, expected) \ + verifyImplFunction((x), \ + (expected), \ + __FILE__, \ + __LINE__, \ + sizeof(T), \ + IsSigned<T>::value) + +#define VERIFY(x) VERIFY_IMPL(x, true) +#define VERIFY_IS_FALSE(x) VERIFY_IMPL(x, false) +#define VERIFY_IS_VALID(x) VERIFY_IMPL((x).isValid(), true) +#define VERIFY_IS_INVALID(x) VERIFY_IMPL((x).isValid(), false) +#define VERIFY_IS_VALID_IF(x,condition) VERIFY_IMPL((x).isValid(), (condition)) + +template<typename T, size_t Size = sizeof(T)> +struct testTwiceBiggerType +{ + static void run() + { + VERIFY(detail::IsSupported<typename detail::TwiceBiggerType<T>::Type>::value); + VERIFY(sizeof(typename detail::TwiceBiggerType<T>::Type) == 2 * sizeof(T)); + VERIFY(bool(IsSigned<typename detail::TwiceBiggerType<T>::Type>::value) == + bool(IsSigned<T>::value)); + } +}; + +template<typename T> +struct testTwiceBiggerType<T, 8> +{ + static void run() + { + VERIFY_IS_FALSE(detail::IsSupported< + typename detail::TwiceBiggerType<T>::Type + >::value); + } +}; + + +template<typename T> +void test() +{ + static bool alreadyRun = false; + // Integer types from different families may just be typedefs for types from + // other families. E.g. int32_t might be just a typedef for int. No point + // re-running the same tests then. + if (alreadyRun) { + return; + } + alreadyRun = true; + + VERIFY(detail::IsSupported<T>::value); + const bool isTSigned = IsSigned<T>::value; + VERIFY(bool(isTSigned) == !bool(T(-1) > T(0))); + + testTwiceBiggerType<T>::run(); + + typedef typename MakeUnsigned<T>::Type unsignedT; + + VERIFY(sizeof(unsignedT) == sizeof(T)); + VERIFY(IsSigned<unsignedT>::value == false); + + const CheckedInt<T> max(MaxValue<T>::value); + const CheckedInt<T> min(MinValue<T>::value); + + // Check MinValue and MaxValue, since they are custom implementations and a + // mistake there could potentially NOT be caught by any other tests... while + // making everything wrong! + + unsignedT bit = 1; + unsignedT unsignedMinValue(min.value()); + unsignedT unsignedMaxValue(max.value()); + for (size_t i = 0; i < sizeof(T) * CHAR_BIT - 1; i++) { + VERIFY((unsignedMinValue & bit) == 0); + bit <<= 1; + } + VERIFY((unsignedMinValue & bit) == (isTSigned ? bit : unsignedT(0))); + VERIFY(unsignedMaxValue == unsignedT(~unsignedMinValue)); + + const CheckedInt<T> zero(0); + const CheckedInt<T> one(1); + const CheckedInt<T> two(2); + const CheckedInt<T> three(3); + const CheckedInt<T> four(4); + + /* Addition / subtraction checks */ + + VERIFY_IS_VALID(zero + zero); + VERIFY(zero + zero == zero); + VERIFY_IS_FALSE(zero + zero == one); // Check == doesn't always return true + VERIFY_IS_VALID(zero + one); + VERIFY(zero + one == one); + VERIFY_IS_VALID(one + one); + VERIFY(one + one == two); + + const CheckedInt<T> maxMinusOne = max - one; + const CheckedInt<T> maxMinusTwo = max - two; + VERIFY_IS_VALID(maxMinusOne); + VERIFY_IS_VALID(maxMinusTwo); + VERIFY_IS_VALID(maxMinusOne + one); + VERIFY_IS_VALID(maxMinusTwo + one); + VERIFY_IS_VALID(maxMinusTwo + two); + VERIFY(maxMinusOne + one == max); + VERIFY(maxMinusTwo + one == maxMinusOne); + VERIFY(maxMinusTwo + two == max); + + VERIFY_IS_VALID(max + zero); + VERIFY_IS_VALID(max - zero); + VERIFY_IS_INVALID(max + one); + VERIFY_IS_INVALID(max + two); + VERIFY_IS_INVALID(max + maxMinusOne); + VERIFY_IS_INVALID(max + max); + + const CheckedInt<T> minPlusOne = min + one; + const CheckedInt<T> minPlusTwo = min + two; + VERIFY_IS_VALID(minPlusOne); + VERIFY_IS_VALID(minPlusTwo); + VERIFY_IS_VALID(minPlusOne - one); + VERIFY_IS_VALID(minPlusTwo - one); + VERIFY_IS_VALID(minPlusTwo - two); + VERIFY(minPlusOne - one == min); + VERIFY(minPlusTwo - one == minPlusOne); + VERIFY(minPlusTwo - two == min); + + const CheckedInt<T> minMinusOne = min - one; + VERIFY_IS_VALID(min + zero); + VERIFY_IS_VALID(min - zero); + VERIFY_IS_INVALID(min - one); + VERIFY_IS_INVALID(min - two); + VERIFY_IS_INVALID(min - minMinusOne); + VERIFY_IS_VALID(min - min); + + const CheckedInt<T> maxOverTwo = max / two; + VERIFY_IS_VALID(maxOverTwo + maxOverTwo); + VERIFY_IS_VALID(maxOverTwo + one); + VERIFY((maxOverTwo + one) - one == maxOverTwo); + VERIFY_IS_VALID(maxOverTwo - maxOverTwo); + VERIFY(maxOverTwo - maxOverTwo == zero); + + const CheckedInt<T> minOverTwo = min / two; + VERIFY_IS_VALID(minOverTwo + minOverTwo); + VERIFY_IS_VALID(minOverTwo + one); + VERIFY((minOverTwo + one) - one == minOverTwo); + VERIFY_IS_VALID(minOverTwo - minOverTwo); + VERIFY(minOverTwo - minOverTwo == zero); + + VERIFY_IS_INVALID(min - one); + VERIFY_IS_INVALID(min - two); + + if (isTSigned) { + VERIFY_IS_INVALID(min + min); + VERIFY_IS_INVALID(minOverTwo + minOverTwo + minOverTwo); + VERIFY_IS_INVALID(zero - min + min); + VERIFY_IS_INVALID(one - min + min); + } + + /* Modulo checks */ + VERIFY_IS_INVALID(zero % zero); + VERIFY_IS_INVALID(one % zero); + VERIFY_IS_VALID(zero % one); + VERIFY_IS_VALID(zero % max); + VERIFY_IS_VALID(one % max); + VERIFY_IS_VALID(max % one); + VERIFY_IS_VALID(max % max); + if (isTSigned) { + const CheckedInt<T> minusOne = zero - one; + VERIFY_IS_INVALID(minusOne % minusOne); + VERIFY_IS_INVALID(zero % minusOne); + VERIFY_IS_INVALID(one % minusOne); + VERIFY_IS_INVALID(minusOne % one); + + VERIFY_IS_INVALID(min % min); + VERIFY_IS_INVALID(zero % min); + VERIFY_IS_INVALID(min % one); + } + + /* Unary operator- checks */ + + const CheckedInt<T> negOne = -one; + const CheckedInt<T> negTwo = -two; + + if (isTSigned) { + VERIFY_IS_VALID(-max); + VERIFY_IS_INVALID(-min); + VERIFY(-max - min == one); + VERIFY_IS_VALID(-max - one); + VERIFY_IS_VALID(negOne); + VERIFY_IS_VALID(-max + negOne); + VERIFY_IS_VALID(negOne + one); + VERIFY(negOne + one == zero); + VERIFY_IS_VALID(negTwo); + VERIFY_IS_VALID(negOne + negOne); + VERIFY(negOne + negOne == negTwo); + } else { + VERIFY_IS_INVALID(-max); + VERIFY_IS_VALID(-min); + VERIFY(min == zero); + VERIFY_IS_INVALID(negOne); + } + + /* multiplication checks */ + + VERIFY_IS_VALID(zero * zero); + VERIFY(zero * zero == zero); + VERIFY_IS_VALID(zero * one); + VERIFY(zero * one == zero); + VERIFY_IS_VALID(one * zero); + VERIFY(one * zero == zero); + VERIFY_IS_VALID(one * one); + VERIFY(one * one == one); + VERIFY_IS_VALID(one * three); + VERIFY(one * three == three); + VERIFY_IS_VALID(two * two); + VERIFY(two * two == four); + + VERIFY_IS_INVALID(max * max); + VERIFY_IS_INVALID(maxOverTwo * max); + VERIFY_IS_INVALID(maxOverTwo * maxOverTwo); + + const CheckedInt<T> maxApproxSqrt(T(T(1) << (CHAR_BIT*sizeof(T)/2))); + + VERIFY_IS_VALID(maxApproxSqrt); + VERIFY_IS_VALID(maxApproxSqrt * two); + VERIFY_IS_INVALID(maxApproxSqrt * maxApproxSqrt); + VERIFY_IS_INVALID(maxApproxSqrt * maxApproxSqrt * maxApproxSqrt); + + if (isTSigned) { + VERIFY_IS_INVALID(min * min); + VERIFY_IS_INVALID(minOverTwo * min); + VERIFY_IS_INVALID(minOverTwo * minOverTwo); + + const CheckedInt<T> minApproxSqrt = -maxApproxSqrt; + + VERIFY_IS_VALID(minApproxSqrt); + VERIFY_IS_VALID(minApproxSqrt * two); + VERIFY_IS_INVALID(minApproxSqrt * maxApproxSqrt); + VERIFY_IS_INVALID(minApproxSqrt * minApproxSqrt); + } + + // make sure to check all 4 paths in signed multiplication validity check. + // test positive * positive + VERIFY_IS_VALID(max * one); + VERIFY(max * one == max); + VERIFY_IS_INVALID(max * two); + VERIFY_IS_VALID(maxOverTwo * two); + VERIFY((maxOverTwo + maxOverTwo) == (maxOverTwo * two)); + + if (isTSigned) { + // test positive * negative + VERIFY_IS_VALID(max * negOne); + VERIFY_IS_VALID(-max); + VERIFY(max * negOne == -max); + VERIFY_IS_VALID(one * min); + VERIFY_IS_INVALID(max * negTwo); + VERIFY_IS_VALID(maxOverTwo * negTwo); + VERIFY_IS_VALID(two * minOverTwo); + VERIFY_IS_VALID((maxOverTwo + one) * negTwo); + VERIFY_IS_INVALID((maxOverTwo + two) * negTwo); + VERIFY_IS_INVALID(two * (minOverTwo - one)); + + // test negative * positive + VERIFY_IS_VALID(min * one); + VERIFY_IS_VALID(minPlusOne * one); + VERIFY_IS_INVALID(min * two); + VERIFY_IS_VALID(minOverTwo * two); + VERIFY(minOverTwo * two == min); + VERIFY_IS_INVALID((minOverTwo - one) * negTwo); + VERIFY_IS_INVALID(negTwo * max); + VERIFY_IS_VALID(minOverTwo * two); + VERIFY(minOverTwo * two == min); + VERIFY_IS_VALID(negTwo * maxOverTwo); + VERIFY_IS_INVALID((minOverTwo - one) * two); + VERIFY_IS_VALID(negTwo * (maxOverTwo + one)); + VERIFY_IS_INVALID(negTwo * (maxOverTwo + two)); + + // test negative * negative + VERIFY_IS_INVALID(min * negOne); + VERIFY_IS_VALID(minPlusOne * negOne); + VERIFY(minPlusOne * negOne == max); + VERIFY_IS_INVALID(min * negTwo); + VERIFY_IS_INVALID(minOverTwo * negTwo); + VERIFY_IS_INVALID(negOne * min); + VERIFY_IS_VALID(negOne * minPlusOne); + VERIFY(negOne * minPlusOne == max); + VERIFY_IS_INVALID(negTwo * min); + VERIFY_IS_INVALID(negTwo * minOverTwo); + } + + /* Division checks */ + + VERIFY_IS_VALID(one / one); + VERIFY(one / one == one); + VERIFY_IS_VALID(three / three); + VERIFY(three / three == one); + VERIFY_IS_VALID(four / two); + VERIFY(four / two == two); + VERIFY((four*three)/four == three); + + // Check that div by zero is invalid + VERIFY_IS_INVALID(zero / zero); + VERIFY_IS_INVALID(one / zero); + VERIFY_IS_INVALID(two / zero); + VERIFY_IS_INVALID(negOne / zero); + VERIFY_IS_INVALID(max / zero); + VERIFY_IS_INVALID(min / zero); + + if (isTSigned) { + // Check that min / -1 is invalid + VERIFY_IS_INVALID(min / negOne); + + // Check that the test for div by -1 isn't banning other numerators than min + VERIFY_IS_VALID(one / negOne); + VERIFY_IS_VALID(zero / negOne); + VERIFY_IS_VALID(negOne / negOne); + VERIFY_IS_VALID(max / negOne); + } + + /* Check that invalidity is correctly preserved by arithmetic ops */ + + const CheckedInt<T> someInvalid = max + max; + VERIFY_IS_INVALID(someInvalid + zero); + VERIFY_IS_INVALID(someInvalid - zero); + VERIFY_IS_INVALID(zero + someInvalid); + VERIFY_IS_INVALID(zero - someInvalid); + VERIFY_IS_INVALID(-someInvalid); + VERIFY_IS_INVALID(someInvalid * zero); + VERIFY_IS_INVALID(someInvalid * one); + VERIFY_IS_INVALID(zero * someInvalid); + VERIFY_IS_INVALID(one * someInvalid); + VERIFY_IS_INVALID(someInvalid / zero); + VERIFY_IS_INVALID(someInvalid / one); + VERIFY_IS_INVALID(zero / someInvalid); + VERIFY_IS_INVALID(one / someInvalid); + VERIFY_IS_INVALID(someInvalid % zero); + VERIFY_IS_INVALID(someInvalid % one); + VERIFY_IS_INVALID(zero % someInvalid); + VERIFY_IS_INVALID(one % someInvalid); + VERIFY_IS_INVALID(someInvalid + someInvalid); + VERIFY_IS_INVALID(someInvalid - someInvalid); + VERIFY_IS_INVALID(someInvalid * someInvalid); + VERIFY_IS_INVALID(someInvalid / someInvalid); + VERIFY_IS_INVALID(someInvalid % someInvalid); + + // Check that mixing checked integers with plain integers in expressions is + // allowed + + VERIFY(one + T(2) == three); + VERIFY(2 + one == three); + { + CheckedInt<T> x = one; + x += 2; + VERIFY(x == three); + } + VERIFY(two - 1 == one); + VERIFY(2 - one == one); + { + CheckedInt<T> x = two; + x -= 1; + VERIFY(x == one); + } + VERIFY(one * 2 == two); + VERIFY(2 * one == two); + { + CheckedInt<T> x = one; + x *= 2; + VERIFY(x == two); + } + VERIFY(four / 2 == two); + VERIFY(4 / two == two); + { + CheckedInt<T> x = four; + x /= 2; + VERIFY(x == two); + } + VERIFY(three % 2 == one); + VERIFY(3 % two == one); + { + CheckedInt<T> x = three; + x %= 2; + VERIFY(x == one); + } + + VERIFY(one == 1); + VERIFY(1 == one); + VERIFY_IS_FALSE(two == 1); + VERIFY_IS_FALSE(1 == two); + VERIFY_IS_FALSE(someInvalid == 1); + VERIFY_IS_FALSE(1 == someInvalid); + + // Check that compound operators work when both sides of the expression + // are checked integers + { + CheckedInt<T> x = one; + x += two; + VERIFY(x == three); + } + { + CheckedInt<T> x = two; + x -= one; + VERIFY(x == one); + } + { + CheckedInt<T> x = one; + x *= two; + VERIFY(x == two); + } + { + CheckedInt<T> x = four; + x /= two; + VERIFY(x == two); + } + { + CheckedInt<T> x = three; + x %= two; + VERIFY(x == one); + } + + // Check that compound operators work when both sides of the expression + // are checked integers and the right-hand side is invalid + { + CheckedInt<T> x = one; + x += someInvalid; + VERIFY_IS_INVALID(x); + } + { + CheckedInt<T> x = two; + x -= someInvalid; + VERIFY_IS_INVALID(x); + } + { + CheckedInt<T> x = one; + x *= someInvalid; + VERIFY_IS_INVALID(x); + } + { + CheckedInt<T> x = four; + x /= someInvalid; + VERIFY_IS_INVALID(x); + } + { + CheckedInt<T> x = three; + x %= someInvalid; + VERIFY_IS_INVALID(x); + } + + // Check simple casting between different signedness and sizes. + { + CheckedInt<uint8_t> foo = CheckedInt<uint16_t>(2).toChecked<uint8_t>(); + VERIFY_IS_VALID(foo); + VERIFY(foo == 2); + } + { + CheckedInt<uint8_t> foo = CheckedInt<uint16_t>(255).toChecked<uint8_t>(); + VERIFY_IS_VALID(foo); + VERIFY(foo == 255); + } + { + CheckedInt<uint8_t> foo = CheckedInt<uint16_t>(256).toChecked<uint8_t>(); + VERIFY_IS_INVALID(foo); + } + { + CheckedInt<uint8_t> foo = CheckedInt<int8_t>(-2).toChecked<uint8_t>(); + VERIFY_IS_INVALID(foo); + } + + // Check that construction of CheckedInt from an integer value of a + // mismatched type is checked Also check casting between all types. + + #define VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE2(U,V,PostVExpr) \ + { \ + bool isUSigned = IsSigned<U>::value; \ + VERIFY_IS_VALID(CheckedInt<T>(V( 0)PostVExpr)); \ + VERIFY_IS_VALID(CheckedInt<T>(V( 1)PostVExpr)); \ + VERIFY_IS_VALID(CheckedInt<T>(V(100)PostVExpr)); \ + if (isUSigned) { \ + VERIFY_IS_VALID_IF(CheckedInt<T>(V(-1)PostVExpr), isTSigned); \ + } \ + if (sizeof(U) > sizeof(T)) { \ + VERIFY_IS_INVALID(CheckedInt<T>(V(MaxValue<T>::value)PostVExpr + one.value())); \ + } \ + VERIFY_IS_VALID_IF(CheckedInt<T>(MaxValue<U>::value), \ + (sizeof(T) > sizeof(U) || ((sizeof(T) == sizeof(U)) && (isUSigned || !isTSigned)))); \ + VERIFY_IS_VALID_IF(CheckedInt<T>(MinValue<U>::value), \ + isUSigned == false ? 1 \ + : bool(isTSigned) == false ? 0 \ + : sizeof(T) >= sizeof(U)); \ + } + #define VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(U) \ + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE2(U,U,+zero) \ + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE2(U,CheckedInt<U>,.toChecked<T>()) + + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int8_t) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(uint8_t) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int16_t) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(uint16_t) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int32_t) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(uint32_t) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int64_t) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(uint64_t) + + typedef signed char signedChar; + typedef unsigned char unsignedChar; + typedef unsigned short unsignedShort; + typedef unsigned int unsignedInt; + typedef unsigned long unsignedLong; + typedef long long longLong; + typedef unsigned long long unsignedLongLong; + + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(char) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(signedChar) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsignedChar) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(short) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsignedShort) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsignedInt) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(long) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsignedLong) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(longLong) + VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(unsignedLongLong) + + /* Test increment/decrement operators */ + + CheckedInt<T> x, y; + x = one; + y = x++; + VERIFY(x == two); + VERIFY(y == one); + x = one; + y = ++x; + VERIFY(x == two); + VERIFY(y == two); + x = one; + y = x--; + VERIFY(x == zero); + VERIFY(y == one); + x = one; + y = --x; + VERIFY(x == zero); + VERIFY(y == zero); + x = max; + VERIFY_IS_VALID(x++); + x = max; + VERIFY_IS_INVALID(++x); + x = min; + VERIFY_IS_VALID(x--); + x = min; + VERIFY_IS_INVALID(--x); + + gIntegerTypesTested++; +} + +int +main() +{ + test<int8_t>(); + test<uint8_t>(); + test<int16_t>(); + test<uint16_t>(); + test<int32_t>(); + test<uint32_t>(); + test<int64_t>(); + test<uint64_t>(); + + test<char>(); + test<signed char>(); + test<unsigned char>(); + test<short>(); + test<unsigned short>(); + test<int>(); + test<unsigned int>(); + test<long>(); + test<unsigned long>(); + test<long long>(); + test<unsigned long long>(); + + const int MIN_TYPES_TESTED = 9; + if (gIntegerTypesTested < MIN_TYPES_TESTED) { + std::cerr << "Only " << gIntegerTypesTested << " have been tested. " + << "This should not be less than " << MIN_TYPES_TESTED << "." + << std::endl; + gTestsFailed++; + } + + std::cerr << gTestsFailed << " tests failed, " + << gTestsPassed << " tests passed out of " + << gTestsFailed + gTestsPassed + << " tests, covering " << gIntegerTypesTested + << " distinct integer types." << std::endl; + + return gTestsFailed > 0; +} diff --git a/mfbt/tests/TestCountPopulation.cpp b/mfbt/tests/TestCountPopulation.cpp new file mode 100644 index 000000000..272a0dc34 --- /dev/null +++ b/mfbt/tests/TestCountPopulation.cpp @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/MathAlgorithms.h" + +using mozilla::CountPopulation32; + +static void +TestCountPopulation32() +{ + MOZ_RELEASE_ASSERT(CountPopulation32(0xFFFFFFFF) == 32); + MOZ_RELEASE_ASSERT(CountPopulation32(0xF0FF1000) == 13); + MOZ_RELEASE_ASSERT(CountPopulation32(0x7F8F0001) == 13); + MOZ_RELEASE_ASSERT(CountPopulation32(0x3FFF0100) == 15); + MOZ_RELEASE_ASSERT(CountPopulation32(0x1FF50010) == 12); + MOZ_RELEASE_ASSERT(CountPopulation32(0x00800000) == 1); + MOZ_RELEASE_ASSERT(CountPopulation32(0x00400000) == 1); + MOZ_RELEASE_ASSERT(CountPopulation32(0x00008000) == 1); + MOZ_RELEASE_ASSERT(CountPopulation32(0x00004000) == 1); + MOZ_RELEASE_ASSERT(CountPopulation32(0x00000080) == 1); + MOZ_RELEASE_ASSERT(CountPopulation32(0x00000040) == 1); + MOZ_RELEASE_ASSERT(CountPopulation32(0x00000001) == 1); + MOZ_RELEASE_ASSERT(CountPopulation32(0x00000000) == 0); +} + +int +main() +{ + TestCountPopulation32(); + return 0; +} diff --git a/mfbt/tests/TestCountZeroes.cpp b/mfbt/tests/TestCountZeroes.cpp new file mode 100644 index 000000000..799ee8421 --- /dev/null +++ b/mfbt/tests/TestCountZeroes.cpp @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/MathAlgorithms.h" + +using mozilla::CountLeadingZeroes32; +using mozilla::CountLeadingZeroes64; +using mozilla::CountTrailingZeroes32; +using mozilla::CountTrailingZeroes64; + +static void +TestLeadingZeroes32() +{ + MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0xF0FF1000) == 0); + MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x7F8F0001) == 1); + MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x3FFF0100) == 2); + MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x1FF50010) == 3); + MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x00800000) == 8); + MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x00400000) == 9); + MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x00008000) == 16); + MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x00004000) == 17); + MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x00000080) == 24); + MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x00000040) == 25); + MOZ_RELEASE_ASSERT(CountLeadingZeroes32(0x00000001) == 31); +} + +static void +TestLeadingZeroes64() +{ + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0xF000F0F010000000) == 0); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x70F080F000000001) == 1); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x30F0F0F000100000) == 2); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x10F0F05000000100) == 3); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0080000000000001) == 8); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0040000010001000) == 9); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x000080F010000000) == 16); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x000040F010000000) == 17); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000008000100100) == 24); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000004100010010) == 25); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000080100100) == 32); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000041001010) == 33); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000000800100) == 40); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000000411010) == 41); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000000008001) == 48); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000000004010) == 49); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000000000081) == 56); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000000000040) == 57); + MOZ_RELEASE_ASSERT(CountLeadingZeroes64(0x0000000000000001) == 63); +} + +static void +TestTrailingZeroes32() +{ + MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x0100FFFF) == 0); + MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x7000FFFE) == 1); + MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x0080FFFC) == 2); + MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x0080FFF8) == 3); + MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x010FFF00) == 8); + MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x7000FE00) == 9); + MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x10CF0000) == 16); + MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x0BDE0000) == 17); + MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x0F000000) == 24); + MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0xDE000000) == 25); + MOZ_RELEASE_ASSERT(CountTrailingZeroes32(0x80000000) == 31); +} + +static void +TestTrailingZeroes64() +{ + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x000100000F0F0F0F) == 0); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x070000000F0F0F0E) == 1); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x000008000F0F0F0C) == 2); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x000008000F0F0F08) == 3); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0xC001000F0F0F0F00) == 8); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x0200000F0F0F0E00) == 9); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0xB0C10F0FEFDF0000) == 16); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x0AAA00F0FF0E0000) == 17); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0xD010F0FEDF000000) == 24); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x7AAF0CF0BE000000) == 25); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x20F0A5D100000000) == 32); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x489BF0B200000000) == 33); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0xE0F0D10000000000) == 40); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x97F0B20000000000) == 41); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x2C07000000000000) == 48); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x1FBA000000000000) == 49); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x0100000000000000) == 56); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x0200000000000000) == 57); + MOZ_RELEASE_ASSERT(CountTrailingZeroes64(0x8000000000000000) == 63); +} + +int +main() +{ + TestLeadingZeroes32(); + TestLeadingZeroes64(); + TestTrailingZeroes32(); + TestTrailingZeroes64(); + return 0; +} diff --git a/mfbt/tests/TestEndian.cpp b/mfbt/tests/TestEndian.cpp new file mode 100644 index 000000000..1fb8ac98f --- /dev/null +++ b/mfbt/tests/TestEndian.cpp @@ -0,0 +1,472 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/EndianUtils.h" + +#include <stddef.h> + +using mozilla::BigEndian; +using mozilla::LittleEndian; +using mozilla::NativeEndian; + +template<typename T> +void +TestSingleSwap(T aValue, T aSwappedValue) +{ +#if MOZ_LITTLE_ENDIAN + MOZ_RELEASE_ASSERT(NativeEndian::swapToBigEndian(aValue) == aSwappedValue); + MOZ_RELEASE_ASSERT(NativeEndian::swapFromBigEndian(aValue) == aSwappedValue); + MOZ_RELEASE_ASSERT(NativeEndian::swapToNetworkOrder(aValue) == aSwappedValue); + MOZ_RELEASE_ASSERT(NativeEndian::swapFromNetworkOrder(aValue) == + aSwappedValue); +#else + MOZ_RELEASE_ASSERT(NativeEndian::swapToLittleEndian(aValue) == aSwappedValue); + MOZ_RELEASE_ASSERT(NativeEndian::swapFromLittleEndian(aValue) == + aSwappedValue); +#endif +} + +template<typename T> +void +TestSingleNoSwap(T aValue, T aUnswappedValue) +{ +#if MOZ_LITTLE_ENDIAN + MOZ_RELEASE_ASSERT(NativeEndian::swapToLittleEndian(aValue) == + aUnswappedValue); + MOZ_RELEASE_ASSERT(NativeEndian::swapFromLittleEndian(aValue) == + aUnswappedValue); +#else + MOZ_RELEASE_ASSERT(NativeEndian::swapToBigEndian(aValue) == aUnswappedValue); + MOZ_RELEASE_ASSERT(NativeEndian::swapFromBigEndian(aValue) == aUnswappedValue); + MOZ_RELEASE_ASSERT(NativeEndian::swapToNetworkOrder(aValue) == + aUnswappedValue); + MOZ_RELEASE_ASSERT(NativeEndian::swapFromNetworkOrder(aValue) == + aUnswappedValue); +#endif +} + +// EndianUtils.h functions are declared as protected in a base class and +// then re-exported as public in public derived classes. The +// standardese around explicit instantiation of templates is not clear +// in such cases. Provide these wrappers to make things more explicit. +// For your own enlightenment, you may wish to peruse: +// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56152 and subsequently +// http://j.mp/XosS6S . +#define WRAP_COPYTO(NAME) \ + template<typename T> \ + void \ + NAME(void* aDst, const T* aSrc, size_t aCount) \ + { \ + NativeEndian::NAME<T>(aDst, aSrc, aCount); \ + } + +WRAP_COPYTO(copyAndSwapToLittleEndian) +WRAP_COPYTO(copyAndSwapToBigEndian) +WRAP_COPYTO(copyAndSwapToNetworkOrder) + +#define WRAP_COPYFROM(NAME) \ + template<typename T> \ + void \ + NAME(T* aDst, const void* aSrc, size_t aCount) \ + { \ + NativeEndian::NAME<T>(aDst, aSrc, aCount); \ + } + +WRAP_COPYFROM(copyAndSwapFromLittleEndian) +WRAP_COPYFROM(copyAndSwapFromBigEndian) +WRAP_COPYFROM(copyAndSwapFromNetworkOrder) + +#define WRAP_IN_PLACE(NAME) \ + template<typename T> \ + void \ + NAME(T* aP, size_t aCount) \ + { \ + NativeEndian::NAME<T>(aP, aCount); \ + } +WRAP_IN_PLACE(swapToLittleEndianInPlace) +WRAP_IN_PLACE(swapFromLittleEndianInPlace) +WRAP_IN_PLACE(swapToBigEndianInPlace) +WRAP_IN_PLACE(swapFromBigEndianInPlace) +WRAP_IN_PLACE(swapToNetworkOrderInPlace) +WRAP_IN_PLACE(swapFromNetworkOrderInPlace) + +enum SwapExpectation +{ + Swap, + NoSwap +}; + +template<typename T, size_t Count> +void +TestBulkSwapToSub(enum SwapExpectation aExpectSwap, + const T (&aValues)[Count], + void (*aSwapperFunc)(void*, const T*, size_t), + T (*aReaderFunc)(const void*)) +{ + const size_t arraySize = 2 * Count; + const size_t bufferSize = arraySize * sizeof(T); + static uint8_t buffer[bufferSize]; + const uint8_t fillValue = 0xa5; + static uint8_t checkBuffer[bufferSize]; + + MOZ_RELEASE_ASSERT(bufferSize > 2 * sizeof(T)); + + memset(checkBuffer, fillValue, bufferSize); + + for (size_t startPosition = 0; startPosition < sizeof(T); ++startPosition) { + for (size_t nValues = 0; nValues < Count; ++nValues) { + memset(buffer, fillValue, bufferSize); + aSwapperFunc(buffer + startPosition, aValues, nValues); + + MOZ_RELEASE_ASSERT(memcmp(buffer, checkBuffer, startPosition) == 0); + size_t valuesEndPosition = startPosition + sizeof(T) * nValues; + MOZ_RELEASE_ASSERT(memcmp(buffer + valuesEndPosition, + checkBuffer + valuesEndPosition, + bufferSize - valuesEndPosition) == 0); + if (aExpectSwap == NoSwap) { + MOZ_RELEASE_ASSERT(memcmp(buffer + startPosition, aValues, + nValues * sizeof(T)) == 0); + } + for (size_t i = 0; i < nValues; ++i) { + MOZ_RELEASE_ASSERT( + aReaderFunc(buffer + startPosition + sizeof(T) * i) == aValues[i]); + } + } + } +} + +template<typename T, size_t Count> +void +TestBulkSwapFromSub(enum SwapExpectation aExpectSwap, + const T (&aValues)[Count], + void (*aSwapperFunc)(T*, const void*, size_t), + T (*aReaderFunc)(const void*)) +{ + const size_t arraySize = 2 * Count; + const size_t bufferSize = arraySize * sizeof(T); + static T buffer[arraySize]; + const uint8_t fillValue = 0xa5; + static T checkBuffer[arraySize]; + + memset(checkBuffer, fillValue, bufferSize); + + for (size_t startPosition = 0; startPosition < Count; ++startPosition) { + for (size_t nValues = 0; nValues < (Count - startPosition); ++nValues) { + memset(buffer, fillValue, bufferSize); + aSwapperFunc(buffer + startPosition, aValues, nValues); + + MOZ_RELEASE_ASSERT( + memcmp(buffer, checkBuffer, startPosition * sizeof(T)) == 0); + size_t valuesEndPosition = startPosition + nValues; + MOZ_RELEASE_ASSERT(memcmp(buffer + valuesEndPosition, + checkBuffer + valuesEndPosition, + (arraySize - valuesEndPosition) * sizeof(T)) == 0); + if (aExpectSwap == NoSwap) { + MOZ_RELEASE_ASSERT(memcmp(buffer + startPosition, aValues, + nValues * sizeof(T)) == 0); + } + for (size_t i = 0; i < nValues; ++i) { + MOZ_RELEASE_ASSERT(aReaderFunc(buffer + startPosition + i) == aValues[i]); + } + } + } +} + + +template<typename T, size_t Count> +void +TestBulkInPlaceSub(enum SwapExpectation aExpectSwap, + const T (&aValues)[Count], + void (*aSwapperFunc)(T*, size_t), + T (*aReaderFunc)(const void*)) +{ + const size_t bufferCount = 4 * Count; + const size_t bufferSize = bufferCount * sizeof(T); + static T buffer[bufferCount]; + const T fillValue = 0xa5; + static T checkBuffer[bufferCount]; + + MOZ_RELEASE_ASSERT(bufferSize > 2 * sizeof(T)); + + memset(checkBuffer, fillValue, bufferSize); + + for (size_t startPosition = 0; startPosition < Count; ++startPosition) { + for (size_t nValues = 0; nValues < Count; ++nValues) { + memset(buffer, fillValue, bufferSize); + memcpy(buffer + startPosition, aValues, nValues * sizeof(T)); + aSwapperFunc(buffer + startPosition, nValues); + + MOZ_RELEASE_ASSERT( + memcmp(buffer, checkBuffer, startPosition * sizeof(T)) == 0); + size_t valuesEndPosition = startPosition + nValues; + MOZ_RELEASE_ASSERT(memcmp(buffer + valuesEndPosition, + checkBuffer + valuesEndPosition, + bufferSize - valuesEndPosition * sizeof(T)) == 0); + if (aExpectSwap == NoSwap) { + MOZ_RELEASE_ASSERT(memcmp(buffer + startPosition, aValues, + nValues * sizeof(T)) == 0); + } + for (size_t i = 0; i < nValues; ++i) { + MOZ_RELEASE_ASSERT(aReaderFunc(buffer + startPosition + i) == aValues[i]); + } + } + } +} + +template<typename T> +struct Reader +{ +}; + +#define SPECIALIZE_READER(TYPE, READ_FUNC) \ + template<> \ + struct Reader<TYPE> \ + { \ + static TYPE readLE(const void* aP) { return LittleEndian::READ_FUNC(aP); }\ + static TYPE readBE(const void* aP) { return BigEndian::READ_FUNC(aP); } \ + }; + +SPECIALIZE_READER(uint16_t, readUint16) +SPECIALIZE_READER(uint32_t, readUint32) +SPECIALIZE_READER(uint64_t, readUint64) +SPECIALIZE_READER(int16_t, readInt16) +SPECIALIZE_READER(int32_t, readInt32) +SPECIALIZE_READER(int64_t, readInt64) + +template<typename T, size_t Count> +void +TestBulkSwap(const T (&aBytes)[Count]) +{ +#if MOZ_LITTLE_ENDIAN + TestBulkSwapToSub(Swap, aBytes, copyAndSwapToBigEndian<T>, + Reader<T>::readBE); + TestBulkSwapFromSub(Swap, aBytes, copyAndSwapFromBigEndian<T>, + Reader<T>::readBE); + TestBulkSwapToSub(Swap, aBytes, copyAndSwapToNetworkOrder<T>, + Reader<T>::readBE); + TestBulkSwapFromSub(Swap, aBytes, copyAndSwapFromNetworkOrder<T>, + Reader<T>::readBE); +#else + TestBulkSwapToSub(Swap, aBytes, copyAndSwapToLittleEndian<T>, + Reader<T>::readLE); + TestBulkSwapFromSub(Swap, aBytes, copyAndSwapFromLittleEndian<T>, + Reader<T>::readLE); +#endif +} + +template<typename T, size_t Count> +void +TestBulkNoSwap(const T (&aBytes)[Count]) +{ +#if MOZ_LITTLE_ENDIAN + TestBulkSwapToSub(NoSwap, aBytes, copyAndSwapToLittleEndian<T>, + Reader<T>::readLE); + TestBulkSwapFromSub(NoSwap, aBytes, copyAndSwapFromLittleEndian<T>, + Reader<T>::readLE); +#else + TestBulkSwapToSub(NoSwap, aBytes, copyAndSwapToBigEndian<T>, + Reader<T>::readBE); + TestBulkSwapFromSub(NoSwap, aBytes, copyAndSwapFromBigEndian<T>, + Reader<T>::readBE); + TestBulkSwapToSub(NoSwap, aBytes, copyAndSwapToNetworkOrder<T>, + Reader<T>::readBE); + TestBulkSwapFromSub(NoSwap, aBytes, copyAndSwapFromNetworkOrder<T>, + Reader<T>::readBE); +#endif +} + +template<typename T, size_t Count> +void +TestBulkInPlaceSwap(const T (&aBytes)[Count]) +{ +#if MOZ_LITTLE_ENDIAN + TestBulkInPlaceSub(Swap, aBytes, swapToBigEndianInPlace<T>, + Reader<T>::readBE); + TestBulkInPlaceSub(Swap, aBytes, swapFromBigEndianInPlace<T>, + Reader<T>::readBE); + TestBulkInPlaceSub(Swap, aBytes, swapToNetworkOrderInPlace<T>, + Reader<T>::readBE); + TestBulkInPlaceSub(Swap, aBytes, swapFromNetworkOrderInPlace<T>, + Reader<T>::readBE); +#else + TestBulkInPlaceSub(Swap, aBytes, swapToLittleEndianInPlace<T>, + Reader<T>::readLE); + TestBulkInPlaceSub(Swap, aBytes, swapFromLittleEndianInPlace<T>, + Reader<T>::readLE); +#endif +} + +template<typename T, size_t Count> +void +TestBulkInPlaceNoSwap(const T (&aBytes)[Count]) +{ +#if MOZ_LITTLE_ENDIAN + TestBulkInPlaceSub(NoSwap, aBytes, swapToLittleEndianInPlace<T>, + Reader<T>::readLE); + TestBulkInPlaceSub(NoSwap, aBytes, swapFromLittleEndianInPlace<T>, + Reader<T>::readLE); +#else + TestBulkInPlaceSub(NoSwap, aBytes, swapToBigEndianInPlace<T>, + Reader<T>::readBE); + TestBulkInPlaceSub(NoSwap, aBytes, swapFromBigEndianInPlace<T>, + Reader<T>::readBE); + TestBulkInPlaceSub(NoSwap, aBytes, swapToNetworkOrderInPlace<T>, + Reader<T>::readBE); + TestBulkInPlaceSub(NoSwap, aBytes, swapFromNetworkOrderInPlace<T>, + Reader<T>::readBE); +#endif +} + +int +main() +{ + static const uint8_t unsigned_bytes[16] = { + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8 + }; + static const int8_t signed_bytes[16] = { + -0x0f, -0x0e, -0x0d, -0x0c, -0x0b, -0x0a, -0x09, -0x08, + -0x0f, -0x0e, -0x0d, -0x0c, -0x0b, -0x0a, -0x09, -0x08 + }; + static const uint16_t uint16_values[8] = { + 0x102, 0x304, 0x506, 0x708, 0x102, 0x304, 0x506, 0x708 + }; + static const int16_t int16_values[8] = { + int16_t(0xf1f2), int16_t(0xf3f4), int16_t(0xf5f6), int16_t(0xf7f8), + int16_t(0xf1f2), int16_t(0xf3f4), int16_t(0xf5f6), int16_t(0xf7f8) + }; + static const uint32_t uint32_values[4] = { + 0x1020304, 0x5060708, 0x1020304, 0x5060708 + }; + static const int32_t int32_values[4] = { + int32_t(0xf1f2f3f4), int32_t(0xf5f6f7f8), + int32_t(0xf1f2f3f4), int32_t(0xf5f6f7f8) + }; + static const uint64_t uint64_values[2] = { + 0x102030405060708, 0x102030405060708 + }; + static const int64_t int64_values[2] = { + int64_t(0xf1f2f3f4f5f6f7f8), int64_t(0xf1f2f3f4f5f6f7f8) + }; + uint8_t buffer[8]; + + MOZ_RELEASE_ASSERT(LittleEndian::readUint16(&unsigned_bytes[0]) == 0x201); + MOZ_RELEASE_ASSERT(BigEndian::readUint16(&unsigned_bytes[0]) == 0x102); + + MOZ_RELEASE_ASSERT( + LittleEndian::readUint32(&unsigned_bytes[0]) == 0x4030201U); + MOZ_RELEASE_ASSERT( + BigEndian::readUint32(&unsigned_bytes[0]) == 0x1020304U); + + MOZ_RELEASE_ASSERT( + LittleEndian::readUint64(&unsigned_bytes[0]) == 0x807060504030201ULL); + MOZ_RELEASE_ASSERT( + BigEndian::readUint64(&unsigned_bytes[0]) == 0x102030405060708ULL); + + LittleEndian::writeUint16(&buffer[0], 0x201); + MOZ_RELEASE_ASSERT( + memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint16_t)) == 0); + BigEndian::writeUint16(&buffer[0], 0x102); + MOZ_RELEASE_ASSERT( + memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint16_t)) == 0); + + LittleEndian::writeUint32(&buffer[0], 0x4030201U); + MOZ_RELEASE_ASSERT( + memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint32_t)) == 0); + BigEndian::writeUint32(&buffer[0], 0x1020304U); + MOZ_RELEASE_ASSERT( + memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint32_t)) == 0); + + LittleEndian::writeUint64(&buffer[0], 0x807060504030201ULL); + MOZ_RELEASE_ASSERT( + memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint64_t)) == 0); + BigEndian::writeUint64(&buffer[0], 0x102030405060708ULL); + MOZ_RELEASE_ASSERT( + memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint64_t)) == 0); + + MOZ_RELEASE_ASSERT( + LittleEndian::readInt16(&signed_bytes[0]) == int16_t(0xf2f1)); + MOZ_RELEASE_ASSERT( + BigEndian::readInt16(&signed_bytes[0]) == int16_t(0xf1f2)); + + MOZ_RELEASE_ASSERT( + LittleEndian::readInt32(&signed_bytes[0]) == int32_t(0xf4f3f2f1)); + MOZ_RELEASE_ASSERT( + BigEndian::readInt32(&signed_bytes[0]) == int32_t(0xf1f2f3f4)); + + MOZ_RELEASE_ASSERT( + LittleEndian::readInt64(&signed_bytes[0]) == int64_t(0xf8f7f6f5f4f3f2f1LL)); + MOZ_RELEASE_ASSERT( + BigEndian::readInt64(&signed_bytes[0]) == int64_t(0xf1f2f3f4f5f6f7f8LL)); + + LittleEndian::writeInt16(&buffer[0], int16_t(0xf2f1)); + MOZ_RELEASE_ASSERT( + memcmp(&signed_bytes[0], &buffer[0], sizeof(int16_t)) == 0); + BigEndian::writeInt16(&buffer[0], int16_t(0xf1f2)); + MOZ_RELEASE_ASSERT( + memcmp(&signed_bytes[0], &buffer[0], sizeof(int16_t)) == 0); + + LittleEndian::writeInt32(&buffer[0], 0xf4f3f2f1); + MOZ_RELEASE_ASSERT( + memcmp(&signed_bytes[0], &buffer[0], sizeof(int32_t)) == 0); + BigEndian::writeInt32(&buffer[0], 0xf1f2f3f4); + MOZ_RELEASE_ASSERT( + memcmp(&signed_bytes[0], &buffer[0], sizeof(int32_t)) == 0); + + LittleEndian::writeInt64(&buffer[0], 0xf8f7f6f5f4f3f2f1LL); + MOZ_RELEASE_ASSERT( + memcmp(&signed_bytes[0], &buffer[0], sizeof(int64_t)) == 0); + BigEndian::writeInt64(&buffer[0], 0xf1f2f3f4f5f6f7f8LL); + MOZ_RELEASE_ASSERT( + memcmp(&signed_bytes[0], &buffer[0], sizeof(int64_t)) == 0); + + TestSingleSwap(uint16_t(0xf2f1), uint16_t(0xf1f2)); + TestSingleSwap(uint32_t(0xf4f3f2f1), uint32_t(0xf1f2f3f4)); + TestSingleSwap(uint64_t(0xf8f7f6f5f4f3f2f1), uint64_t(0xf1f2f3f4f5f6f7f8)); + + TestSingleSwap(int16_t(0xf2f1), int16_t(0xf1f2)); + TestSingleSwap(int32_t(0xf4f3f2f1), int32_t(0xf1f2f3f4)); + TestSingleSwap(int64_t(0xf8f7f6f5f4f3f2f1), int64_t(0xf1f2f3f4f5f6f7f8)); + + TestSingleNoSwap(uint16_t(0xf2f1), uint16_t(0xf2f1)); + TestSingleNoSwap(uint32_t(0xf4f3f2f1), uint32_t(0xf4f3f2f1)); + TestSingleNoSwap(uint64_t(0xf8f7f6f5f4f3f2f1), uint64_t(0xf8f7f6f5f4f3f2f1)); + + TestSingleNoSwap(int16_t(0xf2f1), int16_t(0xf2f1)); + TestSingleNoSwap(int32_t(0xf4f3f2f1), int32_t(0xf4f3f2f1)); + TestSingleNoSwap(int64_t(0xf8f7f6f5f4f3f2f1), int64_t(0xf8f7f6f5f4f3f2f1)); + + TestBulkSwap(uint16_values); + TestBulkSwap(int16_values); + TestBulkSwap(uint32_values); + TestBulkSwap(int32_values); + TestBulkSwap(uint64_values); + TestBulkSwap(int64_values); + + TestBulkNoSwap(uint16_values); + TestBulkNoSwap(int16_values); + TestBulkNoSwap(uint32_values); + TestBulkNoSwap(int32_values); + TestBulkNoSwap(uint64_values); + TestBulkNoSwap(int64_values); + + TestBulkInPlaceSwap(uint16_values); + TestBulkInPlaceSwap(int16_values); + TestBulkInPlaceSwap(uint32_values); + TestBulkInPlaceSwap(int32_values); + TestBulkInPlaceSwap(uint64_values); + TestBulkInPlaceSwap(int64_values); + + TestBulkInPlaceNoSwap(uint16_values); + TestBulkInPlaceNoSwap(int16_values); + TestBulkInPlaceNoSwap(uint32_values); + TestBulkInPlaceNoSwap(int32_values); + TestBulkInPlaceNoSwap(uint64_values); + TestBulkInPlaceNoSwap(int64_values); + + return 0; +} diff --git a/mfbt/tests/TestEnumSet.cpp b/mfbt/tests/TestEnumSet.cpp new file mode 100644 index 000000000..801295fd6 --- /dev/null +++ b/mfbt/tests/TestEnumSet.cpp @@ -0,0 +1,289 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/EnumSet.h" +#include "mozilla/Vector.h" + +using namespace mozilla; + +enum SeaBird +{ + PENGUIN, + ALBATROSS, + FULMAR, + PRION, + SHEARWATER, + GADFLY_PETREL, + TRUE_PETREL, + DIVING_PETREL, + STORM_PETREL, + PELICAN, + GANNET, + BOOBY, + CORMORANT, + FRIGATEBIRD, + TROPICBIRD, + SKUA, + GULL, + TERN, + SKIMMER, + AUK +}; + +class EnumSetSuite +{ +public: + EnumSetSuite() + : mAlcidae() + , mDiomedeidae(ALBATROSS) + , mPetrelProcellariidae(GADFLY_PETREL, TRUE_PETREL) + , mNonPetrelProcellariidae(FULMAR, PRION, SHEARWATER) + , mPetrels(GADFLY_PETREL, TRUE_PETREL, DIVING_PETREL, STORM_PETREL) + { } + + void runTests() + { + testSize(); + testContains(); + testAddTo(); + testAdd(); + testAddAll(); + testUnion(); + testRemoveFrom(); + testRemove(); + testRemoveAllFrom(); + testRemoveAll(); + testIntersect(); + testInsersection(); + testEquality(); + testDuplicates(); + testIteration(); + testInitializerListConstuctor(); + } + +private: + void testSize() + { + MOZ_RELEASE_ASSERT(mAlcidae.size() == 0); + MOZ_RELEASE_ASSERT(mDiomedeidae.size() == 1); + MOZ_RELEASE_ASSERT(mPetrelProcellariidae.size() == 2); + MOZ_RELEASE_ASSERT(mNonPetrelProcellariidae.size() == 3); + MOZ_RELEASE_ASSERT(mPetrels.size() == 4); + } + + void testContains() + { + MOZ_RELEASE_ASSERT(!mPetrels.contains(PENGUIN)); + MOZ_RELEASE_ASSERT(!mPetrels.contains(ALBATROSS)); + MOZ_RELEASE_ASSERT(!mPetrels.contains(FULMAR)); + MOZ_RELEASE_ASSERT(!mPetrels.contains(PRION)); + MOZ_RELEASE_ASSERT(!mPetrels.contains(SHEARWATER)); + MOZ_RELEASE_ASSERT(mPetrels.contains(GADFLY_PETREL)); + MOZ_RELEASE_ASSERT(mPetrels.contains(TRUE_PETREL)); + MOZ_RELEASE_ASSERT(mPetrels.contains(DIVING_PETREL)); + MOZ_RELEASE_ASSERT(mPetrels.contains(STORM_PETREL)); + MOZ_RELEASE_ASSERT(!mPetrels.contains(PELICAN)); + MOZ_RELEASE_ASSERT(!mPetrels.contains(GANNET)); + MOZ_RELEASE_ASSERT(!mPetrels.contains(BOOBY)); + MOZ_RELEASE_ASSERT(!mPetrels.contains(CORMORANT)); + MOZ_RELEASE_ASSERT(!mPetrels.contains(FRIGATEBIRD)); + MOZ_RELEASE_ASSERT(!mPetrels.contains(TROPICBIRD)); + MOZ_RELEASE_ASSERT(!mPetrels.contains(SKUA)); + MOZ_RELEASE_ASSERT(!mPetrels.contains(GULL)); + MOZ_RELEASE_ASSERT(!mPetrels.contains(TERN)); + MOZ_RELEASE_ASSERT(!mPetrels.contains(SKIMMER)); + MOZ_RELEASE_ASSERT(!mPetrels.contains(AUK)); + } + + void testCopy() + { + EnumSet<SeaBird> likes = mPetrels; + likes -= TRUE_PETREL; + MOZ_RELEASE_ASSERT(mPetrels.size() == 4); + MOZ_RELEASE_ASSERT(mPetrels.contains(TRUE_PETREL)); + + MOZ_RELEASE_ASSERT(likes.size() == 3); + MOZ_RELEASE_ASSERT(likes.contains(GADFLY_PETREL)); + MOZ_RELEASE_ASSERT(likes.contains(DIVING_PETREL)); + MOZ_RELEASE_ASSERT(likes.contains(STORM_PETREL)); + } + + void testAddTo() + { + EnumSet<SeaBird> seen = mPetrels; + seen += CORMORANT; + seen += TRUE_PETREL; + MOZ_RELEASE_ASSERT(mPetrels.size() == 4); + MOZ_RELEASE_ASSERT(!mPetrels.contains(CORMORANT)); + MOZ_RELEASE_ASSERT(seen.size() == 5); + MOZ_RELEASE_ASSERT(seen.contains(GADFLY_PETREL)); + MOZ_RELEASE_ASSERT(seen.contains(TRUE_PETREL)); + MOZ_RELEASE_ASSERT(seen.contains(DIVING_PETREL)); + MOZ_RELEASE_ASSERT(seen.contains(STORM_PETREL)); + MOZ_RELEASE_ASSERT(seen.contains(CORMORANT)); + } + + void testAdd() + { + EnumSet<SeaBird> seen = mPetrels + CORMORANT + STORM_PETREL; + MOZ_RELEASE_ASSERT(mPetrels.size() == 4); + MOZ_RELEASE_ASSERT(!mPetrels.contains(CORMORANT)); + MOZ_RELEASE_ASSERT(seen.size() == 5); + MOZ_RELEASE_ASSERT(seen.contains(GADFLY_PETREL)); + MOZ_RELEASE_ASSERT(seen.contains(TRUE_PETREL)); + MOZ_RELEASE_ASSERT(seen.contains(DIVING_PETREL)); + MOZ_RELEASE_ASSERT(seen.contains(STORM_PETREL)); + MOZ_RELEASE_ASSERT(seen.contains(CORMORANT)); + } + + void testAddAll() + { + EnumSet<SeaBird> procellariidae; + procellariidae += mPetrelProcellariidae; + procellariidae += mNonPetrelProcellariidae; + MOZ_RELEASE_ASSERT(procellariidae.size() == 5); + + // Both procellariidae and mPetrels include GADFLY_PERTEL and TRUE_PETREL + EnumSet<SeaBird> procellariiformes; + procellariiformes += mDiomedeidae; + procellariiformes += procellariidae; + procellariiformes += mPetrels; + MOZ_RELEASE_ASSERT(procellariiformes.size() == 8); + } + + void testUnion() + { + EnumSet<SeaBird> procellariidae = mPetrelProcellariidae + + mNonPetrelProcellariidae; + MOZ_RELEASE_ASSERT(procellariidae.size() == 5); + + // Both procellariidae and mPetrels include GADFLY_PETREL and TRUE_PETREL + EnumSet<SeaBird> procellariiformes = mDiomedeidae + procellariidae + + mPetrels; + MOZ_RELEASE_ASSERT(procellariiformes.size() == 8); + } + + void testRemoveFrom() + { + EnumSet<SeaBird> likes = mPetrels; + likes -= TRUE_PETREL; + likes -= DIVING_PETREL; + MOZ_RELEASE_ASSERT(likes.size() == 2); + MOZ_RELEASE_ASSERT(likes.contains(GADFLY_PETREL)); + MOZ_RELEASE_ASSERT(likes.contains(STORM_PETREL)); + } + + void testRemove() + { + EnumSet<SeaBird> likes = mPetrels - TRUE_PETREL - DIVING_PETREL; + MOZ_RELEASE_ASSERT(likes.size() == 2); + MOZ_RELEASE_ASSERT(likes.contains(GADFLY_PETREL)); + MOZ_RELEASE_ASSERT(likes.contains(STORM_PETREL)); + } + + void testRemoveAllFrom() + { + EnumSet<SeaBird> likes = mPetrels; + likes -= mPetrelProcellariidae; + MOZ_RELEASE_ASSERT(likes.size() == 2); + MOZ_RELEASE_ASSERT(likes.contains(DIVING_PETREL)); + MOZ_RELEASE_ASSERT(likes.contains(STORM_PETREL)); + } + + void testRemoveAll() + { + EnumSet<SeaBird> likes = mPetrels - mPetrelProcellariidae; + MOZ_RELEASE_ASSERT(likes.size() == 2); + MOZ_RELEASE_ASSERT(likes.contains(DIVING_PETREL)); + MOZ_RELEASE_ASSERT(likes.contains(STORM_PETREL)); + } + + void testIntersect() + { + EnumSet<SeaBird> likes = mPetrels; + likes &= mPetrelProcellariidae; + MOZ_RELEASE_ASSERT(likes.size() == 2); + MOZ_RELEASE_ASSERT(likes.contains(GADFLY_PETREL)); + MOZ_RELEASE_ASSERT(likes.contains(TRUE_PETREL)); + } + + void testInsersection() + { + EnumSet<SeaBird> likes = mPetrels & mPetrelProcellariidae; + MOZ_RELEASE_ASSERT(likes.size() == 2); + MOZ_RELEASE_ASSERT(likes.contains(GADFLY_PETREL)); + MOZ_RELEASE_ASSERT(likes.contains(TRUE_PETREL)); + } + + void testEquality() + { + EnumSet<SeaBird> likes = mPetrels & mPetrelProcellariidae; + MOZ_RELEASE_ASSERT(likes == EnumSet<SeaBird>(GADFLY_PETREL, + TRUE_PETREL)); + } + + void testDuplicates() + { + EnumSet<SeaBird> likes = mPetrels; + likes += GADFLY_PETREL; + likes += TRUE_PETREL; + likes += DIVING_PETREL; + likes += STORM_PETREL; + MOZ_RELEASE_ASSERT(likes.size() == 4); + MOZ_RELEASE_ASSERT(likes == mPetrels); + } + + void testIteration() + { + EnumSet<SeaBird> birds; + Vector<SeaBird> vec; + + for (auto bird : birds) { + MOZ_RELEASE_ASSERT(vec.append(bird)); + } + MOZ_RELEASE_ASSERT(vec.length() == 0); + + birds += DIVING_PETREL; + birds += GADFLY_PETREL; + birds += STORM_PETREL; + birds += TRUE_PETREL; + for (auto bird : birds) { + MOZ_RELEASE_ASSERT(vec.append(bird)); + } + + MOZ_RELEASE_ASSERT(vec.length() == 4); + MOZ_RELEASE_ASSERT(vec[0] == GADFLY_PETREL); + MOZ_RELEASE_ASSERT(vec[1] == TRUE_PETREL); + MOZ_RELEASE_ASSERT(vec[2] == DIVING_PETREL); + MOZ_RELEASE_ASSERT(vec[3] == STORM_PETREL); + } + + void testInitializerListConstuctor() + { + EnumSet<SeaBird> empty {}; + MOZ_RELEASE_ASSERT(empty.size() == 0); + + EnumSet<SeaBird> someBirds { SKIMMER, GULL, BOOBY }; + MOZ_RELEASE_ASSERT(someBirds.size() == 3); + MOZ_RELEASE_ASSERT(someBirds.contains(SKIMMER)); + MOZ_RELEASE_ASSERT(someBirds.contains(GULL)); + MOZ_RELEASE_ASSERT(someBirds.contains(BOOBY)); + } + + EnumSet<SeaBird> mAlcidae; + EnumSet<SeaBird> mDiomedeidae; + EnumSet<SeaBird> mPetrelProcellariidae; + EnumSet<SeaBird> mNonPetrelProcellariidae; + EnumSet<SeaBird> mPetrels; +}; + +int +main() +{ + EnumSetSuite suite; + suite.runTests(); + return 0; +} diff --git a/mfbt/tests/TestEnumTypeTraits.cpp b/mfbt/tests/TestEnumTypeTraits.cpp new file mode 100644 index 000000000..879d40526 --- /dev/null +++ b/mfbt/tests/TestEnumTypeTraits.cpp @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/IntegerTypeTraits.h" +#include "mozilla/EnumTypeTraits.h" + +using namespace mozilla; + +/* Feature check for EnumTypeFitsWithin. */ + +#define MAKE_FIXED_EMUM_FOR_TYPE(IntType) \ + enum FixedEnumFor_##IntType : IntType { \ + A_##IntType, \ + B_##IntType, \ + C_##IntType, \ + }; + +template<typename EnumType, typename IntType> +static void +TestShouldFit() +{ + static_assert(EnumTypeFitsWithin<EnumType, IntType>::value, + "Should fit within exact/promoted integral type"); +} + +template<typename EnumType, typename IntType> +static void +TestShouldNotFit() +{ + static_assert(!EnumTypeFitsWithin<EnumType, IntType>::value, + "Should not fit within"); +} + +int +main() +{ + // check for int8_t + MAKE_FIXED_EMUM_FOR_TYPE(int8_t); + TestShouldFit<FixedEnumFor_int8_t, int8_t>(); + TestShouldFit<FixedEnumFor_int8_t, int16_t>(); + TestShouldFit<FixedEnumFor_int8_t, int32_t>(); + TestShouldFit<FixedEnumFor_int8_t, int64_t>(); + + TestShouldNotFit<FixedEnumFor_int8_t, uint8_t>(); + TestShouldNotFit<FixedEnumFor_int8_t, uint16_t>(); + TestShouldNotFit<FixedEnumFor_int8_t, uint32_t>(); + TestShouldNotFit<FixedEnumFor_int8_t, uint64_t>(); + + // check for uint8_t + MAKE_FIXED_EMUM_FOR_TYPE(uint8_t); + TestShouldFit<FixedEnumFor_uint8_t, uint8_t>(); + TestShouldFit<FixedEnumFor_uint8_t, uint16_t>(); + TestShouldFit<FixedEnumFor_uint8_t, uint32_t>(); + TestShouldFit<FixedEnumFor_uint8_t, uint64_t>(); + + TestShouldNotFit<FixedEnumFor_uint8_t, int8_t>(); + TestShouldFit<FixedEnumFor_uint8_t, int16_t>(); + TestShouldFit<FixedEnumFor_uint8_t, int32_t>(); + TestShouldFit<FixedEnumFor_uint8_t, int64_t>(); + + // check for int16_t + MAKE_FIXED_EMUM_FOR_TYPE(int16_t); + TestShouldNotFit<FixedEnumFor_int16_t, int8_t>(); + TestShouldFit<FixedEnumFor_int16_t, int16_t>(); + TestShouldFit<FixedEnumFor_int16_t, int32_t>(); + TestShouldFit<FixedEnumFor_int16_t, int64_t>(); + + TestShouldNotFit<FixedEnumFor_int16_t, uint8_t>(); + TestShouldNotFit<FixedEnumFor_int16_t, uint16_t>(); + TestShouldNotFit<FixedEnumFor_int16_t, uint32_t>(); + TestShouldNotFit<FixedEnumFor_int16_t, uint64_t>(); + + // check for uint16_t + MAKE_FIXED_EMUM_FOR_TYPE(uint16_t); + TestShouldNotFit<FixedEnumFor_uint16_t, uint8_t>(); + TestShouldFit<FixedEnumFor_uint16_t, uint16_t>(); + TestShouldFit<FixedEnumFor_uint16_t, uint32_t>(); + TestShouldFit<FixedEnumFor_uint16_t, uint64_t>(); + + TestShouldNotFit<FixedEnumFor_uint16_t, int8_t>(); + TestShouldNotFit<FixedEnumFor_uint16_t, int16_t>(); + TestShouldFit<FixedEnumFor_uint16_t, int32_t>(); + TestShouldFit<FixedEnumFor_uint16_t, int64_t>(); + + // check for int32_t + MAKE_FIXED_EMUM_FOR_TYPE(int32_t); + TestShouldNotFit<FixedEnumFor_int32_t, int8_t>(); + TestShouldNotFit<FixedEnumFor_int32_t, int16_t>(); + TestShouldFit<FixedEnumFor_int32_t, int32_t>(); + TestShouldFit<FixedEnumFor_int32_t, int64_t>(); + + TestShouldNotFit<FixedEnumFor_int32_t, uint8_t>(); + TestShouldNotFit<FixedEnumFor_int32_t, uint16_t>(); + TestShouldNotFit<FixedEnumFor_int32_t, uint32_t>(); + TestShouldNotFit<FixedEnumFor_int32_t, uint64_t>(); + + // check for uint32_t + MAKE_FIXED_EMUM_FOR_TYPE(uint32_t); + TestShouldNotFit<FixedEnumFor_uint32_t, uint8_t>(); + TestShouldNotFit<FixedEnumFor_uint32_t, uint16_t>(); + TestShouldFit<FixedEnumFor_uint32_t, uint32_t>(); + TestShouldFit<FixedEnumFor_uint32_t, uint64_t>(); + + TestShouldNotFit<FixedEnumFor_uint32_t, int8_t>(); + TestShouldNotFit<FixedEnumFor_uint32_t, int16_t>(); + TestShouldNotFit<FixedEnumFor_uint32_t, int32_t>(); + TestShouldFit<FixedEnumFor_uint32_t, int64_t>(); + + // check for int64_t + MAKE_FIXED_EMUM_FOR_TYPE(int64_t); + TestShouldNotFit<FixedEnumFor_int64_t, int8_t>(); + TestShouldNotFit<FixedEnumFor_int64_t, int16_t>(); + TestShouldNotFit<FixedEnumFor_int64_t, int32_t>(); + TestShouldFit<FixedEnumFor_int64_t, int64_t>(); + + TestShouldNotFit<FixedEnumFor_int64_t, uint8_t>(); + TestShouldNotFit<FixedEnumFor_int64_t, uint16_t>(); + TestShouldNotFit<FixedEnumFor_int64_t, uint32_t>(); + TestShouldNotFit<FixedEnumFor_int64_t, uint64_t>(); + + // check for uint64_t + MAKE_FIXED_EMUM_FOR_TYPE(uint64_t); + TestShouldNotFit<FixedEnumFor_uint64_t, uint8_t>(); + TestShouldNotFit<FixedEnumFor_uint64_t, uint16_t>(); + TestShouldNotFit<FixedEnumFor_uint64_t, uint32_t>(); + TestShouldFit<FixedEnumFor_uint64_t, uint64_t>(); + + TestShouldNotFit<FixedEnumFor_uint64_t, int8_t>(); + TestShouldNotFit<FixedEnumFor_uint64_t, int16_t>(); + TestShouldNotFit<FixedEnumFor_uint64_t, int32_t>(); + TestShouldNotFit<FixedEnumFor_uint64_t, int64_t>(); + + return 0; +} diff --git a/mfbt/tests/TestEnumeratedArray.cpp b/mfbt/tests/TestEnumeratedArray.cpp new file mode 100644 index 000000000..bb73f21b7 --- /dev/null +++ b/mfbt/tests/TestEnumeratedArray.cpp @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/EnumeratedArray.h" + +enum class AnimalSpecies +{ + Cow, + Sheep, + Pig, + Count +}; + +void TestInitialValueByConstructor() +{ + using namespace mozilla; + // Style 1 + EnumeratedArray<AnimalSpecies, AnimalSpecies::Count, int> headCount(1, 2, 3); + MOZ_RELEASE_ASSERT(headCount[AnimalSpecies::Cow] == 1); + MOZ_RELEASE_ASSERT(headCount[AnimalSpecies::Sheep] == 2); + MOZ_RELEASE_ASSERT(headCount[AnimalSpecies::Pig] == 3); + // Style 2 + EnumeratedArray<AnimalSpecies, AnimalSpecies::Count, int> headCount2{5, 6, 7}; + MOZ_RELEASE_ASSERT(headCount2[AnimalSpecies::Cow] == 5); + MOZ_RELEASE_ASSERT(headCount2[AnimalSpecies::Sheep] == 6); + MOZ_RELEASE_ASSERT(headCount2[AnimalSpecies::Pig] == 7); + // Style 3 + EnumeratedArray<AnimalSpecies, AnimalSpecies::Count, int> headCount3({8, 9, 10}); + MOZ_RELEASE_ASSERT(headCount3[AnimalSpecies::Cow] == 8); + MOZ_RELEASE_ASSERT(headCount3[AnimalSpecies::Sheep] == 9); + MOZ_RELEASE_ASSERT(headCount3[AnimalSpecies::Pig] == 10); +} + +int +main() +{ + TestInitialValueByConstructor(); + return 0; +}
\ No newline at end of file diff --git a/mfbt/tests/TestFastBernoulliTrial.cpp b/mfbt/tests/TestFastBernoulliTrial.cpp new file mode 100644 index 000000000..62d4b51a7 --- /dev/null +++ b/mfbt/tests/TestFastBernoulliTrial.cpp @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/FastBernoulliTrial.h" + +#include <math.h> + +// Note that because we always provide FastBernoulliTrial with a fixed +// pseudorandom seed in these tests, the results here are completely +// deterministic. +// +// A non-optimized version of this test runs in .009s on my laptop. Using larger +// sample sizes lets us meet tighter bounds on the counts. + +static void +TestProportions() +{ + mozilla::FastBernoulliTrial bernoulli(1.0, + 698079309544035222ULL, + 6012389156611637584ULL); + + for (size_t i = 0; i < 100; i++) + MOZ_RELEASE_ASSERT(bernoulli.trial()); + + { + bernoulli.setProbability(0.5); + size_t count = 0; + for (size_t i = 0; i < 1000; i++) + count += bernoulli.trial(); + MOZ_RELEASE_ASSERT(count == 496); + } + + { + bernoulli.setProbability(0.001); + size_t count = 0; + for (size_t i = 0; i < 1000; i++) + count += bernoulli.trial(); + MOZ_RELEASE_ASSERT(count == 2); + } + + { + bernoulli.setProbability(0.85); + size_t count = 0; + for (size_t i = 0; i < 1000; i++) + count += bernoulli.trial(); + MOZ_RELEASE_ASSERT(count == 852); + } + + bernoulli.setProbability(0.0); + for (size_t i = 0; i < 100; i++) + MOZ_RELEASE_ASSERT(!bernoulli.trial()); +} + +static void +TestHarmonics() +{ + mozilla::FastBernoulliTrial bernoulli(0.1, + 698079309544035222ULL, + 6012389156611637584ULL); + + const size_t n = 100000; + bool trials[n]; + for (size_t i = 0; i < n; i++) + trials[i] = bernoulli.trial(); + + // For each harmonic and phase, check that the proportion sampled is + // within acceptable bounds. + for (size_t harmonic = 1; harmonic < 20; harmonic++) { + size_t expected = n / harmonic / 10; + size_t low_expected = expected * 85 / 100; + size_t high_expected = expected * 115 / 100; + + for (size_t phase = 0; phase < harmonic; phase++) { + size_t count = 0; + for (size_t i = phase; i < n; i += harmonic) + count += trials[i]; + + MOZ_RELEASE_ASSERT(low_expected <= count && count <= high_expected); + } + } +} + +static void +TestTrialN() +{ + mozilla::FastBernoulliTrial bernoulli(0.01, + 0x67ff17e25d855942ULL, + 0x74f298193fe1c5b1ULL); + + { + size_t count = 0; + for (size_t i = 0; i < 10000; i++) + count += bernoulli.trial(1); + + // Expected value: 0.01 * 10000 == 100 + MOZ_RELEASE_ASSERT(count == 97); + } + + { + size_t count = 0; + for (size_t i = 0; i < 10000; i++) + count += bernoulli.trial(3); + + // Expected value: (1 - (1 - 0.01) ** 3) == 0.0297, + // 0.0297 * 10000 == 297 + MOZ_RELEASE_ASSERT(count == 304); + } + + { + size_t count = 0; + for (size_t i = 0; i < 10000; i++) + count += bernoulli.trial(10); + + // Expected value: (1 - (1 - 0.01) ** 10) == 0.0956, + // 0.0956 * 10000 == 956 + MOZ_RELEASE_ASSERT(count == 936); + } + + { + size_t count = 0; + for (size_t i = 0; i < 10000; i++) + count += bernoulli.trial(100); + + // Expected value: (1 - (1 - 0.01) ** 100) == 0.6339 + // 0.6339 * 10000 == 6339 + MOZ_RELEASE_ASSERT(count == 6372); + } + + { + size_t count = 0; + for (size_t i = 0; i < 10000; i++) + count += bernoulli.trial(1000); + + // Expected value: (1 - (1 - 0.01) ** 1000) == 0.9999 + // 0.9999 * 10000 == 9999 + MOZ_RELEASE_ASSERT(count == 9998); + } +} + +static void +TestChangeProbability() +{ + mozilla::FastBernoulliTrial bernoulli(1.0, + 0x67ff17e25d855942ULL, + 0x74f298193fe1c5b1ULL); + + // Establish a very high skip count. + bernoulli.setProbability(0.0); + + // This should re-establish a zero skip count. + bernoulli.setProbability(1.0); + + // So this should return true. + MOZ_RELEASE_ASSERT(bernoulli.trial()); +} + +static void +TestCuspProbabilities() +{ + /* + * FastBernoulliTrial takes care to avoid screwing up on edge cases. The + * checks here all look pretty dumb, but they exercise paths in the code that + * could exhibit undefined behavior if coded naïvely. + */ + + /* + * This should not be perceptibly different from 1; for 64-bit doubles, this + * is a one in ten trillion chance of the trial not succeeding. Overflows + * converting doubles to size_t skip counts may change this, though. + */ + mozilla::FastBernoulliTrial bernoulli(nextafter(1, 0), + 0x67ff17e25d855942ULL, + 0x74f298193fe1c5b1ULL); + + for (size_t i = 0; i < 1000; i++) + MOZ_RELEASE_ASSERT(bernoulli.trial()); + + /* + * This should not be perceptibly different from 0; for 64-bit doubles, + * the FastBernoulliTrial will actually treat this as exactly zero. + */ + bernoulli.setProbability(nextafter(0, 1)); + for (size_t i = 0; i < 1000; i++) + MOZ_RELEASE_ASSERT(!bernoulli.trial()); + + /* + * This should be a vanishingly low probability which FastBernoulliTrial does + * *not* treat as exactly zero. + */ + bernoulli.setProbability(1 - nextafter(1, 0)); + for (size_t i = 0; i < 1000; i++) + MOZ_RELEASE_ASSERT(!bernoulli.trial()); +} + +int +main() +{ + TestProportions(); + TestHarmonics(); + TestTrialN(); + TestChangeProbability(); + TestCuspProbabilities(); + + return 0; +} diff --git a/mfbt/tests/TestFloatingPoint.cpp b/mfbt/tests/TestFloatingPoint.cpp new file mode 100644 index 000000000..f32e698de --- /dev/null +++ b/mfbt/tests/TestFloatingPoint.cpp @@ -0,0 +1,592 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/FloatingPoint.h" + +#include <math.h> + +using mozilla::ExponentComponent; +using mozilla::FloatingPoint; +using mozilla::FuzzyEqualsAdditive; +using mozilla::FuzzyEqualsMultiplicative; +using mozilla::IsFinite; +using mozilla::IsInfinite; +using mozilla::IsNaN; +using mozilla::IsNegative; +using mozilla::IsNegativeZero; +using mozilla::IsPositiveZero; +using mozilla::NegativeInfinity; +using mozilla::NumberEqualsInt32; +using mozilla::NumberIsInt32; +using mozilla::NumbersAreIdentical; +using mozilla::PositiveInfinity; +using mozilla::SpecificNaN; +using mozilla::UnspecifiedNaN; + +#define A(a) MOZ_RELEASE_ASSERT(a) + +template<typename T> +static void +ShouldBeIdentical(T aD1, T aD2) +{ + A(NumbersAreIdentical(aD1, aD2)); + A(NumbersAreIdentical(aD2, aD1)); +} + +template<typename T> +static void +ShouldNotBeIdentical(T aD1, T aD2) +{ + A(!NumbersAreIdentical(aD1, aD2)); + A(!NumbersAreIdentical(aD2, aD1)); +} + +static void +TestDoublesAreIdentical() +{ + ShouldBeIdentical(+0.0, +0.0); + ShouldBeIdentical(-0.0, -0.0); + ShouldNotBeIdentical(+0.0, -0.0); + + ShouldBeIdentical(1.0, 1.0); + ShouldNotBeIdentical(-1.0, 1.0); + ShouldBeIdentical(4294967295.0, 4294967295.0); + ShouldNotBeIdentical(-4294967295.0, 4294967295.0); + ShouldBeIdentical(4294967296.0, 4294967296.0); + ShouldBeIdentical(4294967297.0, 4294967297.0); + ShouldBeIdentical(1e300, 1e300); + + ShouldBeIdentical(PositiveInfinity<double>(), PositiveInfinity<double>()); + ShouldBeIdentical(NegativeInfinity<double>(), NegativeInfinity<double>()); + ShouldNotBeIdentical(PositiveInfinity<double>(), NegativeInfinity<double>()); + + ShouldNotBeIdentical(-0.0, NegativeInfinity<double>()); + ShouldNotBeIdentical(+0.0, NegativeInfinity<double>()); + ShouldNotBeIdentical(1e300, NegativeInfinity<double>()); + ShouldNotBeIdentical(3.141592654, NegativeInfinity<double>()); + + ShouldBeIdentical(UnspecifiedNaN<double>(), UnspecifiedNaN<double>()); + ShouldBeIdentical(-UnspecifiedNaN<double>(), UnspecifiedNaN<double>()); + ShouldBeIdentical(UnspecifiedNaN<double>(), -UnspecifiedNaN<double>()); + + ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(0, 42)); + ShouldBeIdentical(SpecificNaN<double>(1, 17), SpecificNaN<double>(1, 42)); + ShouldBeIdentical(SpecificNaN<double>(0, 17), SpecificNaN<double>(1, 42)); + ShouldBeIdentical(SpecificNaN<double>(1, 17), SpecificNaN<double>(0, 42)); + + const uint64_t Mask = 0xfffffffffffffULL; + for (unsigned i = 0; i < 52; i++) { + for (unsigned j = 0; j < 52; j++) { + for (unsigned sign = 0; i < 2; i++) { + ShouldBeIdentical(SpecificNaN<double>(0, 1ULL << i), + SpecificNaN<double>(sign, 1ULL << j)); + ShouldBeIdentical(SpecificNaN<double>(1, 1ULL << i), + SpecificNaN<double>(sign, 1ULL << j)); + + ShouldBeIdentical(SpecificNaN<double>(0, Mask & ~(1ULL << i)), + SpecificNaN<double>(sign, Mask & ~(1ULL << j))); + ShouldBeIdentical(SpecificNaN<double>(1, Mask & ~(1ULL << i)), + SpecificNaN<double>(sign, Mask & ~(1ULL << j))); + } + } + } + ShouldBeIdentical(SpecificNaN<double>(0, 17), + SpecificNaN<double>(0, 0x8000000000000ULL)); + ShouldBeIdentical(SpecificNaN<double>(0, 17), + SpecificNaN<double>(0, 0x4000000000000ULL)); + ShouldBeIdentical(SpecificNaN<double>(0, 17), + SpecificNaN<double>(0, 0x2000000000000ULL)); + ShouldBeIdentical(SpecificNaN<double>(0, 17), + SpecificNaN<double>(0, 0x1000000000000ULL)); + ShouldBeIdentical(SpecificNaN<double>(0, 17), + SpecificNaN<double>(0, 0x0800000000000ULL)); + ShouldBeIdentical(SpecificNaN<double>(0, 17), + SpecificNaN<double>(0, 0x0400000000000ULL)); + ShouldBeIdentical(SpecificNaN<double>(0, 17), + SpecificNaN<double>(0, 0x0200000000000ULL)); + ShouldBeIdentical(SpecificNaN<double>(0, 17), + SpecificNaN<double>(0, 0x0100000000000ULL)); + ShouldBeIdentical(SpecificNaN<double>(0, 17), + SpecificNaN<double>(0, 0x0080000000000ULL)); + ShouldBeIdentical(SpecificNaN<double>(0, 17), + SpecificNaN<double>(0, 0x0040000000000ULL)); + ShouldBeIdentical(SpecificNaN<double>(0, 17), + SpecificNaN<double>(0, 0x0020000000000ULL)); + ShouldBeIdentical(SpecificNaN<double>(0, 17), + SpecificNaN<double>(0, 0x0010000000000ULL)); + ShouldBeIdentical(SpecificNaN<double>(1, 17), + SpecificNaN<double>(0, 0xff0ffffffffffULL)); + ShouldBeIdentical(SpecificNaN<double>(1, 17), + SpecificNaN<double>(0, 0xfffffffffff0fULL)); + + ShouldNotBeIdentical(UnspecifiedNaN<double>(), +0.0); + ShouldNotBeIdentical(UnspecifiedNaN<double>(), -0.0); + ShouldNotBeIdentical(UnspecifiedNaN<double>(), 1.0); + ShouldNotBeIdentical(UnspecifiedNaN<double>(), -1.0); + ShouldNotBeIdentical(UnspecifiedNaN<double>(), PositiveInfinity<double>()); + ShouldNotBeIdentical(UnspecifiedNaN<double>(), NegativeInfinity<double>()); +} + +static void +TestFloatsAreIdentical() +{ + ShouldBeIdentical(+0.0f, +0.0f); + ShouldBeIdentical(-0.0f, -0.0f); + ShouldNotBeIdentical(+0.0f, -0.0f); + + ShouldBeIdentical(1.0f, 1.0f); + ShouldNotBeIdentical(-1.0f, 1.0f); + ShouldBeIdentical(8388607.0f, 8388607.0f); + ShouldNotBeIdentical(-8388607.0f, 8388607.0f); + ShouldBeIdentical(8388608.0f, 8388608.0f); + ShouldBeIdentical(8388609.0f, 8388609.0f); + ShouldBeIdentical(1e36f, 1e36f); + + ShouldBeIdentical(PositiveInfinity<float>(), PositiveInfinity<float>()); + ShouldBeIdentical(NegativeInfinity<float>(), NegativeInfinity<float>()); + ShouldNotBeIdentical(PositiveInfinity<float>(), NegativeInfinity<float>()); + + ShouldNotBeIdentical(-0.0f, NegativeInfinity<float>()); + ShouldNotBeIdentical(+0.0f, NegativeInfinity<float>()); + ShouldNotBeIdentical(1e36f, NegativeInfinity<float>()); + ShouldNotBeIdentical(3.141592654f, NegativeInfinity<float>()); + + ShouldBeIdentical(UnspecifiedNaN<float>(), UnspecifiedNaN<float>()); + ShouldBeIdentical(-UnspecifiedNaN<float>(), UnspecifiedNaN<float>()); + ShouldBeIdentical(UnspecifiedNaN<float>(), -UnspecifiedNaN<float>()); + + ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 42)); + ShouldBeIdentical(SpecificNaN<float>(1, 17), SpecificNaN<float>(1, 42)); + ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(1, 42)); + ShouldBeIdentical(SpecificNaN<float>(1, 17), SpecificNaN<float>(0, 42)); + + const uint32_t Mask = 0x7fffffUL; + for (unsigned i = 0; i < 23; i++) { + for (unsigned j = 0; j < 23; j++) { + for (unsigned sign = 0; i < 2; i++) { + ShouldBeIdentical(SpecificNaN<float>(0, 1UL << i), + SpecificNaN<float>(sign, 1UL << j)); + ShouldBeIdentical(SpecificNaN<float>(1, 1UL << i), + SpecificNaN<float>(sign, 1UL << j)); + + ShouldBeIdentical(SpecificNaN<float>(0, Mask & ~(1UL << i)), + SpecificNaN<float>(sign, Mask & ~(1UL << j))); + ShouldBeIdentical(SpecificNaN<float>(1, Mask & ~(1UL << i)), + SpecificNaN<float>(sign, Mask & ~(1UL << j))); + } + } + } + ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x700000)); + ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x400000)); + ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x200000)); + ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x100000)); + ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x080000)); + ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x040000)); + ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x020000)); + ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x010000)); + ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x008000)); + ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x004000)); + ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x002000)); + ShouldBeIdentical(SpecificNaN<float>(0, 17), SpecificNaN<float>(0, 0x001000)); + ShouldBeIdentical(SpecificNaN<float>(1, 17), SpecificNaN<float>(0, 0x7f0fff)); + ShouldBeIdentical(SpecificNaN<float>(1, 17), SpecificNaN<float>(0, 0x7fff0f)); + + ShouldNotBeIdentical(UnspecifiedNaN<float>(), +0.0f); + ShouldNotBeIdentical(UnspecifiedNaN<float>(), -0.0f); + ShouldNotBeIdentical(UnspecifiedNaN<float>(), 1.0f); + ShouldNotBeIdentical(UnspecifiedNaN<float>(), -1.0f); + ShouldNotBeIdentical(UnspecifiedNaN<float>(), PositiveInfinity<float>()); + ShouldNotBeIdentical(UnspecifiedNaN<float>(), NegativeInfinity<float>()); +} + +static void +TestAreIdentical() +{ + TestDoublesAreIdentical(); + TestFloatsAreIdentical(); +} + +static void +TestDoubleExponentComponent() +{ + A(ExponentComponent(0.0) == + -int_fast16_t(FloatingPoint<double>::kExponentBias)); + A(ExponentComponent(-0.0) == + -int_fast16_t(FloatingPoint<double>::kExponentBias)); + A(ExponentComponent(0.125) == -3); + A(ExponentComponent(0.5) == -1); + A(ExponentComponent(1.0) == 0); + A(ExponentComponent(1.5) == 0); + A(ExponentComponent(2.0) == 1); + A(ExponentComponent(7.0) == 2); + A(ExponentComponent(PositiveInfinity<double>()) == + FloatingPoint<double>::kExponentBias + 1); + A(ExponentComponent(NegativeInfinity<double>()) == + FloatingPoint<double>::kExponentBias + 1); + A(ExponentComponent(UnspecifiedNaN<double>()) == + FloatingPoint<double>::kExponentBias + 1); +} + +static void +TestFloatExponentComponent() +{ + A(ExponentComponent(0.0f) == + -int_fast16_t(FloatingPoint<float>::kExponentBias)); + A(ExponentComponent(-0.0f) == + -int_fast16_t(FloatingPoint<float>::kExponentBias)); + A(ExponentComponent(0.125f) == -3); + A(ExponentComponent(0.5f) == -1); + A(ExponentComponent(1.0f) == 0); + A(ExponentComponent(1.5f) == 0); + A(ExponentComponent(2.0f) == 1); + A(ExponentComponent(7.0f) == 2); + A(ExponentComponent(PositiveInfinity<float>()) == + FloatingPoint<float>::kExponentBias + 1); + A(ExponentComponent(NegativeInfinity<float>()) == + FloatingPoint<float>::kExponentBias + 1); + A(ExponentComponent(UnspecifiedNaN<float>()) == + FloatingPoint<float>::kExponentBias + 1); +} + +static void +TestExponentComponent() +{ + TestDoubleExponentComponent(); + TestFloatExponentComponent(); +} + +static void +TestDoublesPredicates() +{ + A(IsNaN(UnspecifiedNaN<double>())); + A(IsNaN(SpecificNaN<double>(1, 17)));; + A(IsNaN(SpecificNaN<double>(0, 0xfffffffffff0fULL))); + A(!IsNaN(0.0)); + A(!IsNaN(-0.0)); + A(!IsNaN(1.0)); + A(!IsNaN(PositiveInfinity<double>())); + A(!IsNaN(NegativeInfinity<double>())); + + A(IsInfinite(PositiveInfinity<double>())); + A(IsInfinite(NegativeInfinity<double>())); + A(!IsInfinite(UnspecifiedNaN<double>())); + A(!IsInfinite(0.0)); + A(!IsInfinite(-0.0)); + A(!IsInfinite(1.0)); + + A(!IsFinite(PositiveInfinity<double>())); + A(!IsFinite(NegativeInfinity<double>())); + A(!IsFinite(UnspecifiedNaN<double>())); + A(IsFinite(0.0)); + A(IsFinite(-0.0)); + A(IsFinite(1.0)); + + A(!IsNegative(PositiveInfinity<double>())); + A(IsNegative(NegativeInfinity<double>())); + A(IsNegative(-0.0)); + A(!IsNegative(0.0)); + A(IsNegative(-1.0)); + A(!IsNegative(1.0)); + + A(!IsNegativeZero(PositiveInfinity<double>())); + A(!IsNegativeZero(NegativeInfinity<double>())); + A(!IsNegativeZero(SpecificNaN<double>(1, 17)));; + A(!IsNegativeZero(SpecificNaN<double>(1, 0xfffffffffff0fULL))); + A(!IsNegativeZero(SpecificNaN<double>(0, 17)));; + A(!IsNegativeZero(SpecificNaN<double>(0, 0xfffffffffff0fULL))); + A(!IsNegativeZero(UnspecifiedNaN<double>())); + A(IsNegativeZero(-0.0)); + A(!IsNegativeZero(0.0)); + A(!IsNegativeZero(-1.0)); + A(!IsNegativeZero(1.0)); + + int32_t i; + A(NumberIsInt32(0.0, &i)); + A(i == 0); + A(!NumberIsInt32(-0.0, &i)); + A(NumberEqualsInt32(0.0, &i)); + A(i == 0); + A(NumberEqualsInt32(-0.0, &i)); + A(i == 0); + A(NumberIsInt32(double(INT32_MIN), &i)); + A(i == INT32_MIN); + A(NumberIsInt32(double(INT32_MAX), &i)); + A(i == INT32_MAX); + A(NumberEqualsInt32(double(INT32_MIN), &i)); + A(i == INT32_MIN); + A(NumberEqualsInt32(double(INT32_MAX), &i)); + A(i == INT32_MAX); + A(!NumberIsInt32(0.5, &i)); + A(!NumberIsInt32(double(INT32_MAX) + 0.1, &i)); + A(!NumberIsInt32(double(INT32_MIN) - 0.1, &i)); + A(!NumberIsInt32(NegativeInfinity<double>(), &i)); + A(!NumberIsInt32(PositiveInfinity<double>(), &i)); + A(!NumberIsInt32(UnspecifiedNaN<double>(), &i)); + A(!NumberEqualsInt32(0.5, &i)); + A(!NumberEqualsInt32(double(INT32_MAX) + 0.1, &i)); + A(!NumberEqualsInt32(double(INT32_MIN) - 0.1, &i)); + A(!NumberEqualsInt32(NegativeInfinity<double>(), &i)); + A(!NumberEqualsInt32(PositiveInfinity<double>(), &i)); + A(!NumberEqualsInt32(UnspecifiedNaN<double>(), &i)); +} + +static void +TestFloatsPredicates() +{ + A(IsNaN(UnspecifiedNaN<float>())); + A(IsNaN(SpecificNaN<float>(1, 17)));; + A(IsNaN(SpecificNaN<float>(0, 0x7fff0fUL))); + A(!IsNaN(0.0f)); + A(!IsNaN(-0.0f)); + A(!IsNaN(1.0f)); + A(!IsNaN(PositiveInfinity<float>())); + A(!IsNaN(NegativeInfinity<float>())); + + A(IsInfinite(PositiveInfinity<float>())); + A(IsInfinite(NegativeInfinity<float>())); + A(!IsInfinite(UnspecifiedNaN<float>())); + A(!IsInfinite(0.0f)); + A(!IsInfinite(-0.0f)); + A(!IsInfinite(1.0f)); + + A(!IsFinite(PositiveInfinity<float>())); + A(!IsFinite(NegativeInfinity<float>())); + A(!IsFinite(UnspecifiedNaN<float>())); + A(IsFinite(0.0f)); + A(IsFinite(-0.0f)); + A(IsFinite(1.0f)); + + A(!IsNegative(PositiveInfinity<float>())); + A(IsNegative(NegativeInfinity<float>())); + A(IsNegative(-0.0f)); + A(!IsNegative(0.0f)); + A(IsNegative(-1.0f)); + A(!IsNegative(1.0f)); + + A(!IsNegativeZero(PositiveInfinity<float>())); + A(!IsNegativeZero(NegativeInfinity<float>())); + A(!IsNegativeZero(SpecificNaN<float>(1, 17)));; + A(!IsNegativeZero(SpecificNaN<float>(1, 0x7fff0fUL))); + A(!IsNegativeZero(SpecificNaN<float>(0, 17)));; + A(!IsNegativeZero(SpecificNaN<float>(0, 0x7fff0fUL))); + A(!IsNegativeZero(UnspecifiedNaN<float>())); + A(IsNegativeZero(-0.0f)); + A(!IsNegativeZero(0.0f)); + A(!IsNegativeZero(-1.0f)); + A(!IsNegativeZero(1.0f)); + + A(!IsPositiveZero(PositiveInfinity<float>())); + A(!IsPositiveZero(NegativeInfinity<float>())); + A(!IsPositiveZero(SpecificNaN<float>(1, 17)));; + A(!IsPositiveZero(SpecificNaN<float>(1, 0x7fff0fUL))); + A(!IsPositiveZero(SpecificNaN<float>(0, 17)));; + A(!IsPositiveZero(SpecificNaN<float>(0, 0x7fff0fUL))); + A(!IsPositiveZero(UnspecifiedNaN<float>())); + A(IsPositiveZero(0.0f)); + A(!IsPositiveZero(-0.0f)); + A(!IsPositiveZero(-1.0f)); + A(!IsPositiveZero(1.0f)); + + int32_t i; + const int32_t BIG = 2097151; + A(NumberIsInt32(0.0f, &i)); + A(i == 0); + A(!NumberIsInt32(-0.0f, &i)); + A(NumberEqualsInt32(0.0f, &i)); + A(i == 0); + A(NumberEqualsInt32(-0.0f, &i)); + A(i == 0); + A(NumberIsInt32(float(INT32_MIN), &i)); + A(i == INT32_MIN); + A(NumberIsInt32(float(BIG), &i)); + A(i == BIG); + A(NumberEqualsInt32(float(INT32_MIN), &i)); + A(i == INT32_MIN); + A(NumberEqualsInt32(float(BIG), &i)); + A(i == BIG); + A(!NumberIsInt32(0.5f, &i)); + A(!NumberIsInt32(float(BIG) + 0.1f, &i)); + A(!NumberIsInt32(NegativeInfinity<float>(), &i)); + A(!NumberIsInt32(PositiveInfinity<float>(), &i)); + A(!NumberIsInt32(UnspecifiedNaN<float>(), &i)); + A(!NumberEqualsInt32(0.5f, &i)); + A(!NumberEqualsInt32(float(BIG) + 0.1f, &i)); + A(!NumberEqualsInt32(NegativeInfinity<float>(), &i)); + A(!NumberEqualsInt32(PositiveInfinity<float>(), &i)); + A(!NumberEqualsInt32(UnspecifiedNaN<float>(), &i)); +} + +static void +TestPredicates() +{ + TestFloatsPredicates(); + TestDoublesPredicates(); +} + +static void +TestFloatsAreApproximatelyEqual() +{ + float epsilon = mozilla::detail::FuzzyEqualsEpsilon<float>::value(); + float lessThanEpsilon = epsilon / 2.0f; + float moreThanEpsilon = epsilon * 2.0f; + + // Additive tests using the default epsilon + // ... around 1.0 + A(FuzzyEqualsAdditive(1.0f, 1.0f + lessThanEpsilon)); + A(FuzzyEqualsAdditive(1.0f, 1.0f - lessThanEpsilon)); + A(FuzzyEqualsAdditive(1.0f, 1.0f + epsilon)); + A(FuzzyEqualsAdditive(1.0f, 1.0f - epsilon)); + A(!FuzzyEqualsAdditive(1.0f, 1.0f + moreThanEpsilon)); + A(!FuzzyEqualsAdditive(1.0f, 1.0f - moreThanEpsilon)); + // ... around 1.0e2 (this is near the upper bound of the range where + // adding moreThanEpsilon will still be representable and return false) + A(FuzzyEqualsAdditive(1.0e2f, 1.0e2f + lessThanEpsilon)); + A(FuzzyEqualsAdditive(1.0e2f, 1.0e2f + epsilon)); + A(!FuzzyEqualsAdditive(1.0e2f, 1.0e2f + moreThanEpsilon)); + // ... around 1.0e-10 + A(FuzzyEqualsAdditive(1.0e-10f, 1.0e-10f + lessThanEpsilon)); + A(FuzzyEqualsAdditive(1.0e-10f, 1.0e-10f + epsilon)); + A(!FuzzyEqualsAdditive(1.0e-10f, 1.0e-10f + moreThanEpsilon)); + // ... straddling 0 + A(FuzzyEqualsAdditive(1.0e-6f, -1.0e-6f)); + A(!FuzzyEqualsAdditive(1.0e-5f, -1.0e-5f)); + // Using a small epsilon + A(FuzzyEqualsAdditive(1.0e-5f, 1.0e-5f + 1.0e-10f, 1.0e-9f)); + A(!FuzzyEqualsAdditive(1.0e-5f, 1.0e-5f + 1.0e-10f, 1.0e-11f)); + // Using a big epsilon + A(FuzzyEqualsAdditive(1.0e20f, 1.0e20f + 1.0e15f, 1.0e16f)); + A(!FuzzyEqualsAdditive(1.0e20f, 1.0e20f + 1.0e15f, 1.0e14f)); + + // Multiplicative tests using the default epsilon + // ... around 1.0 + A(FuzzyEqualsMultiplicative(1.0f, 1.0f + lessThanEpsilon)); + A(FuzzyEqualsMultiplicative(1.0f, 1.0f - lessThanEpsilon)); + A(FuzzyEqualsMultiplicative(1.0f, 1.0f + epsilon)); + A(!FuzzyEqualsMultiplicative(1.0f, 1.0f - epsilon)); + A(!FuzzyEqualsMultiplicative(1.0f, 1.0f + moreThanEpsilon)); + A(!FuzzyEqualsMultiplicative(1.0f, 1.0f - moreThanEpsilon)); + // ... around 1.0e10 + A(FuzzyEqualsMultiplicative(1.0e10f, 1.0e10f + (lessThanEpsilon * 1.0e10f))); + A(!FuzzyEqualsMultiplicative(1.0e10f, 1.0e10f + (moreThanEpsilon * 1.0e10f))); + // ... around 1.0e-10 + A(FuzzyEqualsMultiplicative(1.0e-10f, + 1.0e-10f + (lessThanEpsilon * 1.0e-10f))); + A(!FuzzyEqualsMultiplicative(1.0e-10f, + 1.0e-10f + (moreThanEpsilon * 1.0e-10f))); + // ... straddling 0 + A(!FuzzyEqualsMultiplicative(1.0e-6f, -1.0e-6f)); + A(FuzzyEqualsMultiplicative(1.0e-6f, -1.0e-6f, 1.0e2f)); + // Using a small epsilon + A(FuzzyEqualsMultiplicative(1.0e-5f, 1.0e-5f + 1.0e-10f, 1.0e-4f)); + A(!FuzzyEqualsMultiplicative(1.0e-5f, 1.0e-5f + 1.0e-10f, 1.0e-5f)); + // Using a big epsilon + A(FuzzyEqualsMultiplicative(1.0f, 2.0f, 1.0f)); + A(!FuzzyEqualsMultiplicative(1.0f, 2.0f, 0.1f)); + + // "real world case" + float oneThird = 10.0f / 3.0f; + A(FuzzyEqualsAdditive(10.0f, 3.0f * oneThird)); + A(FuzzyEqualsMultiplicative(10.0f, 3.0f * oneThird)); + // NaN check + A(!FuzzyEqualsAdditive(SpecificNaN<float>(1, 1), SpecificNaN<float>(1, 1))); + A(!FuzzyEqualsAdditive(SpecificNaN<float>(1, 2), SpecificNaN<float>(0, 8))); + A(!FuzzyEqualsMultiplicative(SpecificNaN<float>(1, 1), + SpecificNaN<float>(1, 1))); + A(!FuzzyEqualsMultiplicative(SpecificNaN<float>(1, 2), + SpecificNaN<float>(0, 200))); +} + +static void +TestDoublesAreApproximatelyEqual() +{ + double epsilon = mozilla::detail::FuzzyEqualsEpsilon<double>::value(); + double lessThanEpsilon = epsilon / 2.0; + double moreThanEpsilon = epsilon * 2.0; + + // Additive tests using the default epsilon + // ... around 1.0 + A(FuzzyEqualsAdditive(1.0, 1.0 + lessThanEpsilon)); + A(FuzzyEqualsAdditive(1.0, 1.0 - lessThanEpsilon)); + A(FuzzyEqualsAdditive(1.0, 1.0 + epsilon)); + A(FuzzyEqualsAdditive(1.0, 1.0 - epsilon)); + A(!FuzzyEqualsAdditive(1.0, 1.0 + moreThanEpsilon)); + A(!FuzzyEqualsAdditive(1.0, 1.0 - moreThanEpsilon)); + // ... around 1.0e4 (this is near the upper bound of the range where + // adding moreThanEpsilon will still be representable and return false) + A(FuzzyEqualsAdditive(1.0e4, 1.0e4 + lessThanEpsilon)); + A(FuzzyEqualsAdditive(1.0e4, 1.0e4 + epsilon)); + A(!FuzzyEqualsAdditive(1.0e4, 1.0e4 + moreThanEpsilon)); + // ... around 1.0e-25 + A(FuzzyEqualsAdditive(1.0e-25, 1.0e-25 + lessThanEpsilon)); + A(FuzzyEqualsAdditive(1.0e-25, 1.0e-25 + epsilon)); + A(!FuzzyEqualsAdditive(1.0e-25, 1.0e-25 + moreThanEpsilon)); + // ... straddling 0 + A(FuzzyEqualsAdditive(1.0e-13, -1.0e-13)); + A(!FuzzyEqualsAdditive(1.0e-12, -1.0e-12)); + // Using a small epsilon + A(FuzzyEqualsAdditive(1.0e-15, 1.0e-15 + 1.0e-30, 1.0e-29)); + A(!FuzzyEqualsAdditive(1.0e-15, 1.0e-15 + 1.0e-30, 1.0e-31)); + // Using a big epsilon + A(FuzzyEqualsAdditive(1.0e40, 1.0e40 + 1.0e25, 1.0e26)); + A(!FuzzyEqualsAdditive(1.0e40, 1.0e40 + 1.0e25, 1.0e24)); + + // Multiplicative tests using the default epsilon + // ... around 1.0 + A(FuzzyEqualsMultiplicative(1.0, 1.0 + lessThanEpsilon)); + A(FuzzyEqualsMultiplicative(1.0, 1.0 - lessThanEpsilon)); + A(FuzzyEqualsMultiplicative(1.0, 1.0 + epsilon)); + A(!FuzzyEqualsMultiplicative(1.0, 1.0 - epsilon)); + A(!FuzzyEqualsMultiplicative(1.0, 1.0 + moreThanEpsilon)); + A(!FuzzyEqualsMultiplicative(1.0, 1.0 - moreThanEpsilon)); + // ... around 1.0e30 + A(FuzzyEqualsMultiplicative(1.0e30, 1.0e30 + (lessThanEpsilon * 1.0e30))); + A(!FuzzyEqualsMultiplicative(1.0e30, 1.0e30 + (moreThanEpsilon * 1.0e30))); + // ... around 1.0e-30 + A(FuzzyEqualsMultiplicative(1.0e-30, 1.0e-30 + (lessThanEpsilon * 1.0e-30))); + A(!FuzzyEqualsMultiplicative(1.0e-30, 1.0e-30 + (moreThanEpsilon * 1.0e-30))); + // ... straddling 0 + A(!FuzzyEqualsMultiplicative(1.0e-6, -1.0e-6)); + A(FuzzyEqualsMultiplicative(1.0e-6, -1.0e-6, 1.0e2)); + // Using a small epsilon + A(FuzzyEqualsMultiplicative(1.0e-15, 1.0e-15 + 1.0e-30, 1.0e-15)); + A(!FuzzyEqualsMultiplicative(1.0e-15, 1.0e-15 + 1.0e-30, 1.0e-16)); + // Using a big epsilon + A(FuzzyEqualsMultiplicative(1.0e40, 2.0e40, 1.0)); + A(!FuzzyEqualsMultiplicative(1.0e40, 2.0e40, 0.1)); + + // "real world case" + double oneThird = 10.0 / 3.0; + A(FuzzyEqualsAdditive(10.0, 3.0 * oneThird)); + A(FuzzyEqualsMultiplicative(10.0, 3.0 * oneThird)); + // NaN check + A(!FuzzyEqualsAdditive(SpecificNaN<double>(1, 1), + SpecificNaN<double>(1, 1))); + A(!FuzzyEqualsAdditive(SpecificNaN<double>(1, 2), + SpecificNaN<double>(0, 8))); + A(!FuzzyEqualsMultiplicative(SpecificNaN<double>(1, 1), + SpecificNaN<double>(1, 1))); + A(!FuzzyEqualsMultiplicative(SpecificNaN<double>(1, 2), + SpecificNaN<double>(0, 200))); +} + +static void +TestAreApproximatelyEqual() +{ + TestFloatsAreApproximatelyEqual(); + TestDoublesAreApproximatelyEqual(); +} + +#undef A + +int +main() +{ + TestAreIdentical(); + TestExponentComponent(); + TestPredicates(); + TestAreApproximatelyEqual(); + return 0; +} diff --git a/mfbt/tests/TestFunction.cpp b/mfbt/tests/TestFunction.cpp new file mode 100644 index 000000000..f9f36ce27 --- /dev/null +++ b/mfbt/tests/TestFunction.cpp @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + /* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/Function.h" + +using mozilla::function; + +#define CHECK(c) \ + do { \ + bool cond = !!(c); \ + MOZ_RELEASE_ASSERT(cond, "Failed assertion: " #c); \ + } while (false) + +struct ConvertibleToInt +{ + operator int() const { return 42; } +}; + +int increment(int arg) { return arg + 1; } + +struct S { + S() {} + static int increment(int arg) { return arg + 1; } + int decrement(int arg) { return arg - 1;} + int sum(int arg1, int arg2) const { return arg1 + arg2;} +}; + +struct Incrementor { + int operator()(int arg) { return arg + 1; } +}; + +static void +TestNonmemberFunction() +{ + function<int(int)> f = &increment; + CHECK(f(42) == 43); +} + +static void +TestStaticMemberFunction() +{ + function<int(int)> f = &S::increment; + CHECK(f(42) == 43); +} + +static void +TestFunctionObject() +{ + function<int(int)> f = Incrementor(); + CHECK(f(42) == 43); +} + +static void +TestLambda() +{ + // Test non-capturing lambda + function<int(int)> f = [](int arg){ return arg + 1; }; + CHECK(f(42) == 43); + + // Test capturing lambda + int one = 1; + function<int(int)> g = [one](int arg){ return arg + one; }; + CHECK(g(42) == 43); +} + +static void +TestDefaultConstructionAndAssignmentLater() +{ + function<int(int)> f; // allowed + // Would get an assertion if we tried calling f now. + f = &increment; + CHECK(f(42) == 43); +} + +static void +TestReassignment() +{ + function<int(int)> f = &increment; + CHECK(f(42) == 43); + f = [](int arg){ return arg + 2; }; + CHECK(f(42) == 44); +} + +static void +TestMemberFunction() +{ + function<int(S&, int)> f = &S::decrement; + S s; + CHECK((f(s, 1) == 0)); +} + +static void +TestConstMemberFunction() +{ + function<int(const S*, int, int)> f = &S::sum; + const S s; + CHECK((f(&s, 1, 1) == 2)); +} +int +main() +{ + TestNonmemberFunction(); + TestStaticMemberFunction(); + TestFunctionObject(); + TestLambda(); + TestDefaultConstructionAndAssignmentLater(); + TestReassignment(); + TestMemberFunction(); + TestConstMemberFunction(); + return 0; +} diff --git a/mfbt/tests/TestIntegerPrintfMacros.cpp b/mfbt/tests/TestIntegerPrintfMacros.cpp new file mode 100644 index 000000000..475a88b63 --- /dev/null +++ b/mfbt/tests/TestIntegerPrintfMacros.cpp @@ -0,0 +1,1269 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/IntegerPrintfMacros.h" // this must pick up <stdint.h> +#include "mozilla/Sprintf.h" + +#include <stddef.h> +#include <stdio.h> +#include <string.h> + +/* Output array and poisoning method shared by all tests. */ +static char gOutput[32]; + +static void +PoisonOutput() +{ + memset(gOutput, 0xDA, sizeof(gOutput)); +} + +/* + * The fprintf macros for signed integers are: + * + * PRIdN PRIdLEASTN PRIdFASTN PRIdMAX PRIdPTR + * PRIiN PRIiLEASTN PRIiFASTN PRIiMAX PRIiPTR + * + * In these names N is the width of the type as described in C99 7.18.1. + */ + +static void +TestPrintSigned8() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRId8, int8_t(-17)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-17")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIi8, int8_t(42)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42")); +} + +static void +TestPrintSigned16() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRId16, int16_t(-289)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-289")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIi16, int16_t(728)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "728")); +} + +static void +TestPrintSigned32() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRId32, int32_t(-342178)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-342178")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIi32, int32_t(5719283)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "5719283")); +} + +static void +TestPrintSigned64() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRId64, int64_t(-INT64_C(432157943248732))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-432157943248732")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIi64, int64_t(INT64_C(325719232983))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "325719232983")); +} + +static void +TestPrintSignedN() +{ + TestPrintSigned8(); + TestPrintSigned16(); + TestPrintSigned32(); + TestPrintSigned64(); +} + +static void +TestPrintSignedLeast8() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIdLEAST8, int_least8_t(-17)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-17")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIiLEAST8, int_least8_t(42)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42")); +} + +static void +TestPrintSignedLeast16() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIdLEAST16, int_least16_t(-289)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-289")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIiLEAST16, int_least16_t(728)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "728")); +} + +static void +TestPrintSignedLeast32() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIdLEAST32, int_least32_t(-342178)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-342178")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIiLEAST32, int_least32_t(5719283)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "5719283")); +} + +static void +TestPrintSignedLeast64() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIdLEAST64, int_least64_t(-INT64_C(432157943248732))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-432157943248732")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIiLEAST64, int_least64_t(INT64_C(325719232983))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "325719232983")); +} + +static void +TestPrintSignedLeastN() +{ + TestPrintSignedLeast8(); + TestPrintSignedLeast16(); + TestPrintSignedLeast32(); + TestPrintSignedLeast64(); +} + +static void +TestPrintSignedFast8() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIdFAST8, int_fast8_t(-17)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-17")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIiFAST8, int_fast8_t(42)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42")); +} + +static void +TestPrintSignedFast16() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIdFAST16, int_fast16_t(-289)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-289")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIiFAST16, int_fast16_t(728)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "728")); +} + +static void +TestPrintSignedFast32() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIdFAST32, int_fast32_t(-342178)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-342178")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIiFAST32, int_fast32_t(5719283)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "5719283")); +} + +static void +TestPrintSignedFast64() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIdFAST64, int_fast64_t(-INT64_C(432157943248732))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-432157943248732")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIiFAST64, int_fast64_t(INT64_C(325719232983))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "325719232983")); +} + +static void +TestPrintSignedFastN() +{ + TestPrintSignedFast8(); + TestPrintSignedFast16(); + TestPrintSignedFast32(); + TestPrintSignedFast64(); +} + +static void +TestPrintSignedMax() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIdMAX, intmax_t(-INTMAX_C(432157943248732))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "-432157943248732")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIiMAX, intmax_t(INTMAX_C(325719232983))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "325719232983")); +} + +static void +TestPrintSignedPtr() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIdPTR, intptr_t(reinterpret_cast<void*>(12345678))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "12345678")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIiPTR, intptr_t(reinterpret_cast<void*>(87654321))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "87654321")); +} + +static void +TestPrintSigned() +{ + TestPrintSignedN(); + TestPrintSignedLeastN(); + TestPrintSignedFastN(); + TestPrintSignedMax(); + TestPrintSignedPtr(); +} + +/* + * The fprintf macros for unsigned integers are: + * + * PRIoN PRIoLEASTN PRIoFASTN PRIoMAX PRIoPTR + * PRIuN PRIuLEASTN PRIuFASTN PRIuMAX PRIuPTR + * PRIxN PRIxLEASTN PRIxFASTN PRIxMAX PRIxPTR + * PRIXN PRIXLEASTN PRIXFASTN PRIXMAX PRIXPTR + * + * In these names N is the width of the type as described in C99 7.18.1. + */ + +static void +TestPrintUnsigned8() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIo8, uint8_t(042)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIu8, uint8_t(17)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIx8, uint8_t(0x2a)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIX8, uint8_t(0xCD)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CD")); +} + +static void +TestPrintUnsigned16() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIo16, uint16_t(04242)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "4242")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIu16, uint16_t(1717)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "1717")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIx16, uint16_t(0x2a2a)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIX16, uint16_t(0xCDCD)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCD")); +} + +static void +TestPrintUnsigned32() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIo32, uint32_t(0424242)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIu32, uint32_t(171717)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "171717")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIx32, uint32_t(0x2a2a2a)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIX32, uint32_t(0xCDCDCD)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCD")); +} + +static void +TestPrintUnsigned64() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIo64, uint64_t(UINT64_C(0424242424242))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242424242")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIu64, uint64_t(UINT64_C(17171717171717171717))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17171717171717171717")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIx64, uint64_t(UINT64_C(0x2a2a2a2a2a2a2a))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a2a2a2a2a")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIX64, uint64_t(UINT64_C(0xCDCDCDCDCDCD))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCDCDCDCD")); +} + +static void +TestPrintUnsignedN() +{ + TestPrintUnsigned8(); + TestPrintUnsigned16(); + TestPrintUnsigned32(); + TestPrintUnsigned64(); +} + +static void +TestPrintUnsignedLeast8() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIoLEAST8, uint_least8_t(042)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIuLEAST8, uint_least8_t(17)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIxLEAST8, uint_least8_t(0x2a)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIXLEAST8, uint_least8_t(0xCD)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CD")); +} + +static void +TestPrintUnsignedLeast16() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIoLEAST16, uint_least16_t(04242)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "4242")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIuLEAST16, uint_least16_t(1717)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "1717")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIxLEAST16, uint_least16_t(0x2a2a)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIXLEAST16, uint_least16_t(0xCDCD)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCD")); +} + +static void +TestPrintUnsignedLeast32() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIoLEAST32, uint_least32_t(0424242)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIuLEAST32, uint_least32_t(171717)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "171717")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIxLEAST32, uint_least32_t(0x2a2a2a)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIXLEAST32, uint_least32_t(0xCDCDCD)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCD")); +} + +static void +TestPrintUnsignedLeast64() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIoLEAST64, uint_least64_t(UINT64_C(0424242424242))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242424242")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIuLEAST64, + uint_least64_t(UINT64_C(17171717171717171717))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17171717171717171717")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIxLEAST64, uint_least64_t(UINT64_C(0x2a2a2a2a2a2a2a))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a2a2a2a2a")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIXLEAST64, uint_least64_t(UINT64_C(0xCDCDCDCDCDCD))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCDCDCDCD")); +} + +static void +TestPrintUnsignedLeastN() +{ + TestPrintUnsignedLeast8(); + TestPrintUnsignedLeast16(); + TestPrintUnsignedLeast32(); + TestPrintUnsignedLeast64(); +} + +static void +TestPrintUnsignedFast8() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIoFAST8, uint_fast8_t(042)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "42")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIuFAST8, uint_fast8_t(17)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIxFAST8, uint_fast8_t(0x2a)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIXFAST8, uint_fast8_t(0xCD)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CD")); +} + +static void +TestPrintUnsignedFast16() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIoFAST16, uint_fast16_t(04242)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "4242")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIuFAST16, uint_fast16_t(1717)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "1717")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIxFAST16, uint_fast16_t(0x2a2a)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIXFAST16, uint_fast16_t(0xCDCD)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCD")); +} + +static void +TestPrintUnsignedFast32() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIoFAST32, uint_fast32_t(0424242)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIuFAST32, uint_fast32_t(171717)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "171717")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIxFAST32, uint_fast32_t(0x2a2a2a)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIXFAST32, uint_fast32_t(0xCDCDCD)); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCD")); +} + +static void +TestPrintUnsignedFast64() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIoFAST64, uint_fast64_t(UINT64_C(0424242424242))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "424242424242")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIuFAST64, + uint_fast64_t(UINT64_C(17171717171717171717))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "17171717171717171717")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIxFAST64, uint_fast64_t(UINT64_C(0x2a2a2a2a2a2a2a))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "2a2a2a2a2a2a2a")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIXFAST64, uint_fast64_t(UINT64_C(0xCDCDCDCDCDCD))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "CDCDCDCDCDCD")); +} + +static void +TestPrintUnsignedFastN() +{ + TestPrintUnsignedFast8(); + TestPrintUnsignedFast16(); + TestPrintUnsignedFast32(); + TestPrintUnsignedFast64(); +} + +static void +TestPrintUnsignedMax() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIoMAX, uintmax_t(UINTMAX_C(432157943248732))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "14220563454333534")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIuMAX, uintmax_t(UINTMAX_C(325719232983))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "325719232983")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIxMAX, uintmax_t(UINTMAX_C(327281321873))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "4c337ca791")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIXMAX, uintmax_t(UINTMAX_C(912389523743523))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "33DD03D75A323")); +} + +static void +TestPrintUnsignedPtr() +{ + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIoPTR, uintptr_t(reinterpret_cast<void*>(12345678))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "57060516")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIuPTR, uintptr_t(reinterpret_cast<void*>(87654321))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "87654321")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIxPTR, uintptr_t(reinterpret_cast<void*>(0x4c3a791))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "4c3a791")); + + PoisonOutput(); + SprintfLiteral(gOutput, "%" PRIXPTR, uintptr_t(reinterpret_cast<void*>(0xF328DB))); + MOZ_RELEASE_ASSERT(!strcmp(gOutput, "F328DB")); +} + +static void +TestPrintUnsigned() +{ + TestPrintUnsignedN(); + TestPrintUnsignedLeastN(); + TestPrintUnsignedFastN(); + TestPrintUnsignedMax(); + TestPrintUnsignedPtr(); +} + +static void +TestPrint() +{ + TestPrintSigned(); + TestPrintUnsigned(); +} + +/* + * The fscanf macros for signed integers are: + * + * SCNdN SCNdLEASTN SCNdFASTN SCNdMAX SCNdPTR + * SCNiN SCNiLEASTN SCNiFASTN SCNiMAX SCNiPTR + * + * In these names N is the width of the type as described in C99 7.18.1. + */ + +/* + * MSVC's scanf is insufficiently powerful to implement all the SCN* macros. + * Rather than support some subset of them, we instead support none of them. + * See the comment at the top of IntegerPrintfMacros.h. But in case we ever do + * support them, the following tests should adequately test implementation + * correctness. (Indeed, these tests *revealed* MSVC's limitations.) + * + * That said, even if MSVC ever picks up complete support, we still probably + * don't want to support these, because of the undefined-behavior issue noted + * further down in the comment atop IntegerPrintfMacros.h. + */ +#define SHOULD_TEST_SCANF_MACROS 0 + +#if SHOULD_TEST_SCANF_MACROS + +/* + * glibc header definitions for SCN{d,i,o,u,x}{,LEAST,FAST}8 use the "hh" length + * modifier, which is new in C99 (and C++11, by reference). We compile this + * file as C++11, so if "hh" is used in these macros, it's standard. But some + * versions of gcc wrongly think it isn't and warn about a "non-standard" + * modifier. And since these tests mostly exist to verify format-macro/type + * consistency (particularly through compiler warnings about incorrect formats), + * these warnings are unacceptable. So for now, compile tests for those macros + * only if we aren't compiling with gcc. + */ +#define SHOULD_TEST_8BIT_FORMAT_MACROS (!(MOZ_IS_GCC)) + +template<typename T> +union Input +{ + T mI; + unsigned char mPun[16]; +}; + +template<typename T> +static void +PoisonInput(Input<T>& aInput) +{ + memset(aInput.mPun, 0xDA, sizeof(aInput.mPun)); +} + +template<typename T> +static bool +ExtraBitsUntouched(const Input<T>& aInput) +{ + for (size_t i = sizeof(aInput.mI); i < sizeof(aInput); i++) { + if (aInput.mPun[i] != 0xDA) { + return false; + } + } + + return true; +} + +static void +TestScanSigned8() +{ +#if SHOULD_TEST_8BIT_FORMAT_MACROS + Input<int8_t> u; + + PoisonInput(u); + sscanf("-17", "%" SCNd8, &u.i); + MOZ_RELEASE_ASSERT(u.i == -17); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("042", "%" SCNi8, &u.i); + MOZ_RELEASE_ASSERT(u.i == 042); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +#endif +} + +static void +TestScanSigned16() +{ + Input<int16_t> u; + + PoisonInput(u); + sscanf("-1742", "%" SCNd16, &u.i); + MOZ_RELEASE_ASSERT(u.i == -1742); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("04217", "%" SCNi16, &u.i); + MOZ_RELEASE_ASSERT(u.i == 04217); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanSigned32() +{ + Input<int32_t> u; + + PoisonInput(u); + sscanf("-174257", "%" SCNd32, &u.i); + MOZ_RELEASE_ASSERT(u.i == -174257); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("0423571", "%" SCNi32, &u.i); + MOZ_RELEASE_ASSERT(u.i == 0423571); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanSigned64() +{ + Input<int64_t> u; + + PoisonInput(u); + sscanf("-17425238927232", "%" SCNd64, &u.i); + MOZ_RELEASE_ASSERT(u.i == -INT64_C(17425238927232)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("042333576571", "%" SCNi64, &u.i); + MOZ_RELEASE_ASSERT(u.i == INT64_C(042333576571)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanSignedN() +{ + TestScanSigned8(); + TestScanSigned16(); + TestScanSigned32(); + TestScanSigned64(); +} + +static void +TestScanSignedLeast8() +{ +#if SHOULD_TEST_8BIT_FORMAT_MACROS + Input<int_least8_t> u; + + PoisonInput(u); + sscanf("-17", "%" SCNdLEAST8, &u.i); + MOZ_RELEASE_ASSERT(u.i == -17); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("042", "%" SCNiLEAST8, &u.i); + MOZ_RELEASE_ASSERT(u.i == 042); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +#endif +} + +static void +TestScanSignedLeast16() +{ + Input<int_least16_t> u; + + PoisonInput(u); + sscanf("-1742", "%" SCNdLEAST16, &u.i); + MOZ_RELEASE_ASSERT(u.i == -1742); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("04217", "%" SCNiLEAST16, &u.i); + MOZ_RELEASE_ASSERT(u.i == 04217); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanSignedLeast32() +{ + Input<int_least32_t> u; + + PoisonInput(u); + sscanf("-174257", "%" SCNdLEAST32, &u.i); + MOZ_RELEASE_ASSERT(u.i == -174257); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("0423571", "%" SCNiLEAST32, &u.i); + MOZ_RELEASE_ASSERT(u.i == 0423571); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanSignedLeast64() +{ + Input<int_least64_t> u; + + PoisonInput(u); + sscanf("-17425238927232", "%" SCNdLEAST64, &u.i); + MOZ_RELEASE_ASSERT(u.i == -INT64_C(17425238927232)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("042333576571", "%" SCNiLEAST64, &u.i); + MOZ_RELEASE_ASSERT(u.i == INT64_C(042333576571)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanSignedLeastN() +{ + TestScanSignedLeast8(); + TestScanSignedLeast16(); + TestScanSignedLeast32(); + TestScanSignedLeast64(); +} + +static void +TestScanSignedFast8() +{ +#if SHOULD_TEST_8BIT_FORMAT_MACROS + Input<int_fast8_t> u; + + PoisonInput(u); + sscanf("-17", "%" SCNdFAST8, &u.i); + MOZ_RELEASE_ASSERT(u.i == -17); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("042", "%" SCNiFAST8, &u.i); + MOZ_RELEASE_ASSERT(u.i == 042); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +#endif +} + +static void +TestScanSignedFast16() +{ + Input<int_fast16_t> u; + + PoisonInput(u); + sscanf("-1742", "%" SCNdFAST16, &u.i); + MOZ_RELEASE_ASSERT(u.i == -1742); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("04217", "%" SCNiFAST16, &u.i); + MOZ_RELEASE_ASSERT(u.i == 04217); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanSignedFast32() +{ + Input<int_fast32_t> u; + + PoisonInput(u); + sscanf("-174257", "%" SCNdFAST32, &u.i); + MOZ_RELEASE_ASSERT(u.i == -174257); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("0423571", "%" SCNiFAST32, &u.i); + MOZ_RELEASE_ASSERT(u.i == 0423571); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanSignedFast64() +{ + Input<int_fast64_t> u; + + PoisonInput(u); + sscanf("-17425238927232", "%" SCNdFAST64, &u.i); + MOZ_RELEASE_ASSERT(u.i == -INT64_C(17425238927232)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("042333576571", "%" SCNiFAST64, &u.i); + MOZ_RELEASE_ASSERT(u.i == INT64_C(042333576571)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanSignedFastN() +{ + TestScanSignedFast8(); + TestScanSignedFast16(); + TestScanSignedFast32(); + TestScanSignedFast64(); +} + +static void +TestScanSignedMax() +{ + Input<intmax_t> u; + + PoisonInput(u); + sscanf("-432157943248732", "%" SCNdMAX, &u.i); + MOZ_RELEASE_ASSERT(u.i == -INTMAX_C(432157943248732)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("04233357236571", "%" SCNiMAX, &u.i); + MOZ_RELEASE_ASSERT(u.i == INTMAX_C(04233357236571)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanSignedPtr() +{ + Input<intptr_t> u; + + PoisonInput(u); + sscanf("12345678", "%" SCNdPTR, &u.i); + MOZ_RELEASE_ASSERT(u.i == intptr_t(reinterpret_cast<void*>(12345678))); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("04233357236", "%" SCNiPTR, &u.i); + MOZ_RELEASE_ASSERT(u.i == intptr_t(reinterpret_cast<void*>(04233357236))); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanSigned() +{ + TestScanSignedN(); + TestScanSignedLeastN(); + TestScanSignedFastN(); + TestScanSignedMax(); + TestScanSignedPtr(); +} + +/* + * The fscanf macros for unsigned integers are: + * + * SCNoN SCNoLEASTN SCNoFASTN SCNoMAX SCNoPTR + * SCNuN SCNuLEASTN SCNuFASTN SCNuMAX SCNuPTR + * SCNxN SCNxLEASTN SCNxFASTN SCNxMAX SCNxPTR + * + * In these names N is the width of the type as described in C99 7.18.1. + */ + +static void +TestScanUnsigned8() +{ +#if SHOULD_TEST_8BIT_FORMAT_MACROS + Input<uint8_t> u; + + PoisonInput(u); + sscanf("17", "%" SCNo8, &u.i); + MOZ_RELEASE_ASSERT(u.i == 017); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("42", "%" SCNu8, &u.i); + MOZ_RELEASE_ASSERT(u.i == 42); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("2A", "%" SCNx8, &u.i); + MOZ_RELEASE_ASSERT(u.i == 0x2A); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +#endif +} + +static void +TestScanUnsigned16() +{ + Input<uint16_t> u; + + PoisonInput(u); + sscanf("1742", "%" SCNo16, &u.i); + MOZ_RELEASE_ASSERT(u.i == 01742); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("4217", "%" SCNu16, &u.i); + MOZ_RELEASE_ASSERT(u.i == 4217); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("2ABC", "%" SCNx16, &u.i); + MOZ_RELEASE_ASSERT(u.i == 0x2ABC); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanUnsigned32() +{ + Input<uint32_t> u; + + PoisonInput(u); + sscanf("17421742", "%" SCNo32, &u.i); + MOZ_RELEASE_ASSERT(u.i == 017421742); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("4217867", "%" SCNu32, &u.i); + MOZ_RELEASE_ASSERT(u.i == 4217867); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("2ABCBEEF", "%" SCNx32, &u.i); + MOZ_RELEASE_ASSERT(u.i == 0x2ABCBEEF); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanUnsigned64() +{ + Input<uint64_t> u; + + PoisonInput(u); + sscanf("17421742173", "%" SCNo64, &u.i); + MOZ_RELEASE_ASSERT(u.i == UINT64_C(017421742173)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("421786713579", "%" SCNu64, &u.i); + MOZ_RELEASE_ASSERT(u.i == UINT64_C(421786713579)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("DEADBEEF7457E", "%" SCNx64, &u.i); + MOZ_RELEASE_ASSERT(u.i == UINT64_C(0xDEADBEEF7457E)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanUnsignedN() +{ + TestScanUnsigned8(); + TestScanUnsigned16(); + TestScanUnsigned32(); + TestScanUnsigned64(); +} + +static void +TestScanUnsignedLeast8() +{ +#if SHOULD_TEST_8BIT_FORMAT_MACROS + Input<uint_least8_t> u; + + PoisonInput(u); + sscanf("17", "%" SCNoLEAST8, &u.i); + MOZ_RELEASE_ASSERT(u.i == 017); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("42", "%" SCNuLEAST8, &u.i); + MOZ_RELEASE_ASSERT(u.i == 42); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("2A", "%" SCNxLEAST8, &u.i); + MOZ_RELEASE_ASSERT(u.i == 0x2A); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +#endif +} + +static void +TestScanUnsignedLeast16() +{ + Input<uint_least16_t> u; + + PoisonInput(u); + sscanf("1742", "%" SCNoLEAST16, &u.i); + MOZ_RELEASE_ASSERT(u.i == 01742); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("4217", "%" SCNuLEAST16, &u.i); + MOZ_RELEASE_ASSERT(u.i == 4217); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("2ABC", "%" SCNxLEAST16, &u.i); + MOZ_RELEASE_ASSERT(u.i == 0x2ABC); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanUnsignedLeast32() +{ + Input<uint_least32_t> u; + + PoisonInput(u); + sscanf("17421742", "%" SCNoLEAST32, &u.i); + MOZ_RELEASE_ASSERT(u.i == 017421742); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("4217867", "%" SCNuLEAST32, &u.i); + MOZ_RELEASE_ASSERT(u.i == 4217867); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("2ABCBEEF", "%" SCNxLEAST32, &u.i); + MOZ_RELEASE_ASSERT(u.i == 0x2ABCBEEF); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanUnsignedLeast64() +{ + Input<uint_least64_t> u; + + PoisonInput(u); + sscanf("17421742173", "%" SCNoLEAST64, &u.i); + MOZ_RELEASE_ASSERT(u.i == UINT64_C(017421742173)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("421786713579", "%" SCNuLEAST64, &u.i); + MOZ_RELEASE_ASSERT(u.i == UINT64_C(421786713579)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("DEADBEEF7457E", "%" SCNxLEAST64, &u.i); + MOZ_RELEASE_ASSERT(u.i == UINT64_C(0xDEADBEEF7457E)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanUnsignedLeastN() +{ + TestScanUnsignedLeast8(); + TestScanUnsignedLeast16(); + TestScanUnsignedLeast32(); + TestScanUnsignedLeast64(); +} + +static void +TestScanUnsignedFast8() +{ +#if SHOULD_TEST_8BIT_FORMAT_MACROS + Input<uint_fast8_t> u; + + PoisonInput(u); + sscanf("17", "%" SCNoFAST8, &u.i); + MOZ_RELEASE_ASSERT(u.i == 017); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("42", "%" SCNuFAST8, &u.i); + MOZ_RELEASE_ASSERT(u.i == 42); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("2A", "%" SCNxFAST8, &u.i); + MOZ_RELEASE_ASSERT(u.i == 0x2A); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +#endif +} + +static void +TestScanUnsignedFast16() +{ + Input<uint_fast16_t> u; + + PoisonInput(u); + sscanf("1742", "%" SCNoFAST16, &u.i); + MOZ_RELEASE_ASSERT(u.i == 01742); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("4217", "%" SCNuFAST16, &u.i); + MOZ_RELEASE_ASSERT(u.i == 4217); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("2ABC", "%" SCNxFAST16, &u.i); + MOZ_RELEASE_ASSERT(u.i == 0x2ABC); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanUnsignedFast32() +{ + Input<uint_fast32_t> u; + + PoisonInput(u); + sscanf("17421742", "%" SCNoFAST32, &u.i); + MOZ_RELEASE_ASSERT(u.i == 017421742); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("4217867", "%" SCNuFAST32, &u.i); + MOZ_RELEASE_ASSERT(u.i == 4217867); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("2ABCBEEF", "%" SCNxFAST32, &u.i); + MOZ_RELEASE_ASSERT(u.i == 0x2ABCBEEF); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanUnsignedFast64() +{ + Input<uint_fast64_t> u; + + PoisonInput(u); + sscanf("17421742173", "%" SCNoFAST64, &u.i); + MOZ_RELEASE_ASSERT(u.i == UINT64_C(017421742173)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("421786713579", "%" SCNuFAST64, &u.i); + MOZ_RELEASE_ASSERT(u.i == UINT64_C(421786713579)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("DEADBEEF7457E", "%" SCNxFAST64, &u.i); + MOZ_RELEASE_ASSERT(u.i == UINT64_C(0xDEADBEEF7457E)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanUnsignedFastN() +{ + TestScanUnsignedFast8(); + TestScanUnsignedFast16(); + TestScanUnsignedFast32(); + TestScanUnsignedFast64(); +} + +static void +TestScanUnsignedMax() +{ + Input<uintmax_t> u; + + PoisonInput(u); + sscanf("14220563454333534", "%" SCNoMAX, &u.i); + MOZ_RELEASE_ASSERT(u.i == UINTMAX_C(432157943248732)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("432157943248732", "%" SCNuMAX, &u.i); + MOZ_RELEASE_ASSERT(u.i == UINTMAX_C(432157943248732)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("4c337ca791", "%" SCNxMAX, &u.i); + MOZ_RELEASE_ASSERT(u.i == UINTMAX_C(327281321873)); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanUnsignedPtr() +{ + Input<uintptr_t> u; + + PoisonInput(u); + sscanf("57060516", "%" SCNoPTR, &u.i); + MOZ_RELEASE_ASSERT(u.i == uintptr_t(reinterpret_cast<void*>(12345678))); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("87654321", "%" SCNuPTR, &u.i); + MOZ_RELEASE_ASSERT(u.i == uintptr_t(reinterpret_cast<void*>(87654321))); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); + + PoisonInput(u); + sscanf("4c3a791", "%" SCNxPTR, &u.i); + MOZ_RELEASE_ASSERT(u.i == uintptr_t(reinterpret_cast<void*>(0x4c3a791))); + MOZ_RELEASE_ASSERT(ExtraBitsUntouched(u)); +} + +static void +TestScanUnsigned() +{ + TestScanUnsignedN(); + TestScanUnsignedLeastN(); + TestScanUnsignedFastN(); + TestScanUnsignedMax(); + TestScanUnsignedPtr(); +} + +static void +TestScan() +{ + TestScanSigned(); + TestScanUnsigned(); +} + +#endif /* SHOULD_TEST_SCANF_MACROS */ + +int +main() +{ + TestPrint(); +#if SHOULD_TEST_SCANF_MACROS + TestScan(); +#endif + return 0; +} diff --git a/mfbt/tests/TestIntegerRange.cpp b/mfbt/tests/TestIntegerRange.cpp new file mode 100644 index 000000000..c2691a26c --- /dev/null +++ b/mfbt/tests/TestIntegerRange.cpp @@ -0,0 +1,163 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/IntegerRange.h" + +#include <stddef.h> + +using mozilla::IsSame; +using mozilla::MakeRange; +using mozilla::Reversed; + +const size_t kMaxNumber = 50; +const size_t kArraySize = 256; + +template<typename IntType> +static IntType +GenerateNumber() +{ + return static_cast<IntType>(rand() % kMaxNumber + 1); +} + +template<typename IntType> +static void +TestSingleParamRange(const IntType aN) +{ + IntType array[kArraySize]; + IntType* ptr = array; + for (auto i : MakeRange(aN)) { + static_assert(IsSame<decltype(i), IntType>::value, + "type of the loop var and the param should be the same"); + *ptr++ = i; + } + + MOZ_RELEASE_ASSERT(ptr - array == static_cast<ptrdiff_t>(aN), + "Should iterates N items"); + for (size_t i = 0; i < static_cast<size_t>(aN); i++) { + MOZ_RELEASE_ASSERT(array[i] == static_cast<IntType>(i), + "Values should equal to the index"); + } +} + +template<typename IntType> +static void +TestSingleParamReverseRange(const IntType aN) +{ + IntType array[kArraySize]; + IntType* ptr = array; + for (auto i : Reversed(MakeRange(aN))) { + static_assert(IsSame<decltype(i), IntType>::value, + "type of the loop var and the param should be the same"); + *ptr++ = i; + } + + MOZ_RELEASE_ASSERT(ptr - array == static_cast<ptrdiff_t>(aN), + "Should iterates N items"); + for (size_t i = 0; i < static_cast<size_t>(aN); i++) { + MOZ_RELEASE_ASSERT(array[i] == static_cast<IntType>(aN - i - 1), + "Values should be the reverse of their index"); + } +} + +template<typename IntType> +static void +TestSingleParamIntegerRange() +{ + const auto kN = GenerateNumber<IntType>(); + TestSingleParamRange<IntType>(0); + TestSingleParamReverseRange<IntType>(0); + TestSingleParamRange<IntType>(kN); + TestSingleParamReverseRange<IntType>(kN); +} + +template<typename IntType1, typename IntType2> +static void +TestDoubleParamRange(const IntType1 aBegin, const IntType2 aEnd) +{ + IntType2 array[kArraySize]; + IntType2* ptr = array; + for (auto i : MakeRange(aBegin, aEnd)) { + static_assert(IsSame<decltype(i), IntType2>::value, "type of the loop var " + "should be same as that of the second param"); + *ptr++ = i; + } + + MOZ_RELEASE_ASSERT(ptr - array == static_cast<ptrdiff_t>(aEnd - aBegin), + "Should iterates (aEnd - aBegin) times"); + for (size_t i = 0; i < static_cast<size_t>(aEnd - aBegin); i++) { + MOZ_RELEASE_ASSERT(array[i] == static_cast<IntType2>(aBegin + i), + "Should iterate integers in [aBegin, aEnd) in order"); + } +} + +template<typename IntType1, typename IntType2> +static void +TestDoubleParamReverseRange(const IntType1 aBegin, const IntType2 aEnd) +{ + IntType2 array[kArraySize]; + IntType2* ptr = array; + for (auto i : Reversed(MakeRange(aBegin, aEnd))) { + static_assert(IsSame<decltype(i), IntType2>::value, "type of the loop var " + "should be same as that of the second param"); + *ptr++ = i; + } + + MOZ_RELEASE_ASSERT(ptr - array == static_cast<ptrdiff_t>(aEnd - aBegin), + "Should iterates (aEnd - aBegin) times"); + for (size_t i = 0; i < static_cast<size_t>(aEnd - aBegin); i++) { + MOZ_RELEASE_ASSERT(array[i] == static_cast<IntType2>(aEnd - i - 1), + "Should iterate integers in [aBegin, aEnd) in reverse order"); + } +} + +template<typename IntType1, typename IntType2> +static void +TestDoubleParamIntegerRange() +{ + const auto kStart = GenerateNumber<IntType1>(); + const auto kEnd = static_cast<IntType2>(kStart + GenerateNumber<IntType2>()); + TestDoubleParamRange(kStart, static_cast<IntType2>(kStart)); + TestDoubleParamReverseRange(kStart, static_cast<IntType2>(kStart)); + TestDoubleParamRange(kStart, kEnd); + TestDoubleParamReverseRange(kStart, kEnd); +} + +int +main() +{ + TestSingleParamIntegerRange<int8_t>(); + TestSingleParamIntegerRange<int16_t>(); + TestSingleParamIntegerRange<int32_t>(); + TestSingleParamIntegerRange<int64_t>(); + + TestSingleParamIntegerRange<uint8_t>(); + TestSingleParamIntegerRange<uint16_t>(); + TestSingleParamIntegerRange<uint32_t>(); + TestSingleParamIntegerRange<uint64_t>(); + + TestDoubleParamIntegerRange<int8_t, int8_t>(); + TestDoubleParamIntegerRange<int16_t, int16_t>(); + TestDoubleParamIntegerRange<int32_t, int32_t>(); + TestDoubleParamIntegerRange<int64_t, int64_t>(); + + TestDoubleParamIntegerRange<uint8_t, uint8_t>(); + TestDoubleParamIntegerRange<uint16_t, uint16_t>(); + TestDoubleParamIntegerRange<uint32_t, uint32_t>(); + TestDoubleParamIntegerRange<uint64_t, uint64_t>(); + + TestDoubleParamIntegerRange<int8_t, int16_t>(); + TestDoubleParamIntegerRange<int16_t, int32_t>(); + TestDoubleParamIntegerRange<int32_t, int64_t>(); + TestDoubleParamIntegerRange<int64_t, int8_t>(); + + TestDoubleParamIntegerRange<uint8_t, uint64_t>(); + TestDoubleParamIntegerRange<uint16_t, uint8_t>(); + TestDoubleParamIntegerRange<uint32_t, uint16_t>(); + TestDoubleParamIntegerRange<uint64_t, uint32_t>(); + + return 0; +} diff --git a/mfbt/tests/TestJSONWriter.cpp b/mfbt/tests/TestJSONWriter.cpp new file mode 100644 index 000000000..6c081d170 --- /dev/null +++ b/mfbt/tests/TestJSONWriter.cpp @@ -0,0 +1,539 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/JSONWriter.h" +#include "mozilla/UniquePtr.h" +#include <stdio.h> +#include <string.h> + +using mozilla::JSONWriteFunc; +using mozilla::JSONWriter; +using mozilla::MakeUnique; + +// This writes all the output into a big buffer. +struct StringWriteFunc : public JSONWriteFunc +{ + const static size_t kLen = 100000; + char mBuf[kLen]; + char* mPtr; + + StringWriteFunc() : mPtr(mBuf) {} + + void Write(const char* aStr) + { + char* last = mPtr + strlen(aStr); // where the nul will be added + + // If you change this test and this assertion fails, just make kLen bigger. + MOZ_RELEASE_ASSERT(last < mBuf + kLen); + sprintf(mPtr, "%s", aStr); + mPtr = last; + } +}; + +void Check(JSONWriteFunc* aFunc, const char* aExpected) +{ + const char* actual = static_cast<StringWriteFunc*>(aFunc)->mBuf; + if (strcmp(aExpected, actual) != 0) { + fprintf(stderr, + "---- EXPECTED ----\n<<<%s>>>\n" + "---- ACTUAL ----\n<<<%s>>>\n", + aExpected, actual); + MOZ_RELEASE_ASSERT(false, "expected and actual output don't match"); + } +} + +// Note: to convert actual output into |expected| strings that C++ can handle, +// apply the following substitutions, in order, to each line. +// - s/\\/\\\\/g # escapes backslashes +// - s/"/\\"/g # escapes quotes +// - s/$/\\n\\/ # adds a newline and string continuation char to each line + +void TestBasicProperties() +{ + const char* expected = "\ +{\n\ + \"null\": null,\n\ + \"bool1\": true,\n\ + \"bool2\": false,\n\ + \"int1\": 123,\n\ + \"int2\": -123,\n\ + \"int3\": -123456789000,\n\ + \"double1\": 1.2345,\n\ + \"double2\": -3,\n\ + \"double3\": 1e-7,\n\ + \"double4\": 1.1111111111111111e+21,\n\ + \"string1\": \"\",\n\ + \"string2\": \"1234\",\n\ + \"string3\": \"hello\",\n\ + \"string4\": \"\\\" \\\\ \\u0007 \\b \\t \\n \\u000b \\f \\r\",\n\ + \"len 0 array, multi-line\": [\n\ + ],\n\ + \"len 0 array, single-line\": [],\n\ + \"len 1 array\": [\n\ + 1\n\ + ],\n\ + \"len 5 array, multi-line\": [\n\ + 1,\n\ + 2,\n\ + 3,\n\ + 4,\n\ + 5\n\ + ],\n\ + \"len 3 array, single-line\": [1, [{}, 2, []], 3],\n\ + \"len 0 object, multi-line\": {\n\ + },\n\ + \"len 0 object, single-line\": {},\n\ + \"len 1 object\": {\n\ + \"one\": 1\n\ + },\n\ + \"len 5 object\": {\n\ + \"one\": 1,\n\ + \"two\": 2,\n\ + \"three\": 3,\n\ + \"four\": 4,\n\ + \"five\": 5\n\ + },\n\ + \"len 3 object, single-line\": {\"a\": 1, \"b\": [{}, 2, []], \"c\": 3}\n\ +}\n\ +"; + + JSONWriter w(MakeUnique<StringWriteFunc>()); + + w.Start(); + { + w.NullProperty("null"); + + w.BoolProperty("bool1", true); + w.BoolProperty("bool2", false); + + w.IntProperty("int1", 123); + w.IntProperty("int2", -0x7b); + w.IntProperty("int3", -123456789000ll); + + w.DoubleProperty("double1", 1.2345); + w.DoubleProperty("double2", -3); + w.DoubleProperty("double3", 1e-7); + w.DoubleProperty("double4", 1.1111111111111111e+21); + + w.StringProperty("string1", ""); + w.StringProperty("string2", "1234"); + w.StringProperty("string3", "hello"); + w.StringProperty("string4", "\" \\ \a \b \t \n \v \f \r"); + + w.StartArrayProperty("len 0 array, multi-line", w.MultiLineStyle); + w.EndArray(); + + w.StartArrayProperty("len 0 array, single-line", w.SingleLineStyle); + w.EndArray(); + + w.StartArrayProperty("len 1 array"); + { + w.IntElement(1); + } + w.EndArray(); + + w.StartArrayProperty("len 5 array, multi-line", w.MultiLineStyle); + { + w.IntElement(1); + w.IntElement(2); + w.IntElement(3); + w.IntElement(4); + w.IntElement(5); + } + w.EndArray(); + + w.StartArrayProperty("len 3 array, single-line", w.SingleLineStyle); + { + w.IntElement(1); + w.StartArrayElement(); + { + w.StartObjectElement(w.SingleLineStyle); + w.EndObject(); + + w.IntElement(2); + + w.StartArrayElement(w.MultiLineStyle); // style overridden from above + w.EndArray(); + } + w.EndArray(); + w.IntElement(3); + } + w.EndArray(); + + w.StartObjectProperty("len 0 object, multi-line"); + w.EndObject(); + + w.StartObjectProperty("len 0 object, single-line", w.SingleLineStyle); + w.EndObject(); + + w.StartObjectProperty("len 1 object"); + { + w.IntProperty("one", 1); + } + w.EndObject(); + + w.StartObjectProperty("len 5 object"); + { + w.IntProperty("one", 1); + w.IntProperty("two", 2); + w.IntProperty("three", 3); + w.IntProperty("four", 4); + w.IntProperty("five", 5); + } + w.EndObject(); + + w.StartObjectProperty("len 3 object, single-line", w.SingleLineStyle); + { + w.IntProperty("a", 1); + w.StartArrayProperty("b"); + { + w.StartObjectElement(); + w.EndObject(); + + w.IntElement(2); + + w.StartArrayElement(w.SingleLineStyle); + w.EndArray(); + } + w.EndArray(); + w.IntProperty("c", 3); + } + w.EndObject(); + } + w.End(); + + Check(w.WriteFunc(), expected); +} + +void TestBasicElements() +{ + const char* expected = "\ +{\n\ + \"array\": [\n\ + null,\n\ + true,\n\ + false,\n\ + 123,\n\ + -123,\n\ + -123456789000,\n\ + 1.2345,\n\ + -3,\n\ + 1e-7,\n\ + 1.1111111111111111e+21,\n\ + \"\",\n\ + \"1234\",\n\ + \"hello\",\n\ + \"\\\" \\\\ \\u0007 \\b \\t \\n \\u000b \\f \\r\",\n\ + [\n\ + ],\n\ + [],\n\ + [\n\ + 1\n\ + ],\n\ + [\n\ + 1,\n\ + 2,\n\ + 3,\n\ + 4,\n\ + 5\n\ + ],\n\ + [1, [{}, 2, []], 3],\n\ + {\n\ + },\n\ + {},\n\ + {\n\ + \"one\": 1\n\ + },\n\ + {\n\ + \"one\": 1,\n\ + \"two\": 2,\n\ + \"three\": 3,\n\ + \"four\": 4,\n\ + \"five\": 5\n\ + },\n\ + {\"a\": 1, \"b\": [{}, 2, []], \"c\": 3}\n\ + ]\n\ +}\n\ +"; + + JSONWriter w(MakeUnique<StringWriteFunc>()); + + w.Start(); + w.StartArrayProperty("array"); + { + w.NullElement(); + + w.BoolElement(true); + w.BoolElement(false); + + w.IntElement(123); + w.IntElement(-0x7b); + w.IntElement(-123456789000ll); + + w.DoubleElement(1.2345); + w.DoubleElement(-3); + w.DoubleElement(1e-7); + w.DoubleElement(1.1111111111111111e+21); + + w.StringElement(""); + w.StringElement("1234"); + w.StringElement("hello"); + w.StringElement("\" \\ \a \b \t \n \v \f \r"); + + w.StartArrayElement(); + w.EndArray(); + + w.StartArrayElement(w.SingleLineStyle); + w.EndArray(); + + w.StartArrayElement(); + { + w.IntElement(1); + } + w.EndArray(); + + w.StartArrayElement(); + { + w.IntElement(1); + w.IntElement(2); + w.IntElement(3); + w.IntElement(4); + w.IntElement(5); + } + w.EndArray(); + + w.StartArrayElement(w.SingleLineStyle); + { + w.IntElement(1); + w.StartArrayElement(); + { + w.StartObjectElement(w.SingleLineStyle); + w.EndObject(); + + w.IntElement(2); + + w.StartArrayElement(w.MultiLineStyle); // style overridden from above + w.EndArray(); + } + w.EndArray(); + w.IntElement(3); + } + w.EndArray(); + + w.StartObjectElement(); + w.EndObject(); + + w.StartObjectElement(w.SingleLineStyle); + w.EndObject(); + + w.StartObjectElement(); + { + w.IntProperty("one", 1); + } + w.EndObject(); + + w.StartObjectElement(); + { + w.IntProperty("one", 1); + w.IntProperty("two", 2); + w.IntProperty("three", 3); + w.IntProperty("four", 4); + w.IntProperty("five", 5); + } + w.EndObject(); + + w.StartObjectElement(w.SingleLineStyle); + { + w.IntProperty("a", 1); + w.StartArrayProperty("b"); + { + w.StartObjectElement(); + w.EndObject(); + + w.IntElement(2); + + w.StartArrayElement(w.SingleLineStyle); + w.EndArray(); + } + w.EndArray(); + w.IntProperty("c", 3); + } + w.EndObject(); + } + w.EndArray(); + w.End(); + + Check(w.WriteFunc(), expected); +} + +void TestOneLineObject() +{ + const char* expected = "\ +{\"i\": 1, \"array\": [null, [{}], {\"o\": {}}, \"s\"], \"d\": 3.33}\n\ +"; + + JSONWriter w(MakeUnique<StringWriteFunc>()); + + w.Start(w.SingleLineStyle); + + w.IntProperty("i", 1); + + w.StartArrayProperty("array"); + { + w.NullElement(); + + w.StartArrayElement(w.MultiLineStyle); // style overridden from above + { + w.StartObjectElement(); + w.EndObject(); + } + w.EndArray(); + + w.StartObjectElement(); + { + w.StartObjectProperty("o"); + w.EndObject(); + } + w.EndObject(); + + w.StringElement("s"); + } + w.EndArray(); + + w.DoubleProperty("d", 3.33); + + w.End(); + + Check(w.WriteFunc(), expected); +} + +void TestStringEscaping() +{ + // This test uses hexadecimal character escapes because UTF8 literals cause + // problems for some compilers (see bug 1069726). + const char* expected = "\ +{\n\ + \"ascii\": \"\x7F~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\\\"! \\u001f\\u001e\\u001d\\u001c\\u001b\\u001a\\u0019\\u0018\\u0017\\u0016\\u0015\\u0014\\u0013\\u0012\\u0011\\u0010\\u000f\\u000e\\r\\f\\u000b\\n\\t\\b\\u0007\\u0006\\u0005\\u0004\\u0003\\u0002\\u0001\",\n\ + \"\xD9\x85\xD8\xB1\xD8\xAD\xD8\xA8\xD8\xA7 \xD9\x87\xD9\x86\xD8\xA7\xD9\x83\": true,\n\ + \"\xD5\xA2\xD5\xA1\xD6\x80\xD5\xA5\xD6\x82 \xD5\xB9\xD5\xAF\xD5\xA1\": -123,\n\ + \"\xE4\xBD\xA0\xE5\xA5\xBD\": 1.234,\n\ + \"\xCE\xB3\xCE\xB5\xCE\xB9\xCE\xB1 \xCE\xB5\xCE\xBA\xCE\xB5\xCE\xAF\": \"\xD8\xB3\xD9\x84\xD8\xA7\xD9\x85\",\n\ + \"hall\xC3\xB3 \xC3\xBE" "arna\": 4660,\n\ + \"\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF\": {\n\ + \"\xD0\xBF\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82\": [\n\ + ]\n\ + }\n\ +}\n\ +"; + + JSONWriter w(MakeUnique<StringWriteFunc>()); + + // Test the string escaping behaviour. + w.Start(); + { + // Test all 127 ascii values. Do it in reverse order so that the 0 + // at the end serves as the null char. + char buf[128]; + for (int i = 0; i < 128; i++) { + buf[i] = 127 - i; + } + w.StringProperty("ascii", buf); + + // Test lots of unicode stuff. Note that this file is encoded as UTF-8. + w.BoolProperty("\xD9\x85\xD8\xB1\xD8\xAD\xD8\xA8\xD8\xA7 \xD9\x87\xD9\x86\xD8\xA7\xD9\x83", true); + w.IntProperty("\xD5\xA2\xD5\xA1\xD6\x80\xD5\xA5\xD6\x82 \xD5\xB9\xD5\xAF\xD5\xA1", -123); + w.DoubleProperty("\xE4\xBD\xA0\xE5\xA5\xBD", 1.234); + w.StringProperty("\xCE\xB3\xCE\xB5\xCE\xB9\xCE\xB1 \xCE\xB5\xCE\xBA\xCE\xB5\xCE\xAF", "\xD8\xB3\xD9\x84\xD8\xA7\xD9\x85"); + w.IntProperty("hall\xC3\xB3 \xC3\xBE" "arna", 0x1234); + w.StartObjectProperty("\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF"); + { + w.StartArrayProperty("\xD0\xBF\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82"); + w.EndArray(); + } + w.EndObject(); + } + w.End(); + + Check(w.WriteFunc(), expected); +} + +void TestDeepNesting() +{ + const char* expected = "\ +{\n\ + \"a\": [\n\ + {\n\ + \"a\": [\n\ + {\n\ + \"a\": [\n\ + {\n\ + \"a\": [\n\ + {\n\ + \"a\": [\n\ + {\n\ + \"a\": [\n\ + {\n\ + \"a\": [\n\ + {\n\ + \"a\": [\n\ + {\n\ + \"a\": [\n\ + {\n\ + \"a\": [\n\ + {\n\ + }\n\ + ]\n\ + }\n\ + ]\n\ + }\n\ + ]\n\ + }\n\ + ]\n\ + }\n\ + ]\n\ + }\n\ + ]\n\ + }\n\ + ]\n\ + }\n\ + ]\n\ + }\n\ + ]\n\ + }\n\ + ]\n\ +}\n\ +"; + + JSONWriter w(MakeUnique<StringWriteFunc>()); + + w.Start(); + { + static const int n = 10; + for (int i = 0; i < n; i++) { + w.StartArrayProperty("a"); + w.StartObjectElement(); + } + for (int i = 0; i < n; i++) { + w.EndObject(); + w.EndArray(); + } + } + w.End(); + + Check(w.WriteFunc(), expected); +} + +int main(void) +{ + TestBasicProperties(); + TestBasicElements(); + TestOneLineObject(); + TestStringEscaping(); + TestDeepNesting(); + + return 0; +} diff --git a/mfbt/tests/TestLinkedList.cpp b/mfbt/tests/TestLinkedList.cpp new file mode 100644 index 000000000..0421668b9 --- /dev/null +++ b/mfbt/tests/TestLinkedList.cpp @@ -0,0 +1,268 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/LinkedList.h" + +using mozilla::LinkedList; +using mozilla::LinkedListElement; + +struct SomeClass : public LinkedListElement<SomeClass> { + unsigned int mValue; + explicit SomeClass(int aValue = 0) : mValue(aValue) {} + void incr() { ++mValue; } +}; + +template <size_t N> +static void +CheckListValues(LinkedList<SomeClass>& list, unsigned int (&values)[N]) +{ + size_t count = 0; + for (SomeClass* x : list) { + MOZ_RELEASE_ASSERT(x->mValue == values[count]); + ++count; + } + MOZ_RELEASE_ASSERT(count == N); +} + +static void +TestList() +{ + LinkedList<SomeClass> list; + + SomeClass one(1), two(2), three(3); + + MOZ_RELEASE_ASSERT(list.isEmpty()); + MOZ_RELEASE_ASSERT(!list.getFirst()); + MOZ_RELEASE_ASSERT(!list.getLast()); + MOZ_RELEASE_ASSERT(!list.popFirst()); + MOZ_RELEASE_ASSERT(!list.popLast()); + + for (SomeClass* x : list) { + MOZ_RELEASE_ASSERT(x); + MOZ_RELEASE_ASSERT(false); + } + + list.insertFront(&one); + { unsigned int check[] { 1 }; CheckListValues(list, check); } + + MOZ_RELEASE_ASSERT(one.isInList()); + MOZ_RELEASE_ASSERT(!two.isInList()); + MOZ_RELEASE_ASSERT(!three.isInList()); + + MOZ_RELEASE_ASSERT(!list.isEmpty()); + MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 1); + MOZ_RELEASE_ASSERT(list.getLast()->mValue == 1); + + list.insertFront(&two); + { unsigned int check[] { 2, 1 }; CheckListValues(list, check); } + + MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 2); + MOZ_RELEASE_ASSERT(list.getLast()->mValue == 1); + + list.insertBack(&three); + { unsigned int check[] { 2, 1, 3 }; CheckListValues(list, check); } + + MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 2); + MOZ_RELEASE_ASSERT(list.getLast()->mValue == 3); + + one.removeFrom(list); + { unsigned int check[] { 2, 3 }; CheckListValues(list, check); } + + three.setPrevious(&one); + { unsigned int check[] { 2, 1, 3 }; CheckListValues(list, check); } + + three.removeFrom(list); + { unsigned int check[] { 2, 1 }; CheckListValues(list, check); } + + two.setPrevious(&three); + { unsigned int check[] { 3, 2, 1 }; CheckListValues(list, check); } + + three.removeFrom(list); + { unsigned int check[] { 2, 1 }; CheckListValues(list, check); } + + two.setNext(&three); + { unsigned int check[] { 2, 3, 1 }; CheckListValues(list, check); } + + one.remove(); + { unsigned int check[] { 2, 3 }; CheckListValues(list, check); } + + two.remove(); + { unsigned int check[] { 3 }; CheckListValues(list, check); } + + three.setPrevious(&two); + { unsigned int check[] { 2, 3 }; CheckListValues(list, check); } + + three.remove(); + { unsigned int check[] { 2 }; CheckListValues(list, check); } + + two.remove(); + + list.insertBack(&three); + { unsigned int check[] { 3 }; CheckListValues(list, check); } + + list.insertFront(&two); + { unsigned int check[] { 2, 3 }; CheckListValues(list, check); } + + for (SomeClass* x : list) { + x->incr(); + } + + MOZ_RELEASE_ASSERT(list.getFirst() == &two); + MOZ_RELEASE_ASSERT(list.getLast() == &three); + MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 3); + MOZ_RELEASE_ASSERT(list.getLast()->mValue == 4); +} + +static void +TestMove() +{ + auto MakeSomeClass = + [] (unsigned int aValue) -> SomeClass { return SomeClass(aValue); }; + + LinkedList<SomeClass> list1; + + // Test move constructor for LinkedListElement. + SomeClass c1(MakeSomeClass(1)); + list1.insertBack(&c1); + + // Test move assignment for LinkedListElement from an element not in a + // list. + SomeClass c2; + c2 = MakeSomeClass(2); + list1.insertBack(&c2); + + // Test move assignment of LinkedListElement from an element already in a + // list. + SomeClass c3; + c3 = Move(c2); + MOZ_RELEASE_ASSERT(!c2.isInList()); + MOZ_RELEASE_ASSERT(c3.isInList()); + + // Test move constructor for LinkedList. + LinkedList<SomeClass> list2(Move(list1)); + { unsigned int check[] { 1, 2 }; CheckListValues(list2, check); } + MOZ_RELEASE_ASSERT(list1.isEmpty()); + + // Test move assignment for LinkedList. + LinkedList<SomeClass> list3; + list3 = Move(list2); + { unsigned int check[] { 1, 2 }; CheckListValues(list3, check); } + MOZ_RELEASE_ASSERT(list2.isEmpty()); + + list3.clear(); +} + +static void +TestRemoveAndGet() +{ + LinkedList<SomeClass> list; + + SomeClass one(1), two(2), three(3); + list.insertBack(&one); + list.insertBack(&two); + list.insertBack(&three); + { unsigned int check[] { 1, 2, 3 }; CheckListValues(list, check); } + + MOZ_RELEASE_ASSERT(two.removeAndGetNext() == &three); + { unsigned int check[] { 1, 3 }; CheckListValues(list, check); } + + MOZ_RELEASE_ASSERT(three.removeAndGetPrevious() == &one); + { unsigned int check[] { 1 }; CheckListValues(list, check); } +} + +struct PrivateClass : private LinkedListElement<PrivateClass> { + friend class mozilla::LinkedList<PrivateClass>; + friend class mozilla::LinkedListElement<PrivateClass>; +}; + +static void +TestPrivate() +{ + LinkedList<PrivateClass> list; + PrivateClass one, two; + list.insertBack(&one); + list.insertBack(&two); + + size_t count = 0; + for (PrivateClass* p : list) { + MOZ_RELEASE_ASSERT(p, "cannot have null elements in list"); + count++; + } + MOZ_RELEASE_ASSERT(count == 2); +} + +struct CountedClass : public LinkedListElement<RefPtr<CountedClass>> +{ + int mCount; + void AddRef() { mCount++; } + void Release() { mCount--; } + + CountedClass() : mCount(0) {} + ~CountedClass() { MOZ_RELEASE_ASSERT(mCount == 0); } +}; + +static void +TestRefPtrList() +{ + LinkedList<RefPtr<CountedClass>> list; + CountedClass* elt1 = new CountedClass; + CountedClass* elt2 = new CountedClass; + + list.insertBack(elt1); + list.insertBack(elt2); + + MOZ_RELEASE_ASSERT(elt1->mCount == 1); + MOZ_RELEASE_ASSERT(elt2->mCount == 1); + + for (RefPtr<CountedClass> p : list) { + MOZ_RELEASE_ASSERT(p->mCount == 2); + } + + RefPtr<CountedClass> ptr = list.getFirst(); + while (ptr) { + MOZ_RELEASE_ASSERT(ptr->mCount == 2); + RefPtr<CountedClass> next = ptr->getNext(); + ptr->remove(); + ptr = Move(next); + } + ptr = nullptr; + + MOZ_RELEASE_ASSERT(elt1->mCount == 0); + MOZ_RELEASE_ASSERT(elt2->mCount == 0); + + list.insertBack(elt1); + elt1->setNext(elt2); + + MOZ_RELEASE_ASSERT(elt1->mCount == 1); + MOZ_RELEASE_ASSERT(elt2->mCount == 1); + + RefPtr<CountedClass> first = list.popFirst(); + + MOZ_RELEASE_ASSERT(elt1->mCount == 1); + MOZ_RELEASE_ASSERT(elt2->mCount == 1); + + RefPtr<CountedClass> second = list.popFirst(); + + MOZ_RELEASE_ASSERT(elt1->mCount == 1); + MOZ_RELEASE_ASSERT(elt2->mCount == 1); + + first = second = nullptr; + + delete elt1; + delete elt2; +} + +int +main() +{ + TestList(); + TestPrivate(); + TestMove(); + TestRemoveAndGet(); + TestRefPtrList(); + return 0; +} diff --git a/mfbt/tests/TestMacroArgs.cpp b/mfbt/tests/TestMacroArgs.cpp new file mode 100644 index 000000000..0d9760463 --- /dev/null +++ b/mfbt/tests/TestMacroArgs.cpp @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/MacroArgs.h" + +MOZ_STATIC_ASSERT_VALID_ARG_COUNT(1); +MOZ_STATIC_ASSERT_VALID_ARG_COUNT(1, 2); + +static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(100, a, b, c) == 1003, ""); + +static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(, a, b, c) == 3, ""); +static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(, a) == 1, ""); +static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(, !a) == 1, ""); +static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(, (a, b)) == 1, ""); + +static_assert(MOZ_PASTE_PREFIX_AND_ARG_COUNT(, MOZ_ARGS_AFTER_1(a, b, c)) == 2, + "MOZ_ARGS_AFTER_1(a, b, c) should expand to 'b, c'"); +static_assert(MOZ_ARGS_AFTER_2(a, b, 3) == 3, + "MOZ_ARGS_AFTER_2(a, b, 3) should expand to '3'"); + +static_assert(MOZ_ARG_1(10, 20, 30) == 10, ""); +static_assert(MOZ_ARG_2(10, 20, 30) == 20, ""); + +int +main() +{ + return 0; +} diff --git a/mfbt/tests/TestMacroForEach.cpp b/mfbt/tests/TestMacroForEach.cpp new file mode 100644 index 000000000..04a78d4b3 --- /dev/null +++ b/mfbt/tests/TestMacroForEach.cpp @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/MacroForEach.h" + +#define HELPER_IDENTITY_PLUS(x) x + +static_assert(MOZ_FOR_EACH(HELPER_IDENTITY_PLUS, (), (10)) 0 == 10, ""); +static_assert(MOZ_FOR_EACH(HELPER_IDENTITY_PLUS, (), (1, 1, 1)) 0 == 3, ""); + +#define HELPER_DEFINE_VAR(x) const int test1_##x = x; +MOZ_FOR_EACH(HELPER_DEFINE_VAR, (), (10, 20)) +static_assert(test1_10 == 10 && test1_20 == 20, ""); + +#define HELPER_DEFINE_VAR2(k, x) const int test2_##x = k + x; +MOZ_FOR_EACH(HELPER_DEFINE_VAR2, (5,), (10, 20)) +static_assert(test2_10 == 15 && test2_20 == 25, ""); + +#define HELPER_IDENTITY_COMMA(k1, k2, x) k1, k2, x, + +int +main() +{ + const int a[] = { + MOZ_FOR_EACH(HELPER_IDENTITY_COMMA, (1, 2,), (10, 20, 30)) + }; + MOZ_RELEASE_ASSERT(a[0] == 1 && a[1] == 2 && a[2] == 10 && + a[3] == 1 && a[4] == 2 && a[5] == 20 && + a[6] == 1 && a[7] == 2 && a[8] == 30, + "MOZ_FOR_EACH args enumerated in incorrect order"); + return 0; +} diff --git a/mfbt/tests/TestMathAlgorithms.cpp b/mfbt/tests/TestMathAlgorithms.cpp new file mode 100644 index 000000000..4e2629cba --- /dev/null +++ b/mfbt/tests/TestMathAlgorithms.cpp @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/MathAlgorithms.h" + +using mozilla::Clamp; +using mozilla::IsPowerOfTwo; + +static void +TestClamp() +{ + MOZ_RELEASE_ASSERT(Clamp(0, 0, 0) == 0); + MOZ_RELEASE_ASSERT(Clamp(1, 0, 0) == 0); + MOZ_RELEASE_ASSERT(Clamp(-1, 0, 0) == 0); + + MOZ_RELEASE_ASSERT(Clamp(0, 1, 1) == 1); + MOZ_RELEASE_ASSERT(Clamp(0, 1, 2) == 1); + + MOZ_RELEASE_ASSERT(Clamp(0, -1, -1) == -1); + MOZ_RELEASE_ASSERT(Clamp(0, -2, -1) == -1); + + MOZ_RELEASE_ASSERT(Clamp(0, 1, 3) == 1); + MOZ_RELEASE_ASSERT(Clamp(1, 1, 3) == 1); + MOZ_RELEASE_ASSERT(Clamp(2, 1, 3) == 2); + MOZ_RELEASE_ASSERT(Clamp(3, 1, 3) == 3); + MOZ_RELEASE_ASSERT(Clamp(4, 1, 3) == 3); + MOZ_RELEASE_ASSERT(Clamp(5, 1, 3) == 3); + + MOZ_RELEASE_ASSERT(Clamp<uint8_t>(UINT8_MAX, 0, UINT8_MAX) == UINT8_MAX); + MOZ_RELEASE_ASSERT(Clamp<uint8_t>(0, 0, UINT8_MAX) == 0); + + MOZ_RELEASE_ASSERT(Clamp<int8_t>(INT8_MIN, INT8_MIN, INT8_MAX) == INT8_MIN); + MOZ_RELEASE_ASSERT(Clamp<int8_t>(INT8_MIN, 0, INT8_MAX) == 0); + MOZ_RELEASE_ASSERT(Clamp<int8_t>(INT8_MAX, INT8_MIN, INT8_MAX) == INT8_MAX); + MOZ_RELEASE_ASSERT(Clamp<int8_t>(INT8_MAX, INT8_MIN, 0) == 0); +} + +static void +TestIsPowerOfTwo() +{ + static_assert(!IsPowerOfTwo(0u), "0 isn't a power of two"); + static_assert(IsPowerOfTwo(1u), "1 is a power of two"); + static_assert(IsPowerOfTwo(2u), "2 is a power of two"); + static_assert(!IsPowerOfTwo(3u), "3 isn't a power of two"); + static_assert(IsPowerOfTwo(4u), "4 is a power of two"); + static_assert(!IsPowerOfTwo(5u), "5 isn't a power of two"); + static_assert(!IsPowerOfTwo(6u), "6 isn't a power of two"); + static_assert(!IsPowerOfTwo(7u), "7 isn't a power of two"); + static_assert(IsPowerOfTwo(8u), "8 is a power of two"); + static_assert(!IsPowerOfTwo(9u), "9 isn't a power of two"); + + static_assert(!IsPowerOfTwo(uint8_t(UINT8_MAX/2)), "127, 0x7f isn't a power of two"); + static_assert(IsPowerOfTwo(uint8_t(UINT8_MAX/2 + 1)), "128, 0x80 is a power of two"); + static_assert(!IsPowerOfTwo(uint8_t(UINT8_MAX/2 + 2)), "129, 0x81 isn't a power of two"); + static_assert(!IsPowerOfTwo(uint8_t(UINT8_MAX - 1)), "254, 0xfe isn't a power of two"); + static_assert(!IsPowerOfTwo(uint8_t(UINT8_MAX)), "255, 0xff isn't a power of two"); + + static_assert(!IsPowerOfTwo(uint16_t(UINT16_MAX/2)), "0x7fff isn't a power of two"); + static_assert(IsPowerOfTwo(uint16_t(UINT16_MAX/2 + 1)), "0x8000 is a power of two"); + static_assert(!IsPowerOfTwo(uint16_t(UINT16_MAX/2 + 2)), "0x8001 isn't a power of two"); + static_assert(!IsPowerOfTwo(uint16_t(UINT16_MAX - 1)), "0xfffe isn't a power of two"); + static_assert(!IsPowerOfTwo(uint16_t(UINT16_MAX)), "0xffff isn't a power of two"); + + static_assert(!IsPowerOfTwo(uint32_t(UINT32_MAX/2)), "0x7fffffff isn't a power of two"); + static_assert(IsPowerOfTwo(uint32_t(UINT32_MAX/2 + 1)), "0x80000000 is a power of two"); + static_assert(!IsPowerOfTwo(uint32_t(UINT32_MAX/2 + 2)), "0x80000001 isn't a power of two"); + static_assert(!IsPowerOfTwo(uint32_t(UINT32_MAX - 1)), "0xfffffffe isn't a power of two"); + static_assert(!IsPowerOfTwo(uint32_t(UINT32_MAX)), "0xffffffff isn't a power of two"); + + static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX/2)), "0x7fffffffffffffff isn't a power of two"); + static_assert(IsPowerOfTwo(uint64_t(UINT64_MAX/2 + 1)), "0x8000000000000000 is a power of two"); + static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX/2 + 2)), "0x8000000000000001 isn't a power of two"); + static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX - 1)), "0xfffffffffffffffe isn't a power of two"); + static_assert(!IsPowerOfTwo(uint64_t(UINT64_MAX)), "0xffffffffffffffff isn't a power of two"); +} + +int +main() +{ + TestIsPowerOfTwo(); + TestClamp(); + + return 0; +} diff --git a/mfbt/tests/TestMaybe.cpp b/mfbt/tests/TestMaybe.cpp new file mode 100644 index 000000000..5817ab428 --- /dev/null +++ b/mfbt/tests/TestMaybe.cpp @@ -0,0 +1,860 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <utility> + +#include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" +#include "mozilla/Compiler.h" +#include "mozilla/Maybe.h" +#include "mozilla/Move.h" +#include "mozilla/Types.h" +#include "mozilla/TypeTraits.h" +#include "mozilla/UniquePtr.h" + +using mozilla::IsSame; +using mozilla::Maybe; +using mozilla::Move; +using mozilla::Nothing; +using mozilla::Some; +using mozilla::Swap; +using mozilla::ToMaybe; +using mozilla::UniquePtr; + +#if MOZ_IS_MSVC + template<typename T> struct Identity { typedef T type; }; +# define DECLTYPE(EXPR) Identity<decltype(EXPR)>::type +#else +# define DECLTYPE(EXPR) decltype(EXPR) +#endif + +#define RUN_TEST(t) \ + do { \ + bool cond = (t()); \ + if (!cond) \ + return 1; \ + cond = AllDestructorsWereCalled(); \ + MOZ_ASSERT(cond, "Failed to destroy all objects during test: " #t); \ + if (!cond) \ + return 1; \ + } while (false) + +enum Status +{ + eWasDefaultConstructed, + eWasConstructed, + eWasCopyConstructed, + eWasMoveConstructed, + eWasCopyAssigned, + eWasMoveAssigned, + eWasMovedFrom +}; + +static size_t sUndestroyedObjects = 0; + +static bool AllDestructorsWereCalled() +{ + return sUndestroyedObjects == 0; +} + +struct BasicValue +{ + BasicValue() + : mStatus(eWasDefaultConstructed) + , mTag(0) + { + ++sUndestroyedObjects; + } + + explicit BasicValue(int aTag) + : mStatus(eWasConstructed) + , mTag(aTag) + { + ++sUndestroyedObjects; + } + + BasicValue(const BasicValue& aOther) + : mStatus(eWasCopyConstructed) + , mTag(aOther.mTag) + { + ++sUndestroyedObjects; + } + + BasicValue(BasicValue&& aOther) + : mStatus(eWasMoveConstructed) + , mTag(aOther.mTag) + { + ++sUndestroyedObjects; + aOther.mStatus = eWasMovedFrom; + aOther.mTag = 0; + } + + ~BasicValue() { --sUndestroyedObjects; } + + BasicValue& operator=(const BasicValue& aOther) + { + mStatus = eWasCopyAssigned; + mTag = aOther.mTag; + return *this; + } + + BasicValue& operator=(BasicValue&& aOther) + { + mStatus = eWasMoveAssigned; + mTag = aOther.mTag; + aOther.mStatus = eWasMovedFrom; + aOther.mTag = 0; + return *this; + } + + bool operator==(const BasicValue& aOther) const + { + return mTag == aOther.mTag; + } + + bool operator<(const BasicValue& aOther) const + { + return mTag < aOther.mTag; + } + + Status GetStatus() const { return mStatus; } + void SetTag(int aValue) { mTag = aValue; } + int GetTag() const { return mTag; } + +private: + Status mStatus; + int mTag; +}; + +struct UncopyableValue +{ + UncopyableValue() + : mStatus(eWasDefaultConstructed) + { + ++sUndestroyedObjects; + } + + UncopyableValue(UncopyableValue&& aOther) + : mStatus(eWasMoveConstructed) + { + ++sUndestroyedObjects; + aOther.mStatus = eWasMovedFrom; + } + + ~UncopyableValue() { --sUndestroyedObjects; } + + UncopyableValue& operator=(UncopyableValue&& aOther) + { + mStatus = eWasMoveAssigned; + aOther.mStatus = eWasMovedFrom; + return *this; + } + + Status GetStatus() { return mStatus; } + +private: + UncopyableValue(const UncopyableValue& aOther) = delete; + UncopyableValue& operator=(const UncopyableValue& aOther) = delete; + + Status mStatus; +}; + +struct UnmovableValue +{ + UnmovableValue() + : mStatus(eWasDefaultConstructed) + { + ++sUndestroyedObjects; + } + + UnmovableValue(const UnmovableValue& aOther) + : mStatus(eWasCopyConstructed) + { + ++sUndestroyedObjects; + } + + ~UnmovableValue() { --sUndestroyedObjects; } + + UnmovableValue& operator=(const UnmovableValue& aOther) + { + mStatus = eWasCopyAssigned; + return *this; + } + + Status GetStatus() { return mStatus; } + +private: + UnmovableValue(UnmovableValue&& aOther) = delete; + UnmovableValue& operator=(UnmovableValue&& aOther) = delete; + + Status mStatus; +}; + +struct UncopyableUnmovableValue +{ + UncopyableUnmovableValue() + : mStatus(eWasDefaultConstructed) + { + ++sUndestroyedObjects; + } + + explicit UncopyableUnmovableValue(int) + : mStatus(eWasConstructed) + { + ++sUndestroyedObjects; + } + + ~UncopyableUnmovableValue() { --sUndestroyedObjects; } + + Status GetStatus() { return mStatus; } + +private: + UncopyableUnmovableValue(const UncopyableUnmovableValue& aOther) = delete; + UncopyableUnmovableValue& operator=(const UncopyableUnmovableValue& aOther) = delete; + UncopyableUnmovableValue(UncopyableUnmovableValue&& aOther) = delete; + UncopyableUnmovableValue& operator=(UncopyableUnmovableValue&& aOther) = delete; + + Status mStatus; +}; + +static bool +TestBasicFeatures() +{ + // Check that a Maybe<T> is initialized to Nothing. + Maybe<BasicValue> mayValue; + static_assert(IsSame<BasicValue, DECLTYPE(mayValue)::ValueType>::value, + "Should have BasicValue ValueType"); + MOZ_RELEASE_ASSERT(!mayValue); + MOZ_RELEASE_ASSERT(!mayValue.isSome()); + MOZ_RELEASE_ASSERT(mayValue.isNothing()); + + // Check that emplace() default constructs and the accessors work. + mayValue.emplace(); + MOZ_RELEASE_ASSERT(mayValue); + MOZ_RELEASE_ASSERT(mayValue.isSome()); + MOZ_RELEASE_ASSERT(!mayValue.isNothing()); + MOZ_RELEASE_ASSERT(*mayValue == BasicValue()); + MOZ_RELEASE_ASSERT(mayValue.value() == BasicValue()); + static_assert(IsSame<BasicValue, DECLTYPE(mayValue.value())>::value, + "value() should return a BasicValue"); + MOZ_RELEASE_ASSERT(mayValue.ref() == BasicValue()); + static_assert(IsSame<BasicValue&, DECLTYPE(mayValue.ref())>::value, + "ref() should return a BasicValue&"); + MOZ_RELEASE_ASSERT(mayValue.ptr() != nullptr); + static_assert(IsSame<BasicValue*, DECLTYPE(mayValue.ptr())>::value, + "ptr() should return a BasicValue*"); + MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasDefaultConstructed); + + // Check that reset() works. + mayValue.reset(); + MOZ_RELEASE_ASSERT(!mayValue); + MOZ_RELEASE_ASSERT(!mayValue.isSome()); + MOZ_RELEASE_ASSERT(mayValue.isNothing()); + + // Check that emplace(T1) calls the correct constructor. + mayValue.emplace(1); + MOZ_RELEASE_ASSERT(mayValue); + MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasConstructed); + MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1); + mayValue.reset(); + MOZ_RELEASE_ASSERT(!mayValue); + + // Check that Some() and Nothing() work. + mayValue = Some(BasicValue(2)); + MOZ_RELEASE_ASSERT(mayValue); + MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasMoveConstructed); + MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2); + mayValue = Nothing(); + MOZ_RELEASE_ASSERT(!mayValue); + + // Check that the accessors work through a const ref. + mayValue.emplace(); + const Maybe<BasicValue>& mayValueCRef = mayValue; + MOZ_RELEASE_ASSERT(mayValueCRef); + MOZ_RELEASE_ASSERT(mayValueCRef.isSome()); + MOZ_RELEASE_ASSERT(!mayValueCRef.isNothing()); + MOZ_RELEASE_ASSERT(*mayValueCRef == BasicValue()); + MOZ_RELEASE_ASSERT(mayValueCRef.value() == BasicValue()); + static_assert(IsSame<BasicValue, DECLTYPE(mayValueCRef.value())>::value, + "value() should return a BasicValue"); + MOZ_RELEASE_ASSERT(mayValueCRef.ref() == BasicValue()); + static_assert(IsSame<const BasicValue&, + DECLTYPE(mayValueCRef.ref())>::value, + "ref() should return a const BasicValue&"); + MOZ_RELEASE_ASSERT(mayValueCRef.ptr() != nullptr); + static_assert(IsSame<const BasicValue*, + DECLTYPE(mayValueCRef.ptr())>::value, + "ptr() should return a const BasicValue*"); + MOZ_RELEASE_ASSERT(mayValueCRef->GetStatus() == eWasDefaultConstructed); + mayValue.reset(); + + return true; +} + +static bool +TestCopyAndMove() +{ + // Check that we get moves when possible for types that can support both moves + // and copies. + Maybe<BasicValue> mayBasicValue = Some(BasicValue(1)); + MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveConstructed); + MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 1); + mayBasicValue = Some(BasicValue(2)); + MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveAssigned); + MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 2); + mayBasicValue.reset(); + mayBasicValue.emplace(BasicValue(3)); + MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveConstructed); + MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 3); + + // Check that we get copies when moves aren't possible. + Maybe<BasicValue> mayBasicValue2 = Some(*mayBasicValue); + MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyConstructed); + MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 3); + mayBasicValue->SetTag(4); + mayBasicValue2 = mayBasicValue; + // This test should work again when we fix bug 1052940. + //MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyAssigned); + MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 4); + mayBasicValue->SetTag(5); + mayBasicValue2.reset(); + mayBasicValue2.emplace(*mayBasicValue); + MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyConstructed); + MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 5); + + // Check that Move() works. (Another sanity check for move support.) + Maybe<BasicValue> mayBasicValue3 = Some(Move(*mayBasicValue)); + MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMoveConstructed); + MOZ_RELEASE_ASSERT(mayBasicValue3->GetTag() == 5); + MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMovedFrom); + mayBasicValue2->SetTag(6); + mayBasicValue3 = Some(Move(*mayBasicValue2)); + MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMoveAssigned); + MOZ_RELEASE_ASSERT(mayBasicValue3->GetTag() == 6); + MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasMovedFrom); + Maybe<BasicValue> mayBasicValue4; + mayBasicValue4.emplace(Move(*mayBasicValue3)); + MOZ_RELEASE_ASSERT(mayBasicValue4->GetStatus() == eWasMoveConstructed); + MOZ_RELEASE_ASSERT(mayBasicValue4->GetTag() == 6); + MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMovedFrom); + + // Check that we always get copies for types that don't support moves. + // XXX(seth): These tests fail but probably shouldn't. For now we'll just + // consider using Maybe with types that allow copies but have deleted or + // private move constructors, or which do not support copy assignment, to + // be supported only to the extent that we need for existing code to work. + // These tests should work again when we fix bug 1052940. + /* + Maybe<UnmovableValue> mayUnmovableValue = Some(UnmovableValue()); + MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyConstructed); + mayUnmovableValue = Some(UnmovableValue()); + MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyAssigned); + mayUnmovableValue.reset(); + mayUnmovableValue.emplace(UnmovableValue()); + MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyConstructed); + */ + + // Check that types that only support moves, but not copies, work. + Maybe<UncopyableValue> mayUncopyableValue = Some(UncopyableValue()); + MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() == eWasMoveConstructed); + mayUncopyableValue = Some(UncopyableValue()); + MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() == eWasMoveAssigned); + mayUncopyableValue.reset(); + mayUncopyableValue.emplace(UncopyableValue()); + MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() == eWasMoveConstructed); + + // Check that types that support neither moves or copies work. + Maybe<UncopyableUnmovableValue> mayUncopyableUnmovableValue; + mayUncopyableUnmovableValue.emplace(); + MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValue->GetStatus() == eWasDefaultConstructed); + mayUncopyableUnmovableValue.reset(); + mayUncopyableUnmovableValue.emplace(0); + MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValue->GetStatus() == eWasConstructed); + + return true; +} + +static BasicValue* sStaticBasicValue = nullptr; + +static BasicValue +MakeBasicValue() +{ + return BasicValue(9); +} + +static BasicValue& +MakeBasicValueRef() +{ + return *sStaticBasicValue; +} + +static BasicValue* +MakeBasicValuePtr() +{ + return sStaticBasicValue; +} + +static bool +TestFunctionalAccessors() +{ + BasicValue value(9); + sStaticBasicValue = new BasicValue(9); + + // Check that the 'some' case of functional accessors works. + Maybe<BasicValue> someValue = Some(BasicValue(3)); + MOZ_RELEASE_ASSERT(someValue.valueOr(value) == BasicValue(3)); + static_assert(IsSame<BasicValue, + DECLTYPE(someValue.valueOr(value))>::value, + "valueOr should return a BasicValue"); + MOZ_RELEASE_ASSERT(someValue.valueOrFrom(&MakeBasicValue) == BasicValue(3)); + static_assert(IsSame<BasicValue, + DECLTYPE(someValue.valueOrFrom(&MakeBasicValue))>::value, + "valueOrFrom should return a BasicValue"); + MOZ_RELEASE_ASSERT(someValue.ptrOr(&value) != &value); + static_assert(IsSame<BasicValue*, + DECLTYPE(someValue.ptrOr(&value))>::value, + "ptrOr should return a BasicValue*"); + MOZ_RELEASE_ASSERT(*someValue.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(3)); + static_assert(IsSame<BasicValue*, + DECLTYPE(someValue.ptrOrFrom(&MakeBasicValuePtr))>::value, + "ptrOrFrom should return a BasicValue*"); + MOZ_RELEASE_ASSERT(someValue.refOr(value) == BasicValue(3)); + static_assert(IsSame<BasicValue&, + DECLTYPE(someValue.refOr(value))>::value, + "refOr should return a BasicValue&"); + MOZ_RELEASE_ASSERT(someValue.refOrFrom(&MakeBasicValueRef) == BasicValue(3)); + static_assert(IsSame<BasicValue&, + DECLTYPE(someValue.refOrFrom(&MakeBasicValueRef))>::value, + "refOrFrom should return a BasicValue&"); + + // Check that the 'some' case works through a const reference. + const Maybe<BasicValue>& someValueCRef = someValue; + MOZ_RELEASE_ASSERT(someValueCRef.valueOr(value) == BasicValue(3)); + static_assert(IsSame<BasicValue, + DECLTYPE(someValueCRef.valueOr(value))>::value, + "valueOr should return a BasicValue"); + MOZ_RELEASE_ASSERT(someValueCRef.valueOrFrom(&MakeBasicValue) == BasicValue(3)); + static_assert(IsSame<BasicValue, + DECLTYPE(someValueCRef.valueOrFrom(&MakeBasicValue))>::value, + "valueOrFrom should return a BasicValue"); + MOZ_RELEASE_ASSERT(someValueCRef.ptrOr(&value) != &value); + static_assert(IsSame<const BasicValue*, + DECLTYPE(someValueCRef.ptrOr(&value))>::value, + "ptrOr should return a const BasicValue*"); + MOZ_RELEASE_ASSERT(*someValueCRef.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(3)); + static_assert(IsSame<const BasicValue*, + DECLTYPE(someValueCRef.ptrOrFrom(&MakeBasicValuePtr))>::value, + "ptrOrFrom should return a const BasicValue*"); + MOZ_RELEASE_ASSERT(someValueCRef.refOr(value) == BasicValue(3)); + static_assert(IsSame<const BasicValue&, + DECLTYPE(someValueCRef.refOr(value))>::value, + "refOr should return a const BasicValue&"); + MOZ_RELEASE_ASSERT(someValueCRef.refOrFrom(&MakeBasicValueRef) == BasicValue(3)); + static_assert(IsSame<const BasicValue&, + DECLTYPE(someValueCRef.refOrFrom(&MakeBasicValueRef))>::value, + "refOrFrom should return a const BasicValue&"); + + // Check that the 'none' case of functional accessors works. + Maybe<BasicValue> noneValue; + MOZ_RELEASE_ASSERT(noneValue.valueOr(value) == BasicValue(9)); + static_assert(IsSame<BasicValue, + DECLTYPE(noneValue.valueOr(value))>::value, + "valueOr should return a BasicValue"); + MOZ_RELEASE_ASSERT(noneValue.valueOrFrom(&MakeBasicValue) == BasicValue(9)); + static_assert(IsSame<BasicValue, + DECLTYPE(noneValue.valueOrFrom(&MakeBasicValue))>::value, + "valueOrFrom should return a BasicValue"); + MOZ_RELEASE_ASSERT(noneValue.ptrOr(&value) == &value); + static_assert(IsSame<BasicValue*, + DECLTYPE(noneValue.ptrOr(&value))>::value, + "ptrOr should return a BasicValue*"); + MOZ_RELEASE_ASSERT(*noneValue.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(9)); + static_assert(IsSame<BasicValue*, + DECLTYPE(noneValue.ptrOrFrom(&MakeBasicValuePtr))>::value, + "ptrOrFrom should return a BasicValue*"); + MOZ_RELEASE_ASSERT(noneValue.refOr(value) == BasicValue(9)); + static_assert(IsSame<BasicValue&, + DECLTYPE(noneValue.refOr(value))>::value, + "refOr should return a BasicValue&"); + MOZ_RELEASE_ASSERT(noneValue.refOrFrom(&MakeBasicValueRef) == BasicValue(9)); + static_assert(IsSame<BasicValue&, + DECLTYPE(noneValue.refOrFrom(&MakeBasicValueRef))>::value, + "refOrFrom should return a BasicValue&"); + + // Check that the 'none' case works through a const reference. + const Maybe<BasicValue>& noneValueCRef = noneValue; + MOZ_RELEASE_ASSERT(noneValueCRef.valueOr(value) == BasicValue(9)); + static_assert(IsSame<BasicValue, + DECLTYPE(noneValueCRef.valueOr(value))>::value, + "valueOr should return a BasicValue"); + MOZ_RELEASE_ASSERT(noneValueCRef.valueOrFrom(&MakeBasicValue) == BasicValue(9)); + static_assert(IsSame<BasicValue, + DECLTYPE(noneValueCRef.valueOrFrom(&MakeBasicValue))>::value, + "valueOrFrom should return a BasicValue"); + MOZ_RELEASE_ASSERT(noneValueCRef.ptrOr(&value) == &value); + static_assert(IsSame<const BasicValue*, + DECLTYPE(noneValueCRef.ptrOr(&value))>::value, + "ptrOr should return a const BasicValue*"); + MOZ_RELEASE_ASSERT(*noneValueCRef.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(9)); + static_assert(IsSame<const BasicValue*, + DECLTYPE(noneValueCRef.ptrOrFrom(&MakeBasicValuePtr))>::value, + "ptrOrFrom should return a const BasicValue*"); + MOZ_RELEASE_ASSERT(noneValueCRef.refOr(value) == BasicValue(9)); + static_assert(IsSame<const BasicValue&, + DECLTYPE(noneValueCRef.refOr(value))>::value, + "refOr should return a const BasicValue&"); + MOZ_RELEASE_ASSERT(noneValueCRef.refOrFrom(&MakeBasicValueRef) == BasicValue(9)); + static_assert(IsSame<const BasicValue&, + DECLTYPE(noneValueCRef.refOrFrom(&MakeBasicValueRef))>::value, + "refOrFrom should return a const BasicValue&"); + + // Clean up so the undestroyed objects count stays accurate. + delete sStaticBasicValue; + sStaticBasicValue = nullptr; + + return true; +} + +static bool gFunctionWasApplied = false; + +static void +IncrementTag(BasicValue& aValue) +{ + gFunctionWasApplied = true; + aValue.SetTag(aValue.GetTag() + 1); +} + +static void +AccessValue(const BasicValue&) +{ + gFunctionWasApplied = true; +} + +struct IncrementTagFunctor +{ + IncrementTagFunctor() : mBy(1) { } + + void operator()(BasicValue& aValue) + { + aValue.SetTag(aValue.GetTag() + mBy.GetTag()); + } + + BasicValue mBy; +}; + +static bool +TestApply() +{ + // Check that apply handles the 'Nothing' case. + gFunctionWasApplied = false; + Maybe<BasicValue> mayValue; + mayValue.apply(&IncrementTag); + mayValue.apply(&AccessValue); + MOZ_RELEASE_ASSERT(!gFunctionWasApplied); + + // Check that apply handles the 'Some' case. + mayValue = Some(BasicValue(1)); + mayValue.apply(&IncrementTag); + MOZ_RELEASE_ASSERT(gFunctionWasApplied); + MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2); + gFunctionWasApplied = false; + mayValue.apply(&AccessValue); + MOZ_RELEASE_ASSERT(gFunctionWasApplied); + + // Check that apply works with a const reference. + const Maybe<BasicValue>& mayValueCRef = mayValue; + gFunctionWasApplied = false; + mayValueCRef.apply(&AccessValue); + MOZ_RELEASE_ASSERT(gFunctionWasApplied); + + // Check that apply works with functors. + IncrementTagFunctor tagIncrementer; + MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed); + mayValue = Some(BasicValue(1)); + mayValue.apply(tagIncrementer); + MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2); + MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed); + + // Check that apply works with lambda expressions. + int32_t two = 2; + gFunctionWasApplied = false; + mayValue = Some(BasicValue(2)); + mayValue.apply([&](BasicValue& aVal) { aVal.SetTag(aVal.GetTag() * two); }); + MOZ_RELEASE_ASSERT(mayValue->GetTag() == 4); + mayValue.apply([=](BasicValue& aVal) { aVal.SetTag(aVal.GetTag() * two); }); + MOZ_RELEASE_ASSERT(mayValue->GetTag() == 8); + mayValueCRef.apply([&](const BasicValue& aVal) { gFunctionWasApplied = true; }); + MOZ_RELEASE_ASSERT(gFunctionWasApplied == true); + + return true; +} + +static int +TimesTwo(const BasicValue& aValue) +{ + return aValue.GetTag() * 2; +} + +static int +TimesTwoAndResetOriginal(BasicValue& aValue) +{ + int tag = aValue.GetTag(); + aValue.SetTag(1); + return tag * 2; +} + +struct MultiplyTagFunctor +{ + MultiplyTagFunctor() : mBy(2) { } + + int operator()(BasicValue& aValue) + { + return aValue.GetTag() * mBy.GetTag(); + } + + BasicValue mBy; +}; + +static bool +TestMap() +{ + // Check that map handles the 'Nothing' case. + Maybe<BasicValue> mayValue; + MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwo) == Nothing()); + static_assert(IsSame<Maybe<int>, + DECLTYPE(mayValue.map(&TimesTwo))>::value, + "map(TimesTwo) should return a Maybe<int>"); + MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwoAndResetOriginal) == Nothing()); + + // Check that map handles the 'Some' case. + mayValue = Some(BasicValue(2)); + MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwo) == Some(4)); + MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwoAndResetOriginal) == Some(4)); + MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1); + mayValue = Some(BasicValue(2)); + + // Check that map works with a const reference. + mayValue->SetTag(2); + const Maybe<BasicValue>& mayValueCRef = mayValue; + MOZ_RELEASE_ASSERT(mayValueCRef.map(&TimesTwo) == Some(4)); + static_assert(IsSame<Maybe<int>, + DECLTYPE(mayValueCRef.map(&TimesTwo))>::value, + "map(TimesTwo) should return a Maybe<int>"); + + // Check that map works with functors. + MultiplyTagFunctor tagMultiplier; + MOZ_RELEASE_ASSERT(tagMultiplier.mBy.GetStatus() == eWasConstructed); + MOZ_RELEASE_ASSERT(mayValue.map(tagMultiplier) == Some(4)); + MOZ_RELEASE_ASSERT(tagMultiplier.mBy.GetStatus() == eWasConstructed); + + // Check that map works with lambda expressions. + int two = 2; + mayValue = Some(BasicValue(2)); + Maybe<int> mappedValue = + mayValue.map([&](const BasicValue& aVal) { + return aVal.GetTag() * two; + }); + MOZ_RELEASE_ASSERT(mappedValue == Some(4)); + mappedValue = + mayValue.map([=](const BasicValue& aVal) { + return aVal.GetTag() * two; + }); + MOZ_RELEASE_ASSERT(mappedValue == Some(4)); + mappedValue = + mayValueCRef.map([&](const BasicValue& aVal) { + return aVal.GetTag() * two; + }); + MOZ_RELEASE_ASSERT(mappedValue == Some(4)); + + return true; +} + +static bool +TestToMaybe() +{ + BasicValue value(1); + BasicValue* nullPointer = nullptr; + + // Check that a non-null pointer translates into a Some value. + Maybe<BasicValue> mayValue = ToMaybe(&value); + static_assert(IsSame<Maybe<BasicValue>, DECLTYPE(ToMaybe(&value))>::value, + "ToMaybe should return a Maybe<BasicValue>"); + MOZ_RELEASE_ASSERT(mayValue.isSome()); + MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1); + MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasCopyConstructed); + MOZ_RELEASE_ASSERT(value.GetStatus() != eWasMovedFrom); + + // Check that a null pointer translates into a Nothing value. + mayValue = ToMaybe(nullPointer); + static_assert(IsSame<Maybe<BasicValue>, DECLTYPE(ToMaybe(nullPointer))>::value, + "ToMaybe should return a Maybe<BasicValue>"); + MOZ_RELEASE_ASSERT(mayValue.isNothing()); + + return true; +} + +static bool +TestComparisonOperators() +{ + Maybe<BasicValue> nothingValue = Nothing(); + Maybe<BasicValue> anotherNothingValue = Nothing(); + Maybe<BasicValue> oneValue = Some(BasicValue(1)); + Maybe<BasicValue> anotherOneValue = Some(BasicValue(1)); + Maybe<BasicValue> twoValue = Some(BasicValue(2)); + + // Check equality. + MOZ_RELEASE_ASSERT(nothingValue == anotherNothingValue); + MOZ_RELEASE_ASSERT(oneValue == anotherOneValue); + + // Check inequality. + MOZ_RELEASE_ASSERT(nothingValue != oneValue); + MOZ_RELEASE_ASSERT(oneValue != nothingValue); + MOZ_RELEASE_ASSERT(oneValue != twoValue); + + // Check '<'. + MOZ_RELEASE_ASSERT(nothingValue < oneValue); + MOZ_RELEASE_ASSERT(oneValue < twoValue); + + // Check '<='. + MOZ_RELEASE_ASSERT(nothingValue <= anotherNothingValue); + MOZ_RELEASE_ASSERT(nothingValue <= oneValue); + MOZ_RELEASE_ASSERT(oneValue <= oneValue); + MOZ_RELEASE_ASSERT(oneValue <= twoValue); + + // Check '>'. + MOZ_RELEASE_ASSERT(oneValue > nothingValue); + MOZ_RELEASE_ASSERT(twoValue > oneValue); + + // Check '>='. + MOZ_RELEASE_ASSERT(nothingValue >= anotherNothingValue); + MOZ_RELEASE_ASSERT(oneValue >= nothingValue); + MOZ_RELEASE_ASSERT(oneValue >= oneValue); + MOZ_RELEASE_ASSERT(twoValue >= oneValue); + + return true; +} + +// Check that Maybe<> can wrap a superclass that happens to also be a concrete +// class (i.e. that the compiler doesn't warn when we invoke the superclass's +// destructor explicitly in |reset()|. +class MySuperClass { + virtual void VirtualMethod() { /* do nothing */ } +}; + +class MyDerivedClass : public MySuperClass { + void VirtualMethod() override { /* do nothing */ } +}; + +static bool +TestVirtualFunction() { + Maybe<MySuperClass> super; + super.emplace(); + super.reset(); + + Maybe<MyDerivedClass> derived; + derived.emplace(); + derived.reset(); + + // If this compiles successfully, we've passed. + return true; +} + +static Maybe<int*> +ReturnSomeNullptr() +{ + return Some(nullptr); +} + +struct D +{ + explicit D(Maybe<int*>) {} +}; + +static bool +TestSomeNullptrConversion() +{ + Maybe<int*> m1 = Some(nullptr); + MOZ_RELEASE_ASSERT(m1.isSome()); + MOZ_RELEASE_ASSERT(m1); + MOZ_RELEASE_ASSERT(!*m1); + + auto m2 = ReturnSomeNullptr(); + MOZ_RELEASE_ASSERT(m2.isSome()); + MOZ_RELEASE_ASSERT(m2); + MOZ_RELEASE_ASSERT(!*m2); + + Maybe<decltype(nullptr)> m3 = Some(nullptr); + MOZ_RELEASE_ASSERT(m3.isSome()); + MOZ_RELEASE_ASSERT(m3); + MOZ_RELEASE_ASSERT(*m3 == nullptr); + + D d(Some(nullptr)); + + return true; +} + +struct Base {}; +struct Derived : Base {}; + +static Maybe<Base*> +ReturnDerivedPointer() +{ + Derived* d = nullptr; + return Some(d); +} + +struct ExplicitConstructorBasePointer +{ + explicit ExplicitConstructorBasePointer(Maybe<Base*>) {} +}; + +static bool +TestSomePointerConversion() +{ + Base base; + Derived derived; + + Maybe<Base*> m1 = Some(&derived); + MOZ_RELEASE_ASSERT(m1.isSome()); + MOZ_RELEASE_ASSERT(m1); + MOZ_RELEASE_ASSERT(*m1 == &derived); + + auto m2 = ReturnDerivedPointer(); + MOZ_RELEASE_ASSERT(m2.isSome()); + MOZ_RELEASE_ASSERT(m2); + MOZ_RELEASE_ASSERT(*m2 == nullptr); + + Maybe<Base*> m3 = Some(&base); + MOZ_RELEASE_ASSERT(m3.isSome()); + MOZ_RELEASE_ASSERT(m3); + MOZ_RELEASE_ASSERT(*m3 == &base); + + auto s1 = Some(&derived); + Maybe<Base*> c1(s1); + MOZ_RELEASE_ASSERT(c1.isSome()); + MOZ_RELEASE_ASSERT(c1); + MOZ_RELEASE_ASSERT(*c1 == &derived); + + ExplicitConstructorBasePointer ecbp(Some(&derived)); + + return true; +} + +int +main() +{ + RUN_TEST(TestBasicFeatures); + RUN_TEST(TestCopyAndMove); + RUN_TEST(TestFunctionalAccessors); + RUN_TEST(TestApply); + RUN_TEST(TestMap); + RUN_TEST(TestToMaybe); + RUN_TEST(TestComparisonOperators); + RUN_TEST(TestVirtualFunction); + RUN_TEST(TestSomeNullptrConversion); + RUN_TEST(TestSomePointerConversion); + + return 0; +} diff --git a/mfbt/tests/TestNotNull.cpp b/mfbt/tests/TestNotNull.cpp new file mode 100644 index 000000000..4642dd93e --- /dev/null +++ b/mfbt/tests/TestNotNull.cpp @@ -0,0 +1,314 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/NotNull.h" +#include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Unused.h" + +using mozilla::WrapNotNull; +using mozilla::MakeUnique; +using mozilla::NotNull; +using mozilla::UniquePtr; + +#define CHECK MOZ_RELEASE_ASSERT + +class Blah +{ +public: + Blah() : mX(0) {} + void blah() {}; + int mX; +}; + +// A simple smart pointer that implicity converts to and from T*. +template <typename T> +class MyPtr +{ + T* mRawPtr; + +public: + MyPtr() : mRawPtr(nullptr) {} + MOZ_IMPLICIT MyPtr(T* aRawPtr) : mRawPtr(aRawPtr) {} + + T* get() const { return mRawPtr; } + operator T*() const { return get(); } + + T* operator->() const { return get(); } +}; + +// A simple class that works with RefPtr. It keeps track of the maximum +// refcount value for testing purposes. +class MyRefType +{ + int mExpectedMaxRefCnt; + int mMaxRefCnt; + int mRefCnt; +public: + explicit MyRefType(int aExpectedMaxRefCnt) + : mExpectedMaxRefCnt(aExpectedMaxRefCnt) + , mMaxRefCnt(0) + , mRefCnt(0) + {} + + ~MyRefType() { + CHECK(mMaxRefCnt == mExpectedMaxRefCnt); + } + + uint32_t AddRef() { + mRefCnt++; + if (mRefCnt > mMaxRefCnt) { + mMaxRefCnt = mRefCnt; + } + return mRefCnt; + } + + uint32_t Release() { + CHECK(mRefCnt > 0); + mRefCnt--; + if (mRefCnt == 0) { + delete this; + return 0; + } + return mRefCnt; + } +}; + +void f_i(int* aPtr) {} +void f_my(MyPtr<int> aPtr) {} + +void f_nni(NotNull<int*> aPtr) {} +void f_nnmy(NotNull<MyPtr<int>> aPtr) {} + +void +TestNotNullWithMyPtr() +{ + int i4 = 4; + int i5 = 5; + + MyPtr<int> my4 = &i4; + MyPtr<int> my5 = &i5; + + NotNull<int*> nni4 = WrapNotNull(&i4); + NotNull<int*> nni5 = WrapNotNull(&i5); + NotNull<MyPtr<int>> nnmy4 = WrapNotNull(my4); + + //WrapNotNull(nullptr); // no wrapping from nullptr + //WrapNotNull(0); // no wrapping from zero + + // NotNull<int*> construction combinations + //NotNull<int*> nni4a; // no default + //NotNull<int*> nni4a(nullptr); // no nullptr + //NotNull<int*> nni4a(0); // no zero + //NotNull<int*> nni4a(&i4); // no int* + //NotNull<int*> nni4a(my4); // no MyPtr<int> + NotNull<int*> nni4b(WrapNotNull(&i4)); // WrapNotNull(int*) + NotNull<int*> nni4c(WrapNotNull(my4)); // WrapNotNull(MyPtr<int>) + NotNull<int*> nni4d(nni4); // NotNull<int*> + NotNull<int*> nni4e(nnmy4); // NotNull<MyPtr<int>> + CHECK(*nni4b == 4); + CHECK(*nni4c == 4); + CHECK(*nni4d == 4); + CHECK(*nni4e == 4); + + // NotNull<MyPtr<int>> construction combinations + //NotNull<MyPtr<int>> nnmy4a; // no default + //NotNull<MyPtr<int>> nnmy4a(nullptr); // no nullptr + //NotNull<MyPtr<int>> nnmy4a(0); // no zero + //NotNull<MyPtr<int>> nnmy4a(&i4); // no int* + //NotNull<MyPtr<int>> nnmy4a(my4); // no MyPtr<int> + NotNull<MyPtr<int>> nnmy4b(WrapNotNull(&i4)); // WrapNotNull(int*) + NotNull<MyPtr<int>> nnmy4c(WrapNotNull(my4)); // WrapNotNull(MyPtr<int>) + NotNull<MyPtr<int>> nnmy4d(nni4); // NotNull<int*> + NotNull<MyPtr<int>> nnmy4e(nnmy4); // NotNull<MyPtr<int>> + CHECK(*nnmy4b == 4); + CHECK(*nnmy4c == 4); + CHECK(*nnmy4d == 4); + CHECK(*nnmy4e == 4); + + // NotNull<int*> assignment combinations + //nni4b = nullptr; // no nullptr + //nni4b = 0; // no zero + //nni4a = &i4; // no int* + //nni4a = my4; // no MyPtr<int> + nni4b = WrapNotNull(&i4); // WrapNotNull(int*) + nni4c = WrapNotNull(my4); // WrapNotNull(MyPtr<int>) + nni4d = nni4; // NotNull<int*> + nni4e = nnmy4; // NotNull<MyPtr<int>> + CHECK(*nni4b == 4); + CHECK(*nni4c == 4); + CHECK(*nni4d == 4); + CHECK(*nni4e == 4); + + // NotNull<MyPtr<int>> assignment combinations + //nnmy4a = nullptr; // no nullptr + //nnmy4a = 0; // no zero + //nnmy4a = &i4; // no int* + //nnmy4a = my4; // no MyPtr<int> + nnmy4b = WrapNotNull(&i4); // WrapNotNull(int*) + nnmy4c = WrapNotNull(my4); // WrapNotNull(MyPtr<int>) + nnmy4d = nni4; // NotNull<int*> + nnmy4e = nnmy4; // NotNull<MyPtr<int>> + CHECK(*nnmy4b == 4); + CHECK(*nnmy4c == 4); + CHECK(*nnmy4d == 4); + CHECK(*nnmy4e == 4); + + NotNull<MyPtr<int>> nnmy5 = WrapNotNull(&i5); + CHECK(*nnmy5 == 5); + CHECK(nnmy5 == &i5); // NotNull<MyPtr<int>> == int* + CHECK(nnmy5 == my5); // NotNull<MyPtr<int>> == MyPtr<int> + CHECK(nnmy5 == nni5); // NotNull<MyPtr<int>> == NotNull<int*> + CHECK(nnmy5 == nnmy5); // NotNull<MyPtr<int>> == NotNull<MyPtr<int>> + CHECK(&i5 == nnmy5); // int* == NotNull<MyPtr<int>> + CHECK(my5 == nnmy5); // MyPtr<int> == NotNull<MyPtr<int>> + CHECK(nni5 == nnmy5); // NotNull<int*> == NotNull<MyPtr<int>> + CHECK(nnmy5 == nnmy5); // NotNull<MyPtr<int>> == NotNull<MyPtr<int>> + //CHECK(nni5 == nullptr); // no comparisons with nullptr + //CHECK(nullptr == nni5); // no comparisons with nullptr + //CHECK(nni5 == 0); // no comparisons with zero + //CHECK(0 == nni5); // no comparisons with zero + + CHECK(*nnmy5 == 5); + CHECK(nnmy5 != &i4); // NotNull<MyPtr<int>> != int* + CHECK(nnmy5 != my4); // NotNull<MyPtr<int>> != MyPtr<int> + CHECK(nnmy5 != nni4); // NotNull<MyPtr<int>> != NotNull<int*> + CHECK(nnmy5 != nnmy4); // NotNull<MyPtr<int>> != NotNull<MyPtr<int>> + CHECK(&i4 != nnmy5); // int* != NotNull<MyPtr<int>> + CHECK(my4 != nnmy5); // MyPtr<int> != NotNull<MyPtr<int>> + CHECK(nni4 != nnmy5); // NotNull<int*> != NotNull<MyPtr<int>> + CHECK(nnmy4 != nnmy5); // NotNull<MyPtr<int>> != NotNull<MyPtr<int>> + //CHECK(nni4 != nullptr); // no comparisons with nullptr + //CHECK(nullptr != nni4); // no comparisons with nullptr + //CHECK(nni4 != 0); // no comparisons with zero + //CHECK(0 != nni4); // no comparisons with zero + + // int* parameter + f_i(&i4); // identity int* --> int* + f_i(my4); // implicit MyPtr<int> --> int* + f_i(my4.get()); // explicit MyPtr<int> --> int* + f_i(nni4); // implicit NotNull<int*> --> int* + f_i(nni4.get()); // explicit NotNull<int*> --> int* + //f_i(nnmy4); // no implicit NotNull<MyPtr<int>> --> int* + f_i(nnmy4.get()); // explicit NotNull<MyPtr<int>> --> int* + f_i(nnmy4.get().get());// doubly-explicit NotNull<MyPtr<int>> --> int* + + // MyPtr<int> parameter + f_my(&i4); // implicit int* --> MyPtr<int> + f_my(my4); // identity MyPtr<int> --> MyPtr<int> + f_my(my4.get()); // explicit MyPtr<int> --> MyPtr<int> + //f_my(nni4); // no implicit NotNull<int*> --> MyPtr<int> + f_my(nni4.get()); // explicit NotNull<int*> --> MyPtr<int> + f_my(nnmy4); // implicit NotNull<MyPtr<int>> --> MyPtr<int> + f_my(nnmy4.get()); // explicit NotNull<MyPtr<int>> --> MyPtr<int> + f_my(nnmy4.get().get());// doubly-explicit NotNull<MyPtr<int>> --> MyPtr<int> + + // NotNull<int*> parameter + f_nni(nni4); // identity NotNull<int*> --> NotNull<int*> + f_nni(nnmy4); // implicit NotNull<MyPtr<int>> --> NotNull<int*> + + // NotNull<MyPtr<int>> parameter + f_nnmy(nni4); // implicit NotNull<int*> --> NotNull<MyPtr<int>> + f_nnmy(nnmy4); // identity NotNull<MyPtr<int>> --> NotNull<MyPtr<int>> + + //CHECK(nni4); // disallow boolean conversion / unary expression usage + //CHECK(nnmy4); // ditto + + // '->' dereferencing. + Blah blah; + MyPtr<Blah> myblah = &blah; + NotNull<Blah*> nnblah = WrapNotNull(&blah); + NotNull<MyPtr<Blah>> nnmyblah = WrapNotNull(myblah); + (&blah)->blah(); // int* + myblah->blah(); // MyPtr<int> + nnblah->blah(); // NotNull<int*> + nnmyblah->blah(); // NotNull<MyPtr<int>> + + (&blah)->mX = 1; + CHECK((&blah)->mX == 1); + myblah->mX = 2; + CHECK(myblah->mX == 2); + nnblah->mX = 3; + CHECK(nnblah->mX == 3); + nnmyblah->mX = 4; + CHECK(nnmyblah->mX == 4); + + // '*' dereferencing (lvalues and rvalues) + *(&i4) = 7; // int* + CHECK(*(&i4) == 7); + *my4 = 6; // MyPtr<int> + CHECK(*my4 == 6); + *nni4 = 5; // NotNull<int*> + CHECK(*nni4 == 5); + *nnmy4 = 4; // NotNull<MyPtr<int>> + CHECK(*nnmy4 == 4); + + // Non-null arrays. + static const int N = 20; + int a[N]; + NotNull<int*> nna = WrapNotNull(a); + for (int i = 0; i < N; i++) { + nna[i] = i; + } + for (int i = 0; i < N; i++) { + nna[i] *= 2; + } + for (int i = 0; i < N; i++) { + CHECK(nna[i] == i * 2); + } +} + +void f_ref(NotNull<MyRefType*> aR) +{ + NotNull<RefPtr<MyRefType>> r = aR; +} + +void +TestNotNullWithRefPtr() +{ + // This MyRefType object will have a maximum refcount of 5. + NotNull<RefPtr<MyRefType>> r1 = WrapNotNull(new MyRefType(5)); + + // At this point the refcount is 1. + + NotNull<RefPtr<MyRefType>> r2 = r1; + + // At this point the refcount is 2. + + NotNull<MyRefType*> r3 = r2; + (void)r3; + + // At this point the refcount is still 2. + + RefPtr<MyRefType> r4 = r2; + mozilla::Unused << r4; + + // At this point the refcount is 3. + + RefPtr<MyRefType> r5 = r3.get(); + mozilla::Unused << r5; + + // At this point the refcount is 4. + + // No change to the refcount occurs because of the argument passing. Within + // f_ref() the refcount temporarily hits 5, due to the local RefPtr. + f_ref(r2); + + // At this point the refcount is 4. + + // At function's end all RefPtrs are destroyed and the refcount drops to 0 + // and the MyRefType is destroyed. +} + +int +main() +{ + TestNotNullWithMyPtr(); + TestNotNullWithRefPtr(); + + return 0; +} + diff --git a/mfbt/tests/TestPair.cpp b/mfbt/tests/TestPair.cpp new file mode 100644 index 000000000..f1fa9c554 --- /dev/null +++ b/mfbt/tests/TestPair.cpp @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Pair.h" +#include "mozilla/TypeTraits.h" + +using mozilla::IsSame; +using mozilla::MakePair; +using mozilla::Pair; + +// Sizes aren't part of the guaranteed Pair interface, but we want to verify our +// attempts at compactness through EBO are moderately functional, *somewhere*. +#define INSTANTIATE(T1, T2, name, size) \ + Pair<T1, T2> name##_1(T1(0), T2(0)); \ + static_assert(sizeof(name##_1.first()) > 0, \ + "first method should work on Pair<" #T1 ", " #T2 ">"); \ + static_assert(sizeof(name##_1.second()) > 0, \ + "second method should work on Pair<" #T1 ", " #T2 ">"); \ + static_assert(sizeof(name##_1) == (size), \ + "Pair<" #T1 ", " #T2 "> has an unexpected size"); \ + Pair<T2, T1> name##_2(T2(0), T1(0)); \ + static_assert(sizeof(name##_2.first()) > 0, \ + "first method should work on Pair<" #T2 ", " #T1 ">"); \ + static_assert(sizeof(name##_2.second()) > 0, \ + "second method should work on Pair<" #T2 ", " #T1 ">"); \ + static_assert(sizeof(name##_2) == (size), \ + "Pair<" #T2 ", " #T1 "> has an unexpected size"); + +INSTANTIATE(int, int, prim1, 2 * sizeof(int)); +INSTANTIATE(int, long, prim2, 2 * sizeof(long)); + +struct EmptyClass { explicit EmptyClass(int) {} }; +struct NonEmpty { char mC; explicit NonEmpty(int) {} }; + +INSTANTIATE(int, EmptyClass, both1, sizeof(int)); +INSTANTIATE(int, NonEmpty, both2, 2 * sizeof(int)); +INSTANTIATE(EmptyClass, NonEmpty, both3, 1); + +struct A { char dummy; explicit A(int) {} }; +struct B : A { explicit B(int aI) : A(aI) {} }; + +INSTANTIATE(A, A, class1, 2); +INSTANTIATE(A, B, class2, 2); +INSTANTIATE(A, EmptyClass, class3, 1); + +struct OtherEmpty : EmptyClass { explicit OtherEmpty(int aI) : EmptyClass(aI) {} }; + +// C++11 requires distinct objects of the same type, within the same "most +// derived object", to have different addresses. Pair allocates its elements as +// two bases, a base and a member, or two members. If the two elements have +// non-zero size or are unrelated, no big deal. But if they're both empty and +// related, something -- possibly both -- must be inflated. Exactly which are +// inflated depends which PairHelper specialization is used. We could +// potentially assert something about size for this case, but whatever we could +// assert would be very finicky. Plus it's two empty classes -- hardly likely. +// So don't bother trying to assert anything about this case. +//INSTANTIATE(EmptyClass, OtherEmpty, class4, ...something finicky...); + +int +main() +{ + A a(0); + B b(0); + const A constA(0); + const B constB(0); + + // Check that MakePair generates Pair objects of the correct types. + static_assert(IsSame<decltype(MakePair(A(0), B(0))), Pair<A, B>>::value, + "MakePair should strip rvalue references"); + static_assert(IsSame<decltype(MakePair(a, b)), Pair<A, B>>::value, + "MakePair should strip lvalue references"); + static_assert(IsSame<decltype(MakePair(constA, constB)), Pair<A, B>>::value, + "MakePair should strip CV-qualifiers"); + + // Check that copy assignment and move assignment work. + a = constA; + a = A(0); + + return 0; +} diff --git a/mfbt/tests/TestPoisonArea.cpp b/mfbt/tests/TestPoisonArea.cpp new file mode 100644 index 000000000..6f1b61ed3 --- /dev/null +++ b/mfbt/tests/TestPoisonArea.cpp @@ -0,0 +1,549 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/* Code in this file needs to be kept in sync with code in nsPresArena.cpp. + * + * We want to use a fixed address for frame poisoning so that it is readily + * identifiable in crash dumps. Whether such an address is available + * without any special setup depends on the system configuration. + * + * All current 64-bit CPUs (with the possible exception of PowerPC64) + * reserve the vast majority of the virtual address space for future + * hardware extensions; valid addresses must be below some break point + * between 2**48 and 2**54, depending on exactly which chip you have. Some + * chips (notably amd64) also allow the use of the *highest* 2**48 -- 2**54 + * addresses. Thus, if user space pointers are 64 bits wide, we can just + * use an address outside this range, and no more is required. To + * accommodate the chips that allow very high addresses to be valid, the + * value chosen is close to 2**63 (that is, in the middle of the space). + * + * In most cases, a purely 32-bit operating system must reserve some + * fraction of the address space for its own use. Contemporary 32-bit OSes + * tend to take the high gigabyte or so (0xC000_0000 on up). If we can + * prove that high addresses are reserved to the kernel, we can use an + * address in that region. Unfortunately, not all 32-bit OSes do this; + * OSX 10.4 might not, and it is unclear what mobile OSes are like + * (some 32-bit CPUs make it very easy for the kernel to exist in its own + * private address space). + * + * Furthermore, when a 32-bit user space process is running on a 64-bit + * kernel, the operating system has no need to reserve any of the space that + * the process can see, and generally does not do so. This is the scenario + * of greatest concern, since it covers all contemporary OSX iterations + * (10.5+) as well as Windows Vista and 7 on newer amd64 hardware. Linux on + * amd64 is generally run as a pure 64-bit environment, but its 32-bit + * compatibility mode also has this property. + * + * Thus, when user space pointers are 32 bits wide, we need to validate + * our chosen address, and possibly *make* it a good poison address by + * allocating a page around it and marking it inaccessible. The algorithm + * for this is: + * + * 1. Attempt to make the page surrounding the poison address a reserved, + * inaccessible memory region using OS primitives. On Windows, this is + * done with VirtualAlloc(MEM_RESERVE); on Unix, mmap(PROT_NONE). + * + * 2. If mmap/VirtualAlloc failed, there are two possible reasons: either + * the region is reserved to the kernel and no further action is + * required, or there is already usable memory in this area and we have + * to pick a different address. The tricky part is knowing which case + * we have, without attempting to access the region. On Windows, we + * rely on GetSystemInfo()'s reported upper and lower bounds of the + * application memory area. On Unix, there is nothing devoted to the + * purpose, but seeing if madvise() fails is close enough (it *might* + * disrupt someone else's use of the memory region, but not by as much + * as anything else available). + * + * Be aware of these gotchas: + * + * 1. We cannot use mmap() with MAP_FIXED. MAP_FIXED is defined to + * _replace_ any existing mapping in the region, if necessary to satisfy + * the request. Obviously, as we are blindly attempting to acquire a + * page at a constant address, we must not do this, lest we overwrite + * someone else's allocation. + * + * 2. For the same reason, we cannot blindly use mprotect() if mmap() fails. + * + * 3. madvise() may fail when applied to a 'magic' memory region provided as + * a kernel/user interface. Fortunately, the only such case I know about + * is the "vsyscall" area (not to be confused with the "vdso" area) for + * *64*-bit processes on Linux - and we don't even run this code for + * 64-bit processes. + * + * 4. VirtualQuery() does not produce any useful information if + * applied to kernel memory - in fact, it doesn't write its output + * at all. Thus, it is not used here. + */ + +#include "mozilla/IntegerPrintfMacros.h" + +// MAP_ANON(YMOUS) is not in any standard. Add defines as necessary. +#define _GNU_SOURCE 1 +#define _DARWIN_C_SOURCE 1 + +#include <stddef.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef _WIN32 +#include <windows.h> +#else +#include <sys/types.h> +#include <fcntl.h> +#include <signal.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <sys/mman.h> +#ifndef MAP_ANON +#ifdef MAP_ANONYMOUS +#define MAP_ANON MAP_ANONYMOUS +#else +#error "Don't know how to get anonymous memory" +#endif +#endif +#endif + +#define SIZxPTR ((int)(sizeof(uintptr_t)*2)) + +/* This program assumes that a whole number of return instructions fit into + * 32 bits, and that 32-bit alignment is sufficient for a branch destination. + * For architectures where this is not true, fiddling with RETURN_INSTR_TYPE + * can be enough. + */ + +#if defined __i386__ || defined __x86_64__ || \ + defined __i386 || defined __x86_64 || \ + defined _M_IX86 || defined _M_AMD64 +#define RETURN_INSTR 0xC3C3C3C3 /* ret; ret; ret; ret */ + +#elif defined __arm__ || defined _M_ARM +#define RETURN_INSTR 0xE12FFF1E /* bx lr */ + +// PPC has its own style of CPU-id #defines. There is no Windows for +// PPC as far as I know, so no _M_ variant. +#elif defined _ARCH_PPC || defined _ARCH_PWR || defined _ARCH_PWR2 +#define RETURN_INSTR 0x4E800020 /* blr */ + +#elif defined __sparc || defined __sparcv9 +#define RETURN_INSTR 0x81c3e008 /* retl */ + +#elif defined __alpha +#define RETURN_INSTR 0x6bfa8001 /* ret */ + +#elif defined __hppa +#define RETURN_INSTR 0xe840c002 /* bv,n r0(rp) */ + +#elif defined __mips +#define RETURN_INSTR 0x03e00008 /* jr ra */ + +#ifdef __MIPSEL +/* On mipsel, jr ra needs to be followed by a nop. + 0x03e00008 as a 64 bits integer just does that */ +#define RETURN_INSTR_TYPE uint64_t +#endif + +#elif defined __s390__ +#define RETURN_INSTR 0x07fe0000 /* br %r14 */ + +#elif defined __aarch64__ +#define RETURN_INSTR 0xd65f03c0 /* ret */ + +#elif defined __ia64 +struct ia64_instr { uint32_t mI[4]; }; +static const ia64_instr _return_instr = + {{ 0x00000011, 0x00000001, 0x80000200, 0x00840008 }}; /* br.ret.sptk.many b0 */ + +#define RETURN_INSTR _return_instr +#define RETURN_INSTR_TYPE ia64_instr + +#else +#error "Need return instruction for this architecture" +#endif + +#ifndef RETURN_INSTR_TYPE +#define RETURN_INSTR_TYPE uint32_t +#endif + +// Miscellaneous Windows/Unix portability gumph + +#ifdef _WIN32 +// Uses of this function deliberately leak the string. +static LPSTR +StrW32Error(DWORD aErrcode) +{ + LPSTR errmsg; + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, aErrcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&errmsg, 0, nullptr); + + // FormatMessage puts an unwanted newline at the end of the string + size_t n = strlen(errmsg)-1; + while (errmsg[n] == '\r' || errmsg[n] == '\n') { + n--; + } + errmsg[n+1] = '\0'; + return errmsg; +} +#define LastErrMsg() (StrW32Error(GetLastError())) + +// Because we use VirtualAlloc in MEM_RESERVE mode, the "page size" we want +// is the allocation granularity. +static SYSTEM_INFO sInfo_; + +static inline uint32_t +PageSize() +{ + return sInfo_.dwAllocationGranularity; +} + +static void* +ReserveRegion(uintptr_t aRequest, bool aAccessible) +{ + return VirtualAlloc((void*)aRequest, PageSize(), + aAccessible ? MEM_RESERVE|MEM_COMMIT : MEM_RESERVE, + aAccessible ? PAGE_EXECUTE_READWRITE : PAGE_NOACCESS); +} + +static void +ReleaseRegion(void* aPage) +{ + VirtualFree(aPage, PageSize(), MEM_RELEASE); +} + +static bool +ProbeRegion(uintptr_t aPage) +{ + return aPage >= (uintptr_t)sInfo_.lpMaximumApplicationAddress && + aPage + PageSize() >= (uintptr_t)sInfo_.lpMaximumApplicationAddress; +} + +static bool +MakeRegionExecutable(void*) +{ + return false; +} + +#undef MAP_FAILED +#define MAP_FAILED 0 + +#else // Unix + +#define LastErrMsg() (strerror(errno)) + +static unsigned long gUnixPageSize; + +static inline unsigned long +PageSize() +{ + return gUnixPageSize; +} + +static void* +ReserveRegion(uintptr_t aRequest, bool aAccessible) +{ + return mmap(reinterpret_cast<void*>(aRequest), PageSize(), + aAccessible ? PROT_READ|PROT_WRITE : PROT_NONE, + MAP_PRIVATE|MAP_ANON, -1, 0); +} + +static void +ReleaseRegion(void* aPage) +{ + munmap(aPage, PageSize()); +} + +static bool +ProbeRegion(uintptr_t aPage) +{ + return !!madvise(reinterpret_cast<void*>(aPage), PageSize(), MADV_NORMAL); +} + +static int +MakeRegionExecutable(void* aPage) +{ + return mprotect((caddr_t)aPage, PageSize(), PROT_READ|PROT_WRITE|PROT_EXEC); +} + +#endif + +static uintptr_t +ReservePoisonArea() +{ + if (sizeof(uintptr_t) == 8) { + // Use the hardware-inaccessible region. + // We have to avoid 64-bit constants and shifts by 32 bits, since this + // code is compiled in 32-bit mode, although it is never executed there. + uintptr_t result = (((uintptr_t(0x7FFFFFFFu) << 31) << 1 | + uintptr_t(0xF0DEAFFFu)) & + ~uintptr_t(PageSize()-1)); + printf("INFO | poison area assumed at 0x%.*" PRIxPTR "\n", SIZxPTR, result); + return result; + } + + // First see if we can allocate the preferred poison address from the OS. + uintptr_t candidate = (0xF0DEAFFF & ~(PageSize() - 1)); + void* result = ReserveRegion(candidate, false); + if (result == reinterpret_cast<void*>(candidate)) { + // success - inaccessible page allocated + printf("INFO | poison area allocated at 0x%.*" PRIxPTR + " (preferred addr)\n", SIZxPTR, reinterpret_cast<uintptr_t>(result)); + return candidate; + } + + // That didn't work, so see if the preferred address is within a range + // of permanently inacessible memory. + if (ProbeRegion(candidate)) { + // success - selected page cannot be usable memory + if (result != MAP_FAILED) { + ReleaseRegion(result); + } + printf("INFO | poison area assumed at 0x%.*" PRIxPTR + " (preferred addr)\n", SIZxPTR, candidate); + return candidate; + } + + // The preferred address is already in use. Did the OS give us a + // consolation prize? + if (result != MAP_FAILED) { + uintptr_t ures = reinterpret_cast<uintptr_t>(result); + printf("INFO | poison area allocated at 0x%.*" PRIxPTR + " (consolation prize)\n", SIZxPTR, ures); + return ures; + } + + // It didn't, so try to allocate again, without any constraint on + // the address. + result = ReserveRegion(0, false); + if (result != MAP_FAILED) { + uintptr_t ures = reinterpret_cast<uintptr_t>(result); + printf("INFO | poison area allocated at 0x%.*" PRIxPTR + " (fallback)\n", SIZxPTR, ures); + return ures; + } + + printf("ERROR | no usable poison area found\n"); + return 0; +} + +/* The "positive control" area confirms that we can allocate a page with the + * proper characteristics. + */ +static uintptr_t +ReservePositiveControl() +{ + + void* result = ReserveRegion(0, false); + if (result == MAP_FAILED) { + printf("ERROR | allocating positive control | %s\n", LastErrMsg()); + return 0; + } + printf("INFO | positive control allocated at 0x%.*" PRIxPTR "\n", + SIZxPTR, (uintptr_t)result); + return (uintptr_t)result; +} + +/* The "negative control" area confirms that our probe logic does detect a + * page that is readable, writable, or executable. + */ +static uintptr_t +ReserveNegativeControl() +{ + void* result = ReserveRegion(0, true); + if (result == MAP_FAILED) { + printf("ERROR | allocating negative control | %s\n", LastErrMsg()); + return 0; + } + + // Fill the page with return instructions. + RETURN_INSTR_TYPE* p = reinterpret_cast<RETURN_INSTR_TYPE*>(result); + RETURN_INSTR_TYPE* limit = + reinterpret_cast<RETURN_INSTR_TYPE*>( + reinterpret_cast<char*>(result) + PageSize()); + while (p < limit) { + *p++ = RETURN_INSTR; + } + + // Now mark it executable as well as readable and writable. + // (mmap(PROT_EXEC) may fail when applied to anonymous memory.) + + if (MakeRegionExecutable(result)) { + printf("ERROR | making negative control executable | %s\n", LastErrMsg()); + return 0; + } + + printf("INFO | negative control allocated at 0x%.*" PRIxPTR "\n", + SIZxPTR, (uintptr_t)result); + return (uintptr_t)result; +} + +static void +JumpTo(uintptr_t aOpaddr) +{ +#ifdef __ia64 + struct func_call + { + uintptr_t mFunc; + uintptr_t mGp; + } call = { aOpaddr, }; + ((void (*)())&call)(); +#else + ((void (*)())aOpaddr)(); +#endif +} + +#ifdef _WIN32 +static BOOL +IsBadExecPtr(uintptr_t aPtr) +{ + BOOL ret = false; + +#ifdef _MSC_VER + __try { + JumpTo(aPtr); + } __except (EXCEPTION_EXECUTE_HANDLER) { + ret = true; + } +#else + printf("INFO | exec test not supported on MinGW build\n"); + // We do our best + ret = IsBadReadPtr((const void*)aPtr, 1); +#endif + return ret; +} +#endif + +/* Test each page. */ +static bool +TestPage(const char* aPageLabel, uintptr_t aPageAddr, int aShouldSucceed) +{ + const char* oplabel; + uintptr_t opaddr; + + bool failed = false; + for (unsigned int test = 0; test < 3; test++) { + switch (test) { + // The execute test must be done before the write test, because the + // write test will clobber memory at the target address. + case 0: oplabel = "reading"; opaddr = aPageAddr + PageSize()/2 - 1; break; + case 1: oplabel = "executing"; opaddr = aPageAddr + PageSize()/2; break; + case 2: oplabel = "writing"; opaddr = aPageAddr + PageSize()/2 - 1; break; + default: abort(); + } + +#ifdef _WIN32 + BOOL badptr; + + switch (test) { + case 0: badptr = IsBadReadPtr((const void*)opaddr, 1); break; + case 1: badptr = IsBadExecPtr(opaddr); break; + case 2: badptr = IsBadWritePtr((void*)opaddr, 1); break; + default: abort(); + } + + if (badptr) { + if (aShouldSucceed) { + printf("TEST-UNEXPECTED-FAIL | %s %s\n", oplabel, aPageLabel); + failed = true; + } else { + printf("TEST-PASS | %s %s\n", oplabel, aPageLabel); + } + } else { + // if control reaches this point the probe succeeded + if (aShouldSucceed) { + printf("TEST-PASS | %s %s\n", oplabel, aPageLabel); + } else { + printf("TEST-UNEXPECTED-FAIL | %s %s\n", oplabel, aPageLabel); + failed = true; + } + } +#else + pid_t pid = fork(); + if (pid == -1) { + printf("ERROR | %s %s | fork=%s\n", oplabel, aPageLabel, + LastErrMsg()); + exit(2); + } else if (pid == 0) { + volatile unsigned char scratch; + switch (test) { + case 0: scratch = *(volatile unsigned char*)opaddr; break; + case 1: JumpTo(opaddr); break; + case 2: *(volatile unsigned char*)opaddr = 0; break; + default: abort(); + } + (void)scratch; + _exit(0); + } else { + int status; + if (waitpid(pid, &status, 0) != pid) { + printf("ERROR | %s %s | wait=%s\n", oplabel, aPageLabel, + LastErrMsg()); + exit(2); + } + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + if (aShouldSucceed) { + printf("TEST-PASS | %s %s\n", oplabel, aPageLabel); + } else { + printf("TEST-UNEXPECTED-FAIL | %s %s | unexpected successful exit\n", + oplabel, aPageLabel); + failed = true; + } + } else if (WIFEXITED(status)) { + printf("ERROR | %s %s | unexpected exit code %d\n", + oplabel, aPageLabel, WEXITSTATUS(status)); + exit(2); + } else if (WIFSIGNALED(status)) { + if (aShouldSucceed) { + printf("TEST-UNEXPECTED-FAIL | %s %s | unexpected signal %d\n", + oplabel, aPageLabel, WTERMSIG(status)); + failed = true; + } else { + printf("TEST-PASS | %s %s | signal %d (as expected)\n", + oplabel, aPageLabel, WTERMSIG(status)); + } + } else { + printf("ERROR | %s %s | unexpected exit status %d\n", + oplabel, aPageLabel, status); + exit(2); + } + } +#endif + } + return failed; +} + +int +main() +{ +#ifdef _WIN32 + GetSystemInfo(&sInfo_); +#else + gUnixPageSize = sysconf(_SC_PAGESIZE); +#endif + + uintptr_t ncontrol = ReserveNegativeControl(); + uintptr_t pcontrol = ReservePositiveControl(); + uintptr_t poison = ReservePoisonArea(); + + if (!ncontrol || !pcontrol || !poison) { + return 2; + } + + bool failed = false; + failed |= TestPage("negative control", ncontrol, 1); + failed |= TestPage("positive control", pcontrol, 0); + failed |= TestPage("poison area", poison, 0); + + return failed ? 1 : 0; +} diff --git a/mfbt/tests/TestRange.cpp b/mfbt/tests/TestRange.cpp new file mode 100644 index 000000000..419c1cc26 --- /dev/null +++ b/mfbt/tests/TestRange.cpp @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Range.h" +#include "mozilla/TypeTraits.h" + +using mozilla::IsConvertible; +using mozilla::Range; + +static_assert(IsConvertible<Range<int>, Range<const int>>::value, + "Range should convert into const"); +static_assert(!IsConvertible<Range<const int>, Range<int>>::value, + "Range should not drop const in conversion"); + +// We need a proper program so we have someplace to hang the static_asserts. +int +main() +{ + return 0; +} diff --git a/mfbt/tests/TestRefPtr.cpp b/mfbt/tests/TestRefPtr.cpp new file mode 100644 index 000000000..4d9e712bd --- /dev/null +++ b/mfbt/tests/TestRefPtr.cpp @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/RefPtr.h" +#include "mozilla/RefCounted.h" + +using mozilla::RefCounted; + +class Foo : public RefCounted<Foo> +{ +public: + MOZ_DECLARE_REFCOUNTED_TYPENAME(Foo) + + Foo() : mDead(false) {} + + static int sNumDestroyed; + + ~Foo() + { + MOZ_ASSERT(!mDead); + mDead = true; + sNumDestroyed++; + } + +private: + bool mDead; +}; +int Foo::sNumDestroyed; + +struct Bar : public Foo {}; + +already_AddRefed<Foo> +NewFoo() +{ + RefPtr<Foo> f(new Foo()); + return f.forget(); +} + +already_AddRefed<Foo> +NewBar() +{ + RefPtr<Bar> bar = new Bar(); + return bar.forget(); +} + +void +GetNewFoo(Foo** aFoo) +{ + *aFoo = new Bar(); + // Kids, don't try this at home + (*aFoo)->AddRef(); +} + +void +GetNewFoo(RefPtr<Foo>* aFoo) +{ + *aFoo = new Bar(); +} + +already_AddRefed<Foo> +GetNullFoo() +{ + return 0; +} + +int +main() +{ + MOZ_RELEASE_ASSERT(0 == Foo::sNumDestroyed); + { + RefPtr<Foo> f = new Foo(); + MOZ_RELEASE_ASSERT(f->refCount() == 1); + } + MOZ_RELEASE_ASSERT(1 == Foo::sNumDestroyed); + + { + RefPtr<Foo> f1 = NewFoo(); + RefPtr<Foo> f2(NewFoo()); + MOZ_RELEASE_ASSERT(1 == Foo::sNumDestroyed); + } + MOZ_RELEASE_ASSERT(3 == Foo::sNumDestroyed); + + { + RefPtr<Foo> b = NewBar(); + MOZ_RELEASE_ASSERT(3 == Foo::sNumDestroyed); + } + MOZ_RELEASE_ASSERT(4 == Foo::sNumDestroyed); + + { + RefPtr<Foo> f1; + { + f1 = new Foo(); + RefPtr<Foo> f2(f1); + RefPtr<Foo> f3 = f2; + MOZ_RELEASE_ASSERT(4 == Foo::sNumDestroyed); + } + MOZ_RELEASE_ASSERT(4 == Foo::sNumDestroyed); + } + MOZ_RELEASE_ASSERT(5 == Foo::sNumDestroyed); + + { + { + RefPtr<Foo> f = new Foo(); + RefPtr<Foo> g = f.forget(); + } + MOZ_RELEASE_ASSERT(6 == Foo::sNumDestroyed); + } + + { + RefPtr<Foo> f = new Foo(); + GetNewFoo(getter_AddRefs(f)); + MOZ_RELEASE_ASSERT(7 == Foo::sNumDestroyed); + } + MOZ_RELEASE_ASSERT(8 == Foo::sNumDestroyed); + + { + RefPtr<Foo> f = new Foo(); + GetNewFoo(&f); + MOZ_RELEASE_ASSERT(9 == Foo::sNumDestroyed); + } + MOZ_RELEASE_ASSERT(10 == Foo::sNumDestroyed); + + { + RefPtr<Foo> f1 = new Bar(); + } + MOZ_RELEASE_ASSERT(11 == Foo::sNumDestroyed); + + { + RefPtr<Foo> f = GetNullFoo(); + MOZ_RELEASE_ASSERT(11 == Foo::sNumDestroyed); + } + MOZ_RELEASE_ASSERT(11 == Foo::sNumDestroyed); + + return 0; +} + diff --git a/mfbt/tests/TestRollingMean.cpp b/mfbt/tests/TestRollingMean.cpp new file mode 100644 index 000000000..bec77786f --- /dev/null +++ b/mfbt/tests/TestRollingMean.cpp @@ -0,0 +1,129 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/RollingMean.h" + +using mozilla::RollingMean; + +class MyClass +{ +public: + uint32_t mValue; + + explicit MyClass(uint32_t aValue = 0) : mValue(aValue) { } + + bool operator==(const MyClass& aOther) const + { + return mValue == aOther.mValue; + } + + MyClass operator+(const MyClass& aOther) const + { + return MyClass(mValue + aOther.mValue); + } + + MyClass operator-(const MyClass& aOther) const + { + return MyClass(mValue - aOther.mValue); + } + + MyClass operator/(uint32_t aDiv) const + { + return MyClass(mValue / aDiv); + } +}; + +class RollingMeanSuite +{ +public: + RollingMeanSuite() { } + + void runTests() { + testZero(); + testClear(); + testRolling(); + testClass(); + testMove(); + } + +private: + void testZero() + { + RollingMean<uint32_t, uint64_t> mean(3); + MOZ_RELEASE_ASSERT(mean.empty()); + } + + void testClear() + { + RollingMean<uint32_t, uint64_t> mean(3); + + mean.insert(4); + MOZ_RELEASE_ASSERT(mean.mean() == 4); + + mean.clear(); + MOZ_RELEASE_ASSERT(mean.empty()); + + mean.insert(3); + MOZ_RELEASE_ASSERT(mean.mean() == 3); + } + + void testRolling() + { + RollingMean<uint32_t, uint64_t> mean(3); + + mean.insert(10); + MOZ_RELEASE_ASSERT(mean.mean() == 10); + + mean.insert(20); + MOZ_RELEASE_ASSERT(mean.mean() == 15); + + mean.insert(35); + MOZ_RELEASE_ASSERT(mean.mean() == 21); + + mean.insert(5); + MOZ_RELEASE_ASSERT(mean.mean() == 20); + + mean.insert(10); + MOZ_RELEASE_ASSERT(mean.mean() == 16); + } + + void testClass() + { + RollingMean<MyClass, MyClass> mean(3); + + mean.insert(MyClass(4)); + MOZ_RELEASE_ASSERT(mean.mean() == MyClass(4)); + + mean.clear(); + MOZ_RELEASE_ASSERT(mean.empty()); + } + + void testMove() + { + RollingMean<uint32_t, uint64_t> mean(3); + mean = RollingMean<uint32_t, uint64_t>(4); + MOZ_RELEASE_ASSERT(mean.maxValues() == 4); + + mean.insert(10); + MOZ_RELEASE_ASSERT(mean.mean() == 10); + + mean = RollingMean<uint32_t, uint64_t>(3); + mean.insert(30); + mean.insert(40); + mean.insert(50); + mean.insert(60); + MOZ_RELEASE_ASSERT(mean.mean() == 50); + } +}; + +int +main() +{ + RollingMeanSuite suite; + suite.runTests(); + return 0; +} diff --git a/mfbt/tests/TestSHA1.cpp b/mfbt/tests/TestSHA1.cpp new file mode 100644 index 000000000..fd64df1ca --- /dev/null +++ b/mfbt/tests/TestSHA1.cpp @@ -0,0 +1,208 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/SHA1.h" + +using mozilla::SHA1Sum; + +static unsigned int gTestV[1024] = { + 0x048edc1a, 0x4345588c, 0x0ef03cbf, 0x1d6438f5, 0x094e0a1e, 0x68535f60, + 0x14e8c927, 0x60190043, 0x5d640ab7, 0x73dc7c62, 0x364223f9, 0x47320292, + 0x3924cae0, 0x5f6b26d3, 0x5efa04ef, 0x7aab361e, 0x2773b1aa, 0x1631b07d, + 0x385b5dd1, 0x26c809b0, 0x28ad3a9f, 0x0315292a, 0x1a544e67, 0x1e79dcb9, + 0x787683e8, 0x3a591c75, 0x1dd338c7, 0x01c539e5, 0x1c15b23e, 0x0697c25c, + 0x4df5fd45, 0x672aa324, 0x39f74e6e, 0x269cdd5f, 0x087b6fce, 0x293509db, + 0x0aef54a9, 0x210c4cc5, 0x29d6dc4a, 0x16320825, 0x3ab7b181, 0x56d6fd25, + 0x6837fda2, 0x3e7994c2, 0x37f77529, 0x48c85472, 0x424fd84d, 0x00aba7fa, + 0x6d8475de, 0x354634a7, 0x0c73bb49, 0x0a335de6, 0x0a9ea542, 0x5ffb31f1, + 0x00a6a3f2, 0x76b14a03, 0x1e436a37, 0x173b766a, 0x33cf3ca0, 0x34eb0f1a, + 0x4ca073ee, 0x27591fe6, 0x5eaf3356, 0x10c24493, 0x1bad88b6, 0x676f2309, + 0x7f5e2d91, 0x74bd4c83, 0x66549b43, 0x52ffdf24, 0x2dfa0a83, 0x7c3e1cbf, + 0x1edf87fc, 0x1f6fa930, 0x7c29bc74, 0x374bcd2f, 0x5b43de94, 0x0d09a3a6, + 0x7437ecb0, 0x635117f8, 0x2aa78f65, 0x2c788958, 0x098cb9f3, 0x13ed5b3f, + 0x41b7c7ba, 0x696b2d88, 0x42e20d63, 0x69585b1d, 0x4a9b027c, 0x0c761cba, + 0x563bdbc4, 0x3bde2f5b, 0x0bab9730, 0x7740104c, 0x11641702, 0x26f03c32, + 0x011a87c6, 0x2c5e4e6c, 0x46c34200, 0x6a167e84, 0x34205728, 0x0e8a6152, + 0x0014604b, 0x6793bacd, 0x442bca9c, 0x6f2018ce, 0x4313e07e, 0x77f2c69c, + 0x62621441, 0x47bf6358, 0x59c45e04, 0x16ba3426, 0x6ac0c19d, 0x20218c6b, + 0x510b4ddc, 0x585f6c9d, 0x1ed02b0c, 0x366bf0a9, 0x131c7f59, 0x0ebcd320, + 0x00ca858a, 0x5efbcb77, 0x2a7a1859, 0x64bb5afd, 0x76258886, 0x6505c895, + 0x602cfa32, 0x17040942, 0x783df744, 0x3838e0ae, 0x6a021e39, 0x4c8c9c5a, + 0x4a5e96b6, 0x10f4477d, 0x247fda4f, 0x4c390400, 0x0cbe048c, 0x7b547d26, + 0x1e2e6897, 0x4ba7e01b, 0x5cfea1bb, 0x39a2d199, 0x45aee64a, 0x12615500, + 0x0151615f, 0x1a9f5d33, 0x4542ed44, 0x101357eb, 0x35a16b1f, 0x3420b3e1, + 0x6442bac7, 0x1c0f2a8c, 0x68d642f1, 0x45744fc4, 0x048e60cb, 0x5f217f44, + 0x6cc7d151, 0x27f41984, 0x2d01eb09, 0x2bb15aea, 0x6dda49f8, 0x590dd6bc, + 0x280cc20b, 0x7e2592b5, 0x043642f0, 0x292b5d29, 0x2e0a9b69, 0x41162471, + 0x1e55db6b, 0x648b96fe, 0x05f8f9d1, 0x4a9d4cbb, 0x38517039, 0x2b0f8917, + 0x4d1e67bb, 0x713e0974, 0x64fdf214, 0x11223963, 0x2bd09d24, 0x19924092, + 0x4b4a70f0, 0x1ece6b03, 0x1780c9c1, 0x09b4c3ac, 0x58ac7e73, 0x5c9a4747, + 0x321f943b, 0x41167667, 0x3a19cf8c, 0x53f4144d, 0x03a498de, 0x6fb4b742, + 0x54d793cb, 0x7ee164e2, 0x501af74c, 0x43201e7f, 0x0ad581be, 0x497f046a, + 0x3b1d2a9f, 0x53b88eb0, 0x2c3a26c5, 0x5ae970ba, 0x7d7ee4ff, 0x471366c5, + 0x46119703, 0x3bfc2e58, 0x456d6c4f, 0x4b6bb181, 0x45d7c872, 0x0d023221, + 0x021176d1, 0x4195ad44, 0x4621ec90, 0x3ae68279, 0x57952f71, 0x1796080c, + 0x228077bb, 0x5e2b7fee, 0x3d71dd88, 0x4a651849, 0x7f1c8081, 0x04c333fc, + 0x1f99bff6, 0x11b7754c, 0x740be324, 0x069bf2e2, 0x0802f3e0, 0x371cf30e, + 0x1d44dda5, 0x6033b9e5, 0x5639a9b0, 0x6526bfff, 0x14d7d9b7, 0x4182b6a7, + 0x01a5fa76, 0x7aa5e581, 0x762465e6, 0x386b3a2e, 0x495a3ab0, 0x04421b2e, + 0x46e04591, 0x472af458, 0x6a007dd3, 0x2e8be484, 0x18660abe, 0x7969af82, + 0x5a242a83, 0x581b5f72, 0x5f0eff6d, 0x38aea98c, 0x2acb5853, 0x6d650b35, + 0x10b750d7, 0x18fdcd14, 0x09b4816c, 0x3ceef016, 0x6957153c, 0x27cf39fb, + 0x60e3495d, 0x381e1da6, 0x4b5be02d, 0x14b6f309, 0x6380c589, 0x1a31f436, + 0x4b5e50c1, 0x493ac048, 0x314baad1, 0x71e24ab7, 0x718af49c, 0x022f4658, + 0x1a419d5b, 0x1854610d, 0x2ec4e99a, 0x7096ce50, 0x5467ba00, 0x404aab4c, + 0x1a5ab015, 0x217580f7, 0x2d50071e, 0x71a9f437, 0x27f758b5, 0x11cd8b3f, + 0x63b089c9, 0x53c860c1, 0x2fa6b7d7, 0x61e54771, 0x5c0ba6b9, 0x3138f796, + 0x5c7359cd, 0x4c2c5654, 0x549d581c, 0x3129ebf7, 0x4958a248, 0x1a460541, + 0x68e64964, 0x597c0609, 0x57afcbab, 0x2f1c6479, 0x57a0ad5c, 0x5936938f, + 0x536a5cbe, 0x29aacf0b, 0x43eca70d, 0x6e7a3e4e, 0x563c1e3b, 0x32f23909, + 0x12faa42d, 0x28b0bbde, 0x797e2842, 0x1b827bdf, 0x0df96a6e, 0x542ef7f4, + 0x6226d368, 0x01cb4258, 0x77bcba08, 0x7e6dc041, 0x0571eda3, 0x0fdf5065, + 0x5c9b9f7a, 0x2b496dd6, 0x02d3b40b, 0x3a5752db, 0x4843a293, 0x6fdc9c3f, + 0x42963996, 0x39c9e4eb, 0x01db58ad, 0x7e79381c, 0x5bb207bb, 0x2df5de51, + 0x1549ec82, 0x64f01e70, 0x536eb0d0, 0x10fa6e03, 0x5b7f9a20, 0x2d8b625d, + 0x397410c7, 0x7778284e, 0x1ab75170, 0x254f304e, 0x395ba877, 0x0c2e2815, + 0x5c723dec, 0x63b91327, 0x7c5954b5, 0x67dd69a3, 0x21d220c7, 0x5a287fcd, + 0x0d0b9c59, 0x22444c9f, 0x6305cb43, 0x12f717cc, 0x77c11945, 0x0e79bda8, + 0x6e014391, 0x441d0179, 0x5e17dd2f, 0x53e57a5c, 0x692f4b9a, 0x76c1e94b, + 0x5a872d81, 0x044f7e7e, 0x0970844f, 0x25e34e73, 0x57865d3c, 0x640771d2, + 0x12d410ed, 0x1424e079, 0x3e1c7fd7, 0x0e89295a, 0x48dcf262, 0x55a29550, + 0x0fd4d360, 0x7494d449, 0x41e6f260, 0x2230d4e7, 0x5ad1cd49, 0x7f8dd428, + 0x7722b48a, 0x7a14848d, 0x2a83335a, 0x548c0d9b, 0x24f5d43b, 0x33a417cb, + 0x3061e078, 0x1a1bc935, 0x5aedb5df, 0x6755f3e4, 0x795e4cdb, 0x64dfcd1c, + 0x6d5164fc, 0x34a3df0e, 0x2cc92142, 0x2569127d, 0x130f3d86, 0x43617cc2, + 0x25eaf1fa, 0x044ae792, 0x4b47ee17, 0x6879ea87, 0x7eb455fa, 0x54481e19, + 0x13bba2f0, 0x6da3fe79, 0x19c306ff, 0x42591e38, 0x2b0e205d, 0x60bd48bc, + 0x550aa0ce, 0x2296a6ef, 0x551eb052, 0x76df1b8e, 0x242a2d22, 0x0ada0b06, + 0x58b661ec, 0x490bec94, 0x20bd7c59, 0x760de8c3, 0x7a048ee8, 0x44ba6dcd, + 0x3816abd9, 0x47e8527e, 0x2194a188, 0x6967a480, 0x7f7e2083, 0x0ec455f3, + 0x78198eab, 0x3d710773, 0x05969198, 0x76ffcffe, 0x54be4797, 0x11105781, + 0x3a851719, 0x516284b8, 0x4295de1c, 0x3905be43, 0x6d4e7d6a, 0x0877796d, + 0x0b9e986a, 0x5e2b853f, 0x7e6c79cd, 0x4a44a54c, 0x1e28b9a2, 0x5b1e408e, + 0x6a1c8eac, 0x62a87929, 0x4f075dac, 0x5c030e8c, 0x3df73ce9, 0x321c3c69, + 0x2325cc45, 0x4eaf0759, 0x486a31fb, 0x12d04b94, 0x714e15d5, 0x420d1910, + 0x092dc45b, 0x0119beac, 0x68b2bfdb, 0x74863a17, 0x3c7ab8e5, 0x035bc2df, + 0x4e7a7965, 0x017f58d6, 0x6414074e, 0x3a1e64ae, 0x2d6725d8, 0x0f22f82a, + 0x0a0affa0, 0x4159f31e, 0x4002cb9d, 0x234e393f, 0x6028169f, 0x3b804078, + 0x0c16e2e1, 0x0e198020, 0x24b13c40, 0x1ceb2143, 0x38dd4246, 0x6f483590, + 0x69b20a6e, 0x105580b1, 0x5d60f184, 0x065d18eb, 0x09a28739, 0x70345728, + 0x595a5934, 0x14a78a43, 0x449f05c7, 0x6556fcfc, 0x260bc0b2, 0x3afb600e, + 0x1f47bb91, 0x145c14b6, 0x541832fe, 0x54f10f23, 0x3013650e, 0x6c0d32ba, + 0x4f202c8d, 0x66bcc661, 0x6131dc7f, 0x04828b25, 0x1737565d, 0x520e967f, + 0x16cf0438, 0x6f2bc19e, 0x553c3dda, 0x356906b0, 0x333916d5, 0x2887c195, + 0x11e7440b, 0x6354f182, 0x06b2f977, 0x6d2c9a5c, 0x2d02bfb7, 0x74fafcf6, + 0x2b955161, 0x74035c38, 0x6e9bc991, 0x09a3a5b9, 0x460f416a, 0x11afabfc, + 0x66e32d10, 0x4a56ac6e, 0x6448afa8, 0x680b0044, 0x05d0e296, 0x49569eac, + 0x0adb563b, 0x4a9da168, 0x4f857004, 0x0f234600, 0x6db386ec, 0x280b94bf, + 0x7cd258a5, 0x6165fd88, 0x3bf2aac9, 0x2cb47c44, 0x2381c2a4, 0x4fe42552, + 0x21d4c81e, 0x24baa9af, 0x365231cb, 0x11b7fc81, 0x419748fb, 0x38ff637e, + 0x065f3365, 0x21f1aba8, 0x2df41ace, 0x5cec1d95, 0x22c078a8, 0x7bb894fc, + 0x2d66fc53, 0x7ed82ccc, 0x4485c9d7, 0x1af210fc, 0x5d2faa09, 0x3b33412e, + 0x79d12ea8, 0x7bb8103b, 0x5cea1a7b, 0x2779db45, 0x1250ed5b, 0x0c4d8964, + 0x6c18e9f5, 0x501ddc60, 0x3de43ae4, 0x6c0e8577, 0x0adfb426, 0x7ec718f5, + 0x1991f387, 0x101ccb9c, 0x632360b4, 0x7d52ce4d, 0x0b58c91c, 0x1fa59d53, + 0x0b0b48b0, 0x297315d0, 0x7f3132ff, 0x323b85d1, 0x2f852141, 0x23e84bdc, + 0x3732cb25, 0x1274eb57, 0x21a882c3, 0x095288a9, 0x2120e253, 0x617799ce, + 0x5e4926b3, 0x52575363, 0x696722e0, 0x509c9117, 0x3b60f14f, 0x423310fa, + 0x4e694e80, 0x000a647e, 0x453e283a, 0x3f1d21ef, 0x527c91f0, 0x7ac2e88a, + 0x1ba3b840, 0x1c3f253a, 0x04c40280, 0x437dc361, 0x7247859c, 0x61e5b34c, + 0x20746a53, 0x58cfc2df, 0x79edf48e, 0x5b48e723, 0x7b08baac, 0x1d1035ea, + 0x023fc918, 0x2de0427c, 0x71540904, 0x4030e8f5, 0x2b0961f6, 0x4ec98ef0, + 0x781076ee, 0x0dac959b, 0x16f66214, 0x273411e5, 0x02334297, 0x3b568cd1, + 0x7cf4e8c0, 0x0f4c2c91, 0x2d8dd28e, 0x4a7b3fb0, 0x237969ae, 0x363d6cb6, + 0x75fee60a, 0x5825f4df, 0x29f79f9d, 0x22de4f33, 0x2309590e, 0x1977c2bd, + 0x67f7bebe, 0x452b8330, 0x5dc70832, 0x5cddbea4, 0x59091e0b, 0x4d287830, + 0x2bbc2ce6, 0x420ee023, 0x02d6e086, 0x228a7a14, 0x48207207, 0x1d5ccc5a, + 0x37d32cdc, 0x50dc6508, 0x0b795304, 0x5b9fd543, 0x2a3f2925, 0x72e71606, + 0x0dc8ba42, 0x3279a910, 0x6bd2c2e2, 0x775065d8, 0x547c59a6, 0x4b5374cf, + 0x0c45cd18, 0x532096d6, 0x351c9bd1, 0x107fdce0, 0x3ae69075, 0x5dddd5de, + 0x3bb0ba8b, 0x0b1a0019, 0x6c226525, 0x109e9002, 0x312191be, 0x16fa3de8, + 0x4a5197aa, 0x0931b2d2, 0x79ee6e1b, 0x657a142b, 0x6ab74d38, 0x77440cff, + 0x11e37956, 0x5c335799, 0x269d3be3, 0x18923cfd, 0x4dd71b00, 0x77c58014, + 0x07145324, 0x1678546a, 0x5dfd4f6a, 0x207f4e13, 0x6b0a98c0, 0x015bc2cf, + 0x1636d8fe, 0x7bc5f038, 0x183a0661, 0x573ec5f3, 0x54cf2255, 0x2fcc905c, + 0x71bb70b9, 0x2b122a89, 0x59f86e5b, 0x5528273d, 0x464cf857, 0x27efdeec, + 0x1d0bcfcc, 0x64d7837f, 0x1e7a659a, 0x02aa611c, 0x53969ad5, 0x0e83f59f, + 0x50a6d11b, 0x79513c59, 0x0e5c3c98, 0x2ed7bbcf, 0x117de9d9, 0x375ec696, + 0x19c830aa, 0x66950511, 0x2b6dbbaa, 0x5ca18c9b, 0x0a487514, 0x6f44a887, + 0x6921bc6e, 0x3ef8130b, 0x26f6cde3, 0x686d7605, 0x6583553a, 0x29bcf7cc, + 0x55d42201, 0x1c93497c, 0x64c53231, 0x32088f6e, 0x381c5770, 0x617574d8, + 0x09757952, 0x1a616eb0, 0x1140e8aa, 0x0ff66ffb, 0x32039001, 0x5a455e7c, + 0x0027b906, 0x21cf154c, 0x67d3527f, 0x56fd7602, 0x150f8b25, 0x2ae8e4c8, + 0x0bf10aec, 0x3d26a40f, 0x5c4c8ffc, 0x3c291322, 0x737fd02c, 0x4b506209, + 0x484ddaa4, 0x00b44669, 0x5974bdd1, 0x7d39d617, 0x12995404, 0x48f00bbe, + 0x44f7c59a, 0x23cb9292, 0x6476f20b, 0x034fbd59, 0x2893161c, 0x1dbae8c0, + 0x50348c2e, 0x797f0957, 0x685ddeaf, 0x36fb8a2e, 0x0fceb6f4, 0x10347ab4, + 0x72720bfc, 0x292a4304, 0x0cbf8a27, 0x3cea6db7, 0x4b0c6b15, 0x57e8e716, + 0x4e9c54cc, 0x4fc7f7ca, 0x49a6d3e2, 0x10fc2df3, 0x73db387e, 0x72cb89c3, + 0x71dba437, 0x4b14048c, 0x6e1af265, 0x1084b213, 0x3842107d, 0x6ecdc171, + 0x647919b2, 0x41a80841, 0x7b387c76, 0x46bc094b, 0x331b312a, 0x2f140cc4, + 0x355d0a11, 0x19390200, 0x69b05263, 0x582963fa, 0x44897e31, 0x66a473f0, + 0x0374f08d, 0x35879e45, 0x5e1dd7ef, 0x34d6a311, 0x6e4e18eb, 0x7b44734b, + 0x0e421333, 0x3da026d8, 0x5becbf4b, 0x56db4a1f, 0x1f2089bc, 0x28c733f2, + 0x04b0975d, 0x6156f224, 0x12d1f40f, 0x7f4d30f4, 0x2c0b9861, 0x769a083b, + 0x739544fb, 0x1dbd1067, 0x0e8cd717, 0x4c246fb2, 0x115eff39, 0x19e22f2a, + 0x4563ba61, 0x5d33a617, 0x54af83cf, 0x030bde73, 0x54b4736d, 0x0f01dfec, + 0x08869c01, 0x4e9e4d7b, 0x4739855a, 0x62d964a3, 0x26948fde, 0x30adf212, + 0x1f57b400, 0x3766c914, 0x1e7f9d1c, 0x33258b59, 0x522ab2c2, 0x3dc99798, + 0x15f53fe2, 0x05636669, 0x354b59c3, 0x1c37ebd4, 0x0bb7ebf9, 0x0e4e87f9, + 0x680d3124, 0x2770d549, 0x0c5e112e, 0x74aaa7ed, 0x06c0b550, 0x342b5922, + 0x4532ab5b, 0x4257dbee, 0x087f32a9, 0x45ada3e3, 0x7a854272, 0x061625f2, + 0x47c85a91, 0x25ad375d, 0x2809bd9d, 0x168b9348, 0x4381b0a3, 0x6f2dc6ca, + 0x122e54f6, 0x6c3228a6, 0x653c1652, 0x60b60584, 0x1d304b77, 0x4cc74c58, + 0x087e3dd5, 0x79bd540e, 0x79ab7a70, 0x26fcd1c9, 0x342abaaf, 0x644716b0, + 0x01f076cb, 0x73628937, 0x20b01ff8, 0x5832b80b, 0x2f77fc92, 0x4468d962, + 0x2bac2679, 0x7f850778, 0x47d2997c, 0x02690cb7, 0x7de54951, 0x54d80b14, + 0x5e0c6854, 0x313cc749, 0x622b86ba, 0x38dbf6d3, 0x045d3e52, 0x574f87fd, + 0x09f1b078, 0x31784f71, 0x4f01dd2f, 0x1874c9f9, 0x5837c7af, 0x2372f768, + 0x531bd1e8, 0x61816c0b, 0x4592995f, 0x156463c0, 0x250c5afe, 0x40c83178, + 0x4396f6b7, 0x29bdbec0, 0x43ea8ca5, 0x5c474696, 0x2c869192, 0x2ff2f51a, + 0x7c963fe5, 0x294319c1, 0x019fbe26, 0x72fa8e68, 0x245ca463, 0x4ca88208, + 0x72ac845a, 0x25307181, 0x2cdf88f7, 0x0adbfebd, 0x2eea465b, 0x52e4eee0, + 0x084daacd, 0x717ce67e, 0x594087c2, 0x2b8ee5c7, 0x4558f811, 0x76b65ba4, + 0x5de05e09, 0x3db76e27, 0x3c75110d, 0x04ca67e7, 0x51cd6d09, 0x7b4e9c3e, + 0x7cdda4d2, 0x674fb021, 0x7d372d2d, 0x13f7978b, 0x5fb106b1, 0x034377d1, + 0x2e5336f3, 0x099bb17d, 0x04e6755e, 0x34f73c1e, 0x004e0a0d, 0x7f2c32e2, + 0x1fc8f910, 0x67d0859d, 0x76462b25, 0x59fa9a17, 0x028e53ef, 0x3d6d5fdd, + 0x79a4671e, 0x5cbec506, 0x2c23ee6d, 0x628a2c1e, 0x4dae87bd, 0x07a189ea, + 0x3a414a96, 0x5915f622, 0x6bea011e, 0x412674cf, 0x07ecc314, 0x6a7dbce8, + 0x7e176f10, 0x68e60d47, 0x079ea970, 0x79f3b55c, 0x65a46098, 0x56155533, + 0x7e5d0272, 0x795bfad5, 0x094da770, 0x05ba427c, 0x152e430e, 0x187d8470, + 0x08e607bc, 0x45ce5ef9, 0x654231ae, 0x38d8cb48, 0x605632f8, 0x25cf8ee9, + 0x11497170, 0x171a3b00, 0x0f103d49, 0x24826483, 0x2848e187, 0x7498919b, + 0x1bb788cb, 0x791ad5c7, 0x5129330e, 0x016c4436, 0x430f05bf, 0x1f06b5cd, + 0x62df1378, 0x0423b9b4, 0x0341acaf, 0x3189543c, 0x7b96b2ea, 0x6c4865c3, + 0x4cc7adc3, 0x78a2bff6, 0x642db7c7, 0x70d02300, 0x7cd43ac0, 0x4f5fe414, + 0x333b52c2, 0x500d3c74, 0x65782c01, 0x3f72a2c5, 0x278f59d8, 0x493bf7f8, + 0x16bf51a0, 0x6cc70ced, 0x6ed15979, 0x1a77abae, 0x08cadbb7, 0x2f2e0bc0, + 0x236f5e8d, 0x1a4b4495, 0x360bd008, 0x32227d40 +}; + +int +main() +{ + SHA1Sum sum; + SHA1Sum::Hash hash; + sum.update(reinterpret_cast<const uint8_t*>(gTestV), sizeof(gTestV)); + sum.finish(hash); + + static const uint8_t expected[20] = { + 0xc8, 0xf2, 0x09, 0x59, 0x4e, 0x64, 0x40, 0xaa, 0x7b, 0xf7, 0xb8, 0xe0, + 0xfa, 0x44, 0xb2, 0x31, 0x95, 0xad, 0x94, 0x81 + }; + + static_assert(sizeof(expected) == sizeof(SHA1Sum::Hash), + "expected-data size should be the same as the actual hash " + "size"); + + for (size_t i = 0; i < SHA1Sum::kHashSize; i++) { + MOZ_RELEASE_ASSERT(hash[i] == expected[i]); + } + + return 0; +} diff --git a/mfbt/tests/TestSaturate.cpp b/mfbt/tests/TestSaturate.cpp new file mode 100644 index 000000000..06573ba4a --- /dev/null +++ b/mfbt/tests/TestSaturate.cpp @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <mozilla/Saturate.h> + +#include <mozilla/Assertions.h> + +#include <limits> + +using mozilla::detail::Saturate; + +#define A(a) MOZ_RELEASE_ASSERT(a, "Test \'" #a "\' failed.") + +static const unsigned long sNumOps = 32; + +template<typename T> +static T +StartValue() +{ + // Specialize |StartValue| for the given type. + A(false); +} + +template<> +int8_t +StartValue<int8_t>() +{ + return 0; +} + +template<> +int16_t +StartValue<int16_t>() +{ + return 0; +} + +template<> +int32_t +StartValue<int32_t>() +{ + return 0; +} + +template<> +uint8_t +StartValue<uint8_t>() +{ + // Picking a value near middle of uint8_t's range. + return static_cast<uint8_t>(std::numeric_limits<int8_t>::max()); +} + +template<> +uint16_t +StartValue<uint16_t>() +{ + // Picking a value near middle of uint16_t's range. + return static_cast<uint8_t>(std::numeric_limits<int16_t>::max()); +} + +template<> +uint32_t +StartValue<uint32_t>() +{ + // Picking a value near middle of uint32_t's range. + return static_cast<uint8_t>(std::numeric_limits<int32_t>::max()); +} + +// Add +// + +template<typename T> +static void +TestPrefixIncr() +{ + T value = StartValue<T>(); + Saturate<T> satValue(value); + + for (T i = 0; i < static_cast<T>(sNumOps); ++i) { + A(++value == ++satValue); + } +} + +template<typename T> +static void +TestPostfixIncr() +{ + T value = StartValue<T>(); + Saturate<T> satValue(value); + + for (T i = 0; i < static_cast<T>(sNumOps); ++i) { + A(value++ == satValue++); + } +} + +template<typename T> +static void +TestAdd() +{ + T value = StartValue<T>(); + Saturate<T> satValue(value); + + for (T i = 0; i < static_cast<T>(sNumOps); ++i) { + A((value + i) == (satValue + i)); + } +} + +// Subtract +// + +template<typename T> +static void +TestPrefixDecr() +{ + T value = StartValue<T>(); + Saturate<T> satValue(value); + + for (T i = 0; i < static_cast<T>(sNumOps); ++i) { + A(--value == --satValue); + } +} + +template<typename T> +static void +TestPostfixDecr() +{ + T value = StartValue<T>(); + Saturate<T> satValue(value); + + for (T i = 0; i < static_cast<T>(sNumOps); ++i) { + A(value-- == satValue--); + } +} + +template<typename T> +static void +TestSub() +{ + T value = StartValue<T>(); + Saturate<T> satValue(value); + + for (T i = 0; i < static_cast<T>(sNumOps); ++i) { + A((value - i) == (satValue - i)); + } +} + +// Corner cases near bounds +// + +template<typename T> +static void +TestUpperBound() +{ + Saturate<T> satValue(std::numeric_limits<T>::max()); + + A(--satValue == (std::numeric_limits<T>::max() - 1)); + A(++satValue == (std::numeric_limits<T>::max())); + A(++satValue == (std::numeric_limits<T>::max())); // don't overflow here + A(++satValue == (std::numeric_limits<T>::max())); // don't overflow here + A(--satValue == (std::numeric_limits<T>::max() - 1)); // back at (max - 1) + A(--satValue == (std::numeric_limits<T>::max() - 2)); +} + +template<typename T> +static void +TestLowerBound() +{ + Saturate<T> satValue(std::numeric_limits<T>::min()); + + A(++satValue == (std::numeric_limits<T>::min() + 1)); + A(--satValue == (std::numeric_limits<T>::min())); + A(--satValue == (std::numeric_limits<T>::min())); // don't overflow here + A(--satValue == (std::numeric_limits<T>::min())); // don't overflow here + A(++satValue == (std::numeric_limits<T>::min() + 1)); // back at (max + 1) + A(++satValue == (std::numeric_limits<T>::min() + 2)); +} + +// Framework +// + +template<typename T> +static void +TestAll() +{ + // Assert that we don't accidently hit type's range limits in tests. + const T value = StartValue<T>(); + A(std::numeric_limits<T>::min() + static_cast<T>(sNumOps) <= value); + A(std::numeric_limits<T>::max() - static_cast<T>(sNumOps) >= value); + + TestPrefixIncr<T>(); + TestPostfixIncr<T>(); + TestAdd<T>(); + + TestPrefixDecr<T>(); + TestPostfixDecr<T>(); + TestSub<T>(); + + TestUpperBound<T>(); + TestLowerBound<T>(); +} + +int +main() +{ + TestAll<int8_t>(); + TestAll<int16_t>(); + TestAll<int32_t>(); + TestAll<uint8_t>(); + TestAll<uint16_t>(); + TestAll<uint32_t>(); + return 0; +} diff --git a/mfbt/tests/TestScopeExit.cpp b/mfbt/tests/TestScopeExit.cpp new file mode 100644 index 000000000..fadd3a10a --- /dev/null +++ b/mfbt/tests/TestScopeExit.cpp @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/ScopeExit.h" + +using mozilla::MakeScopeExit; + +#define CHECK(c) \ + do { \ + bool cond = !!(c); \ + MOZ_RELEASE_ASSERT(cond, "Failed assertion: " #c); \ + if (!cond) { \ + return false; \ + } \ + } while (false) + +static bool +Test() +{ + int a = 1; + int b = 1; + + { + a++; + auto guardA = MakeScopeExit([&] { + a--; + }); + + b++; + auto guardB = MakeScopeExit([&] { + b--; + }); + + guardB.release(); + } + + CHECK(a == 1); + CHECK(b == 2); + + return true; +} + +int +main() +{ + if (!Test()) { + return 1; + } + return 0; +} diff --git a/mfbt/tests/TestSegmentedVector.cpp b/mfbt/tests/TestSegmentedVector.cpp new file mode 100644 index 000000000..2bf2540aa --- /dev/null +++ b/mfbt/tests/TestSegmentedVector.cpp @@ -0,0 +1,279 @@ +/* -*- Mode: C++; tab-width: 9; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This is included first to ensure it doesn't implicitly depend on anything +// else. +#include "mozilla/SegmentedVector.h" + +#include "mozilla/Alignment.h" +#include "mozilla/Assertions.h" + +using mozilla::SegmentedVector; + +// It would be nice if we could use the InfallibleAllocPolicy from mozalloc, +// but MFBT cannot use mozalloc. +class InfallibleAllocPolicy +{ +public: + template <typename T> + T* pod_malloc(size_t aNumElems) + { + if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) { + MOZ_CRASH("TestSegmentedVector.cpp: overflow"); + } + T* rv = static_cast<T*>(malloc(aNumElems * sizeof(T))); + if (!rv) { + MOZ_CRASH("TestSegmentedVector.cpp: out of memory"); + } + return rv; + } + + void free_(void* aPtr) { free(aPtr); } +}; + +// We want to test Append(), which is fallible and marked with +// MOZ_MUST_USE. But we're using an infallible alloc policy, and so +// don't really need to check the result. Casting to |void| works with clang +// but not GCC, so we instead use this dummy variable which works with both +// compilers. +static int gDummy; + +// This tests basic segmented vector construction and iteration. +void TestBasics() +{ + // A SegmentedVector with a POD element type. + typedef SegmentedVector<int, 1024, InfallibleAllocPolicy> MyVector; + MyVector v; + int i, n; + + MOZ_RELEASE_ASSERT(v.IsEmpty()); + + // Add 100 elements, then check various things. + i = 0; + for ( ; i < 100; i++) { + gDummy = v.Append(mozilla::Move(i)); + } + MOZ_RELEASE_ASSERT(!v.IsEmpty()); + MOZ_RELEASE_ASSERT(v.Length() == 100); + + n = 0; + for (auto iter = v.Iter(); !iter.Done(); iter.Next()) { + MOZ_RELEASE_ASSERT(iter.Get() == n); + n++; + } + MOZ_RELEASE_ASSERT(n == 100); + + // Add another 900 elements, then re-check. + for ( ; i < 1000; i++) { + v.InfallibleAppend(mozilla::Move(i)); + } + MOZ_RELEASE_ASSERT(!v.IsEmpty()); + MOZ_RELEASE_ASSERT(v.Length() == 1000); + + n = 0; + for (auto iter = v.Iter(); !iter.Done(); iter.Next()) { + MOZ_RELEASE_ASSERT(iter.Get() == n); + n++; + } + MOZ_RELEASE_ASSERT(n == 1000); + + // Pop off all of the elements. + MOZ_RELEASE_ASSERT(v.Length() == 1000); + for (int len = (int)v.Length(); len > 0; len--) { + MOZ_RELEASE_ASSERT(v.GetLast() == len - 1); + v.PopLast(); + } + MOZ_RELEASE_ASSERT(v.IsEmpty()); + MOZ_RELEASE_ASSERT(v.Length() == 0); + + // Fill the vector up again to prepare for the clear. + for (i = 0; i < 1000; i++) { + v.InfallibleAppend(mozilla::Move(i)); + } + MOZ_RELEASE_ASSERT(!v.IsEmpty()); + MOZ_RELEASE_ASSERT(v.Length() == 1000); + + v.Clear(); + MOZ_RELEASE_ASSERT(v.IsEmpty()); + MOZ_RELEASE_ASSERT(v.Length() == 0); + + // Fill the vector up to verify PopLastN works. + for (i = 0; i < 1000; ++i) { + v.InfallibleAppend(mozilla::Move(i)); + } + MOZ_RELEASE_ASSERT(!v.IsEmpty()); + MOZ_RELEASE_ASSERT(v.Length() == 1000); + + // Verify we pop the right amount of elements. + v.PopLastN(300); + MOZ_RELEASE_ASSERT(v.Length() == 700); + + // Verify the contents are what we expect. + n = 0; + for (auto iter = v.Iter(); !iter.Done(); iter.Next()) { + MOZ_RELEASE_ASSERT(iter.Get() == n); + n++; + } + MOZ_RELEASE_ASSERT(n == 700); +} + +static size_t gNumDefaultCtors; +static size_t gNumExplicitCtors; +static size_t gNumCopyCtors; +static size_t gNumMoveCtors; +static size_t gNumDtors; + +struct NonPOD +{ + NonPOD() { gNumDefaultCtors++; } + explicit NonPOD(int x) { gNumExplicitCtors++; } + NonPOD(NonPOD&) { gNumCopyCtors++; } + NonPOD(NonPOD&&) { gNumMoveCtors++; } + ~NonPOD() { gNumDtors++; } +}; + +// This tests how segmented vectors with non-POD elements construct and +// destruct those elements. +void TestConstructorsAndDestructors() +{ + size_t defaultCtorCalls = 0; + size_t explicitCtorCalls = 0; + size_t copyCtorCalls = 0; + size_t moveCtorCalls = 0; + size_t dtorCalls = 0; + + { + static const size_t segmentSize = 64; + + // A SegmentedVector with a non-POD element type. + NonPOD x(1); // explicit constructor called + explicitCtorCalls++; + SegmentedVector<NonPOD, segmentSize, InfallibleAllocPolicy> v; + // default constructor called 0 times + MOZ_RELEASE_ASSERT(v.IsEmpty()); + gDummy = v.Append(x); // copy constructor called + copyCtorCalls++; + NonPOD y(1); // explicit constructor called + explicitCtorCalls++; + gDummy = v.Append(mozilla::Move(y)); // move constructor called + moveCtorCalls++; + NonPOD z(1); // explicit constructor called + explicitCtorCalls++; + v.InfallibleAppend(mozilla::Move(z)); // move constructor called + moveCtorCalls++; + v.PopLast(); // destructor called 1 time + dtorCalls++; + MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls); + v.Clear(); // destructor called 2 times + dtorCalls += 2; + + // Test that PopLastN() correctly calls the destructors of all the + // elements in the segments it destroys. + // + // We depend on the size of NonPOD when determining how many things + // to push onto the vector. It would be nicer to get this information + // from SegmentedVector itself... + static_assert(sizeof(NonPOD) == 1, "Fix length calculations!"); + + size_t nonFullLastSegmentSize = segmentSize - 1; + for (size_t i = 0; i < nonFullLastSegmentSize; ++i) { + gDummy = v.Append(x); // copy constructor called + copyCtorCalls++; + } + MOZ_RELEASE_ASSERT(gNumCopyCtors == copyCtorCalls); + + // Pop some of the elements. + { + size_t partialPopAmount = 5; + MOZ_RELEASE_ASSERT(nonFullLastSegmentSize > partialPopAmount); + v.PopLastN(partialPopAmount); // destructor called partialPopAmount times + dtorCalls += partialPopAmount; + MOZ_RELEASE_ASSERT(v.Length() == nonFullLastSegmentSize - partialPopAmount); + MOZ_RELEASE_ASSERT(!v.IsEmpty()); + MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls); + } + + // Pop a full segment. + { + size_t length = v.Length(); + v.PopLastN(length); + dtorCalls += length; + // These two tests *are* semantically different given the underlying + // implementation; Length sums up the sizes of the internal segments, + // while IsEmpty looks at the sequence of internal segments. + MOZ_RELEASE_ASSERT(v.Length() == 0); + MOZ_RELEASE_ASSERT(v.IsEmpty()); + MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls); + } + + size_t multipleSegmentsSize = (segmentSize * 3) / 2; + for (size_t i = 0; i < multipleSegmentsSize; ++i) { + gDummy = v.Append(x); // copy constructor called + copyCtorCalls++; + } + MOZ_RELEASE_ASSERT(gNumCopyCtors == copyCtorCalls); + + // Pop across segment boundaries. + { + v.PopLastN(segmentSize); + dtorCalls += segmentSize; + MOZ_RELEASE_ASSERT(v.Length() == (multipleSegmentsSize - segmentSize)); + MOZ_RELEASE_ASSERT(!v.IsEmpty()); + MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls); + } + + // Clear everything here to make calculations easier. + { + size_t length = v.Length(); + v.Clear(); + dtorCalls += length; + MOZ_RELEASE_ASSERT(v.IsEmpty()); + MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls); + } + + MOZ_RELEASE_ASSERT(gNumDefaultCtors == defaultCtorCalls); + MOZ_RELEASE_ASSERT(gNumExplicitCtors == explicitCtorCalls); + MOZ_RELEASE_ASSERT(gNumCopyCtors == copyCtorCalls); + MOZ_RELEASE_ASSERT(gNumMoveCtors == moveCtorCalls); + MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls); + } // destructor called for x, y, z + dtorCalls += 3; + MOZ_RELEASE_ASSERT(gNumDtors == dtorCalls); +} + +struct A { int mX; int mY; }; +struct B { int mX; char mY; double mZ; }; +struct C { A mA; B mB; }; +struct D { char mBuf[101]; }; +struct E { }; + +// This tests that we get the right segment capacities for specified segment +// sizes, and that the elements are aligned appropriately. +void TestSegmentCapacitiesAndAlignments() +{ + // When SegmentedVector's constructor is passed a size, it asserts that the + // vector's segment capacity results in a segment size equal to (or very + // close to) the passed size. + // + // Also, SegmentedVector has a static assertion that elements are + // appropriately aligned. + SegmentedVector<double, 512> v1(512); + SegmentedVector<A, 1024> v2(1024); + SegmentedVector<B, 999> v3(999); + SegmentedVector<C, 10> v4(10); + SegmentedVector<D, 1234> v5(1234); + SegmentedVector<E> v6(4096); // 4096 is the default segment size + SegmentedVector<mozilla::AlignedElem<16>, 100> v7(100); +} + +int main(void) +{ + TestBasics(); + TestConstructorsAndDestructors(); + TestSegmentCapacitiesAndAlignments(); + + return 0; +} diff --git a/mfbt/tests/TestSplayTree.cpp b/mfbt/tests/TestSplayTree.cpp new file mode 100644 index 000000000..030336fa7 --- /dev/null +++ b/mfbt/tests/TestSplayTree.cpp @@ -0,0 +1,212 @@ +/* -*- Mode: C++; tab-width: 9; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/SplayTree.h" +#include "mozilla/Unused.h" + +using mozilla::SplayTree; +using mozilla::SplayTreeNode; + +// The following array contains the values 0..999 in random order, as computed +// with the following Python program: +// +// from random import shuffle +// x = range(1000) +// shuffle(x) +// print(x); +// +static int gValues[] = { + 778, 999, 248, 795, 607, 177, 725, 33, 215, 565, 436, 821, 941, 802, 322, 54, + 151, 416, 531, 65, 818, 99, 340, 401, 274, 767, 278, 617, 425, 629, 833, 878, + 440, 984, 724, 519, 100, 369, 490, 131, 422, 169, 932, 476, 823, 521, 390, + 781, 747, 218, 376, 461, 717, 532, 471, 298, 720, 608, 334, 788, 161, 500, + 280, 963, 430, 484, 779, 572, 96, 333, 650, 158, 199, 137, 991, 399, 882, + 689, 358, 548, 196, 718, 211, 388, 133, 188, 321, 892, 25, 694, 735, 886, + 872, 785, 195, 275, 696, 975, 393, 619, 894, 18, 281, 191, 792, 846, 861, + 351, 542, 806, 570, 702, 931, 585, 444, 284, 217, 132, 251, 253, 302, 808, + 224, 37, 63, 863, 409, 49, 780, 790, 31, 638, 890, 186, 114, 152, 949, 491, + 207, 392, 170, 460, 794, 482, 877, 407, 263, 909, 249, 710, 614, 51, 431, + 915, 62, 332, 74, 495, 901, 23, 365, 752, 89, 660, 745, 741, 547, 669, 449, + 465, 605, 107, 774, 205, 852, 266, 247, 690, 835, 765, 410, 140, 122, 400, + 510, 664, 105, 935, 230, 134, 106, 959, 375, 884, 361, 527, 715, 840, 272, + 232, 102, 415, 903, 117, 313, 153, 463, 464, 876, 406, 967, 713, 381, 836, + 555, 190, 859, 172, 483, 61, 633, 294, 993, 72, 337, 11, 896, 523, 101, 916, + 244, 566, 706, 533, 439, 201, 222, 695, 739, 553, 571, 289, 918, 209, 189, + 357, 814, 670, 866, 910, 579, 246, 636, 750, 891, 494, 758, 341, 626, 426, + 772, 254, 682, 588, 104, 347, 184, 977, 126, 498, 165, 955, 241, 516, 235, + 497, 121, 123, 791, 844, 259, 995, 283, 602, 417, 221, 308, 855, 429, 86, + 345, 928, 44, 679, 796, 363, 402, 445, 492, 450, 964, 749, 925, 847, 637, + 982, 648, 635, 481, 564, 867, 940, 291, 159, 290, 929, 59, 712, 986, 611, + 954, 820, 103, 622, 316, 142, 204, 225, 678, 314, 84, 578, 315, 141, 990, + 880, 504, 969, 412, 746, 47, 517, 124, 848, 466, 438, 674, 979, 782, 651, + 181, 26, 435, 832, 386, 951, 229, 642, 655, 91, 162, 921, 647, 113, 686, 56, + 805, 763, 245, 581, 287, 998, 525, 641, 135, 634, 237, 728, 112, 828, 228, + 899, 1, 723, 16, 613, 144, 659, 97, 185, 312, 292, 733, 624, 276, 387, 926, + 339, 768, 960, 610, 807, 656, 851, 219, 582, 709, 927, 514, 680, 870, 597, + 536, 77, 164, 512, 149, 900, 85, 335, 997, 8, 705, 777, 653, 815, 311, 701, + 507, 202, 530, 827, 541, 958, 82, 874, 55, 487, 383, 885, 684, 180, 829, 760, + 109, 194, 540, 816, 906, 657, 469, 446, 857, 907, 38, 600, 618, 797, 950, + 822, 277, 842, 116, 513, 255, 424, 643, 163, 372, 129, 67, 118, 754, 529, + 917, 687, 473, 174, 538, 939, 663, 775, 474, 242, 883, 20, 837, 293, 584, + 943, 32, 176, 904, 14, 448, 893, 888, 744, 171, 714, 454, 691, 261, 934, 606, + 789, 825, 671, 397, 338, 317, 612, 737, 130, 41, 923, 574, 136, 980, 850, 12, + 729, 197, 403, 57, 783, 360, 146, 75, 432, 447, 192, 799, 740, 267, 214, 250, + 367, 853, 968, 120, 736, 391, 881, 784, 665, 68, 398, 350, 839, 268, 697, + 567, 428, 738, 48, 182, 70, 220, 865, 418, 374, 148, 945, 353, 539, 589, 307, + 427, 506, 265, 558, 128, 46, 336, 299, 349, 309, 377, 304, 420, 30, 34, 875, + 948, 212, 394, 442, 719, 273, 269, 157, 502, 675, 751, 838, 897, 862, 831, + 676, 590, 811, 966, 854, 477, 15, 598, 573, 108, 98, 81, 408, 421, 296, 73, + 644, 456, 362, 666, 550, 331, 368, 193, 470, 203, 769, 342, 36, 604, 60, 970, + 748, 813, 522, 515, 90, 672, 243, 793, 947, 595, 632, 912, 475, 258, 80, 873, + 623, 524, 546, 262, 727, 216, 505, 330, 373, 58, 297, 609, 908, 150, 206, + 703, 755, 260, 511, 213, 198, 766, 898, 992, 488, 405, 974, 770, 936, 743, + 554, 0, 499, 976, 94, 160, 919, 434, 324, 156, 757, 830, 677, 183, 630, 871, + 640, 938, 518, 344, 366, 742, 552, 306, 535, 200, 652, 496, 233, 419, 787, + 318, 981, 371, 166, 143, 384, 88, 508, 698, 812, 559, 658, 549, 208, 599, + 621, 961, 668, 563, 93, 154, 587, 560, 389, 3, 210, 326, 4, 924, 300, 2, 804, + 914, 801, 753, 654, 27, 236, 19, 708, 451, 985, 596, 478, 922, 240, 127, 994, + 983, 385, 472, 40, 528, 288, 111, 543, 568, 155, 625, 759, 937, 956, 545, + 953, 962, 382, 479, 809, 557, 501, 354, 414, 343, 378, 843, 379, 178, 556, + 800, 803, 592, 627, 942, 576, 920, 704, 707, 726, 223, 119, 404, 24, 879, + 722, 868, 5, 238, 817, 520, 631, 946, 462, 457, 295, 480, 957, 441, 145, 286, + 303, 688, 17, 628, 493, 364, 226, 110, 615, 69, 320, 534, 593, 721, 411, 285, + 869, 952, 849, 139, 356, 346, 28, 887, 810, 92, 798, 544, 458, 996, 692, 396, + 667, 328, 173, 22, 773, 50, 645, 987, 42, 685, 734, 700, 683, 601, 580, 639, + 913, 323, 858, 179, 761, 6, 841, 905, 234, 730, 29, 21, 575, 586, 902, 443, + 826, 646, 257, 125, 649, 53, 453, 252, 13, 87, 971, 227, 485, 168, 380, 711, + 79, 732, 325, 52, 468, 76, 551, 39, 395, 327, 973, 459, 45, 583, 989, 147, + 455, 776, 944, 569, 889, 256, 35, 175, 834, 756, 933, 860, 526, 845, 864, + 764, 771, 282, 9, 693, 352, 731, 7, 577, 264, 319, 138, 467, 819, 930, 231, + 115, 988, 978, 762, 486, 301, 616, 10, 78, 603, 452, 965, 279, 972, 413, 895, + 591, 662, 594, 348, 423, 489, 43, 699, 433, 509, 355, 270, 66, 83, 95, 561, + 661, 562, 329, 620, 370, 64, 187, 503, 716, 856, 310, 786, 167, 71, 239, 359, + 537, 437, 305, 673, 824, 911, 681, 271 +}; + +struct SplayInt : SplayTreeNode<SplayInt> +{ + explicit SplayInt(int aValue) : mValue(aValue) {} + + static int compare(const SplayInt& aOne, const SplayInt& aTwo) + { + if (aOne.mValue < aTwo.mValue) { + return -1; + } + if (aOne.mValue > aTwo.mValue) { + return 1; + } + return 0; + } + + int mValue; +}; + +struct SplayNoCopy : SplayTreeNode<SplayNoCopy> +{ + SplayNoCopy(const SplayNoCopy&) = delete; + SplayNoCopy(SplayNoCopy&&) = delete; + + static int compare(const SplayNoCopy&, const SplayNoCopy&) { return 0; } +}; + +static SplayTree<SplayNoCopy, SplayNoCopy> testNoCopy; + +int +main() +{ + mozilla::Unused << testNoCopy; + + SplayTree<SplayInt, SplayInt> tree; + + MOZ_RELEASE_ASSERT(tree.empty()); + + MOZ_RELEASE_ASSERT(!tree.find(SplayInt(0))); + + static const int N = mozilla::ArrayLength(gValues); + + // Insert the values, and check each one is findable just after insertion. + for (int i = 0; i < N; i++) { + tree.insert(new SplayInt(gValues[i])); + SplayInt* inserted = tree.find(SplayInt(gValues[i])); + MOZ_RELEASE_ASSERT(inserted); + MOZ_RELEASE_ASSERT(tree.findOrInsert(SplayInt(gValues[i])) == inserted); + tree.checkCoherency(); + } + + // Check they're all findable after all insertions. + for (int i = 0; i < N; i++) { + MOZ_RELEASE_ASSERT(tree.find(SplayInt(gValues[i]))); + MOZ_RELEASE_ASSERT(tree.findOrInsert(SplayInt(gValues[i]))); + tree.checkCoherency(); + } + + // Check that non-inserted values cannot be found. + MOZ_RELEASE_ASSERT(!tree.find(SplayInt(-1))); + MOZ_RELEASE_ASSERT(!tree.find(SplayInt(N))); + MOZ_RELEASE_ASSERT(!tree.find(SplayInt(0x7fffffff))); + + // Remove the values, and check each one is not findable just after removal. + for (int i = 0; i < N; i++) { + SplayInt* removed = tree.remove(SplayInt(gValues[i])); + MOZ_RELEASE_ASSERT(removed->mValue == gValues[i]); + MOZ_RELEASE_ASSERT(!tree.find(*removed)); + delete removed; + tree.checkCoherency(); + } + + MOZ_RELEASE_ASSERT(tree.empty()); + + // Insert the values, and check each one is findable just after insertion. + for (int i = 0; i < N; i++) { + SplayInt* inserted = tree.findOrInsert(SplayInt(gValues[i])); + MOZ_RELEASE_ASSERT(tree.find(SplayInt(gValues[i])) == inserted); + MOZ_RELEASE_ASSERT(tree.findOrInsert(SplayInt(gValues[i])) == inserted); + tree.checkCoherency(); + } + + // Check they're all findable after all insertions. + for (int i = 0; i < N; i++) { + MOZ_RELEASE_ASSERT(tree.find(SplayInt(gValues[i]))); + MOZ_RELEASE_ASSERT(tree.findOrInsert(SplayInt(gValues[i]))); + tree.checkCoherency(); + } + + // Check that non-inserted values cannot be found. + MOZ_RELEASE_ASSERT(!tree.find(SplayInt(-1))); + MOZ_RELEASE_ASSERT(!tree.find(SplayInt(N))); + MOZ_RELEASE_ASSERT(!tree.find(SplayInt(0x7fffffff))); + + // Remove the values, and check each one is not findable just after removal. + for (int i = 0; i < N; i++) { + SplayInt* removed = tree.remove(SplayInt(gValues[i])); + MOZ_RELEASE_ASSERT(removed->mValue == gValues[i]); + MOZ_RELEASE_ASSERT(!tree.find(*removed)); + delete removed; + tree.checkCoherency(); + } + + MOZ_RELEASE_ASSERT(tree.empty()); + + // Reinsert the values, in reverse order to last time. + for (int i = 0; i < N; i++) { + tree.insert(new SplayInt(gValues[N - i - 1])); + tree.checkCoherency(); + } + + // Remove the minimum value repeatedly. + for (int i = 0; i < N; i++) { + SplayInt* removed = tree.removeMin(); + MOZ_RELEASE_ASSERT(removed->mValue == i); + delete removed; + tree.checkCoherency(); + } + + MOZ_RELEASE_ASSERT(tree.empty()); + + return 0; +} diff --git a/mfbt/tests/TestTemplateLib.cpp b/mfbt/tests/TestTemplateLib.cpp new file mode 100644 index 000000000..7ad0831ed --- /dev/null +++ b/mfbt/tests/TestTemplateLib.cpp @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/TemplateLib.h" + +using mozilla::tl::And; + +static_assert(And<>::value == true, + "And<>::value should be true"); +static_assert(And<true>::value == true, + "And<true>::value should be true"); +static_assert(And<false>::value == false, + "And<false>::value should be false"); +static_assert(And<false, true>::value == false, + "And<false, true>::value should be false"); +static_assert(And<false, false>::value == false, + "And<false, false>::value should be false"); +static_assert(And<true, false>::value == false, + "And<true, false>::value should be false"); +static_assert(And<true, true>::value == true, + "And<true, true>::value should be true"); +static_assert(And<true, true, true>::value == true, + "And<true, true, true>::value should be true"); +static_assert(And<true, false, true>::value == false, + "And<true, false, true>::value should be false"); + +int +main() +{ + // Nothing to do here. + return 0; +} diff --git a/mfbt/tests/TestTuple.cpp b/mfbt/tests/TestTuple.cpp new file mode 100644 index 000000000..a85552fef --- /dev/null +++ b/mfbt/tests/TestTuple.cpp @@ -0,0 +1,296 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + /* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/Move.h" +#include "mozilla/Pair.h" +#include "mozilla/Tuple.h" +#include "mozilla/TypeTraits.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Unused.h" + +#include <stddef.h> +#include <utility> + +using mozilla::Get; +using mozilla::IsSame; +using mozilla::MakeTuple; +using mozilla::MakeUnique; +using mozilla::Move; +using mozilla::Pair; +using mozilla::Tie; +using mozilla::Tuple; +using mozilla::UniquePtr; +using mozilla::Unused; +using std::pair; + +#define CHECK(c) \ + do { \ + bool cond = !!(c); \ + MOZ_RELEASE_ASSERT(cond, "Failed assertion: " #c); \ + } while (false) + +// The second argument is the expected type. It's variadic to allow the +// type to contain commas. +#define CHECK_TYPE(expression, ...) \ + static_assert(IsSame<decltype(expression), __VA_ARGS__>::value, \ + "Type mismatch!") + +struct ConvertibleToInt +{ + operator int() const { return 42; } +}; + +static void +TestConstruction() +{ + // Default construction + Tuple<> a; + Unused << a; + Tuple<int> b; + Unused << b; + + // Construction from elements + int x = 1, y = 1; + Tuple<int, int> c{x, y}; + Tuple<int&, const int&> d{x, y}; + x = 42; + y = 42; + CHECK(Get<0>(c) == 1); + CHECK(Get<1>(c) == 1); + CHECK(Get<0>(d) == 42); + CHECK(Get<1>(d) == 42); + + // Construction from objects convertible to the element types + Tuple<int, int> e{1.0, ConvertibleToInt{}}; + + // Copy construction + Tuple<int> x1; + Tuple<int> x2{x1}; + + Tuple<int, int> f(c); + CHECK(Get<0>(f) == 1); + CHECK(Get<0>(f) == 1); + + // Move construction + Tuple<UniquePtr<int>> g{MakeUnique<int>(42)}; + Tuple<UniquePtr<int>> h{Move(g)}; + CHECK(Get<0>(g) == nullptr); + CHECK(*Get<0>(h) == 42); +} + +static void +TestConstructionFromMozPair() +{ + // Construction from elements + int x = 1, y = 1; + Pair<int, int> a{x, y}; + Pair<int&, const int&> b{x, y}; + Tuple<int, int> c(a); + Tuple<int&, const int&> d(b); + x = 42; + y = 42; + CHECK(Get<0>(c) == 1); + CHECK(Get<1>(c) == 1); + CHECK(Get<0>(d) == 42); + CHECK(Get<1>(d) == 42); +} + +static void +TestConstructionFromStdPair() +{ + // Construction from elements + int x = 1, y = 1; + pair<int, int> a{x, y}; + pair<int&, const int&> b{x, y}; + Tuple<int, int> c(a); + Tuple<int&, const int&> d(b); + x = 42; + y = 42; + CHECK(Get<0>(c) == 1); + CHECK(Get<1>(c) == 1); + CHECK(Get<0>(d) == 42); + CHECK(Get<1>(d) == 42); +} + +static void +TestAssignment() +{ + // Copy assignment + Tuple<int> a{0}; + Tuple<int> b{42}; + a = b; + CHECK(Get<0>(a) == 42); + + // Assignment to reference member + int i = 0; + int j = 42; + Tuple<int&> c{i}; + Tuple<int&> d{j}; + c = d; + CHECK(i == 42); + + // Move assignment + Tuple<UniquePtr<int>> e{MakeUnique<int>(0)}; + Tuple<UniquePtr<int>> f{MakeUnique<int>(42)}; + e = Move(f); + CHECK(*Get<0>(e) == 42); + CHECK(Get<0>(f) == nullptr); +} + +static void +TestAssignmentFromMozPair() +{ + // Copy assignment + Tuple<int, int> a{0, 0}; + Pair<int, int> b{42, 42}; + a = b; + CHECK(Get<0>(a) == 42); + CHECK(Get<1>(a) == 42); + + // Assignment to reference member + int i = 0; + int j = 0; + int k = 42; + Tuple<int&, int&> c{i, j}; + Pair<int&, int&> d{k, k}; + c = d; + CHECK(i == 42); + CHECK(j == 42); + + // Move assignment + Tuple<UniquePtr<int>, UniquePtr<int>> e{MakeUnique<int>(0), + MakeUnique<int>(0)}; + Pair<UniquePtr<int>, UniquePtr<int>> f{MakeUnique<int>(42), + MakeUnique<int>(42)}; + e = Move(f); + CHECK(*Get<0>(e) == 42); + CHECK(*Get<1>(e) == 42); + CHECK(f.first() == nullptr); + CHECK(f.second() == nullptr); +} + +static void +TestAssignmentFromStdPair() +{ + // Copy assignment + Tuple<int, int> a{0, 0}; + pair<int, int> b{42, 42}; + a = b; + CHECK(Get<0>(a) == 42); + CHECK(Get<1>(a) == 42); + + // Assignment to reference member + int i = 0; + int j = 0; + int k = 42; + Tuple<int&, int&> c{i, j}; + pair<int&, int&> d{k, k}; + c = d; + CHECK(i == 42); + CHECK(j == 42); + + // Move assignment. + Tuple<UniquePtr<int>, UniquePtr<int>> e{MakeUnique<int>(0), MakeUnique<int>(0)}; + // XXX: On some platforms std::pair doesn't support move constructor. + pair<UniquePtr<int>, UniquePtr<int>> f; + f.first = MakeUnique<int>(42); + f.second = MakeUnique<int>(42); + + e = Move(f); + CHECK(*Get<0>(e) == 42); + CHECK(*Get<1>(e) == 42); + CHECK(f.first == nullptr); + CHECK(f.second == nullptr); +} + +static void +TestGet() +{ + int x = 1; + int y = 2; + int z = 3; + Tuple<int, int&, const int&> tuple(x, y, z); + + // Using Get<>() to read elements + CHECK(Get<0>(tuple) == 1); + CHECK(Get<1>(tuple) == 2); + CHECK(Get<2>(tuple) == 3); + + // Using Get<>() to write to elements + Get<0>(tuple) = 41; + CHECK(Get<0>(tuple) == 41); + + // Writing through reference elements + Get<1>(tuple) = 42; + CHECK(Get<1>(tuple) == 42); + CHECK(y == 42); +} + +static void +TestMakeTuple() +{ + auto tuple = MakeTuple(42, 0.5f, 'c'); + CHECK_TYPE(tuple, Tuple<int, float, char>); + CHECK(Get<0>(tuple) == 42); + CHECK(Get<1>(tuple) == 0.5f); + CHECK(Get<2>(tuple) == 'c'); + + // Make sure we don't infer the type to be Tuple<int&>. + int x = 1; + auto tuple2 = MakeTuple(x); + CHECK_TYPE(tuple2, Tuple<int>); + x = 2; + CHECK(Get<0>(tuple2) == 1); +} + +static bool +TestTie() +{ + int i; + float f; + char c; + Tuple<int, float, char> rhs1(42, 0.5f, 'c'); + Tie(i, f, c) = rhs1; + CHECK(i == Get<0>(rhs1)); + CHECK(f == Get<1>(rhs1)); + CHECK(c == Get<2>(rhs1)); + // Test conversions + Tuple<ConvertibleToInt, double, unsigned char> rhs2(ConvertibleToInt(), + 0.7f, 'd'); + Tie(i, f, c) = rhs2; + CHECK(i == Get<0>(rhs2)); + CHECK(f == Get<1>(rhs2)); + CHECK(c == Get<2>(rhs2)); + + // Test Pair + Pair<int, float> rhs3(-1, 1.2f); + Tie(i, f) = rhs3; + CHECK(i == rhs3.first()); + CHECK(f == rhs3.second()); + + pair<int, float> rhs4(42, 1.5f); + Tie(i, f) = rhs4; + CHECK(i == rhs4.first); + CHECK(f == rhs4.second); + + return true; +} + +int +main() +{ + TestConstruction(); + TestConstructionFromMozPair(); + TestConstructionFromStdPair(); + TestAssignment(); + TestAssignmentFromMozPair(); + TestAssignmentFromStdPair(); + TestGet(); + TestMakeTuple(); + TestTie(); + return 0; +} diff --git a/mfbt/tests/TestTypeTraits.cpp b/mfbt/tests/TestTypeTraits.cpp new file mode 100644 index 000000000..f0a565142 --- /dev/null +++ b/mfbt/tests/TestTypeTraits.cpp @@ -0,0 +1,660 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/TypeTraits.h" + +#define TEST_CV_QUALIFIERS(test, type, ...) \ + test(type, __VA_ARGS__) \ + test(const type, __VA_ARGS__) \ + test(volatile type, __VA_ARGS__) \ + test(const volatile type, __VA_ARGS__) + +using mozilla::AddLvalueReference; +using mozilla::AddPointer; +using mozilla::AddRvalueReference; +using mozilla::Decay; +using mozilla::DeclVal; +using mozilla::IsFunction; +using mozilla::IsArray; +using mozilla::IsBaseOf; +using mozilla::IsClass; +using mozilla::IsConvertible; +using mozilla::IsEmpty; +using mozilla::IsLvalueReference; +using mozilla::IsPointer; +using mozilla::IsReference; +using mozilla::IsRvalueReference; +using mozilla::IsSame; +using mozilla::IsSigned; +using mozilla::IsUnsigned; +using mozilla::IsDestructible; +using mozilla::MakeSigned; +using mozilla::MakeUnsigned; +using mozilla::RemoveExtent; +using mozilla::RemovePointer; + +static_assert(!IsFunction<int>::value, + "int is not a function type"); +static_assert(IsFunction<void(int)>::value, + "void(int) is a function type"); +static_assert(!IsFunction<void(*)(int)>::value, + "void(*)(int) is not a function type"); + +static_assert(!IsArray<bool>::value, + "bool not an array"); +static_assert(IsArray<bool[]>::value, + "bool[] is an array"); +static_assert(IsArray<bool[5]>::value, + "bool[5] is an array"); + +static_assert(!IsPointer<bool>::value, + "bool not a pointer"); +static_assert(IsPointer<bool*>::value, + "bool* is a pointer"); +static_assert(IsPointer<bool* const>::value, + "bool* const is a pointer"); +static_assert(IsPointer<bool* volatile>::value, + "bool* volatile is a pointer"); +static_assert(IsPointer<bool* const volatile>::value, + "bool* const volatile is a pointer"); +static_assert(IsPointer<bool**>::value, + "bool** is a pointer"); +static_assert(IsPointer<void (*)(void)>::value, + "void (*)(void) is a pointer"); +struct IsPointerTest { bool m; void f(); }; +static_assert(!IsPointer<IsPointerTest>::value, + "IsPointerTest not a pointer"); +static_assert(IsPointer<IsPointerTest*>::value, + "IsPointerTest* is a pointer"); +static_assert(!IsPointer<bool(IsPointerTest::*)>::value, + "bool(IsPointerTest::*) not a pointer"); +static_assert(!IsPointer<void(IsPointerTest::*)(void)>::value, + "void(IsPointerTest::*)(void) not a pointer"); + +static_assert(!IsLvalueReference<bool>::value, + "bool not an lvalue reference"); +static_assert(!IsLvalueReference<bool*>::value, + "bool* not an lvalue reference"); +static_assert(IsLvalueReference<bool&>::value, + "bool& is an lvalue reference"); +static_assert(!IsLvalueReference<bool&&>::value, + "bool&& not an lvalue reference"); + +static_assert(!IsLvalueReference<void>::value, + "void not an lvalue reference"); +static_assert(!IsLvalueReference<void*>::value, + "void* not an lvalue reference"); + +static_assert(!IsLvalueReference<int>::value, + "int not an lvalue reference"); +static_assert(!IsLvalueReference<int*>::value, + "int* not an lvalue reference"); +static_assert(IsLvalueReference<int&>::value, + "int& is an lvalue reference"); +static_assert(!IsLvalueReference<int&&>::value, + "int&& not an lvalue reference"); + +static_assert(!IsRvalueReference<bool>::value, + "bool not an rvalue reference"); +static_assert(!IsRvalueReference<bool*>::value, + "bool* not an rvalue reference"); +static_assert(!IsRvalueReference<bool&>::value, + "bool& not an rvalue reference"); +static_assert(IsRvalueReference<bool&&>::value, + "bool&& is an rvalue reference"); + +static_assert(!IsRvalueReference<void>::value, + "void not an rvalue reference"); +static_assert(!IsRvalueReference<void*>::value, + "void* not an rvalue reference"); + +static_assert(!IsRvalueReference<int>::value, + "int not an rvalue reference"); +static_assert(!IsRvalueReference<int*>::value, + "int* not an rvalue reference"); +static_assert(!IsRvalueReference<int&>::value, + "int& not an rvalue reference"); +static_assert(IsRvalueReference<int&&>::value, + "int&& is an rvalue reference"); + +static_assert(!IsReference<bool>::value, + "bool not a reference"); +static_assert(!IsReference<bool*>::value, + "bool* not a reference"); +static_assert(IsReference<bool&>::value, + "bool& is a reference"); +static_assert(IsReference<bool&&>::value, + "bool&& is a reference"); + +static_assert(!IsReference<void>::value, + "void not a reference"); +static_assert(!IsReference<void*>::value, + "void* not a reference"); + +static_assert(!IsReference<int>::value, + "int not a reference"); +static_assert(!IsReference<int*>::value, + "int* not a reference"); +static_assert(IsReference<int&>::value, + "int& is a reference"); +static_assert(IsReference<int&&>::value, + "int&& is a reference"); + +namespace CPlusPlus11IsMemberPointer { + +using mozilla::IsMemberPointer; + +struct S {}; +union U {}; + +#define ASSERT_IS_MEMBER_POINTER(type, msg) \ + static_assert(IsMemberPointer<type>::value, #type msg); +#define TEST_IS_MEMBER_POINTER(type) \ + TEST_CV_QUALIFIERS(ASSERT_IS_MEMBER_POINTER, type, \ + " is a member pointer type") + +TEST_IS_MEMBER_POINTER(int S::*) +TEST_IS_MEMBER_POINTER(int U::*) + +#undef TEST_IS_MEMBER_POINTER +#undef ASSERT_IS_MEMBER_POINTER + +#define ASSERT_IS_NOT_MEMBER_POINTER(type, msg) \ + static_assert(!IsMemberPointer<type>::value, #type msg); +#define TEST_IS_NOT_MEMBER_POINTER(type) \ + TEST_CV_QUALIFIERS(ASSERT_IS_NOT_MEMBER_POINTER, type, \ + " is not a member pointer type") + +TEST_IS_NOT_MEMBER_POINTER(int*) + +#undef TEST_IS_NOT_MEMBER_POINTER +#undef ASSERT_IS_NOT_MEMBER_POINTER + +} // CPlusPlus11IsMemberPointer + +namespace CPlusPlus11IsScalar { + +using mozilla::IsScalar; + +enum E {}; +enum class EC {}; +class C {}; +struct S {}; +union U {}; + +#define ASSERT_IS_SCALAR(type, msg) \ + static_assert(IsScalar<type>::value, #type msg); +#define TEST_IS_SCALAR(type) \ + TEST_CV_QUALIFIERS(ASSERT_IS_SCALAR, type, " is a scalar type") + +TEST_IS_SCALAR(int) +TEST_IS_SCALAR(float) +TEST_IS_SCALAR(E) +TEST_IS_SCALAR(EC) +TEST_IS_SCALAR(S*) +TEST_IS_SCALAR(int S::*) + +#undef TEST_IS_SCALAR +#undef ASSERT_IS_SCALAR + +#define ASSERT_IS_NOT_SCALAR(type, msg) \ + static_assert(!IsScalar<type>::value, #type msg); +#define TEST_IS_NOT_SCALAR(type) \ + TEST_CV_QUALIFIERS(ASSERT_IS_NOT_SCALAR, type, " is not a scalar type") + +TEST_IS_NOT_SCALAR(C) +TEST_IS_NOT_SCALAR(S) +TEST_IS_NOT_SCALAR(U) + +#undef TEST_IS_NOT_SCALAR +#undef ASSERT_IS_NOT_SCALAR + +} // CPlusPlus11IsScalar + +struct S1 {}; +union U1 { int mX; }; + +static_assert(!IsClass<int>::value, + "int isn't a class"); +static_assert(IsClass<const S1>::value, + "S is a class"); +static_assert(!IsClass<U1>::value, + "U isn't a class"); + +static_assert(!mozilla::IsEmpty<int>::value, + "not a class => not empty"); +static_assert(!mozilla::IsEmpty<bool[5]>::value, + "not a class => not empty"); + +static_assert(!mozilla::IsEmpty<U1>::value, + "not a class => not empty"); + +struct E1 {}; +struct E2 { int : 0; }; +struct E3 : E1 {}; +struct E4 : E2 {}; + +static_assert(IsEmpty<const volatile S1>::value, + "S should be empty"); + +static_assert(mozilla::IsEmpty<E1>::value && + mozilla::IsEmpty<E2>::value && + mozilla::IsEmpty<E3>::value && + mozilla::IsEmpty<E4>::value, + "all empty"); + +union U2 { E1 e1; }; +static_assert(!mozilla::IsEmpty<U2>::value, + "not a class => not empty"); + +struct NE1 { int mX; }; +struct NE2 : virtual E1 {}; +struct NE3 : E2 { virtual ~NE3() {} }; +struct NE4 { virtual void f() {} }; + +static_assert(!mozilla::IsEmpty<NE1>::value && + !mozilla::IsEmpty<NE2>::value && + !mozilla::IsEmpty<NE3>::value && + !mozilla::IsEmpty<NE4>::value, + "all empty"); + +static_assert(!IsSigned<bool>::value, + "bool shouldn't be signed"); +static_assert(IsUnsigned<bool>::value, + "bool should be unsigned"); + +static_assert(!IsSigned<const bool>::value, + "const bool shouldn't be signed"); +static_assert(IsUnsigned<const bool>::value, + "const bool should be unsigned"); + +static_assert(!IsSigned<volatile bool>::value, + "volatile bool shouldn't be signed"); +static_assert(IsUnsigned<volatile bool>::value, + "volatile bool should be unsigned"); + +static_assert(!IsSigned<unsigned char>::value, + "unsigned char shouldn't be signed"); +static_assert(IsUnsigned<unsigned char>::value, + "unsigned char should be unsigned"); +static_assert(IsSigned<signed char>::value, + "signed char should be signed"); +static_assert(!IsUnsigned<signed char>::value, + "signed char shouldn't be unsigned"); + +static_assert(!IsSigned<unsigned short>::value, + "unsigned short shouldn't be signed"); +static_assert(IsUnsigned<unsigned short>::value, + "unsigned short should be unsigned"); +static_assert(IsSigned<short>::value, + "short should be signed"); +static_assert(!IsUnsigned<short>::value, + "short shouldn't be unsigned"); + +static_assert(!IsSigned<unsigned int>::value, + "unsigned int shouldn't be signed"); +static_assert(IsUnsigned<unsigned int>::value, + "unsigned int should be unsigned"); +static_assert(IsSigned<int>::value, + "int should be signed"); +static_assert(!IsUnsigned<int>::value, + "int shouldn't be unsigned"); + +static_assert(!IsSigned<unsigned long>::value, + "unsigned long shouldn't be signed"); +static_assert(IsUnsigned<unsigned long>::value, + "unsigned long should be unsigned"); +static_assert(IsSigned<long>::value, + "long should be signed"); +static_assert(!IsUnsigned<long>::value, + "long shouldn't be unsigned"); + +static_assert(IsSigned<float>::value, + "float should be signed"); +static_assert(!IsUnsigned<float>::value, + "float shouldn't be unsigned"); + +static_assert(IsSigned<const float>::value, + "const float should be signed"); +static_assert(!IsUnsigned<const float>::value, + "const float shouldn't be unsigned"); + +static_assert(IsSigned<double>::value, + "double should be signed"); +static_assert(!IsUnsigned<double>::value, + "double shouldn't be unsigned"); + +static_assert(IsSigned<volatile double>::value, + "volatile double should be signed"); +static_assert(!IsUnsigned<volatile double>::value, + "volatile double shouldn't be unsigned"); + +static_assert(IsSigned<long double>::value, + "long double should be signed"); +static_assert(!IsUnsigned<long double>::value, + "long double shouldn't be unsigned"); + +static_assert(IsSigned<const volatile long double>::value, + "const volatile long double should be signed"); +static_assert(!IsUnsigned<const volatile long double>::value, + "const volatile long double shouldn't be unsigned"); + +class NotIntConstructible +{ + NotIntConstructible(int) = delete; +}; + +static_assert(!IsSigned<NotIntConstructible>::value, + "non-arithmetic types are not signed"); +static_assert(!IsUnsigned<NotIntConstructible>::value, + "non-arithmetic types are not unsigned"); + +class PublicDestructible +{ +public: + ~PublicDestructible(); +}; +class PrivateDestructible +{ +private: + ~PrivateDestructible(); +}; +class TrivialDestructible +{ +}; + +static_assert(IsDestructible<PublicDestructible>::value, + "public destructible class is destructible"); +static_assert(!IsDestructible<PrivateDestructible>::value, + "private destructible class is not destructible"); +static_assert(IsDestructible<TrivialDestructible>::value, + "trivial destructible class is destructible"); + +namespace CPlusPlus11IsBaseOf { + +// Adapted from C++11 § 20.9.6. +struct B {}; +struct B1 : B {}; +struct B2 : B {}; +struct D : private B1, private B2 {}; + +static void +StandardIsBaseOfTests() +{ + static_assert((IsBaseOf<B, D>::value) == true, + "IsBaseOf fails on diamond"); + static_assert((IsBaseOf<const B, D>::value) == true, + "IsBaseOf fails on diamond plus constness change"); + static_assert((IsBaseOf<B, const D>::value) == true, + "IsBaseOf fails on diamond plus constness change"); + static_assert((IsBaseOf<B, const B>::value) == true, + "IsBaseOf fails on constness change"); + static_assert((IsBaseOf<D, B>::value) == false, + "IsBaseOf got the direction of inheritance wrong"); + static_assert((IsBaseOf<B&, D&>::value) == false, + "IsBaseOf should return false on references"); + static_assert((IsBaseOf<B[3], D[3]>::value) == false, + "IsBaseOf should return false on arrays"); + // We fail at the following test. To fix it, we need to specialize IsBaseOf + // for all built-in types. + // static_assert((IsBaseOf<int, int>::value) == false); +} + +} /* namespace CPlusPlus11IsBaseOf */ + +class A { }; +class B : public A { }; +class C : private A { }; +class D { }; +class E : public A { }; +class F : public B, public E { }; + +static void +TestIsBaseOf() +{ + static_assert((IsBaseOf<A, B>::value), + "A is a base of B"); + static_assert((!IsBaseOf<B, A>::value), + "B is not a base of A"); + static_assert((IsBaseOf<A, C>::value), + "A is a base of C"); + static_assert((!IsBaseOf<C, A>::value), + "C is not a base of A"); + static_assert((IsBaseOf<A, F>::value), + "A is a base of F"); + static_assert((!IsBaseOf<F, A>::value), + "F is not a base of A"); + static_assert((!IsBaseOf<A, D>::value), + "A is not a base of D"); + static_assert((!IsBaseOf<D, A>::value), + "D is not a base of A"); + static_assert((IsBaseOf<B, B>::value), + "B is the same as B (and therefore, a base of B)"); +} + +class ExplicitCopyConstructor { + explicit ExplicitCopyConstructor(const ExplicitCopyConstructor&) = default; +}; + +static void +TestIsConvertible() +{ + // Pointer type convertibility + static_assert((IsConvertible<A*, A*>::value), + "A* should convert to A*"); + static_assert((IsConvertible<B*, A*>::value), + "B* should convert to A*"); + static_assert((!IsConvertible<A*, B*>::value), + "A* shouldn't convert to B*"); + static_assert((!IsConvertible<A*, C*>::value), + "A* shouldn't convert to C*"); + static_assert((!IsConvertible<A*, D*>::value), + "A* shouldn't convert to unrelated D*"); + static_assert((!IsConvertible<D*, A*>::value), + "D* shouldn't convert to unrelated A*"); + + // Instance type convertibility + static_assert((IsConvertible<A, A>::value), + "A is A"); + static_assert((IsConvertible<B, A>::value), + "B converts to A"); + static_assert((!IsConvertible<D, A>::value), + "D and A are unrelated"); + static_assert((!IsConvertible<A, D>::value), + "A and D are unrelated"); + + static_assert(IsConvertible<void, void>::value, "void is void"); + static_assert(!IsConvertible<A, void>::value, "A shouldn't convert to void"); + static_assert(!IsConvertible<void, B>::value, "void shouldn't convert to B"); + + static_assert(!IsConvertible<const ExplicitCopyConstructor&, + ExplicitCopyConstructor>::value, + "IsConvertible should test for implicit convertibility"); + + // These cases seem to require C++11 support to properly implement them, so + // for now just disable them. + //static_assert((!IsConvertible<C*, A*>::value), + // "C* shouldn't convert to A* (private inheritance)"); + //static_assert((!IsConvertible<C, A>::value), + // "C doesn't convert to A (private inheritance)"); +} + +static_assert(IsSame<AddLvalueReference<int>::Type, int&>::value, + "not adding & to int correctly"); +static_assert(IsSame<AddLvalueReference<volatile int&>::Type, volatile int&>::value, + "not adding & to volatile int& correctly"); +static_assert(IsSame<AddLvalueReference<void*>::Type, void*&>::value, + "not adding & to void* correctly"); +static_assert(IsSame<AddLvalueReference<void>::Type, void>::value, + "void shouldn't be transformed by AddLvalueReference"); +static_assert(IsSame<AddLvalueReference<struct S1&&>::Type, struct S1&>::value, + "not reference-collapsing struct S1&& & to struct S1& correctly"); + +static_assert(IsSame<AddRvalueReference<int>::Type, int&&>::value, + "not adding && to int correctly"); +static_assert(IsSame<AddRvalueReference<volatile int&>::Type, volatile int&>::value, + "not adding && to volatile int& correctly"); +static_assert(IsSame<AddRvalueReference<const int&&>::Type, const int&&>::value, + "not adding && to volatile int& correctly"); +static_assert(IsSame<AddRvalueReference<void*>::Type, void*&&>::value, + "not adding && to void* correctly"); +static_assert(IsSame<AddRvalueReference<void>::Type, void>::value, + "void shouldn't be transformed by AddRvalueReference"); +static_assert(IsSame<AddRvalueReference<struct S1&>::Type, struct S1&>::value, + "not reference-collapsing struct S1& && to struct S1& correctly"); + +struct TestWithDefaultConstructor +{ + int foo() const { return 0; } +}; +struct TestWithNoDefaultConstructor +{ + explicit TestWithNoDefaultConstructor(int) {} + int foo() const { return 1; } +}; + +static_assert(IsSame<decltype(TestWithDefaultConstructor().foo()), int>::value, + "decltype should work using a struct with a default constructor"); +static_assert(IsSame<decltype(DeclVal<TestWithDefaultConstructor>().foo()), int>::value, + "decltype should work using a DeclVal'd struct with a default constructor"); +static_assert(IsSame<decltype(DeclVal<TestWithNoDefaultConstructor>().foo()), int>::value, + "decltype should work using a DeclVal'd struct without a default constructor"); + +static_assert(IsSame<MakeSigned<const unsigned char>::Type, const signed char>::value, + "const unsigned char won't signify correctly"); +static_assert(IsSame<MakeSigned<volatile unsigned short>::Type, volatile signed short>::value, + "volatile unsigned short won't signify correctly"); +static_assert(IsSame<MakeSigned<const volatile unsigned int>::Type, const volatile signed int>::value, + "const volatile unsigned int won't signify correctly"); +static_assert(IsSame<MakeSigned<unsigned long>::Type, signed long>::value, + "unsigned long won't signify correctly"); +static_assert(IsSame<MakeSigned<const signed char>::Type, const signed char>::value, + "const signed char won't signify correctly"); + +static_assert(IsSame<MakeSigned<volatile signed short>::Type, volatile signed short>::value, + "volatile signed short won't signify correctly"); +static_assert(IsSame<MakeSigned<const volatile signed int>::Type, const volatile signed int>::value, + "const volatile signed int won't signify correctly"); +static_assert(IsSame<MakeSigned<signed long>::Type, signed long>::value, + "signed long won't signify correctly"); + +static_assert(IsSame<MakeSigned<char>::Type, signed char>::value, + "char won't signify correctly"); +static_assert(IsSame<MakeSigned<volatile char>::Type, volatile signed char>::value, + "volatile char won't signify correctly"); +static_assert(IsSame<MakeSigned<const char>::Type, const signed char>::value, + "const char won't signify correctly"); + +static_assert(IsSame<MakeUnsigned<const signed char>::Type, const unsigned char>::value, + "const signed char won't unsignify correctly"); +static_assert(IsSame<MakeUnsigned<volatile signed short>::Type, volatile unsigned short>::value, + "volatile signed short won't unsignify correctly"); +static_assert(IsSame<MakeUnsigned<const volatile signed int>::Type, const volatile unsigned int>::value, + "const volatile signed int won't unsignify correctly"); +static_assert(IsSame<MakeUnsigned<signed long>::Type, unsigned long>::value, + "signed long won't unsignify correctly"); + +static_assert(IsSame<MakeUnsigned<const unsigned char>::Type, const unsigned char>::value, + "const unsigned char won't unsignify correctly"); + +static_assert(IsSame<MakeUnsigned<volatile unsigned short>::Type, volatile unsigned short>::value, + "volatile unsigned short won't unsignify correctly"); +static_assert(IsSame<MakeUnsigned<const volatile unsigned int>::Type, const volatile unsigned int>::value, + "const volatile unsigned int won't unsignify correctly"); +static_assert(IsSame<MakeUnsigned<unsigned long>::Type, unsigned long>::value, + "signed long won't unsignify correctly"); + +static_assert(IsSame<MakeUnsigned<char>::Type, unsigned char>::value, + "char won't unsignify correctly"); +static_assert(IsSame<MakeUnsigned<volatile char>::Type, volatile unsigned char>::value, + "volatile char won't unsignify correctly"); +static_assert(IsSame<MakeUnsigned<const char>::Type, const unsigned char>::value, + "const char won't unsignify correctly"); + +static_assert(IsSame<RemoveExtent<int>::Type, int>::value, + "removing extent from non-array must return the non-array"); +static_assert(IsSame<RemoveExtent<const int[]>::Type, const int>::value, + "removing extent from unknown-bound array must return element type"); +static_assert(IsSame<RemoveExtent<volatile int[5]>::Type, volatile int>::value, + "removing extent from known-bound array must return element type"); +static_assert(IsSame<RemoveExtent<long[][17]>::Type, long[17]>::value, + "removing extent from multidimensional array must return element type"); + +struct TestRemovePointer { bool m; void f(); }; +static_assert(IsSame<RemovePointer<int>::Type, int>::value, + "removing pointer from int must return int"); +static_assert(IsSame<RemovePointer<int*>::Type, int>::value, + "removing pointer from int* must return int"); +static_assert(IsSame<RemovePointer<int* const>::Type, int>::value, + "removing pointer from int* const must return int"); +static_assert(IsSame<RemovePointer<int* volatile>::Type, int>::value, + "removing pointer from int* volatile must return int"); +static_assert(IsSame<RemovePointer<const long*>::Type, const long>::value, + "removing pointer from const long* must return const long"); +static_assert(IsSame<RemovePointer<void* const>::Type, void>::value, + "removing pointer from void* const must return void"); +static_assert(IsSame<RemovePointer<void (TestRemovePointer::*)()>::Type, + void (TestRemovePointer::*)()>::value, + "removing pointer from void (S::*)() must return void (S::*)()"); +static_assert(IsSame<RemovePointer<void (*)()>::Type, void()>::value, + "removing pointer from void (*)() must return void()"); +static_assert(IsSame<RemovePointer<bool TestRemovePointer::*>::Type, + bool TestRemovePointer::*>::value, + "removing pointer from bool S::* must return bool S::*"); + +static_assert(IsSame<AddPointer<int>::Type, int*>::value, + "adding pointer to int must return int*"); +static_assert(IsSame<AddPointer<int*>::Type, int**>::value, + "adding pointer to int* must return int**"); +static_assert(IsSame<AddPointer<int&>::Type, int*>::value, + "adding pointer to int& must return int*"); +static_assert(IsSame<AddPointer<int* const>::Type, int* const*>::value, + "adding pointer to int* const must return int* const*"); +static_assert(IsSame<AddPointer<int* volatile>::Type, int* volatile*>::value, + "adding pointer to int* volatile must return int* volatile*"); + +static_assert(IsSame<Decay<int>::Type, int>::value, + "decaying int must return int"); +static_assert(IsSame<Decay<int*>::Type, int*>::value, + "decaying int* must return int*"); +static_assert(IsSame<Decay<int* const>::Type, int*>::value, + "decaying int* const must return int*"); +static_assert(IsSame<Decay<int* volatile>::Type, int*>::value, + "decaying int* volatile must return int*"); +static_assert(IsSame<Decay<int&>::Type, int>::value, + "decaying int& must return int"); +static_assert(IsSame<Decay<const int&>::Type, int>::value, + "decaying const int& must return int"); +static_assert(IsSame<Decay<int&&>::Type, int>::value, + "decaying int&& must return int"); +static_assert(IsSame<Decay<int[1]>::Type, int*>::value, + "decaying int[1] must return int*"); +static_assert(IsSame<Decay<void(int)>::Type, void(*)(int)>::value, + "decaying void(int) must return void(*)(int)"); + +/* + * Android's broken [u]intptr_t inttype macros are broken because its PRI*PTR + * macros are defined as "ld", but sizeof(long) is 8 and sizeof(intptr_t) + * is 4 on 32-bit Android. We redefine Android's PRI*PTR macros in + * IntegerPrintfMacros.h and assert here that our new definitions match the + * actual type sizes seen at compile time. + */ +#if defined(ANDROID) && !defined(__LP64__) +static_assert(mozilla::IsSame<int, intptr_t>::value, + "emulated PRI[di]PTR definitions will be wrong"); +static_assert(mozilla::IsSame<unsigned int, uintptr_t>::value, + "emulated PRI[ouxX]PTR definitions will be wrong"); +#endif + +int +main() +{ + CPlusPlus11IsBaseOf::StandardIsBaseOfTests(); + TestIsBaseOf(); + TestIsConvertible(); + return 0; +} diff --git a/mfbt/tests/TestTypedEnum.cpp b/mfbt/tests/TestTypedEnum.cpp new file mode 100644 index 000000000..43c36f2b8 --- /dev/null +++ b/mfbt/tests/TestTypedEnum.cpp @@ -0,0 +1,556 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" +#include "mozilla/TypedEnumBits.h" + +#include <stdint.h> + +// A rough feature check for is_literal_type. Not very carefully checked. +// Feel free to amend as needed. +// We leave ANDROID out because it's using stlport which doesn't have std::is_literal_type. +#if __cplusplus >= 201103L && !defined(ANDROID) +# if defined(__clang__) + /* + * Per Clang documentation, "Note that marketing version numbers should not + * be used to check for language features, as different vendors use different + * numbering schemes. Instead, use the feature checking macros." + */ +# ifndef __has_extension +# define __has_extension __has_feature /* compatibility, for older versions of clang */ +# endif +# if __has_extension(is_literal) && __has_include(<type_traits>) +# define MOZ_HAVE_IS_LITERAL +# endif +# elif defined(__GNUC__) || defined(_MSC_VER) +# define MOZ_HAVE_IS_LITERAL +# endif +#endif + +#if defined(MOZ_HAVE_IS_LITERAL) && defined(MOZ_HAVE_CXX11_CONSTEXPR) +#include <type_traits> +template<typename T> +void +RequireLiteralType() +{ + static_assert(std::is_literal_type<T>::value, "Expected a literal type"); +} +#else // not MOZ_HAVE_IS_LITERAL +template<typename T> +void +RequireLiteralType() +{ +} +#endif + +template<typename T> +void +RequireLiteralType(const T&) +{ + RequireLiteralType<T>(); +} + +enum class AutoEnum { + A, + B = -3, + C +}; + +enum class CharEnum : char { + A, + B = 3, + C +}; + +enum class AutoEnumBitField { + A = 0x10, + B = 0x20, + C +}; + +enum class CharEnumBitField : char { + A = 0x10, + B, + C = 0x40 +}; + +struct Nested +{ + enum class AutoEnum { + A, + B, + C = -1 + }; + + enum class CharEnum : char { + A = 4, + B, + C = 1 + }; + + enum class AutoEnumBitField { + A, + B = 0x20, + C + }; + + enum class CharEnumBitField : char { + A = 1, + B = 1, + C = 1 + }; +}; + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AutoEnumBitField) +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CharEnumBitField) +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Nested::AutoEnumBitField) +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Nested::CharEnumBitField) + +#define MAKE_STANDARD_BITFIELD_FOR_TYPE(IntType) \ + enum class BitFieldFor_##IntType : IntType { \ + A = 1, \ + B = 2, \ + C = 4, \ + }; \ + MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(BitFieldFor_##IntType) + +MAKE_STANDARD_BITFIELD_FOR_TYPE(int8_t) +MAKE_STANDARD_BITFIELD_FOR_TYPE(uint8_t) +MAKE_STANDARD_BITFIELD_FOR_TYPE(int16_t) +MAKE_STANDARD_BITFIELD_FOR_TYPE(uint16_t) +MAKE_STANDARD_BITFIELD_FOR_TYPE(int32_t) +MAKE_STANDARD_BITFIELD_FOR_TYPE(uint32_t) +MAKE_STANDARD_BITFIELD_FOR_TYPE(int64_t) +MAKE_STANDARD_BITFIELD_FOR_TYPE(uint64_t) +MAKE_STANDARD_BITFIELD_FOR_TYPE(char) +typedef signed char signed_char; +MAKE_STANDARD_BITFIELD_FOR_TYPE(signed_char) +typedef unsigned char unsigned_char; +MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_char) +MAKE_STANDARD_BITFIELD_FOR_TYPE(short) +typedef unsigned short unsigned_short; +MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_short) +MAKE_STANDARD_BITFIELD_FOR_TYPE(int) +typedef unsigned int unsigned_int; +MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_int) +MAKE_STANDARD_BITFIELD_FOR_TYPE(long) +typedef unsigned long unsigned_long; +MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_long) +typedef long long long_long; +MAKE_STANDARD_BITFIELD_FOR_TYPE(long_long) +typedef unsigned long long unsigned_long_long; +MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_long_long) + +#undef MAKE_STANDARD_BITFIELD_FOR_TYPE + +template<typename T> +void +TestNonConvertibilityForOneType() +{ + using mozilla::IsConvertible; + + static_assert(!IsConvertible<T, bool>::value, "should not be convertible"); + static_assert(!IsConvertible<T, int>::value, "should not be convertible"); + static_assert(!IsConvertible<T, uint64_t>::value, "should not be convertible"); + + static_assert(!IsConvertible<bool, T>::value, "should not be convertible"); + static_assert(!IsConvertible<int, T>::value, "should not be convertible"); + static_assert(!IsConvertible<uint64_t, T>::value, "should not be convertible"); +} + +template<typename TypedEnum> +void +TestTypedEnumBasics() +{ + const TypedEnum a = TypedEnum::A; + int unused = int(a); + (void) unused; + RequireLiteralType(TypedEnum::A); + RequireLiteralType(a); + TestNonConvertibilityForOneType<TypedEnum>(); +} + +// Op wraps a bitwise binary operator, passed as a char template parameter, +// and applies it to its arguments (aT1, aT2). For example, +// +// Op<'|'>(aT1, aT2) +// +// is the same as +// +// aT1 | aT2. +// +template<char o, typename T1, typename T2> +auto Op(const T1& aT1, const T2& aT2) + -> decltype(aT1 | aT2) // See the static_assert's below --- the return type + // depends solely on the operands type, not on the + // choice of operation. +{ + using mozilla::IsSame; + static_assert(IsSame<decltype(aT1 | aT2), decltype(aT1 & aT2)>::value, + "binary ops should have the same result type"); + static_assert(IsSame<decltype(aT1 | aT2), decltype(aT1 ^ aT2)>::value, + "binary ops should have the same result type"); + + static_assert(o == '|' || + o == '&' || + o == '^', "unexpected operator character"); + + return o == '|' ? aT1 | aT2 + : o == '&' ? aT1 & aT2 + : aT1 ^ aT2; +} + +// OpAssign wraps a bitwise binary operator, passed as a char template +// parameter, and applies the corresponding compound-assignment operator to its +// arguments (aT1, aT2). For example, +// +// OpAssign<'|'>(aT1, aT2) +// +// is the same as +// +// aT1 |= aT2. +// +template<char o, typename T1, typename T2> +T1& OpAssign(T1& aT1, const T2& aT2) +{ + static_assert(o == '|' || + o == '&' || + o == '^', "unexpected operator character"); + + switch (o) { + case '|': return aT1 |= aT2; + case '&': return aT1 &= aT2; + case '^': return aT1 ^= aT2; + default: MOZ_CRASH(); + } +} + +// Tests a single binary bitwise operator, using a single set of three operands. +// The operations tested are: +// +// result = aT1 Op aT2; +// result Op= aT3; +// +// Where Op is the operator specified by the char template parameter 'o' and +// can be any of '|', '&', '^'. +// +// Note that the operands aT1, aT2, aT3 are intentionally passed with free +// types (separate template parameters for each) because their type may +// actually be different from TypedEnum: +// +// 1) Their type could be CastableTypedEnumResult<TypedEnum> if they are +// the result of a bitwise operation themselves; +// 2) In the non-c++11 legacy path, the type of enum values is also +// different from TypedEnum. +// +template<typename TypedEnum, char o, typename T1, typename T2, typename T3> +void TestBinOp(const T1& aT1, const T2& aT2, const T3& aT3) +{ + typedef typename mozilla::detail::UnsignedIntegerTypeForEnum<TypedEnum>::Type + UnsignedIntegerType; + + // Part 1: + // Test the bitwise binary operator i.e. + // result = aT1 Op aT2; + auto result = Op<o>(aT1, aT2); + + typedef decltype(result) ResultType; + + RequireLiteralType<ResultType>(); + TestNonConvertibilityForOneType<ResultType>(); + + UnsignedIntegerType unsignedIntegerResult = + Op<o>(UnsignedIntegerType(aT1), UnsignedIntegerType(aT2)); + + MOZ_RELEASE_ASSERT(unsignedIntegerResult == UnsignedIntegerType(result)); + MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerResult) == TypedEnum(result)); + MOZ_RELEASE_ASSERT((!unsignedIntegerResult) == (!result)); + MOZ_RELEASE_ASSERT((!!unsignedIntegerResult) == (!!result)); + MOZ_RELEASE_ASSERT(bool(unsignedIntegerResult) == bool(result)); + + // Part 2: + // Test the compound-assignment operator, i.e. + // result Op= aT3; + TypedEnum newResult = result; + OpAssign<o>(newResult, aT3); + UnsignedIntegerType unsignedIntegerNewResult = unsignedIntegerResult; + OpAssign<o>(unsignedIntegerNewResult, UnsignedIntegerType(aT3)); + MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerNewResult) == newResult); + + // Part 3: + // Test additional boolean operators that we unfortunately had to add to + // CastableTypedEnumResult at some point to please some compiler, + // even though bool convertibility should have been enough. + MOZ_RELEASE_ASSERT(result == TypedEnum(result)); + MOZ_RELEASE_ASSERT(!(result != TypedEnum(result))); + MOZ_RELEASE_ASSERT((result && true) == bool(result)); + MOZ_RELEASE_ASSERT((result && false) == false); + MOZ_RELEASE_ASSERT((true && result) == bool(result)); + MOZ_RELEASE_ASSERT((false && result && false) == false); + MOZ_RELEASE_ASSERT((result || false) == bool(result)); + MOZ_RELEASE_ASSERT((result || true) == true); + MOZ_RELEASE_ASSERT((false || result) == bool(result)); + MOZ_RELEASE_ASSERT((true || result) == true); +} + +// Similar to TestBinOp but testing the unary ~ operator. +template<typename TypedEnum, typename T> +void TestTilde(const T& aT) +{ + typedef typename mozilla::detail::UnsignedIntegerTypeForEnum<TypedEnum>::Type + UnsignedIntegerType; + + auto result = ~aT; + + typedef decltype(result) ResultType; + + RequireLiteralType<ResultType>(); + TestNonConvertibilityForOneType<ResultType>(); + + UnsignedIntegerType unsignedIntegerResult = ~(UnsignedIntegerType(aT)); + + MOZ_RELEASE_ASSERT(unsignedIntegerResult == UnsignedIntegerType(result)); + MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerResult) == TypedEnum(result)); + MOZ_RELEASE_ASSERT((!unsignedIntegerResult) == (!result)); + MOZ_RELEASE_ASSERT((!!unsignedIntegerResult) == (!!result)); + MOZ_RELEASE_ASSERT(bool(unsignedIntegerResult) == bool(result)); +} + +// Helper dispatching a given triple of operands to all operator-specific +// testing functions. +template<typename TypedEnum, typename T1, typename T2, typename T3> +void TestAllOpsForGivenOperands(const T1& aT1, const T2& aT2, const T3& aT3) +{ + TestBinOp<TypedEnum, '|'>(aT1, aT2, aT3); + TestBinOp<TypedEnum, '&'>(aT1, aT2, aT3); + TestBinOp<TypedEnum, '^'>(aT1, aT2, aT3); + TestTilde<TypedEnum>(aT1); +} + +// Helper building various triples of operands using a given operator, +// and testing all operators with them. +template<typename TypedEnum, char o> +void TestAllOpsForOperandsBuiltUsingGivenOp() +{ + // The type of enum values like TypedEnum::A may be different from + // TypedEnum. That is the case in the legacy non-C++11 path. We want to + // ensure good test coverage even when these two types are distinct. + // To that effect, we have both 'auto' typed variables, preserving the + // original type of enum values, and 'plain' typed variables, that + // are plain TypedEnum's. + + const TypedEnum a_plain = TypedEnum::A; + const TypedEnum b_plain = TypedEnum::B; + const TypedEnum c_plain = TypedEnum::C; + + auto a_auto = TypedEnum::A; + auto b_auto = TypedEnum::B; + auto c_auto = TypedEnum::C; + + auto ab_plain = Op<o>(a_plain, b_plain); + auto bc_plain = Op<o>(b_plain, c_plain); + auto ab_auto = Op<o>(a_auto, b_auto); + auto bc_auto = Op<o>(b_auto, c_auto); + + // On each row below, we pass a triple of operands. Keep in mind that this + // is going to be received as (aT1, aT2, aT3) and the actual tests performed + // will be of the form + // + // result = aT1 Op aT2; + // result Op= aT3; + // + // For this reason, we carefully ensure that the values of (aT1, aT2) + // systematically cover all types of such pairs; to limit complexity, + // we are not so careful with aT3, and we just try to pass aT3's + // that may lead to nontrivial bitwise operations. + TestAllOpsForGivenOperands<TypedEnum>(a_plain, b_plain, c_plain); + TestAllOpsForGivenOperands<TypedEnum>(a_plain, bc_plain, b_auto); + TestAllOpsForGivenOperands<TypedEnum>(ab_plain, c_plain, a_plain); + TestAllOpsForGivenOperands<TypedEnum>(ab_plain, bc_plain, a_auto); + + TestAllOpsForGivenOperands<TypedEnum>(a_plain, b_auto, c_plain); + TestAllOpsForGivenOperands<TypedEnum>(a_plain, bc_auto, b_auto); + TestAllOpsForGivenOperands<TypedEnum>(ab_plain, c_auto, a_plain); + TestAllOpsForGivenOperands<TypedEnum>(ab_plain, bc_auto, a_auto); + + TestAllOpsForGivenOperands<TypedEnum>(a_auto, b_plain, c_plain); + TestAllOpsForGivenOperands<TypedEnum>(a_auto, bc_plain, b_auto); + TestAllOpsForGivenOperands<TypedEnum>(ab_auto, c_plain, a_plain); + TestAllOpsForGivenOperands<TypedEnum>(ab_auto, bc_plain, a_auto); + + TestAllOpsForGivenOperands<TypedEnum>(a_auto, b_auto, c_plain); + TestAllOpsForGivenOperands<TypedEnum>(a_auto, bc_auto, b_auto); + TestAllOpsForGivenOperands<TypedEnum>(ab_auto, c_auto, a_plain); + TestAllOpsForGivenOperands<TypedEnum>(ab_auto, bc_auto, a_auto); +} + +// Tests all bitwise operations on a given TypedEnum bitfield. +template<typename TypedEnum> +void +TestTypedEnumBitField() +{ + TestTypedEnumBasics<TypedEnum>(); + + TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '|'>(); + TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '&'>(); + TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '^'>(); +} + +// Checks that enum bitwise expressions have the same non-convertibility +// properties as c++11 enum classes do, i.e. not implicitly convertible to +// anything (though *explicitly* convertible). +void TestNoConversionsBetweenUnrelatedTypes() +{ + using mozilla::IsConvertible; + + // Two typed enum classes having the same underlying integer type, to ensure + // that we would catch bugs accidentally allowing conversions in that case. + typedef CharEnumBitField T1; + typedef Nested::CharEnumBitField T2; + + static_assert(!IsConvertible<T1, T2>::value, + "should not be convertible"); + static_assert(!IsConvertible<T1, decltype(T2::A)>::value, + "should not be convertible"); + static_assert(!IsConvertible<T1, decltype(T2::A | T2::B)>::value, + "should not be convertible"); + + static_assert(!IsConvertible<decltype(T1::A), T2>::value, + "should not be convertible"); + static_assert(!IsConvertible<decltype(T1::A), decltype(T2::A)>::value, + "should not be convertible"); + static_assert(!IsConvertible<decltype(T1::A), decltype(T2::A | T2::B)>::value, + "should not be convertible"); + + static_assert(!IsConvertible<decltype(T1::A | T1::B), T2>::value, + "should not be convertible"); + static_assert(!IsConvertible<decltype(T1::A | T1::B), decltype(T2::A)>::value, + "should not be convertible"); + static_assert(!IsConvertible<decltype(T1::A | T1::B), decltype(T2::A | T2::B)>::value, + "should not be convertible"); +} + +enum class Int8EnumWithHighBits : int8_t { + A = 0x20, + B = 0x40 +}; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int8EnumWithHighBits) + +enum class Uint8EnumWithHighBits : uint8_t { + A = 0x40, + B = 0x80 +}; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint8EnumWithHighBits) + +enum class Int16EnumWithHighBits : int16_t { + A = 0x2000, + B = 0x4000 +}; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int16EnumWithHighBits) + +enum class Uint16EnumWithHighBits : uint16_t { + A = 0x4000, + B = 0x8000 +}; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint16EnumWithHighBits) + +enum class Int32EnumWithHighBits : int32_t { + A = 0x20000000, + B = 0x40000000 +}; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int32EnumWithHighBits) + +enum class Uint32EnumWithHighBits : uint32_t { + A = 0x40000000u, + B = 0x80000000u +}; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint32EnumWithHighBits) + +enum class Int64EnumWithHighBits : int64_t { + A = 0x2000000000000000ll, + B = 0x4000000000000000ll +}; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int64EnumWithHighBits) + +enum class Uint64EnumWithHighBits : uint64_t { + A = 0x4000000000000000ull, + B = 0x8000000000000000ull +}; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint64EnumWithHighBits) + +// Checks that we don't accidentally truncate high bits by coercing to the wrong +// integer type internally when implementing bitwise ops. +template<typename EnumType, typename IntType> +void TestIsNotTruncated() +{ + EnumType a = EnumType::A; + EnumType b = EnumType::B; + MOZ_RELEASE_ASSERT(IntType(a)); + MOZ_RELEASE_ASSERT(IntType(b)); + MOZ_RELEASE_ASSERT(a | EnumType::B); + MOZ_RELEASE_ASSERT(a | b); + MOZ_RELEASE_ASSERT(EnumType::A | EnumType::B); + EnumType c = EnumType::A | EnumType::B; + MOZ_RELEASE_ASSERT(IntType(c)); + MOZ_RELEASE_ASSERT(c & c); + MOZ_RELEASE_ASSERT(c | c); + MOZ_RELEASE_ASSERT(c == (EnumType::A | EnumType::B)); + MOZ_RELEASE_ASSERT(a != (EnumType::A | EnumType::B)); + MOZ_RELEASE_ASSERT(b != (EnumType::A | EnumType::B)); + MOZ_RELEASE_ASSERT(c & EnumType::A); + MOZ_RELEASE_ASSERT(c & EnumType::B); + EnumType d = EnumType::A; + d |= EnumType::B; + MOZ_RELEASE_ASSERT(d == c); +} + +int +main() +{ + TestTypedEnumBasics<AutoEnum>(); + TestTypedEnumBasics<CharEnum>(); + TestTypedEnumBasics<Nested::AutoEnum>(); + TestTypedEnumBasics<Nested::CharEnum>(); + + TestTypedEnumBitField<AutoEnumBitField>(); + TestTypedEnumBitField<CharEnumBitField>(); + TestTypedEnumBitField<Nested::AutoEnumBitField>(); + TestTypedEnumBitField<Nested::CharEnumBitField>(); + + TestTypedEnumBitField<BitFieldFor_uint8_t>(); + TestTypedEnumBitField<BitFieldFor_int8_t>(); + TestTypedEnumBitField<BitFieldFor_uint16_t>(); + TestTypedEnumBitField<BitFieldFor_int16_t>(); + TestTypedEnumBitField<BitFieldFor_uint32_t>(); + TestTypedEnumBitField<BitFieldFor_int32_t>(); + TestTypedEnumBitField<BitFieldFor_uint64_t>(); + TestTypedEnumBitField<BitFieldFor_int64_t>(); + TestTypedEnumBitField<BitFieldFor_char>(); + TestTypedEnumBitField<BitFieldFor_signed_char>(); + TestTypedEnumBitField<BitFieldFor_unsigned_char>(); + TestTypedEnumBitField<BitFieldFor_short>(); + TestTypedEnumBitField<BitFieldFor_unsigned_short>(); + TestTypedEnumBitField<BitFieldFor_int>(); + TestTypedEnumBitField<BitFieldFor_unsigned_int>(); + TestTypedEnumBitField<BitFieldFor_long>(); + TestTypedEnumBitField<BitFieldFor_unsigned_long>(); + TestTypedEnumBitField<BitFieldFor_long_long>(); + TestTypedEnumBitField<BitFieldFor_unsigned_long_long>(); + + TestNoConversionsBetweenUnrelatedTypes(); + + TestIsNotTruncated<Int8EnumWithHighBits, int8_t>(); + TestIsNotTruncated<Int16EnumWithHighBits, int16_t>(); + TestIsNotTruncated<Int32EnumWithHighBits, int32_t>(); + TestIsNotTruncated<Int64EnumWithHighBits, int64_t>(); + TestIsNotTruncated<Uint8EnumWithHighBits, uint8_t>(); + TestIsNotTruncated<Uint16EnumWithHighBits, uint16_t>(); + TestIsNotTruncated<Uint32EnumWithHighBits, uint32_t>(); + TestIsNotTruncated<Uint64EnumWithHighBits, uint64_t>(); + + return 0; +} diff --git a/mfbt/tests/TestUniquePtr.cpp b/mfbt/tests/TestUniquePtr.cpp new file mode 100644 index 000000000..c866fcc3a --- /dev/null +++ b/mfbt/tests/TestUniquePtr.cpp @@ -0,0 +1,592 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/Compiler.h" +#include "mozilla/Move.h" +#include "mozilla/TypeTraits.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Vector.h" + +#include <stddef.h> + +using mozilla::DefaultDelete; +using mozilla::IsSame; +using mozilla::MakeUnique; +using mozilla::Swap; +using mozilla::UniquePtr; +using mozilla::Vector; + +#define CHECK(c) \ + do { \ + bool cond = !!(c); \ + MOZ_ASSERT(cond, "Failed assertion: " #c); \ + if (!cond) { \ + return false; \ + } \ + } while (false) + +typedef UniquePtr<int> NewInt; +static_assert(sizeof(NewInt) == sizeof(int*), "stored most efficiently"); + +static size_t gADestructorCalls = 0; + +struct A +{ +public: + A() : mX(0) {} + virtual ~A() { gADestructorCalls++; } + + int mX; +}; + +static size_t gBDestructorCalls = 0; + +struct B : public A +{ +public: + B() : mY(1) {} + ~B() { gBDestructorCalls++; } + + int mY; +}; + +typedef UniquePtr<A> UniqueA; +typedef UniquePtr<B, UniqueA::DeleterType> UniqueB; // permit interconversion + +static_assert(sizeof(UniqueA) == sizeof(A*), "stored most efficiently"); +static_assert(sizeof(UniqueB) == sizeof(B*), "stored most efficiently"); + +struct DeleterSubclass : UniqueA::DeleterType {}; + +typedef UniquePtr<B, DeleterSubclass> UniqueC; +static_assert(sizeof(UniqueC) == sizeof(B*), "stored most efficiently"); + +static UniqueA +ReturnUniqueA() +{ + return UniqueA(new B); +} + +static UniqueA +ReturnLocalA() +{ + UniqueA a(new A); + return Move(a); +} + +static void +TestDeleterType() +{ + // Make sure UniquePtr will use its deleter's pointer type if it defines one. + typedef int* Ptr; + struct Deleter { + typedef Ptr pointer; + Deleter() {} + void operator()(int*p) { + delete p; + } + }; + UniquePtr<Ptr, Deleter> u(new int, Deleter()); +} + +static bool +TestDefaultFreeGuts() +{ + static_assert(IsSame<NewInt::DeleterType, DefaultDelete<int> >::value, + "weird deleter?"); + + NewInt n1(new int); + CHECK(n1); + CHECK(n1.get() != nullptr); + + n1 = nullptr; + CHECK(!n1); + CHECK(n1.get() == nullptr); + + int* p1 = new int; + n1.reset(p1); + CHECK(n1); + NewInt n2(Move(n1)); + CHECK(!n1); + CHECK(n1.get() == nullptr); + CHECK(n2.get() == p1); + + Swap(n1, n2); + CHECK(n1.get() == p1); + CHECK(n2.get() == nullptr); + + n1.swap(n2); + CHECK(n1.get() == nullptr); + CHECK(n2.get() == p1); + delete n2.release(); + + CHECK(n1.get() == nullptr); + CHECK(n2 == nullptr); + CHECK(nullptr == n2); + + int* p2 = new int; + int* p3 = new int; + n1.reset(p2); + n2.reset(p3); + CHECK(n1.get() == p2); + CHECK(n2.get() == p3); + + n1.swap(n2); + CHECK(n2 != nullptr); + CHECK(nullptr != n2); + CHECK(n2.get() == p2); + CHECK(n1.get() == p3); + + UniqueA a1; + CHECK(a1 == nullptr); + a1.reset(new A); + CHECK(gADestructorCalls == 0); + CHECK(a1->mX == 0); + + B* bp1 = new B; + bp1->mX = 5; + CHECK(gBDestructorCalls == 0); + a1.reset(bp1); + CHECK(gADestructorCalls == 1); + CHECK(a1->mX == 5); + a1.reset(nullptr); + CHECK(gADestructorCalls == 2); + CHECK(gBDestructorCalls == 1); + + B* bp2 = new B; + UniqueB b1(bp2); + UniqueA a2(nullptr); + a2 = Move(b1); + CHECK(gADestructorCalls == 2); + CHECK(gBDestructorCalls == 1); + + UniqueA a3(Move(a2)); + a3 = nullptr; + CHECK(gADestructorCalls == 3); + CHECK(gBDestructorCalls == 2); + + B* bp3 = new B; + bp3->mX = 42; + UniqueB b2(bp3); + UniqueA a4(Move(b2)); + CHECK(b2.get() == nullptr); + CHECK((*a4).mX == 42); + CHECK(gADestructorCalls == 3); + CHECK(gBDestructorCalls == 2); + + UniqueA a5(new A); + UniqueB b3(new B); + a5 = Move(b3); + CHECK(gADestructorCalls == 4); + CHECK(gBDestructorCalls == 2); + + ReturnUniqueA(); + CHECK(gADestructorCalls == 5); + CHECK(gBDestructorCalls == 3); + + ReturnLocalA(); + CHECK(gADestructorCalls == 6); + CHECK(gBDestructorCalls == 3); + + UniqueA a6(ReturnLocalA()); + a6 = nullptr; + CHECK(gADestructorCalls == 7); + CHECK(gBDestructorCalls == 3); + + UniqueC c1(new B); + UniqueA a7(new B); + a7 = Move(c1); + CHECK(gADestructorCalls == 8); + CHECK(gBDestructorCalls == 4); + + c1.reset(new B); + + UniqueA a8(Move(c1)); + CHECK(gADestructorCalls == 8); + CHECK(gBDestructorCalls == 4); + + // These smart pointers still own B resources. + CHECK(a4); + CHECK(a5); + CHECK(a7); + CHECK(a8); + return true; +} + +static bool +TestDefaultFree() +{ + CHECK(TestDefaultFreeGuts()); + CHECK(gADestructorCalls == 12); + CHECK(gBDestructorCalls == 8); + return true; +} + +static size_t FreeClassCounter = 0; + +struct FreeClass +{ +public: + FreeClass() {} + + void operator()(int* aPtr) + { + FreeClassCounter++; + delete aPtr; + } +}; + +typedef UniquePtr<int, FreeClass> NewIntCustom; +static_assert(sizeof(NewIntCustom) == sizeof(int*), + "stored most efficiently"); + +static bool +TestFreeClass() +{ + CHECK(FreeClassCounter == 0); + { + NewIntCustom n1(new int); + CHECK(FreeClassCounter == 0); + } + CHECK(FreeClassCounter == 1); + + NewIntCustom n2; + { + NewIntCustom n3(new int); + CHECK(FreeClassCounter == 1); + n2 = Move(n3); + } + CHECK(FreeClassCounter == 1); + n2 = nullptr; + CHECK(FreeClassCounter == 2); + + n2.reset(nullptr); + CHECK(FreeClassCounter == 2); + n2.reset(new int); + n2.reset(); + CHECK(FreeClassCounter == 3); + + NewIntCustom n4(new int, FreeClass()); + CHECK(FreeClassCounter == 3); + n4.reset(new int); + CHECK(FreeClassCounter == 4); + n4.reset(); + CHECK(FreeClassCounter == 5); + + FreeClass f; + NewIntCustom n5(new int, f); + CHECK(FreeClassCounter == 5); + int* p = n5.release(); + CHECK(FreeClassCounter == 5); + delete p; + + return true; +} + +typedef UniquePtr<int, DefaultDelete<int>&> IntDeleterRef; +typedef UniquePtr<A, DefaultDelete<A>&> ADeleterRef; +typedef UniquePtr<B, DefaultDelete<A>&> BDeleterRef; + +static_assert(sizeof(IntDeleterRef) > sizeof(int*), + "has to be heavier than an int* to store the reference"); +static_assert(sizeof(ADeleterRef) > sizeof(A*), + "has to be heavier than an A* to store the reference"); +static_assert(sizeof(BDeleterRef) > sizeof(int*), + "has to be heavier than a B* to store the reference"); + +static bool +TestReferenceDeleterGuts() +{ + DefaultDelete<int> delInt; + IntDeleterRef id1(new int, delInt); + + IntDeleterRef id2(Move(id1)); + CHECK(id1 == nullptr); + CHECK(nullptr != id2); + CHECK(&id1.get_deleter() == &id2.get_deleter()); + + IntDeleterRef id3(Move(id2)); + + DefaultDelete<A> delA; + ADeleterRef a1(new A, delA); + a1.reset(nullptr); + a1.reset(new B); + a1 = nullptr; + + BDeleterRef b1(new B, delA); + a1 = Move(b1); + + BDeleterRef b2(new B, delA); + + ADeleterRef a2(Move(b2)); + + return true; +} + +static bool +TestReferenceDeleter() +{ + gADestructorCalls = 0; + gBDestructorCalls = 0; + + CHECK(TestReferenceDeleterGuts()); + + CHECK(gADestructorCalls == 4); + CHECK(gBDestructorCalls == 3); + + gADestructorCalls = 0; + gBDestructorCalls = 0; + return true; +} + +typedef void (&FreeSignature)(void*); + +static size_t DeleteIntFunctionCallCount = 0; + +static void +DeleteIntFunction(void* aPtr) +{ + DeleteIntFunctionCallCount++; + delete static_cast<int*>(aPtr); +} + +static void +SetMallocedInt(UniquePtr<int, FreeSignature>& aPtr, int aI) +{ + int* newPtr = static_cast<int*>(malloc(sizeof(int))); + *newPtr = aI; + aPtr.reset(newPtr); +} + +static UniquePtr<int, FreeSignature> +MallocedInt(int aI) +{ + UniquePtr<int, FreeSignature> + ptr(static_cast<int*>(malloc(sizeof(int))), free); + *ptr = aI; + return Move(ptr); +} +static bool +TestFunctionReferenceDeleter() +{ + // Look for allocator mismatches and leaks to verify these bits + UniquePtr<int, FreeSignature> i1(MallocedInt(17)); + CHECK(*i1 == 17); + + SetMallocedInt(i1, 42); + CHECK(*i1 == 42); + + // These bits use a custom deleter so we can instrument deletion. + { + UniquePtr<int, FreeSignature> i2 = + UniquePtr<int, FreeSignature>(new int(42), DeleteIntFunction); + CHECK(DeleteIntFunctionCallCount == 0); + + i2.reset(new int(76)); + CHECK(DeleteIntFunctionCallCount == 1); + } + + CHECK(DeleteIntFunctionCallCount == 2); + + return true; +} + +template<typename T> +struct AppendNullptrTwice +{ + AppendNullptrTwice() {} + + bool operator()(Vector<T>& vec) + { + CHECK(vec.append(nullptr)); + CHECK(vec.append(nullptr)); + return true; + } +}; + +static size_t AAfter; +static size_t BAfter; + +static bool +TestVectorGuts() +{ + Vector<UniqueA> vec; + CHECK(vec.append(new B)); + CHECK(vec.append(new A)); + CHECK(AppendNullptrTwice<UniqueA>()(vec)); + CHECK(vec.append(new B)); + + size_t initialLength = vec.length(); + + UniqueA* begin = vec.begin(); + bool appendA = true; + do { + CHECK(appendA ? vec.append(new A) : vec.append(new B)); + appendA = !appendA; + } while (begin == vec.begin()); + + size_t numAppended = vec.length() - initialLength; + + BAfter = numAppended / 2; + AAfter = numAppended - numAppended / 2; + + CHECK(gADestructorCalls == 0); + CHECK(gBDestructorCalls == 0); + return true; +} + +static bool +TestVector() +{ + gADestructorCalls = 0; + gBDestructorCalls = 0; + + CHECK(TestVectorGuts()); + + CHECK(gADestructorCalls == 3 + AAfter + BAfter); + CHECK(gBDestructorCalls == 2 + BAfter); + return true; +} + +typedef UniquePtr<int[]> IntArray; +static_assert(sizeof(IntArray) == sizeof(int*), + "stored most efficiently"); + +static bool +TestArray() +{ + static_assert(IsSame<IntArray::DeleterType, DefaultDelete<int[]> >::value, + "weird deleter?"); + + IntArray n1(new int[5]); + CHECK(n1); + CHECK(n1.get() != nullptr); + + n1 = nullptr; + CHECK(!n1); + CHECK(n1.get() == nullptr); + + int* p1 = new int[42]; + n1.reset(p1); + CHECK(n1); + IntArray n2(Move(n1)); + CHECK(!n1); + CHECK(n1.get() == nullptr); + CHECK(n2.get() == p1); + + Swap(n1, n2); + CHECK(n1.get() == p1); + CHECK(n2.get() == nullptr); + + n1.swap(n2); + CHECK(n1.get() == nullptr); + CHECK(n2.get() == p1); + delete[] n2.release(); + + CHECK(n1.get() == nullptr); + CHECK(n2.get() == nullptr); + + int* p2 = new int[7]; + int* p3 = new int[42]; + n1.reset(p2); + n2.reset(p3); + CHECK(n1.get() == p2); + CHECK(n2.get() == p3); + + n1.swap(n2); + CHECK(n2.get() == p2); + CHECK(n1.get() == p3); + + n1 = Move(n2); + CHECK(n1.get() == p2); + n1 = Move(n2); + CHECK(n1.get() == nullptr); + + UniquePtr<A[]> a1(new A[17]); + static_assert(sizeof(a1) == sizeof(A*), + "stored most efficiently"); + + UniquePtr<A[]> a2(new A[5], DefaultDelete<A[]>()); + a2.reset(nullptr); + a2.reset(new A[17]); + a2 = nullptr; + + UniquePtr<A[]> a3(nullptr); + a3.reset(new A[7]); + + return true; +} + +struct Q +{ + Q() {} + Q(const Q&) {} + + Q(Q&, char) {} + + template<typename T> + Q(Q, T&&, int) {} + + Q(int, long, double, void*) {} +}; + +static int randomInt() { return 4; } + +static bool +TestMakeUnique() +{ + UniquePtr<int> a1(MakeUnique<int>()); + UniquePtr<long> a2(MakeUnique<long>(4)); + + // no args, easy + UniquePtr<Q> q0(MakeUnique<Q>()); + + // temporary bound to const lval ref + UniquePtr<Q> q1(MakeUnique<Q>(Q())); + + // passing through a non-const lval ref + UniquePtr<Q> q2(MakeUnique<Q>(*q1, 'c')); + + // pass by copying, forward a temporary, pass by value + UniquePtr<Q> q3(MakeUnique<Q>(Q(), UniquePtr<int>(), randomInt())); + + // various type mismatching to test "fuzzy" forwarding + UniquePtr<Q> q4(MakeUnique<Q>('s', 66LL, 3.141592654, &q3)); + + UniquePtr<char[]> c1(MakeUnique<char[]>(5)); + + return true; +} + +int +main() +{ + TestDeleterType(); + + if (!TestDefaultFree()) { + return 1; + } + if (!TestFreeClass()) { + return 1; + } + if (!TestReferenceDeleter()) { + return 1; + } + if (!TestFunctionReferenceDeleter()) { + return 1; + } + if (!TestVector()) { + return 1; + } + if (!TestArray()) { + return 1; + } + if (!TestMakeUnique()) { + return 1; + } + return 0; +} diff --git a/mfbt/tests/TestVariant.cpp b/mfbt/tests/TestVariant.cpp new file mode 100644 index 000000000..d47df70cb --- /dev/null +++ b/mfbt/tests/TestVariant.cpp @@ -0,0 +1,188 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/UniquePtr.h" +#include "mozilla/Variant.h" + +using mozilla::MakeUnique; +using mozilla::UniquePtr; +using mozilla::Variant; + +struct Destroyer { + static int destroyedCount; + ~Destroyer() { + destroyedCount++; + } +}; + +int Destroyer::destroyedCount = 0; + +static void +testSimple() +{ + printf("testSimple\n"); + Variant<uint32_t, uint64_t> v(uint64_t(1)); + MOZ_RELEASE_ASSERT(v.is<uint64_t>()); + MOZ_RELEASE_ASSERT(!v.is<uint32_t>()); + MOZ_RELEASE_ASSERT(v.as<uint64_t>() == 1); +} + +static void +testCopy() +{ + printf("testCopy\n"); + Variant<uint32_t, uint64_t> v1(uint64_t(1)); + Variant<uint32_t, uint64_t> v2(v1); + MOZ_RELEASE_ASSERT(v2.is<uint64_t>()); + MOZ_RELEASE_ASSERT(!v2.is<uint32_t>()); + MOZ_RELEASE_ASSERT(v2.as<uint64_t>() == 1); + + Variant<uint32_t, uint64_t> v3(uint32_t(10)); + v3 = v2; + MOZ_RELEASE_ASSERT(v3.is<uint64_t>()); + MOZ_RELEASE_ASSERT(v3.as<uint64_t>() == 1); +} + +static void +testMove() +{ + printf("testMove\n"); + Variant<UniquePtr<int>, char> v1(MakeUnique<int>(5)); + Variant<UniquePtr<int>, char> v2(Move(v1)); + + MOZ_RELEASE_ASSERT(v2.is<UniquePtr<int>>()); + MOZ_RELEASE_ASSERT(*v2.as<UniquePtr<int>>() == 5); + + MOZ_RELEASE_ASSERT(v1.is<UniquePtr<int>>()); + MOZ_RELEASE_ASSERT(v1.as<UniquePtr<int>>() == nullptr); + + Destroyer::destroyedCount = 0; + { + Variant<char, UniquePtr<Destroyer>> v3(MakeUnique<Destroyer>()); + Variant<char, UniquePtr<Destroyer>> v4(Move(v3)); + + Variant<char, UniquePtr<Destroyer>> v5('a'); + v5 = Move(v4); + + auto ptr = v5.extract<UniquePtr<Destroyer>>(); + MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 0); + } + MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 1); +} + +static void +testDestructor() +{ + printf("testDestructor\n"); + Destroyer::destroyedCount = 0; + + { + Destroyer d; + + { + Variant<char, UniquePtr<char[]>, Destroyer> v(d); + MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 0); // None detroyed yet. + } + + MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 1); // v's copy of d is destroyed. + } + + MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 2); // d is destroyed. +} + +static void +testEquality() +{ + printf("testEquality\n"); + using V = Variant<char, int>; + + V v0('a'); + V v1('b'); + V v2('b'); + V v3(42); + V v4(27); + V v5(27); + V v6(int('b')); + + MOZ_RELEASE_ASSERT(v0 != v1); + MOZ_RELEASE_ASSERT(v1 == v2); + MOZ_RELEASE_ASSERT(v2 != v3); + MOZ_RELEASE_ASSERT(v3 != v4); + MOZ_RELEASE_ASSERT(v4 == v5); + MOZ_RELEASE_ASSERT(v1 != v6); + + MOZ_RELEASE_ASSERT(v0 == v0); + MOZ_RELEASE_ASSERT(v1 == v1); + MOZ_RELEASE_ASSERT(v2 == v2); + MOZ_RELEASE_ASSERT(v3 == v3); + MOZ_RELEASE_ASSERT(v4 == v4); + MOZ_RELEASE_ASSERT(v5 == v5); + MOZ_RELEASE_ASSERT(v6 == v6); +} + +struct Describer +{ + static const char* little; + static const char* medium; + static const char* big; + + const char* match(const uint8_t&) { return little; } + const char* match(const uint32_t&) { return medium; } + const char* match(const uint64_t&) { return big; } +}; + +const char* Describer::little = "little"; +const char* Describer::medium = "medium"; +const char* Describer::big = "big"; + +static void +testMatching() +{ + printf("testMatching\n"); + using V = Variant<uint8_t, uint32_t, uint64_t>; + + Describer desc; + + V v1(uint8_t(1)); + V v2(uint32_t(2)); + V v3(uint64_t(3)); + + MOZ_RELEASE_ASSERT(v1.match(desc) == Describer::little); + MOZ_RELEASE_ASSERT(v2.match(desc) == Describer::medium); + MOZ_RELEASE_ASSERT(v3.match(desc) == Describer::big); + + const V& constRef1 = v1; + const V& constRef2 = v2; + const V& constRef3 = v3; + + MOZ_RELEASE_ASSERT(constRef1.match(desc) == Describer::little); + MOZ_RELEASE_ASSERT(constRef2.match(desc) == Describer::medium); + MOZ_RELEASE_ASSERT(constRef3.match(desc) == Describer::big); +} + +static void +testRvalueMatcher() +{ + printf("testRvalueMatcher\n"); + using V = Variant<uint8_t, uint32_t, uint64_t>; + V v(uint8_t(1)); + MOZ_RELEASE_ASSERT(v.match(Describer()) == Describer::little); +} + +int +main() +{ + testSimple(); + testCopy(); + testMove(); + testDestructor(); + testEquality(); + testMatching(); + testRvalueMatcher(); + + printf("TestVariant OK!\n"); + return 0; +} diff --git a/mfbt/tests/TestVector.cpp b/mfbt/tests/TestVector.cpp new file mode 100644 index 000000000..d969bcbc2 --- /dev/null +++ b/mfbt/tests/TestVector.cpp @@ -0,0 +1,358 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Move.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Vector.h" + +using mozilla::detail::VectorTesting; +using mozilla::MakeUnique; +using mozilla::Move; +using mozilla::UniquePtr; +using mozilla::Vector; + +struct mozilla::detail::VectorTesting +{ + static void testReserved(); + static void testConstRange(); + static void testEmplaceBack(); + static void testReverse(); + static void testExtractRawBuffer(); + static void testExtractOrCopyRawBuffer(); +}; + +void +mozilla::detail::VectorTesting::testReserved() +{ +#ifdef DEBUG + Vector<bool> bv; + MOZ_RELEASE_ASSERT(bv.reserved() == 0); + + MOZ_RELEASE_ASSERT(bv.append(true)); + MOZ_RELEASE_ASSERT(bv.reserved() == 1); + + Vector<bool> otherbv; + MOZ_RELEASE_ASSERT(otherbv.append(false)); + MOZ_RELEASE_ASSERT(otherbv.append(true)); + MOZ_RELEASE_ASSERT(bv.appendAll(otherbv)); + MOZ_RELEASE_ASSERT(bv.reserved() == 3); + + MOZ_RELEASE_ASSERT(bv.reserve(5)); + MOZ_RELEASE_ASSERT(bv.reserved() == 5); + + MOZ_RELEASE_ASSERT(bv.reserve(1)); + MOZ_RELEASE_ASSERT(bv.reserved() == 5); + + Vector<bool> bv2(Move(bv)); + MOZ_RELEASE_ASSERT(bv.reserved() == 0); + MOZ_RELEASE_ASSERT(bv2.reserved() == 5); + + bv2.clearAndFree(); + MOZ_RELEASE_ASSERT(bv2.reserved() == 0); + + Vector<int, 42> iv; + MOZ_RELEASE_ASSERT(iv.reserved() == 0); + + MOZ_RELEASE_ASSERT(iv.append(17)); + MOZ_RELEASE_ASSERT(iv.reserved() == 1); + + Vector<int, 42> otheriv; + MOZ_RELEASE_ASSERT(otheriv.append(42)); + MOZ_RELEASE_ASSERT(otheriv.append(37)); + MOZ_RELEASE_ASSERT(iv.appendAll(otheriv)); + MOZ_RELEASE_ASSERT(iv.reserved() == 3); + + MOZ_RELEASE_ASSERT(iv.reserve(5)); + MOZ_RELEASE_ASSERT(iv.reserved() == 5); + + MOZ_RELEASE_ASSERT(iv.reserve(1)); + MOZ_RELEASE_ASSERT(iv.reserved() == 5); + + MOZ_RELEASE_ASSERT(iv.reserve(55)); + MOZ_RELEASE_ASSERT(iv.reserved() == 55); + + Vector<int, 42> iv2(Move(iv)); + MOZ_RELEASE_ASSERT(iv.reserved() == 0); + MOZ_RELEASE_ASSERT(iv2.reserved() == 55); + + iv2.clearAndFree(); + MOZ_RELEASE_ASSERT(iv2.reserved() == 0); +#endif +} + +void +mozilla::detail::VectorTesting::testConstRange() +{ +#ifdef DEBUG + Vector<int> vec; + + for (int i = 0; i < 10; i++) { + MOZ_RELEASE_ASSERT(vec.append(i)); + } + + const auto &vecRef = vec; + + Vector<int>::ConstRange range = vecRef.all(); + for (int i = 0; i < 10; i++) { + MOZ_RELEASE_ASSERT(!range.empty()); + MOZ_RELEASE_ASSERT(range.front() == i); + range.popFront(); + } +#endif +} + +namespace { + +struct S +{ + size_t j; + UniquePtr<size_t> k; + + static size_t constructCount; + static size_t moveCount; + static size_t destructCount; + + static void resetCounts() { + constructCount = 0; + moveCount = 0; + destructCount = 0; + } + + S(size_t j, size_t k) + : j(j) + , k(MakeUnique<size_t>(k)) + { + constructCount++; + } + + S(S&& rhs) + : j(rhs.j) + , k(Move(rhs.k)) + { + rhs.j = 0; + rhs.k.reset(0); + moveCount++; + } + + ~S() { + destructCount++; + } + + S(const S&) = delete; + S& operator=(const S&) = delete; +}; + +size_t S::constructCount = 0; +size_t S::moveCount = 0; +size_t S::destructCount = 0; + +} + +void +mozilla::detail::VectorTesting::testEmplaceBack() +{ + S::resetCounts(); + + Vector<S> vec; + MOZ_RELEASE_ASSERT(vec.reserve(20)); + + for (size_t i = 0; i < 10; i++) { + S s(i, i * i); + MOZ_RELEASE_ASSERT(vec.append(Move(s))); + } + + MOZ_RELEASE_ASSERT(vec.length() == 10); + MOZ_RELEASE_ASSERT(S::constructCount == 10); + MOZ_RELEASE_ASSERT(S::moveCount == 10); + + for (size_t i = 10; i < 20; i++) { + MOZ_RELEASE_ASSERT(vec.emplaceBack(i, i * i)); + } + + MOZ_RELEASE_ASSERT(vec.length() == 20); + MOZ_RELEASE_ASSERT(S::constructCount == 20); + MOZ_RELEASE_ASSERT(S::moveCount == 10); + + for (size_t i = 0; i < 20; i++) { + MOZ_RELEASE_ASSERT(vec[i].j == i); + MOZ_RELEASE_ASSERT(*vec[i].k == i * i); + } +} + +void +mozilla::detail::VectorTesting::testReverse() +{ + // Use UniquePtr to make sure that reverse() can handler move-only types. + Vector<UniquePtr<uint8_t>, 0> vec; + + // Reverse an odd number of elements. + + for (uint8_t i = 0; i < 5; i++) { + auto p = MakeUnique<uint8_t>(i); + MOZ_RELEASE_ASSERT(p); + MOZ_RELEASE_ASSERT(vec.append(mozilla::Move(p))); + } + + vec.reverse(); + + MOZ_RELEASE_ASSERT(*vec[0] == 4); + MOZ_RELEASE_ASSERT(*vec[1] == 3); + MOZ_RELEASE_ASSERT(*vec[2] == 2); + MOZ_RELEASE_ASSERT(*vec[3] == 1); + MOZ_RELEASE_ASSERT(*vec[4] == 0); + + // Reverse an even number of elements. + + vec.popBack(); + vec.reverse(); + + MOZ_RELEASE_ASSERT(*vec[0] == 1); + MOZ_RELEASE_ASSERT(*vec[1] == 2); + MOZ_RELEASE_ASSERT(*vec[2] == 3); + MOZ_RELEASE_ASSERT(*vec[3] == 4); + + // Reverse an empty vector. + + vec.clear(); + MOZ_RELEASE_ASSERT(vec.length() == 0); + vec.reverse(); + MOZ_RELEASE_ASSERT(vec.length() == 0); + + // Reverse a vector using only inline storage. + + Vector<UniquePtr<uint8_t>, 5> vec2; + for (uint8_t i = 0; i < 5; i++) { + auto p = MakeUnique<uint8_t>(i); + MOZ_RELEASE_ASSERT(p); + MOZ_RELEASE_ASSERT(vec2.append(mozilla::Move(p))); + } + + vec2.reverse(); + + MOZ_RELEASE_ASSERT(*vec2[0] == 4); + MOZ_RELEASE_ASSERT(*vec2[1] == 3); + MOZ_RELEASE_ASSERT(*vec2[2] == 2); + MOZ_RELEASE_ASSERT(*vec2[3] == 1); + MOZ_RELEASE_ASSERT(*vec2[4] == 0); +} + +void +mozilla::detail::VectorTesting::testExtractRawBuffer() +{ + S::resetCounts(); + + Vector<S, 5> vec; + MOZ_RELEASE_ASSERT(vec.reserve(5)); + for (size_t i = 0; i < 5; i++) { + vec.infallibleEmplaceBack(i, i * i); + } + MOZ_RELEASE_ASSERT(vec.length() == 5); + MOZ_ASSERT(vec.reserved() == 5); + MOZ_RELEASE_ASSERT(S::constructCount == 5); + MOZ_RELEASE_ASSERT(S::moveCount == 0); + MOZ_RELEASE_ASSERT(S::destructCount == 0); + + S* buf = vec.extractRawBuffer(); + MOZ_RELEASE_ASSERT(!buf); + MOZ_RELEASE_ASSERT(vec.length() == 5); + MOZ_ASSERT(vec.reserved() == 5); + MOZ_RELEASE_ASSERT(S::constructCount == 5); + MOZ_RELEASE_ASSERT(S::moveCount == 0); + MOZ_RELEASE_ASSERT(S::destructCount == 0); + + MOZ_RELEASE_ASSERT(vec.reserve(10)); + for (size_t i = 5; i < 10; i++) { + vec.infallibleEmplaceBack(i, i * i); + } + MOZ_RELEASE_ASSERT(vec.length() == 10); + MOZ_ASSERT(vec.reserved() == 10); + MOZ_RELEASE_ASSERT(S::constructCount == 10); + MOZ_RELEASE_ASSERT(S::moveCount == 5); + MOZ_RELEASE_ASSERT(S::destructCount == 5); + + buf = vec.extractRawBuffer(); + MOZ_RELEASE_ASSERT(buf); + MOZ_RELEASE_ASSERT(vec.length() == 0); + MOZ_ASSERT(vec.reserved() == 0); + MOZ_RELEASE_ASSERT(S::constructCount == 10); + MOZ_RELEASE_ASSERT(S::moveCount == 5); + MOZ_RELEASE_ASSERT(S::destructCount == 5); + + for (size_t i = 0; i < 10; i++) { + MOZ_RELEASE_ASSERT(buf[i].j == i); + MOZ_RELEASE_ASSERT(*buf[i].k == i * i); + } + + free(buf); +} + +void +mozilla::detail::VectorTesting::testExtractOrCopyRawBuffer() +{ + S::resetCounts(); + + Vector<S, 5> vec; + MOZ_RELEASE_ASSERT(vec.reserve(5)); + for (size_t i = 0; i < 5; i++) { + vec.infallibleEmplaceBack(i, i * i); + } + MOZ_RELEASE_ASSERT(vec.length() == 5); + MOZ_ASSERT(vec.reserved() == 5); + MOZ_RELEASE_ASSERT(S::constructCount == 5); + MOZ_RELEASE_ASSERT(S::moveCount == 0); + MOZ_RELEASE_ASSERT(S::destructCount == 0); + + S* buf = vec.extractOrCopyRawBuffer(); + MOZ_RELEASE_ASSERT(buf); + MOZ_RELEASE_ASSERT(vec.length() == 0); + MOZ_ASSERT(vec.reserved() == 0); + MOZ_RELEASE_ASSERT(S::constructCount == 5); + MOZ_RELEASE_ASSERT(S::moveCount == 5); + MOZ_RELEASE_ASSERT(S::destructCount == 5); + + for (size_t i = 0; i < 5; i++) { + MOZ_RELEASE_ASSERT(buf[i].j == i); + MOZ_RELEASE_ASSERT(*buf[i].k == i * i); + } + + S::resetCounts(); + + MOZ_RELEASE_ASSERT(vec.reserve(10)); + for (size_t i = 0; i < 10; i++) { + vec.infallibleEmplaceBack(i, i * i); + } + MOZ_RELEASE_ASSERT(vec.length() == 10); + MOZ_ASSERT(vec.reserved() == 10); + MOZ_RELEASE_ASSERT(S::constructCount == 10); + MOZ_RELEASE_ASSERT(S::moveCount == 0); + MOZ_RELEASE_ASSERT(S::destructCount == 0); + + buf = vec.extractOrCopyRawBuffer(); + MOZ_RELEASE_ASSERT(buf); + MOZ_RELEASE_ASSERT(vec.length() == 0); + MOZ_ASSERT(vec.reserved() == 0); + MOZ_RELEASE_ASSERT(S::constructCount == 10); + MOZ_RELEASE_ASSERT(S::moveCount == 0); + MOZ_RELEASE_ASSERT(S::destructCount == 0); + + for (size_t i = 0; i < 10; i++) { + MOZ_RELEASE_ASSERT(buf[i].j == i); + MOZ_RELEASE_ASSERT(*buf[i].k == i * i); + } + + free(buf); +} + +int +main() +{ + VectorTesting::testReserved(); + VectorTesting::testConstRange(); + VectorTesting::testEmplaceBack(); + VectorTesting::testReverse(); + VectorTesting::testExtractRawBuffer(); + VectorTesting::testExtractOrCopyRawBuffer(); +} diff --git a/mfbt/tests/TestWeakPtr.cpp b/mfbt/tests/TestWeakPtr.cpp new file mode 100644 index 000000000..15060f00d --- /dev/null +++ b/mfbt/tests/TestWeakPtr.cpp @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/WeakPtr.h" + +using mozilla::SupportsWeakPtr; +using mozilla::WeakPtr; + +// To have a class C support weak pointers, inherit from SupportsWeakPtr<C>. +class C : public SupportsWeakPtr<C> +{ +public: + MOZ_DECLARE_WEAKREFERENCE_TYPENAME(C) + + int mNum; + + C() + : mNum(0) + {} + + ~C() + { + // Setting mNum in the destructor allows us to test against use-after-free below + mNum = 0xDEAD; + } + + void act() {} + + bool isConst() { + return false; + } + + bool isConst() const { + return true; + } +}; + +bool isConst(C*) +{ + return false; +} + +bool isConst(const C*) +{ + return true; +} + +int +main() +{ + C* c1 = new C; + MOZ_RELEASE_ASSERT(c1->mNum == 0); + + // Get weak pointers to c1. The first time, + // a reference-counted WeakReference object is created that + // can live beyond the lifetime of 'c1'. The WeakReference + // object will be notified of 'c1's destruction. + WeakPtr<C> w1 = c1; + // Test a weak pointer for validity before using it. + MOZ_RELEASE_ASSERT(w1); + MOZ_RELEASE_ASSERT(w1 == c1); + w1->mNum = 1; + w1->act(); + + // Test taking another WeakPtr<C> to c1 + WeakPtr<C> w2 = c1; + MOZ_RELEASE_ASSERT(w2); + MOZ_RELEASE_ASSERT(w2 == c1); + MOZ_RELEASE_ASSERT(w2 == w1); + MOZ_RELEASE_ASSERT(w2->mNum == 1); + + // Test a WeakPtr<const C> + WeakPtr<const C> w3const = c1; + MOZ_RELEASE_ASSERT(w3const); + MOZ_RELEASE_ASSERT(w3const == c1); + MOZ_RELEASE_ASSERT(w3const == w1); + MOZ_RELEASE_ASSERT(w3const == w2); + MOZ_RELEASE_ASSERT(w3const->mNum == 1); + + // Test const-correctness of operator-> and operator T* + MOZ_RELEASE_ASSERT(!w1->isConst()); + MOZ_RELEASE_ASSERT(w3const->isConst()); + MOZ_RELEASE_ASSERT(!isConst(w1)); + MOZ_RELEASE_ASSERT(isConst(w3const)); + + // Test that when a WeakPtr is destroyed, it does not destroy the object that it points to, + // and it does not affect other WeakPtrs pointing to the same object (e.g. it does not + // destroy the WeakReference object). + { + WeakPtr<C> w4local = c1; + MOZ_RELEASE_ASSERT(w4local == c1); + } + // Now w4local has gone out of scope. If that had destroyed c1, then the following would fail + // for sure (see C::~C()). + MOZ_RELEASE_ASSERT(c1->mNum == 1); + // Check that w4local going out of scope hasn't affected other WeakPtr's pointing to c1 + MOZ_RELEASE_ASSERT(w1 == c1); + MOZ_RELEASE_ASSERT(w2 == c1); + + // Now construct another C object and test changing what object a WeakPtr points to + C* c2 = new C; + c2->mNum = 2; + MOZ_RELEASE_ASSERT(w2->mNum == 1); // w2 was pointing to c1 + w2 = c2; + MOZ_RELEASE_ASSERT(w2); + MOZ_RELEASE_ASSERT(w2 == c2); + MOZ_RELEASE_ASSERT(w2 != c1); + MOZ_RELEASE_ASSERT(w2 != w1); + MOZ_RELEASE_ASSERT(w2->mNum == 2); + + // Destroying the underlying object clears weak pointers to it. + // It should not affect pointers that are not currently pointing to it. + delete c1; + MOZ_RELEASE_ASSERT(!w1, "Deleting an object should clear WeakPtr's to it."); + MOZ_RELEASE_ASSERT(!w3const, "Deleting an object should clear WeakPtr's to it."); + MOZ_RELEASE_ASSERT(w2, "Deleting an object should not clear WeakPtr that are not pointing to it."); + + delete c2; + MOZ_RELEASE_ASSERT(!w2, "Deleting an object should clear WeakPtr's to it."); +} diff --git a/mfbt/tests/TestXorShift128PlusRNG.cpp b/mfbt/tests/TestXorShift128PlusRNG.cpp new file mode 100644 index 000000000..dbe83d049 --- /dev/null +++ b/mfbt/tests/TestXorShift128PlusRNG.cpp @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <math.h> + +#include "mozilla/Assertions.h" +#include "mozilla/PodOperations.h" +#include "mozilla/XorShift128PlusRNG.h" + +using mozilla::non_crypto::XorShift128PlusRNG; + +static void +TestDumbSequence() +{ + XorShift128PlusRNG rng(1, 4); + + // Calculated by hand following the algorithm given in the paper. The upper + // bits are mostly zero because we started with a poor seed; once it has run + // for a while, we'll get an even mix of ones and zeros in all 64 bits. + MOZ_RELEASE_ASSERT(rng.next() == 0x800049); + MOZ_RELEASE_ASSERT(rng.next() == 0x3000186); + MOZ_RELEASE_ASSERT(rng.next() == 0x400003001145); + + // Using ldexp here lets us write out the mantissa in hex, so we can compare + // them with the results generated by hand. + MOZ_RELEASE_ASSERT(rng.nextDouble() + == ldexp(static_cast<double>(0x1400003105049), -53)); + MOZ_RELEASE_ASSERT(rng.nextDouble() + == ldexp(static_cast<double>(0x2000802e49146), -53)); + MOZ_RELEASE_ASSERT(rng.nextDouble() + == ldexp(static_cast<double>(0x248300468544d), -53)); +} + +static size_t +Population(uint64_t n) +{ + size_t pop = 0; + + while (n > 0) { + n &= n-1; // Clear the rightmost 1-bit in n. + pop++; + } + + return pop; +} + +static void +TestPopulation() +{ + XorShift128PlusRNG rng(698079309544035222ULL, 6012389156611637584ULL); + + // Give it some time to warm up; it should tend towards more + // even distributions of zeros and ones. + for (size_t i = 0; i < 40; i++) + rng.next(); + + for (size_t i = 0; i < 40; i++) { + size_t pop = Population(rng.next()); + MOZ_RELEASE_ASSERT(24 <= pop && pop <= 40); + } +} + +static void +TestSetState() +{ + static const uint64_t seed[2] = { 1795644156779822404ULL, 14162896116325912595ULL }; + XorShift128PlusRNG rng(seed[0], seed[1]); + + const size_t n = 10; + uint64_t log[n]; + + for (size_t i = 0; i < n; i++) + log[i] = rng.next(); + + rng.setState(seed[0], seed[1]); + + for (size_t i = 0; i < n; i++) + MOZ_RELEASE_ASSERT(log[i] == rng.next()); +} + +static void +TestDoubleDistribution() +{ + XorShift128PlusRNG rng(0xa207aaede6859736, 0xaca6ca5060804791); + + const size_t n = 100; + size_t bins[n]; + mozilla::PodArrayZero(bins); + + // This entire file runs in 0.006s on my laptop. Generating + // more numbers lets us put tighter bounds on the bins. + for (size_t i = 0; i < 100000; i++) { + double d = rng.nextDouble(); + MOZ_RELEASE_ASSERT(0.0 <= d && d < 1.0); + bins[(int) (d * n)]++; + } + + for (size_t i = 0; i < n; i++) { + MOZ_RELEASE_ASSERT(900 <= bins[i] && bins[i] <= 1100); + } +} + +int +main() +{ + TestDumbSequence(); + TestPopulation(); + TestSetState(); + TestDoubleDistribution(); + + return 0; +} diff --git a/mfbt/tests/moz.build b/mfbt/tests/moz.build new file mode 100644 index 000000000..f96117e03 --- /dev/null +++ b/mfbt/tests/moz.build @@ -0,0 +1,76 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +CppUnitTests([ + 'TestArray', + 'TestArrayUtils', + 'TestAtomics', + 'TestBinarySearch', + 'TestBloomFilter', + 'TestBufferList', + 'TestCasting', + 'TestCeilingFloor', + 'TestCheckedInt', + 'TestCountPopulation', + 'TestCountZeroes', + 'TestEndian', + 'TestEnumeratedArray', + 'TestEnumSet', + 'TestEnumTypeTraits', + 'TestFastBernoulliTrial', + 'TestFloatingPoint', + 'TestFunction', + 'TestIntegerPrintfMacros', + 'TestIntegerRange', + 'TestJSONWriter', + 'TestLinkedList', + 'TestMacroArgs', + 'TestMacroForEach', + 'TestMathAlgorithms', + 'TestMaybe', + 'TestNotNull', + 'TestPair', + 'TestRange', + 'TestRefPtr', + 'TestRollingMean', + 'TestSaturate', + 'TestScopeExit', + 'TestSegmentedVector', + 'TestSHA1', + 'TestSplayTree', + 'TestTemplateLib', + 'TestTuple', + 'TestTypedEnum', + 'TestTypeTraits', + 'TestUniquePtr', + 'TestVariant', + 'TestVector', + 'TestWeakPtr', + 'TestXorShift128PlusRNG', +]) + +if not CONFIG['MOZ_ASAN']: + CppUnitTests([ + 'TestPoisonArea', + ]) + +# Since we link directly with MFBT object files, define IMPL_MFBT +DEFINES['IMPL_MFBT'] = True + +DISABLE_STL_WRAPPING = True + +if CONFIG['_MSC_VER']: + CXXFLAGS += [ + '-wd4275', # non dll-interface class used as base for dll-interface class + '-wd4530', # C++ exception handler used, but unwind semantics are not enabled + ] + +USE_LIBS += [ + 'mfbt', +] + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wno-error=shadow'] |