From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- mfbt/tests/gtest/TestAlgorithm.cpp | 191 +++ mfbt/tests/gtest/TestBuffer.cpp | 96 ++ mfbt/tests/gtest/TestInitializedOnce.cpp | 200 +++ mfbt/tests/gtest/TestLinkedList.cpp | 78 + mfbt/tests/gtest/TestMainThreadWeakPtr.cpp | 42 + mfbt/tests/gtest/TestMozDbg.cpp | 170 ++ mfbt/tests/gtest/TestResultExtensions.cpp | 579 +++++++ mfbt/tests/gtest/TestReverseIterator.cpp | 104 ++ mfbt/tests/gtest/TestSpan.cpp | 2355 ++++++++++++++++++++++++++++ mfbt/tests/gtest/TestTainting.cpp | 485 ++++++ mfbt/tests/gtest/moz.build | 32 + 11 files changed, 4332 insertions(+) create mode 100644 mfbt/tests/gtest/TestAlgorithm.cpp create mode 100644 mfbt/tests/gtest/TestBuffer.cpp create mode 100644 mfbt/tests/gtest/TestInitializedOnce.cpp create mode 100644 mfbt/tests/gtest/TestLinkedList.cpp create mode 100644 mfbt/tests/gtest/TestMainThreadWeakPtr.cpp create mode 100644 mfbt/tests/gtest/TestMozDbg.cpp create mode 100644 mfbt/tests/gtest/TestResultExtensions.cpp create mode 100644 mfbt/tests/gtest/TestReverseIterator.cpp create mode 100644 mfbt/tests/gtest/TestSpan.cpp create mode 100644 mfbt/tests/gtest/TestTainting.cpp create mode 100644 mfbt/tests/gtest/moz.build (limited to 'mfbt/tests/gtest') diff --git a/mfbt/tests/gtest/TestAlgorithm.cpp b/mfbt/tests/gtest/TestAlgorithm.cpp new file mode 100644 index 0000000000..a01531fa77 --- /dev/null +++ b/mfbt/tests/gtest/TestAlgorithm.cpp @@ -0,0 +1,191 @@ +/* -*- 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 "gtest/gtest.h" + +#include "mozilla/Algorithm.h" +#include "mozilla/Maybe.h" +#include "mozilla/ResultVariant.h" + +#include +#include + +using namespace mozilla; +using std::begin; +using std::end; + +namespace { +struct MoveOnly { + explicit MoveOnly(int32_t aValue) : mValue{Some(aValue)} {} + + MoveOnly(MoveOnly&&) = default; + MoveOnly& operator=(MoveOnly&&) = default; + + Maybe mValue; +}; + +struct TestError {}; + +constexpr static int32_t arr1[3] = {1, 2, 3}; +} // namespace + +TEST(MFBT_Algorithm_TransformAbortOnErr, NoError) +{ + std::vector out; + auto res = TransformAbortOnErr( + begin(arr1), end(arr1), std::back_inserter(out), + [](const int32_t value) -> Result { + return value * 10; + }); + ASSERT_TRUE(res.isOk()); + + const std::vector expected = {10, 20, 30}; + ASSERT_EQ(expected, out); +} + +TEST(MFBT_Algorithm_TransformAbortOnErr, NoError_Range) +{ + std::vector out; + auto res = TransformAbortOnErr( + arr1, std::back_inserter(out), + [](const int32_t value) -> Result { + return value * 10; + }); + ASSERT_TRUE(res.isOk()); + + const std::vector expected = {10, 20, 30}; + ASSERT_EQ(expected, out); +} + +TEST(MFBT_Algorithm_TransformAbortOnErr, ErrorOnFirst) +{ + std::vector out; + auto res = TransformAbortOnErr( + begin(arr1), end(arr1), std::back_inserter(out), + [](const int32_t value) -> Result { + return Err(TestError{}); + }); + ASSERT_TRUE(res.isErr()); + ASSERT_TRUE(out.empty()); +} + +TEST(MFBT_Algorithm_TransformAbortOnErr, ErrorOnOther) +{ + std::vector out; + auto res = TransformAbortOnErr( + begin(arr1), end(arr1), std::back_inserter(out), + [](const int32_t value) -> Result { + if (value > 2) { + return Err(TestError{}); + } + return value * 10; + }); + ASSERT_TRUE(res.isErr()); + + // XXX Should we assert on this, or is the content of out an implementation + // detail? + const std::vector expected = {10, 20}; + ASSERT_EQ(expected, out); +} + +TEST(MFBT_Algorithm_TransformAbortOnErr, ErrorOnOther_Move) +{ + MoveOnly in[3] = {MoveOnly{1}, MoveOnly{2}, MoveOnly{3}}; + std::vector out; + auto res = TransformAbortOnErr( + std::make_move_iterator(begin(in)), std::make_move_iterator(end(in)), + std::back_inserter(out), + [](MoveOnly value) -> Result { + if (*value.mValue > 1) { + return Err(TestError{}); + } + return *value.mValue * 10; + }); + ASSERT_TRUE(res.isErr()); + + ASSERT_FALSE(in[0].mValue); + ASSERT_FALSE(in[1].mValue); + ASSERT_TRUE(in[2].mValue); + + // XXX Should we assert on this, or is the content of out an implementation + // detail? + const std::vector expected = {10}; + ASSERT_EQ(expected, out); +} + +TEST(MFBT_Algorithm_TransformIfAbortOnErr, NoError) +{ + std::vector out; + auto res = TransformIfAbortOnErr( + begin(arr1), end(arr1), std::back_inserter(out), + [](const int32_t value) { return value % 2 == 1; }, + [](const int32_t value) -> Result { + return value * 10; + }); + ASSERT_TRUE(res.isOk()); + + const std::vector expected = {10, 30}; + ASSERT_EQ(expected, out); +} + +TEST(MFBT_Algorithm_TransformIfAbortOnErr, NoError_Range) +{ + std::vector out; + auto res = TransformIfAbortOnErr( + arr1, std::back_inserter(out), + [](const int32_t value) { return value % 2 == 1; }, + [](const int32_t value) -> Result { + return value * 10; + }); + ASSERT_TRUE(res.isOk()); + + const std::vector expected = {10, 30}; + ASSERT_EQ(expected, out); +} + +TEST(MFBT_Algorithm_TransformIfAbortOnErr, ErrorOnOther) +{ + std::vector out; + auto res = TransformIfAbortOnErr( + begin(arr1), end(arr1), std::back_inserter(out), + [](const int32_t value) { return value % 2 == 1; }, + [](const int32_t value) -> Result { + if (value > 2) { + return Err(TestError{}); + } + return value * 10; + }); + ASSERT_TRUE(res.isErr()); + + const std::vector expected = {10}; + ASSERT_EQ(expected, out); +} + +TEST(MFBT_Algorithm_TransformIfAbortOnErr, ErrorOnOther_Move) +{ + MoveOnly in[3] = {MoveOnly{1}, MoveOnly{2}, MoveOnly{3}}; + std::vector out; + auto res = TransformIfAbortOnErr( + std::make_move_iterator(begin(in)), std::make_move_iterator(end(in)), + std::back_inserter(out), + [](const MoveOnly& value) { return *value.mValue % 2 == 1; }, + [](MoveOnly value) -> Result { + if (*value.mValue > 1) { + return Err(TestError{}); + } + return *value.mValue * 10; + }); + ASSERT_TRUE(res.isErr()); + + ASSERT_FALSE(in[0].mValue); + ASSERT_TRUE(in[1].mValue); + ASSERT_FALSE(in[2].mValue); + + // XXX Should we assert on this, or is the content of out an implementation + // detail? + const std::vector expected = {10}; + ASSERT_EQ(expected, out); +} diff --git a/mfbt/tests/gtest/TestBuffer.cpp b/mfbt/tests/gtest/TestBuffer.cpp new file mode 100644 index 0000000000..df36282be1 --- /dev/null +++ b/mfbt/tests/gtest/TestBuffer.cpp @@ -0,0 +1,96 @@ +/* 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 "gtest/gtest.h" + +#include "mozilla/Buffer.h" +#include "mozilla/Array.h" + +using namespace mozilla; + +TEST(Buffer, TestBufferInfallible) +{ + const size_t LEN = 8; + Array arr = {1, 2, 3, 4, 5, 6, 7, 8}; + Buffer buf(arr); + + for (size_t i = 0; i < LEN; i++) { + ASSERT_EQ(buf[i], arr[i]); + } + + auto iter = buf.begin(); + auto end = buf.end(); + for (size_t i = 0; i < LEN; i++) { + ASSERT_EQ(*iter, arr[i]); + iter++; + } + ASSERT_EQ(iter, end); + + Span span = buf; + for (size_t i = 0; i < LEN; i++) { + ASSERT_EQ(span[i], arr[i]); + } + + auto spanIter = span.begin(); + auto spanEnd = span.end(); + for (size_t i = 0; i < LEN; i++) { + ASSERT_EQ(*spanIter, arr[i]); + spanIter++; + } + ASSERT_EQ(spanIter, spanEnd); + + span[3] = 42; + ASSERT_EQ(buf[3], 42); + + Buffer another(std::move(buf)); + ASSERT_EQ(another[3], 42); + ASSERT_EQ(buf.Length(), 0U); +} + +TEST(Buffer, TestBufferFallible) +{ + const size_t LEN = 8; + Array arr = {1, 2, 3, 4, 5, 6, 7, 8}; + auto maybe = Buffer::CopyFrom(arr); + ASSERT_TRUE(maybe.isSome()); + Buffer buf(std::move(*maybe)); + + for (size_t i = 0; i < LEN; i++) { + ASSERT_EQ(buf[i], arr[i]); + } + + auto iter = buf.begin(); + auto end = buf.end(); + for (size_t i = 0; i < LEN; i++) { + ASSERT_EQ(*iter, arr[i]); + iter++; + } + ASSERT_EQ(iter, end); + + Span span = buf; + for (size_t i = 0; i < LEN; i++) { + ASSERT_EQ(span[i], arr[i]); + } + + auto spanIter = span.begin(); + auto spanEnd = span.end(); + for (size_t i = 0; i < LEN; i++) { + ASSERT_EQ(*spanIter, arr[i]); + spanIter++; + } + ASSERT_EQ(spanIter, spanEnd); + + span[3] = 42; + ASSERT_EQ(buf[3], 42); + + Buffer another(std::move(buf)); + ASSERT_EQ(another[3], 42); + ASSERT_EQ(buf.Length(), 0U); +} + +TEST(Buffer, TestBufferElements) +{ + ASSERT_EQ(Buffer().Elements(), + reinterpret_cast(alignof(int32_t))); +} diff --git a/mfbt/tests/gtest/TestInitializedOnce.cpp b/mfbt/tests/gtest/TestInitializedOnce.cpp new file mode 100644 index 0000000000..a043013451 --- /dev/null +++ b/mfbt/tests/gtest/TestInitializedOnce.cpp @@ -0,0 +1,200 @@ +/* -*- 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 "gtest/gtest.h" + +#include "mozilla/InitializedOnce.h" + +#include + +using namespace mozilla; + +namespace { +template +void AssertIsSome(const T& aVal) { + ASSERT_TRUE(aVal); + ASSERT_TRUE(aVal.isSome()); + ASSERT_FALSE(aVal.isNothing()); +} + +template +void AssertIsNothing(const T& aVal) { + ASSERT_FALSE(aVal); + ASSERT_FALSE(aVal.isSome()); + ASSERT_TRUE(aVal.isNothing()); +} + +static_assert(std::is_trivially_destructible_v>); +static_assert(std::is_trivially_destructible_v>); + +static_assert(!std::is_copy_constructible_v>); +static_assert(!std::is_copy_assignable_v>); + +static_assert(!std::is_default_constructible_v>); +static_assert(std::is_default_constructible_v>); +static_assert(std::is_default_constructible_v< + LazyInitializedOnceEarlyDestructible>); + +// XXX We cannot test for move-constructability/move-assignability at the +// moment, since the operations are always defined, but trigger static_assert's +// if they should not be used. This is not too bad, since we are never copyable. + +constexpr InitializedOnce* kPtrInitializedOnceIntLazyInitForbid = + nullptr; +constexpr LazyInitializedOnce* kPtrInitializedOnceIntLazyInitAllow = + nullptr; +constexpr LazyInitializedOnceEarlyDestructible* + kPtrInitializedOnceIntLazyInitAllowResettable = nullptr; + +template ()->destroy())> +constexpr bool test_has_destroy_method(const T*) { + return true; +} +constexpr bool test_has_destroy_method(...) { return false; } + +static_assert(test_has_destroy_method(kPtrInitializedOnceIntLazyInitForbid)); +static_assert(!test_has_destroy_method(kPtrInitializedOnceIntLazyInitAllow)); +static_assert( + test_has_destroy_method(kPtrInitializedOnceIntLazyInitAllowResettable)); + +template ()->init(std::declval()))> +constexpr bool test_has_init_method(const T*) { + return true; +} +constexpr bool test_has_init_method(...) { return false; } + +static_assert(!test_has_init_method(kPtrInitializedOnceIntLazyInitForbid)); +static_assert(test_has_init_method(kPtrInitializedOnceIntLazyInitAllow)); +static_assert( + test_has_init_method(kPtrInitializedOnceIntLazyInitAllowResettable)); + +struct MoveOnly { + explicit constexpr MoveOnly(int aValue) : mValue{aValue} {} + + MoveOnly(MoveOnly&&) = default; + MoveOnly& operator=(MoveOnly&&) = default; + + int mValue; +}; + +} // namespace + +constexpr int testValue = 32; + +TEST(InitializedOnce, ImmediateInit) +{ + constexpr InitializedOnce val{testValue}; + + // compile-time assertions + static_assert(val); + static_assert(val.isSome()); + static_assert(!val.isNothing()); + static_assert(testValue == (*val).mValue); + static_assert(testValue == val->mValue); + static_assert(testValue == val.ref().mValue); + + // run-time assertions + AssertIsSome(val); + ASSERT_EQ(testValue, (*val).mValue); + ASSERT_EQ(testValue, val->mValue); + ASSERT_EQ(testValue, val.ref().mValue); +} + +TEST(InitializedOnce, ImmediateInitReset) +{ + InitializedOnce val{testValue}; + val.destroy(); + + AssertIsNothing(val); +} + +TEST(InitializedOnce, MoveConstruct) +{ + InitializedOnce oldVal{testValue}; + InitializedOnce val{std::move(oldVal)}; + + AssertIsNothing(oldVal); + AssertIsSome(val); +} + +TEST(InitializedOnceAllowLazy, DefaultCtor) +{ + LazyInitializedOnce val; + + AssertIsNothing(val); +} + +TEST(InitializedOnceAllowLazy, Init) +{ + LazyInitializedOnce val; + val.init(testValue); + + AssertIsSome(val); + ASSERT_EQ(testValue, (*val).mValue); + ASSERT_EQ(testValue, val->mValue); + ASSERT_EQ(testValue, val.ref().mValue); +} + +TEST(InitializedOnceAllowLazy, do_Init) +{ + LazyInitializedOnce val; + do_Init(val) = MoveOnly{testValue}; + + AssertIsSome(val); + ASSERT_EQ(testValue, (*val).mValue); + ASSERT_EQ(testValue, val->mValue); + ASSERT_EQ(testValue, val.ref().mValue); +} + +TEST(InitializedOnceAllowLazyResettable, DefaultCtor) +{ + LazyInitializedOnceEarlyDestructible val; + + AssertIsNothing(val); +} + +TEST(InitializedOnceAllowLazyResettable, Init) +{ + LazyInitializedOnceEarlyDestructible val; + val.init(testValue); + + AssertIsSome(val); + ASSERT_EQ(testValue, (*val).mValue); + ASSERT_EQ(testValue, val->mValue); + ASSERT_EQ(testValue, val.ref().mValue); +} + +TEST(InitializedOnceAllowLazyResettable, InitReset) +{ + LazyInitializedOnceEarlyDestructible val; + val.init(testValue); + val.destroy(); + + AssertIsNothing(val); +} + +TEST(InitializedOnceAllowLazyResettable, MoveConstruct) +{ + LazyInitializedOnceEarlyDestructible oldVal{testValue}; + LazyInitializedOnceEarlyDestructible val{std::move(oldVal)}; + + AssertIsNothing(oldVal); + AssertIsSome(val); +} + +TEST(InitializedOnceAllowLazyResettable, MoveAssign) +{ + LazyInitializedOnceEarlyDestructible oldVal{testValue}; + LazyInitializedOnceEarlyDestructible val; + + val = std::move(oldVal); + + AssertIsNothing(oldVal); + AssertIsSome(val); +} + +// XXX How do we test for assertions to be hit? diff --git a/mfbt/tests/gtest/TestLinkedList.cpp b/mfbt/tests/gtest/TestLinkedList.cpp new file mode 100644 index 0000000000..d53cba5920 --- /dev/null +++ b/mfbt/tests/gtest/TestLinkedList.cpp @@ -0,0 +1,78 @@ +/* -*- 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 "gtest/gtest.h" + +#include "mozilla/LinkedList.h" +#include "mozilla/RefPtr.h" + +using mozilla::AutoCleanLinkedList; +using mozilla::LinkedList; +using mozilla::LinkedListElement; + +class PtrClass : public LinkedListElement { + public: + bool* mResult; + + explicit PtrClass(bool* result) : mResult(result) { EXPECT_TRUE(!*mResult); } + + virtual ~PtrClass() { *mResult = true; } +}; + +class InheritedPtrClass : public PtrClass { + public: + bool* mInheritedResult; + + InheritedPtrClass(bool* result, bool* inheritedResult) + : PtrClass(result), mInheritedResult(inheritedResult) { + EXPECT_TRUE(!*mInheritedResult); + } + + virtual ~InheritedPtrClass() { *mInheritedResult = true; } +}; + +TEST(LinkedList, AutoCleanLinkedList) +{ + bool rv1 = false; + bool rv2 = false; + bool rv3 = false; + { + AutoCleanLinkedList list; + list.insertBack(new PtrClass(&rv1)); + list.insertBack(new InheritedPtrClass(&rv2, &rv3)); + } + + EXPECT_TRUE(rv1); + EXPECT_TRUE(rv2); + EXPECT_TRUE(rv3); +} + +class CountedClass final : public LinkedListElement> { + public: + int mCount; + void AddRef() { mCount++; } + void Release() { mCount--; } + + CountedClass() : mCount(0) {} + ~CountedClass() { EXPECT_TRUE(mCount == 0); } +}; + +TEST(LinkedList, AutoCleanLinkedListRefPtr) +{ + RefPtr elt1 = new CountedClass; + CountedClass* elt2 = new CountedClass; + { + AutoCleanLinkedList> list; + list.insertBack(elt1); + list.insertBack(elt2); + + EXPECT_TRUE(elt1->mCount == 2); + EXPECT_TRUE(elt2->mCount == 1); + } + + EXPECT_TRUE(elt1->mCount == 1); + EXPECT_TRUE(elt2->mCount == 0); +} diff --git a/mfbt/tests/gtest/TestMainThreadWeakPtr.cpp b/mfbt/tests/gtest/TestMainThreadWeakPtr.cpp new file mode 100644 index 0000000000..1722ade8c1 --- /dev/null +++ b/mfbt/tests/gtest/TestMainThreadWeakPtr.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 "gtest/gtest.h" + +#include "mozilla/WeakPtr.h" +#include "mozilla/UniquePtr.h" +#include + +using namespace mozilla; + +struct C : public SupportsWeakPtr { + int mNum{0}; +}; + +struct HasWeakPtrToC { + explicit HasWeakPtrToC(C* c) : mPtr(c) {} + + MainThreadWeakPtr mPtr; + + ~HasWeakPtrToC() { + MOZ_RELEASE_ASSERT(!NS_IsMainThread(), "Should be released OMT"); + } +}; + +TEST(MFBT_MainThreadWeakPtr, Basic) +{ + auto c = MakeUnique(); + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + auto weakRef = MakeUnique(c.get()); + + std::thread t([weakRef = std::move(weakRef)] {}); + + MOZ_RELEASE_ASSERT(!weakRef); + c = nullptr; + + t.join(); +} diff --git a/mfbt/tests/gtest/TestMozDbg.cpp b/mfbt/tests/gtest/TestMozDbg.cpp new file mode 100644 index 0000000000..24ccd8ed37 --- /dev/null +++ b/mfbt/tests/gtest/TestMozDbg.cpp @@ -0,0 +1,170 @@ +/* -*- 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 +#include + +#include "gtest/gtest.h" +#include "mozilla/DbgMacro.h" +#include "mozilla/Unused.h" + +using namespace mozilla; + +#define TEST_MOZ_DBG_TYPE_IS(type_, expression_...) \ + static_assert(std::is_same_v, \ + "MOZ_DBG should return the indicated type") + +#define TEST_MOZ_DBG_TYPE_SAME(expression_...) \ + static_assert( \ + std::is_same_v, \ + "MOZ_DBG should return the same type") + +struct Number { + explicit Number(int aValue) : mValue(aValue) {} + + Number(const Number& aOther) = default; + + Number(Number&& aOther) : mValue(aOther.mValue) { aOther.mValue = 0; } + + Number& operator=(const Number& aOther) = default; + + Number& operator=(Number&& aOther) { + mValue = aOther.mValue; + aOther.mValue = 0; + return *this; + } + + ~Number() { mValue = -999; } + + int mValue; + + MOZ_DEFINE_DBG(Number, mValue) +}; + +struct MoveOnly { + explicit MoveOnly(int aValue) : mValue(aValue) {} + + MoveOnly(const MoveOnly& aOther) = delete; + + MoveOnly(MoveOnly&& aOther) : mValue(aOther.mValue) { aOther.mValue = 0; } + + MoveOnly& operator=(MoveOnly& aOther) = default; + + MoveOnly& operator=(MoveOnly&& aOther) { + mValue = aOther.mValue; + aOther.mValue = 0; + return *this; + } + + int mValue; + + MOZ_DEFINE_DBG(MoveOnly) +}; + +void StaticAssertions() { + int x = 123; + Number y(123); + Number z(234); + MoveOnly w(456); + + // Static assertions. + + // lvalues + TEST_MOZ_DBG_TYPE_SAME(x); // int& + TEST_MOZ_DBG_TYPE_SAME(y); // Number& + TEST_MOZ_DBG_TYPE_SAME(x = 234); // int& + TEST_MOZ_DBG_TYPE_SAME(y = z); // Number& + TEST_MOZ_DBG_TYPE_SAME(w); // MoveOnly& + + // prvalues (which MOZ_DBG turns into xvalues by creating objects for them) + TEST_MOZ_DBG_TYPE_IS(int&&, 123); + TEST_MOZ_DBG_TYPE_IS(int&&, 1 + 2); + TEST_MOZ_DBG_TYPE_IS(int*&&, &x); + TEST_MOZ_DBG_TYPE_IS(int&&, x++); + TEST_MOZ_DBG_TYPE_IS(Number&&, Number(123)); + TEST_MOZ_DBG_TYPE_IS(MoveOnly&&, MoveOnly(123)); + + // xvalues + TEST_MOZ_DBG_TYPE_SAME(std::move(y)); // int&& + TEST_MOZ_DBG_TYPE_SAME(std::move(y)); // Number&& + TEST_MOZ_DBG_TYPE_SAME(std::move(w)); // MoveOnly& + + Unused << x; + Unused << y; + Unused << z; +} + +TEST(MozDbg, ObjectValues) +{ + // Test that moves and assignments all operate correctly with MOZ_DBG wrapped + // around various parts of the expression. + + Number a(1); + Number b(4); + + ASSERT_EQ(a.mValue, 1); + + MOZ_DBG(a.mValue); + ASSERT_EQ(a.mValue, 1); + + MOZ_DBG(a.mValue + 1); + ASSERT_EQ(a.mValue, 1); + + MOZ_DBG(a.mValue = 2); + ASSERT_EQ(a.mValue, 2); + + MOZ_DBG(a).mValue = 3; + ASSERT_EQ(a.mValue, 3); + + MOZ_DBG(a = b); + ASSERT_EQ(a.mValue, 4); + ASSERT_EQ(b.mValue, 4); + + b.mValue = 5; + MOZ_DBG(a) = b; + ASSERT_EQ(a.mValue, 5); + ASSERT_EQ(b.mValue, 5); + + b.mValue = 6; + MOZ_DBG(a = std::move(b)); + ASSERT_EQ(a.mValue, 6); + ASSERT_EQ(b.mValue, 0); + + b.mValue = 7; + MOZ_DBG(a) = std::move(b); + ASSERT_EQ(a.mValue, 7); + ASSERT_EQ(b.mValue, 0); + + b.mValue = 8; + a = std::move(MOZ_DBG(b)); + ASSERT_EQ(a.mValue, 8); + ASSERT_EQ(b.mValue, 0); + + a = MOZ_DBG(Number(9)); + ASSERT_EQ(a.mValue, 9); + + MoveOnly c(1); + MoveOnly d(2); + + c = std::move(MOZ_DBG(d)); + ASSERT_EQ(c.mValue, 2); + ASSERT_EQ(d.mValue, 0); + + c.mValue = 3; + d.mValue = 4; + c = MOZ_DBG(std::move(d)); + ASSERT_EQ(c.mValue, 4); + ASSERT_EQ(d.mValue, 0); + + c.mValue = 5; + d.mValue = 6; + MOZ_DBG(c = std::move(d)); + ASSERT_EQ(c.mValue, 6); + ASSERT_EQ(d.mValue, 0); + + c = MOZ_DBG(MoveOnly(7)); + ASSERT_EQ(c.mValue, 7); +} diff --git a/mfbt/tests/gtest/TestResultExtensions.cpp b/mfbt/tests/gtest/TestResultExtensions.cpp new file mode 100644 index 0000000000..711e4f33e4 --- /dev/null +++ b/mfbt/tests/gtest/TestResultExtensions.cpp @@ -0,0 +1,579 @@ +/* -*- 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 "gtest/gtest.h" + +#include "mozilla/ResultExtensions.h" +#include "nsLocalFile.h" + +#include + +using namespace mozilla; + +namespace { +class TestClass { + public: + static constexpr int kTestValue = 42; + + nsresult NonOverloadedNoInput(int* aOut) { + *aOut = kTestValue; + return NS_OK; + } + nsresult NonOverloadedNoInputFails(int* aOut) { return NS_ERROR_FAILURE; } + + nsresult NonOverloadedNoInputConst(int* aOut) const { + *aOut = kTestValue; + return NS_OK; + } + nsresult NonOverloadedNoInputFailsConst(int* aOut) const { + return NS_ERROR_FAILURE; + } + + nsresult NonOverloadedNoInputRef(int& aOut) { + aOut = kTestValue; + return NS_OK; + } + nsresult NonOverloadedNoInputFailsRef(int& aOut) { return NS_ERROR_FAILURE; } + + nsresult NonOverloadedNoInputComplex(std::pair* aOut) { + *aOut = std::pair{kTestValue, kTestValue}; + return NS_OK; + } + nsresult NonOverloadedNoInputFailsComplex(std::pair* aOut) { + return NS_ERROR_FAILURE; + } + + nsresult NonOverloadedWithInput(int aIn, int* aOut) { + *aOut = aIn; + return NS_OK; + } + nsresult NonOverloadedWithInputFails(int aIn, int* aOut) { + return NS_ERROR_FAILURE; + } + + nsresult NonOverloadedNoOutput(int aIn) { return NS_OK; } + nsresult NonOverloadedNoOutputFails(int aIn) { return NS_ERROR_FAILURE; } + + nsresult PolymorphicNoInput(nsIFile** aOut) { + *aOut = MakeAndAddRef().take(); + return NS_OK; + } + nsresult PolymorphicNoInputFails(nsIFile** aOut) { return NS_ERROR_FAILURE; } +}; + +class RefCountedTestClass { + public: + NS_INLINE_DECL_REFCOUNTING(RefCountedTestClass); + + static constexpr int kTestValue = 42; + + nsresult NonOverloadedNoInput(int* aOut) { + *aOut = kTestValue; + return NS_OK; + } + nsresult NonOverloadedNoInputFails(int* aOut) { return NS_ERROR_FAILURE; } + + private: + ~RefCountedTestClass() = default; +}; + +// Check that DerefedType deduces the types as expected +static_assert(std::is_same_v, + RefCountedTestClass>); +static_assert(std::is_same_v, + RefCountedTestClass>); +static_assert( + std::is_same_v>, + RefCountedTestClass>); + +static_assert(std::is_same_v, nsIFile>); +static_assert(std::is_same_v, nsIFile>); +static_assert( + std::is_same_v>, nsIFile>); +} // namespace + +TEST(ResultExtensions_ToResultInvoke, Lambda_NoInput) +{ + TestClass foo; + + // success + { + auto valOrErr = ToResultInvoke( + [&foo](int* out) { return foo.NonOverloadedNoInput(out); }); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = ToResultInvoke( + [&foo](int* out) { return foo.NonOverloadedNoInputFails(out); }); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvoke, MemFn_NoInput) +{ + TestClass foo; + + // success + { + auto valOrErr = + ToResultInvoke(std::mem_fn(&TestClass::NonOverloadedNoInput), foo); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = ToResultInvoke( + std::mem_fn(&TestClass::NonOverloadedNoInputFails), foo); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvoke, MemFn_Polymorphic_NoInput) +{ + TestClass foo; + + // success + { + auto valOrErr = ToResultInvoke>( + std::mem_fn(&TestClass::PolymorphicNoInput), foo); + static_assert(std::is_same_v, nsresult>>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_NE(nullptr, valOrErr.inspect()); + + ASSERT_EQ(ToResultInvoke(std::mem_fn(&nsIFile::GetPath), + *MakeRefPtr()) + .inspect(), + ToResultInvoke(std::mem_fn(&nsIFile::GetPath), + valOrErr.inspect()) + .inspect()); + } + + // failure + { + auto valOrErr = ToResultInvoke>( + std::mem_fn(&TestClass::PolymorphicNoInputFails), foo); + static_assert(std::is_same_v, nsresult>>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, NoInput) +{ + TestClass foo; + + // success + { + auto valOrErr = ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInput); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = + ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputFails); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, NoInput_Const) +{ + const TestClass foo; + + // success + { + auto valOrErr = + ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputConst); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = + ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputFailsConst); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, NoInput_Ref) +{ + TestClass foo; + + // success + { + auto valOrErr = + ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputRef); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = + ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputFailsRef); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, NoInput_Complex) +{ + TestClass foo; + + // success + { + auto valOrErr = + ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputComplex); + static_assert(std::is_same_v, nsresult>>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ((std::pair{TestClass::kTestValue, TestClass::kTestValue}), + valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = + ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputFailsComplex); + static_assert(std::is_same_v, nsresult>>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, WithInput) +{ + TestClass foo; + + // success + { + auto valOrErr = ToResultInvokeMember( + foo, &TestClass::NonOverloadedWithInput, -TestClass::kTestValue); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(-TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = ToResultInvokeMember( + foo, &TestClass::NonOverloadedWithInputFails, -TestClass::kTestValue); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, NoOutput) +{ + TestClass foo; + + // success + { + auto valOrErr = ToResultInvokeMember(foo, &TestClass::NonOverloadedNoOutput, + -TestClass::kTestValue); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + } + + // failure + { + auto valOrErr = ToResultInvokeMember( + foo, &TestClass::NonOverloadedNoOutputFails, -TestClass::kTestValue); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, NoInput_Macro) +{ + TestClass foo; + + // success + { + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInput); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInputFails); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, NoInput_Const_Macro) +{ + const TestClass foo; + + // success + { + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInputConst); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = + MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInputFailsConst); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, NoInput_Ref_Macro) +{ + TestClass foo; + + // success + { + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInputRef); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = + MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInputFailsRef); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, NoInput_Complex_Macro) +{ + TestClass foo; + + // success + { + auto valOrErr = + MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInputComplex); + static_assert(std::is_same_v, nsresult>>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ((std::pair{TestClass::kTestValue, TestClass::kTestValue}), + valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = + MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInputFailsComplex); + + static_assert(std::is_same_v, nsresult>>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, WithInput_Macro) +{ + TestClass foo; + + // success + { + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedWithInput, + -TestClass::kTestValue); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(-TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER( + foo, NonOverloadedWithInputFails, -TestClass::kTestValue); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, NoOutput_Macro) +{ + TestClass foo; + + // success + { + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoOutput, + -TestClass::kTestValue); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + } + + // failure + { + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoOutputFails, + -TestClass::kTestValue); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, NoInput_Complex_Macro_Typed) +{ + TestClass foo; + + // success + { + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER_TYPED( + (std::pair), foo, NonOverloadedNoInputComplex); + static_assert(std::is_same_v, nsresult>>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ((std::pair{TestClass::kTestValue, TestClass::kTestValue}), + valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER_TYPED( + (std::pair), foo, NonOverloadedNoInputFailsComplex); + static_assert(std::is_same_v, nsresult>>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, RefPtr_NoInput) +{ + auto foo = MakeRefPtr(); + + // success + { + auto valOrErr = + ToResultInvokeMember(foo, &RefCountedTestClass::NonOverloadedNoInput); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = ToResultInvokeMember( + foo, &RefCountedTestClass::NonOverloadedNoInputFails); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, RefPtr_NoInput_Macro) +{ + auto foo = MakeRefPtr(); + + // success + { + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInput); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInputFails); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, RawPtr_NoInput_Macro) +{ + auto foo = MakeRefPtr(); + auto* fooPtr = foo.get(); + + // success + { + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(fooPtr, NonOverloadedNoInput); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = + MOZ_TO_RESULT_INVOKE_MEMBER(fooPtr, NonOverloadedNoInputFails); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, nsCOMPtr_AbstractClass_WithInput) +{ + nsCOMPtr file = MakeAndAddRef(); + + auto valOrErr = ToResultInvokeMember(file, &nsIFile::Equals, file); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(true, valOrErr.unwrap()); +} + +TEST(ResultExtensions_ToResultInvokeMember, + RawPtr_AbstractClass_WithInput_Macro) +{ + nsCOMPtr file = MakeAndAddRef(); + auto* filePtr = file.get(); + + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(filePtr, Equals, file); + static_assert(std::is_same_v>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(true, valOrErr.unwrap()); +} + +TEST(ResultExtensions_ToResultInvokeMember, + RawPtr_AbstractClass_NoInput_Macro_Typed) +{ + nsCOMPtr file = MakeAndAddRef(); + auto* filePtr = file.get(); + + auto valOrErr = + MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCOMPtr, filePtr, Clone); + static_assert( + std::is_same_v, nsresult>>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_NE(nullptr, valOrErr.unwrap()); +} diff --git a/mfbt/tests/gtest/TestReverseIterator.cpp b/mfbt/tests/gtest/TestReverseIterator.cpp new file mode 100644 index 0000000000..a1ba019aa1 --- /dev/null +++ b/mfbt/tests/gtest/TestReverseIterator.cpp @@ -0,0 +1,104 @@ +/* -*- 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 "gtest/gtest.h" + +#include "mozilla/ReverseIterator.h" + +using namespace mozilla; + +TEST(ReverseIterator, Const_RangeBasedFor) +{ + const std::vector in = {1, 2, 3, 4}; + const auto reversedRange = + detail::IteratorRange::const_iterator>>{ + ReverseIterator{in.end()}, ReverseIterator{in.begin()}}; + + const std::vector expected = {4, 3, 2, 1}; + std::vector out; + for (auto i : reversedRange) { + out.emplace_back(i); + } + + EXPECT_EQ(expected, out); +} + +TEST(ReverseIterator, NonConst_RangeBasedFor) +{ + std::vector in = {1, 2, 3, 4}; + auto reversedRange = + detail::IteratorRange::iterator>>{ + ReverseIterator{in.end()}, ReverseIterator{in.begin()}}; + + const std::vector expected = {-1, -2, -3, -4}; + for (auto& i : reversedRange) { + i = -i; + } + + EXPECT_EQ(expected, in); +} + +TEST(ReverseIterator, Difference) +{ + const std::vector in = {1, 2, 3, 4}; + using reverse_iterator = ReverseIterator::const_iterator>; + + reverse_iterator rbegin = reverse_iterator{in.end()}, + rend = reverse_iterator{in.begin()}; + EXPECT_EQ(4, rend - rbegin); + EXPECT_EQ(0, rend - rend); + EXPECT_EQ(0, rbegin - rbegin); + + --rend; + EXPECT_EQ(3, rend - rbegin); + + ++rbegin; + EXPECT_EQ(2, rend - rbegin); + + rend--; + EXPECT_EQ(1, rend - rbegin); + + rbegin++; + EXPECT_EQ(0, rend - rbegin); +} + +TEST(ReverseIterator, Comparison) +{ + const std::vector in = {1, 2, 3, 4}; + using reverse_iterator = ReverseIterator::const_iterator>; + + reverse_iterator rbegin = reverse_iterator{in.end()}, + rend = reverse_iterator{in.begin()}; + EXPECT_TRUE(rbegin < rend); + EXPECT_FALSE(rend < rbegin); + EXPECT_FALSE(rend < rend); + EXPECT_FALSE(rbegin < rbegin); + + EXPECT_TRUE(rend > rbegin); + EXPECT_FALSE(rbegin > rend); + EXPECT_FALSE(rend > rend); + EXPECT_FALSE(rbegin > rbegin); + + EXPECT_TRUE(rbegin <= rend); + EXPECT_FALSE(rend <= rbegin); + EXPECT_TRUE(rend <= rend); + EXPECT_TRUE(rbegin <= rbegin); + + EXPECT_TRUE(rend >= rbegin); + EXPECT_FALSE(rbegin >= rend); + EXPECT_TRUE(rend >= rend); + EXPECT_TRUE(rbegin >= rbegin); + + EXPECT_FALSE(rend == rbegin); + EXPECT_FALSE(rbegin == rend); + EXPECT_TRUE(rend == rend); + EXPECT_TRUE(rbegin == rbegin); + + EXPECT_TRUE(rend != rbegin); + EXPECT_TRUE(rbegin != rend); + EXPECT_FALSE(rend != rend); + EXPECT_FALSE(rbegin != rbegin); +} diff --git a/mfbt/tests/gtest/TestSpan.cpp b/mfbt/tests/gtest/TestSpan.cpp new file mode 100644 index 0000000000..fb7db0d158 --- /dev/null +++ b/mfbt/tests/gtest/TestSpan.cpp @@ -0,0 +1,2355 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +// Adapted from +// https://github.com/Microsoft/GSL/blob/3819df6e378ffccf0e29465afe99c3b324c2aa70/tests/Span_tests.cpp + +#include "gtest/gtest.h" + +#include "mozilla/Array.h" +#include "mozilla/Span.h" + +#include "nsString.h" +#include "nsTArray.h" +#include "mozilla/Range.h" + +#include + +#define SPAN_TEST(name) TEST(SpanTest, name) +#define CHECK_THROW(a, b) + +using namespace mozilla; + +static_assert(std::is_convertible_v, Span>, + "Range should convert into const"); +static_assert(std::is_convertible_v, Span>, + "const Range should convert into const"); +static_assert(!std::is_convertible_v, Span>, + "Range should not drop const in conversion"); +static_assert(std::is_convertible_v, Range>, + "Span should convert into const"); +static_assert(std::is_convertible_v, Range>, + "const Span should convert into const"); +static_assert(!std::is_convertible_v, Range>, + "Span should not drop const in conversion"); +static_assert(std::is_convertible_v, Span>, + "const Span should convert into const"); +static_assert(std::is_convertible_v, Span>, + "Span should convert into const"); +static_assert(!std::is_convertible_v, Span>, + "Span should not drop const in conversion"); +static_assert(std::is_convertible_v, Span>, + "const nsTArray should convert into const"); +static_assert(std::is_convertible_v, Span>, + "nsTArray should convert into const"); +static_assert(!std::is_convertible_v, Span>, + "nsTArray should not drop const in conversion"); +static_assert(std::is_convertible_v, Span>, + "nsTArray should convert into const"); +static_assert(!std::is_convertible_v, Span>, + "nsTArray should not drop const in conversion"); + +static_assert(std::is_convertible_v, Span>, + "const std::vector should convert into const"); +static_assert(std::is_convertible_v, Span>, + "std::vector should convert into const"); +static_assert(!std::is_convertible_v, Span>, + "std::vector should not drop const in conversion"); + +/** + * Rust slice-compatible nullptr replacement value. + */ +#define SLICE_CONST_INT_PTR reinterpret_cast(alignof(const int)) + +/** + * Rust slice-compatible nullptr replacement value. + */ +#define SLICE_INT_PTR reinterpret_cast(alignof(int)) + +/** + * Rust slice-compatible nullptr replacement value. + */ +#define SLICE_CONST_INT_PTR_PTR \ + reinterpret_cast(alignof(const int*)) + +/** + * Rust slice-compatible nullptr replacement value. + */ +#define SLICE_INT_PTR_PTR reinterpret_cast(alignof(int*)) + +namespace { +struct BaseClass {}; +struct DerivedClass : BaseClass {}; +} // namespace + +void AssertSpanOfThreeInts(Span s) { + ASSERT_EQ(s.size(), 3U); + ASSERT_EQ(s[0], 1); + ASSERT_EQ(s[1], 2); + ASSERT_EQ(s[2], 3); +} + +void AssertSpanOfThreeChars(Span s) { + ASSERT_EQ(s.size(), 3U); + ASSERT_EQ(s[0], 'a'); + ASSERT_EQ(s[1], 'b'); + ASSERT_EQ(s[2], 'c'); +} + +void AssertSpanOfThreeChar16s(Span s) { + ASSERT_EQ(s.size(), 3U); + ASSERT_EQ(s[0], 'a'); + ASSERT_EQ(s[1], 'b'); + ASSERT_EQ(s[2], 'c'); +} + +void AssertSpanOfThreeCharsViaString(const nsACString& aStr) { + AssertSpanOfThreeChars(aStr); +} + +void AssertSpanOfThreeChar16sViaString(const nsAString& aStr) { + AssertSpanOfThreeChar16s(aStr); +} + +SPAN_TEST(default_constructor) { + { + Span s; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + + Span cs; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR); + } + + { + Span s; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + + Span cs; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + Span s; + ASSERT_EQ(s.Length(), 1U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); // explains why it can't compile +#endif + } + + { + Span s{}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + + Span cs{}; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR); + } +} + +SPAN_TEST(size_optimization) { + { + Span s; + ASSERT_EQ(sizeof(s), sizeof(int*) + sizeof(size_t)); + } + + { + Span s; + ASSERT_EQ(sizeof(s), sizeof(int*)); + } +} + +SPAN_TEST(from_nullptr_constructor) { + { + Span s = nullptr; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + + Span cs = nullptr; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR); + } + + { + Span s = nullptr; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + + Span cs = nullptr; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + Span s = nullptr; + ASSERT_EQ(s.Length(), 1U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); // explains why it can't compile +#endif + } + + { + Span s{nullptr}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + + Span cs{nullptr}; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR); + } + + { + Span s{nullptr}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR_PTR); + + Span cs{nullptr}; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR_PTR); + } +} + +SPAN_TEST(from_nullptr_length_constructor) { + { + Span s{nullptr, static_cast::index_type>(0)}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + + Span cs{nullptr, static_cast::index_type>(0)}; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR); + } + + { + Span s{nullptr, static_cast::index_type>(0)}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + + Span cs{nullptr, static_cast::index_type>(0)}; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR); + } + +#if 0 + { + auto workaround_macro = []() { Span s{ nullptr, static_cast::index_type>(0) }; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + { + auto workaround_macro = []() { Span s{nullptr, 1}; }; + CHECK_THROW(workaround_macro(), fail_fast); + + auto const_workaround_macro = []() { Span cs{nullptr, 1}; }; + CHECK_THROW(const_workaround_macro(), fail_fast); + } + + { + auto workaround_macro = []() { Span s{nullptr, 1}; }; + CHECK_THROW(workaround_macro(), fail_fast); + + auto const_workaround_macro = []() { Span s{nullptr, 1}; }; + CHECK_THROW(const_workaround_macro(), fail_fast); + } +#endif + { + Span s{nullptr, static_cast::index_type>(0)}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR_PTR); + + Span cs{nullptr, static_cast::index_type>(0)}; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR_PTR); + } +} + +SPAN_TEST(from_pointer_length_constructor) { + int arr[4] = {1, 2, 3, 4}; + + { + Span s{&arr[0], 2}; + ASSERT_EQ(s.Length(), 2U); + ASSERT_EQ(s.data(), &arr[0]); + ASSERT_EQ(s[0], 1); + ASSERT_EQ(s[1], 2); + } + + { + Span s{&arr[0], 2}; + ASSERT_EQ(s.Length(), 2U); + ASSERT_EQ(s.data(), &arr[0]); + ASSERT_EQ(s[0], 1); + ASSERT_EQ(s[1], 2); + } + + { + int* p = nullptr; + Span s{p, static_cast::index_type>(0)}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + } + +#if 0 + { + int* p = nullptr; + auto workaround_macro = [=]() { Span s{p, 2}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } +#endif + + { + auto s = Span(&arr[0], 2); + ASSERT_EQ(s.Length(), 2U); + ASSERT_EQ(s.data(), &arr[0]); + ASSERT_EQ(s[0], 1); + ASSERT_EQ(s[1], 2); + } + + { + int* p = nullptr; + auto s = Span(p, static_cast::index_type>(0)); + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + } + +#if 0 + { + int* p = nullptr; + auto workaround_macro = [=]() { Span(p, 2); }; + CHECK_THROW(workaround_macro(), fail_fast); + } +#endif +} + +SPAN_TEST(from_pointer_pointer_constructor) { + int arr[4] = {1, 2, 3, 4}; + + { + Span s{&arr[0], &arr[2]}; + ASSERT_EQ(s.Length(), 2U); + ASSERT_EQ(s.data(), &arr[0]); + ASSERT_EQ(s[0], 1); + ASSERT_EQ(s[1], 2); + } + + { + Span s{&arr[0], &arr[2]}; + ASSERT_EQ(s.Length(), 2U); + ASSERT_EQ(s.data(), &arr[0]); + ASSERT_EQ(s[0], 1); + ASSERT_EQ(s[1], 2); + } + + { + Span s{&arr[0], &arr[0]}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), &arr[0]); + } + + { + Span s{&arr[0], &arr[0]}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), &arr[0]); + } + + // this will fail the std::distance() precondition, which asserts on MSVC + // debug builds + //{ + // auto workaround_macro = [&]() { Span s{&arr[1], &arr[0]}; }; + // CHECK_THROW(workaround_macro(), fail_fast); + //} + + // this will fail the std::distance() precondition, which asserts on MSVC + // debug builds + //{ + // int* p = nullptr; + // auto workaround_macro = [&]() { Span s{&arr[0], p}; }; + // CHECK_THROW(workaround_macro(), fail_fast); + //} + + { + int* p = nullptr; + Span s{p, p}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + } + + { + int* p = nullptr; + Span s{p, p}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + } + + // this will fail the std::distance() precondition, which asserts on MSVC + // debug builds + //{ + // int* p = nullptr; + // auto workaround_macro = [&]() { Span s{&arr[0], p}; }; + // CHECK_THROW(workaround_macro(), fail_fast); + //} + + { + auto s = Span(&arr[0], &arr[2]); + ASSERT_EQ(s.Length(), 2U); + ASSERT_EQ(s.data(), &arr[0]); + ASSERT_EQ(s[0], 1); + ASSERT_EQ(s[1], 2); + } + + { + auto s = Span(&arr[0], &arr[0]); + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), &arr[0]); + } + + { + int* p = nullptr; + auto s = Span(p, p); + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + } +} + +SPAN_TEST(from_array_constructor) { + int arr[5] = {1, 2, 3, 4, 5}; + + { + Span s{arr}; + ASSERT_EQ(s.Length(), 5U); + ASSERT_EQ(s.data(), &arr[0]); + } + + { + Span s{arr}; + ASSERT_EQ(s.Length(), 5U); + ASSERT_EQ(s.data(), &arr[0]); + } + + int arr2d[2][3] = {{1, 2, 3}, {4, 5, 6}}; + +#ifdef CONFIRM_COMPILATION_ERRORS + { Span s{arr}; } + + { + Span s{arr}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), &arr[0]); + } + + { + Span s{arr2d}; + ASSERT_EQ(s.Length(), 6U); + ASSERT_EQ(s.data(), &arr2d[0][0]); + ASSERT_EQ(s[0], 1); + ASSERT_EQ(s[5], 6); + } + + { + Span s{arr2d}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), &arr2d[0][0]); + } + + { Span s{arr2d}; } +#endif + { + Span s{&(arr2d[0]), 1}; + ASSERT_EQ(s.Length(), 1U); + ASSERT_EQ(s.data(), &arr2d[0]); + } + + int arr3d[2][3][2] = {{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}}; + +#ifdef CONFIRM_COMPILATION_ERRORS + { + Span s{arr3d}; + ASSERT_EQ(s.Length(), 12U); + ASSERT_EQ(s.data(), &arr3d[0][0][0]); + ASSERT_EQ(s[0], 1); + ASSERT_EQ(s[11], 12); + } + + { + Span s{arr3d}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), &arr3d[0][0][0]); + } + + { Span s{arr3d}; } + + { + Span s{arr3d}; + ASSERT_EQ(s.Length(), 12U); + ASSERT_EQ(s.data(), &arr3d[0][0][0]); + ASSERT_EQ(s[0], 1); + ASSERT_EQ(s[5], 6); + } +#endif + { + Span s{&arr3d[0], 1}; + ASSERT_EQ(s.Length(), 1U); + ASSERT_EQ(s.data(), &arr3d[0]); + } + + { + auto s = Span(arr); + ASSERT_EQ(s.Length(), 5U); + ASSERT_EQ(s.data(), &arr[0]); + } + + { + auto s = Span(&(arr2d[0]), 1); + ASSERT_EQ(s.Length(), 1U); + ASSERT_EQ(s.data(), &arr2d[0]); + } + + { + auto s = Span(&arr3d[0], 1); + ASSERT_EQ(s.Length(), 1U); + ASSERT_EQ(s.data(), &arr3d[0]); + } +} + +SPAN_TEST(from_dynamic_array_constructor) { + double(*arr)[3][4] = new double[100][3][4]; + + { + Span s(&arr[0][0][0], 10); + ASSERT_EQ(s.Length(), 10U); + ASSERT_EQ(s.data(), &arr[0][0][0]); + } + + { + auto s = Span(&arr[0][0][0], 10); + ASSERT_EQ(s.Length(), 10U); + ASSERT_EQ(s.data(), &arr[0][0][0]); + } + + delete[] arr; +} + +SPAN_TEST(from_std_array_constructor) { + std::array arr = {{1, 2, 3, 4}}; + + { + Span s{arr}; + ASSERT_EQ(s.size(), narrow_cast(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + + Span cs{arr}; + ASSERT_EQ(cs.size(), narrow_cast(arr.size())); + ASSERT_EQ(cs.data(), arr.data()); + } + + { + Span s{arr}; + ASSERT_EQ(s.size(), narrow_cast(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + + Span cs{arr}; + ASSERT_EQ(cs.size(), narrow_cast(arr.size())); + ASSERT_EQ(cs.data(), arr.data()); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + Span s{arr}; + ASSERT_EQ(s.size(), 2U); + ASSERT_EQ(s.data(), arr.data()); + + Span cs{arr}; + ASSERT_EQ(cs.size(), 2U); + ASSERT_EQ(cs.data(), arr.data()); + } + + { + Span s{arr}; + ASSERT_EQ(s.size(), 0U); + ASSERT_EQ(s.data(), arr.data()); + + Span cs{arr}; + ASSERT_EQ(cs.size(), 0U); + ASSERT_EQ(cs.data(), arr.data()); + } + + { Span s{arr}; } + + { + auto get_an_array = []() -> std::array { return {1, 2, 3, 4}; }; + auto take_a_Span = [](Span s) { static_cast(s); }; + // try to take a temporary std::array + take_a_Span(get_an_array()); + } +#endif + + { + auto get_an_array = []() -> std::array { return {{1, 2, 3, 4}}; }; + auto take_a_Span = [](Span s) { static_cast(s); }; + // try to take a temporary std::array + take_a_Span(get_an_array()); + } + + { + auto s = Span(arr); + ASSERT_EQ(s.size(), narrow_cast(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + } +} + +SPAN_TEST(from_const_std_array_constructor) { + const std::array arr = {{1, 2, 3, 4}}; + + { + Span s{arr}; + ASSERT_EQ(s.size(), narrow_cast(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + } + + { + Span s{arr}; + ASSERT_EQ(s.size(), narrow_cast(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + Span s{arr}; + ASSERT_EQ(s.size(), 2U); + ASSERT_EQ(s.data(), arr.data()); + } + + { + Span s{arr}; + ASSERT_EQ(s.size(), 0U); + ASSERT_EQ(s.data(), arr.data()); + } + + { Span s{arr}; } +#endif + + { + auto get_an_array = []() -> const std::array { + return {{1, 2, 3, 4}}; + }; + auto take_a_Span = [](Span s) { static_cast(s); }; + // try to take a temporary std::array + take_a_Span(get_an_array()); + } + + { + auto s = Span(arr); + ASSERT_EQ(s.size(), narrow_cast(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + } +} + +SPAN_TEST(from_std_array_const_constructor) { + std::array arr = {{1, 2, 3, 4}}; + + { + Span s{arr}; + ASSERT_EQ(s.size(), narrow_cast(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + } + + { + Span s{arr}; + ASSERT_EQ(s.size(), narrow_cast(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + Span s{arr}; + ASSERT_EQ(s.size(), 2U); + ASSERT_EQ(s.data(), arr.data()); + } + + { + Span s{arr}; + ASSERT_EQ(s.size(), 0U); + ASSERT_EQ(s.data(), arr.data()); + } + + { Span s{arr}; } + + { Span s{arr}; } +#endif + + { + auto s = Span(arr); + ASSERT_EQ(s.size(), narrow_cast(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + } +} + +SPAN_TEST(from_mozilla_array_constructor) { + mozilla::Array arr(1, 2, 3, 4); + + { + Span s{arr}; + ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + + Span cs{arr}; + ASSERT_EQ(cs.size(), narrow_cast(arr.cend() - arr.cbegin())); + ASSERT_EQ(cs.data(), &arr[0]); + } + + { + Span s{arr}; + ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + + Span cs{arr}; + ASSERT_EQ(cs.size(), narrow_cast(arr.cend() - arr.cbegin())); + ASSERT_EQ(cs.data(), &arr[0]); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + Span s{arr}; + ASSERT_EQ(s.size(), 2U); + ASSERT_EQ(s.data(), &arr[0]); + + Span cs{arr}; + ASSERT_EQ(cs.size(), 2U); + ASSERT_EQ(cs.data(), &arr[0]); + } + + { + Span s{arr}; + ASSERT_EQ(s.size(), 0U); + ASSERT_EQ(s.data(), &arr[0]); + + Span cs{arr}; + ASSERT_EQ(cs.size(), 0U); + ASSERT_EQ(cs.data(), &arr[0]); + } + + { Span s{arr}; } + + { + auto get_an_array = []() -> mozilla::Array { return {1, 2, 3, 4}; }; + auto take_a_Span = [](Span s) { static_cast(s); }; + // try to take a temporary mozilla::Array + take_a_Span(get_an_array()); + } +#endif + + { + auto get_an_array = []() -> mozilla::Array { return {1, 2, 3, 4}; }; + auto take_a_Span = [](Span s) { static_cast(s); }; + // try to take a temporary mozilla::Array + take_a_Span(get_an_array()); + } + + { + auto s = Span(arr); + ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + } +} + +SPAN_TEST(from_const_mozilla_array_constructor) { + const mozilla::Array arr(1, 2, 3, 4); + + { + Span s{arr}; + ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + } + + { + Span s{arr}; + ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + Span s{arr}; + ASSERT_EQ(s.size(), 2U); + ASSERT_EQ(s.data(), &arr[0]); + } + + { + Span s{arr}; + ASSERT_EQ(s.size(), 0U); + ASSERT_EQ(s.data(), &arr[0]); + } + + { Span s{arr}; } +#endif + +#if 0 + { + auto get_an_array = []() -> const mozilla::Array { + return { 1, 2, 3, 4 }; + }; + auto take_a_Span = [](Span s) { static_cast(s); }; + // try to take a temporary mozilla::Array + take_a_Span(get_an_array()); + } +#endif + + { + auto s = Span(arr); + ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + } +} + +SPAN_TEST(from_mozilla_array_const_constructor) { + mozilla::Array arr(1, 2, 3, 4); + + { + Span s{arr}; + ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + } + + { + Span s{arr}; + ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + Span s{arr}; + ASSERT_EQ(s.size(), 2U); + ASSERT_EQ(s.data(), &arr[0]); + } + + { + Span s{arr}; + ASSERT_EQ(s.size(), 0U); + ASSERT_EQ(s.data(), &arr[0]); + } + + { Span s{arr}; } + + { Span s{arr}; } +#endif + + { + auto s = Span(arr); + ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + } +} + +SPAN_TEST(from_container_constructor) { + std::vector v = {1, 2, 3}; + const std::vector cv = v; + + { + AssertSpanOfThreeInts(v); + + Span s{v}; + ASSERT_EQ(s.size(), narrow_cast(v.size())); + ASSERT_EQ(s.data(), v.data()); + + Span cs{v}; + ASSERT_EQ(cs.size(), narrow_cast(v.size())); + ASSERT_EQ(cs.data(), v.data()); + } + + std::string str = "hello"; + const std::string cstr = "hello"; + + { +#ifdef CONFIRM_COMPILATION_ERRORS + Span s{str}; + ASSERT_EQ(s.size(), narrow_cast(str.size())); + ASSERT_EQ(s.data(), str.data()); +#endif + Span cs{str}; + ASSERT_EQ(cs.size(), narrow_cast(str.size())); + ASSERT_EQ(cs.data(), str.data()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + Span s{cstr}; +#endif + Span cs{cstr}; + ASSERT_EQ(cs.size(), narrow_cast(cstr.size())); + ASSERT_EQ(cs.data(), cstr.data()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_vector = []() -> std::vector { return {}; }; + auto use_Span = [](Span s) { static_cast(s); }; + use_Span(get_temp_vector()); +#endif + } + + { + auto get_temp_vector = []() -> std::vector { return {}; }; + auto use_Span = [](Span s) { static_cast(s); }; + use_Span(get_temp_vector()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_string = []() -> std::string { return {}; }; + auto use_Span = [](Span s) { static_cast(s); }; + use_Span(get_temp_string()); +#endif + } + + { + auto get_temp_string = []() -> std::string { return {}; }; + auto use_Span = [](Span s) { static_cast(s); }; + use_Span(get_temp_string()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_vector = []() -> const std::vector { return {}; }; + auto use_Span = [](Span s) { static_cast(s); }; + use_Span(get_temp_vector()); +#endif + } + + { + auto get_temp_string = []() -> const std::string { return {}; }; + auto use_Span = [](Span s) { static_cast(s); }; + use_Span(get_temp_string()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::map m; + Span s{m}; +#endif + } + + { + auto s = Span(v); + ASSERT_EQ(s.size(), narrow_cast(v.size())); + ASSERT_EQ(s.data(), v.data()); + + auto cs = Span(cv); + ASSERT_EQ(cs.size(), narrow_cast(cv.size())); + ASSERT_EQ(cs.data(), cv.data()); + } +} + +SPAN_TEST(from_xpcom_collections) { + { + nsTArray v; + v.AppendElement(1); + v.AppendElement(2); + v.AppendElement(3); + + AssertSpanOfThreeInts(v); + + Span s{v}; + ASSERT_EQ(s.size(), narrow_cast(v.Length())); + ASSERT_EQ(s.data(), v.Elements()); + ASSERT_EQ(s[2], 3); + + Span cs{v}; + ASSERT_EQ(cs.size(), narrow_cast(v.Length())); + ASSERT_EQ(cs.data(), v.Elements()); + ASSERT_EQ(cs[2], 3); + } + { + nsTArray v; + v.AppendElement(1); + v.AppendElement(2); + v.AppendElement(3); + + AssertSpanOfThreeInts(v); + + auto s = Span(v); + ASSERT_EQ(s.size(), narrow_cast(v.Length())); + ASSERT_EQ(s.data(), v.Elements()); + ASSERT_EQ(s[2], 3); + } + { + AutoTArray v; + v.AppendElement(1); + v.AppendElement(2); + v.AppendElement(3); + + AssertSpanOfThreeInts(v); + + Span s{v}; + ASSERT_EQ(s.size(), narrow_cast(v.Length())); + ASSERT_EQ(s.data(), v.Elements()); + ASSERT_EQ(s[2], 3); + + Span cs{v}; + ASSERT_EQ(cs.size(), narrow_cast(v.Length())); + ASSERT_EQ(cs.data(), v.Elements()); + ASSERT_EQ(cs[2], 3); + } + { + AutoTArray v; + v.AppendElement(1); + v.AppendElement(2); + v.AppendElement(3); + + AssertSpanOfThreeInts(v); + + auto s = Span(v); + ASSERT_EQ(s.size(), narrow_cast(v.Length())); + ASSERT_EQ(s.data(), v.Elements()); + ASSERT_EQ(s[2], 3); + } + { + FallibleTArray v; + *(v.AppendElement(fallible)) = 1; + *(v.AppendElement(fallible)) = 2; + *(v.AppendElement(fallible)) = 3; + + AssertSpanOfThreeInts(v); + + Span s{v}; + ASSERT_EQ(s.size(), narrow_cast(v.Length())); + ASSERT_EQ(s.data(), v.Elements()); + ASSERT_EQ(s[2], 3); + + Span cs{v}; + ASSERT_EQ(cs.size(), narrow_cast(v.Length())); + ASSERT_EQ(cs.data(), v.Elements()); + ASSERT_EQ(cs[2], 3); + } + { + FallibleTArray v; + *(v.AppendElement(fallible)) = 1; + *(v.AppendElement(fallible)) = 2; + *(v.AppendElement(fallible)) = 3; + + AssertSpanOfThreeInts(v); + + auto s = Span(v); + ASSERT_EQ(s.size(), narrow_cast(v.Length())); + ASSERT_EQ(s.data(), v.Elements()); + ASSERT_EQ(s[2], 3); + } + { + nsAutoString str; + str.AssignLiteral(u"abc"); + + AssertSpanOfThreeChar16s(str); + AssertSpanOfThreeChar16sViaString(str); + + Span s{str.GetMutableData()}; + ASSERT_EQ(s.size(), narrow_cast(str.Length())); + ASSERT_EQ(s.data(), str.BeginWriting()); + ASSERT_EQ(s[2], 'c'); + + Span cs{str}; + ASSERT_EQ(cs.size(), narrow_cast(str.Length())); + ASSERT_EQ(cs.data(), str.BeginReading()); + ASSERT_EQ(cs[2], 'c'); + } + { + nsAutoString str; + str.AssignLiteral(u"abc"); + + AssertSpanOfThreeChar16s(str); + AssertSpanOfThreeChar16sViaString(str); + + auto s = Span(str); + ASSERT_EQ(s.size(), narrow_cast(str.Length())); + ASSERT_EQ(s.data(), str.BeginReading()); + ASSERT_EQ(s[2], 'c'); + } + { + nsAutoCString str; + str.AssignLiteral("abc"); + + AssertSpanOfThreeChars(str); + AssertSpanOfThreeCharsViaString(str); + + Span cs{str}; + ASSERT_EQ(cs.size(), narrow_cast(str.Length())); + ASSERT_EQ(cs.data(), reinterpret_cast(str.BeginReading())); + ASSERT_EQ(cs[2], 'c'); + } + { + nsAutoCString str; + str.AssignLiteral("abc"); + + AssertSpanOfThreeChars(str); + AssertSpanOfThreeCharsViaString(str); + + auto s = Span(str); + ASSERT_EQ(s.size(), narrow_cast(str.Length())); + ASSERT_EQ(s.data(), str.BeginReading()); + ASSERT_EQ(s[2], 'c'); + } + { + nsTArray v; + v.AppendElement(1); + v.AppendElement(2); + v.AppendElement(3); + + Range r(v.Elements(), v.Length()); + + AssertSpanOfThreeInts(r); + + Span s{r}; + ASSERT_EQ(s.size(), narrow_cast(v.Length())); + ASSERT_EQ(s.data(), v.Elements()); + ASSERT_EQ(s[2], 3); + + Span cs{r}; + ASSERT_EQ(cs.size(), narrow_cast(v.Length())); + ASSERT_EQ(cs.data(), v.Elements()); + ASSERT_EQ(cs[2], 3); + } + { + nsTArray v; + v.AppendElement(1); + v.AppendElement(2); + v.AppendElement(3); + + Range r(v.Elements(), v.Length()); + + AssertSpanOfThreeInts(r); + + auto s = Span(r); + ASSERT_EQ(s.size(), narrow_cast(v.Length())); + ASSERT_EQ(s.data(), v.Elements()); + ASSERT_EQ(s[2], 3); + } +} + +SPAN_TEST(from_cstring) { + { + const char* str = nullptr; + auto cs = MakeStringSpan(str); + ASSERT_EQ(cs.size(), 0U); + } + { + const char* str = "abc"; + + auto cs = MakeStringSpan(str); + ASSERT_EQ(cs.size(), 3U); + ASSERT_EQ(cs.data(), str); + ASSERT_EQ(cs[2], 'c'); + + static_assert(MakeStringSpan("abc").size() == 3U); + static_assert(MakeStringSpan("abc")[2] == 'c'); + +#ifdef CONFIRM_COMPILATION_ERRORS + Span scccl("literal"); // error + + Span sccel; + sccel = "literal"; // error + + cs = Span("literal"); // error +#endif + } + { + char arr[4] = {'a', 'b', 'c', 0}; + + auto cs = MakeStringSpan(arr); + ASSERT_EQ(cs.size(), 3U); + ASSERT_EQ(cs.data(), arr); + ASSERT_EQ(cs[2], 'c'); + + cs = Span(arr); + ASSERT_EQ(cs.size(), 4U); // zero terminator is part of the array span. + ASSERT_EQ(cs.data(), arr); + ASSERT_EQ(cs[2], 'c'); + ASSERT_EQ(cs[3], '\0'); // zero terminator is part of the array span. + +#ifdef CONFIRM_COMPILATION_ERRORS + Span scca(arr); // error + Span sccca(arr); // error + + Span scccea; + scccea = arr; // error +#endif + } + { + const char16_t* str = nullptr; + auto cs = MakeStringSpan(str); + ASSERT_EQ(cs.size(), 0U); + } + { + char16_t arr[4] = {'a', 'b', 'c', 0}; + const char16_t* str = arr; + + auto cs = MakeStringSpan(str); + ASSERT_EQ(cs.size(), 3U); + ASSERT_EQ(cs.data(), str); + ASSERT_EQ(cs[2], 'c'); + + static_assert(MakeStringSpan(u"abc").size() == 3U); + static_assert(MakeStringSpan(u"abc")[2] == u'c'); + + cs = MakeStringSpan(arr); + ASSERT_EQ(cs.size(), 3U); + ASSERT_EQ(cs.data(), str); + ASSERT_EQ(cs[2], 'c'); + + cs = Span(arr); + ASSERT_EQ(cs.size(), 4U); // zero terminator is part of the array span. + ASSERT_EQ(cs.data(), str); + ASSERT_EQ(cs[2], 'c'); + ASSERT_EQ(cs[3], '\0'); // zero terminator is part of the array span. + +#ifdef CONFIRM_COMPILATION_ERRORS + Span scca(arr); // error + + Span scccea; + scccea = arr; // error + + Span scccl(u"literal"); // error + + Span* sccel; + *sccel = u"literal"; // error + + cs = Span(u"literal"); // error +#endif + } +} + +SPAN_TEST(from_convertible_Span_constructor){{Span avd; +Span avcd = avd; +static_cast(avcd); +} + +{ +#ifdef CONFIRM_COMPILATION_ERRORS + Span avd; + Span avb = avd; + static_cast(avb); +#endif +} + +#ifdef CONFIRM_COMPILATION_ERRORS +{ + Span s; + Span s2 = s; + static_cast(s2); +} + +{ + Span s; + Span s2 = s; + static_cast(s2); +} + +{ + Span s; + Span s2 = s; + static_cast(s2); +} +#endif +} + +SPAN_TEST(copy_move_and_assignment) { + Span s1; + ASSERT_TRUE(s1.empty()); + + int arr[] = {3, 4, 5}; + + Span s2 = arr; + ASSERT_EQ(s2.Length(), 3U); + ASSERT_EQ(s2.data(), &arr[0]); + + s2 = s1; + ASSERT_TRUE(s2.empty()); + + auto get_temp_Span = [&]() -> Span { return {&arr[1], 2}; }; + auto use_Span = [&](Span s) { + ASSERT_EQ(s.Length(), 2U); + ASSERT_EQ(s.data(), &arr[1]); + }; + use_Span(get_temp_Span()); + + s1 = get_temp_Span(); + ASSERT_EQ(s1.Length(), 2U); + ASSERT_EQ(s1.data(), &arr[1]); +} + +SPAN_TEST(first) { + int arr[5] = {1, 2, 3, 4, 5}; + + { + Span av = arr; + ASSERT_EQ(av.First<2>().Length(), 2U); + ASSERT_EQ(av.First(2).Length(), 2U); + } + + { + Span av = arr; + ASSERT_EQ(av.First<0>().Length(), 0U); + ASSERT_EQ(av.First(0).Length(), 0U); + } + + { + Span av = arr; + ASSERT_EQ(av.First<5>().Length(), 5U); + ASSERT_EQ(av.First(5).Length(), 5U); + } + +#if 0 + { + Span av = arr; +# ifdef CONFIRM_COMPILATION_ERRORS + ASSERT_EQ(av.First<6>().Length() , 6U); + ASSERT_EQ(av.First<-1>().Length() , -1); +# endif + CHECK_THROW(av.First(6).Length(), fail_fast); + } +#endif + + { + Span av; + ASSERT_EQ(av.First<0>().Length(), 0U); + ASSERT_EQ(av.First(0).Length(), 0U); + } +} + +SPAN_TEST(last) { + int arr[5] = {1, 2, 3, 4, 5}; + + { + Span av = arr; + ASSERT_EQ(av.Last<2>().Length(), 2U); + ASSERT_EQ(av.Last(2).Length(), 2U); + } + + { + Span av = arr; + ASSERT_EQ(av.Last<0>().Length(), 0U); + ASSERT_EQ(av.Last(0).Length(), 0U); + } + + { + Span av = arr; + ASSERT_EQ(av.Last<5>().Length(), 5U); + ASSERT_EQ(av.Last(5).Length(), 5U); + } + +#if 0 + { + Span av = arr; +# ifdef CONFIRM_COMPILATION_ERRORS + ASSERT_EQ(av.Last<6>().Length() , 6U); +# endif + CHECK_THROW(av.Last(6).Length(), fail_fast); + } +#endif + + { + Span av; + ASSERT_EQ(av.Last<0>().Length(), 0U); + ASSERT_EQ(av.Last(0).Length(), 0U); + } +} + +SPAN_TEST(from_to) { + int arr[5] = {1, 2, 3, 4, 5}; + + { + Span av = arr; + ASSERT_EQ(av.From(3).Length(), 2U); + ASSERT_EQ(av.From(2)[1], 4); + } + + { + Span av = arr; + ASSERT_EQ(av.From(5).Length(), 0U); + } + + { + Span av = arr; + ASSERT_EQ(av.From(0).Length(), 5U); + } + + { + Span av = arr; + ASSERT_EQ(av.To(3).Length(), 3U); + ASSERT_EQ(av.To(3)[1], 2); + } + + { + Span av = arr; + ASSERT_EQ(av.To(0).Length(), 0U); + } + + { + Span av = arr; + ASSERT_EQ(av.To(5).Length(), 5U); + } + + { + Span av = arr; + ASSERT_EQ(av.FromTo(1, 4).Length(), 3U); + ASSERT_EQ(av.FromTo(1, 4)[1], 3); + } + + { + Span av = arr; + ASSERT_EQ(av.FromTo(2, 2).Length(), 0U); + } + + { + Span av = arr; + ASSERT_EQ(av.FromTo(0, 5).Length(), 5U); + } +} + +SPAN_TEST(Subspan) { + int arr[5] = {1, 2, 3, 4, 5}; + + { + Span av = arr; + ASSERT_EQ((av.Subspan<2, 2>().Length()), 2U); + ASSERT_EQ(av.Subspan(2, 2).Length(), 2U); + ASSERT_EQ(av.Subspan(2, 3).Length(), 3U); + } + + { + Span av = arr; + ASSERT_EQ((av.Subspan<0, 0>().Length()), 0U); + ASSERT_EQ(av.Subspan(0, 0).Length(), 0U); + } + + { + Span av = arr; + ASSERT_EQ((av.Subspan<0, 5>().Length()), 5U); + ASSERT_EQ(av.Subspan(0, 5).Length(), 5U); + CHECK_THROW(av.Subspan(0, 6).Length(), fail_fast); + CHECK_THROW(av.Subspan(1, 5).Length(), fail_fast); + } + + { + Span av = arr; + ASSERT_EQ((av.Subspan<4, 0>().Length()), 0U); + ASSERT_EQ(av.Subspan(4, 0).Length(), 0U); + ASSERT_EQ(av.Subspan(5, 0).Length(), 0U); + CHECK_THROW(av.Subspan(6, 0).Length(), fail_fast); + } + + { + Span av; + ASSERT_EQ((av.Subspan<0, 0>().Length()), 0U); + ASSERT_EQ(av.Subspan(0, 0).Length(), 0U); + CHECK_THROW((av.Subspan<1, 0>().Length()), fail_fast); + } + + { + Span av; + ASSERT_EQ(av.Subspan(0).Length(), 0U); + CHECK_THROW(av.Subspan(1).Length(), fail_fast); + } + + { + Span av = arr; + ASSERT_EQ(av.Subspan(0).Length(), 5U); + ASSERT_EQ(av.Subspan(1).Length(), 4U); + ASSERT_EQ(av.Subspan(4).Length(), 1U); + ASSERT_EQ(av.Subspan(5).Length(), 0U); + CHECK_THROW(av.Subspan(6).Length(), fail_fast); + auto av2 = av.Subspan(1); + for (int i = 0; i < 4; ++i) ASSERT_EQ(av2[i], i + 2); + } + + { + Span av = arr; + ASSERT_EQ(av.Subspan(0).Length(), 5U); + ASSERT_EQ(av.Subspan(1).Length(), 4U); + ASSERT_EQ(av.Subspan(4).Length(), 1U); + ASSERT_EQ(av.Subspan(5).Length(), 0U); + CHECK_THROW(av.Subspan(6).Length(), fail_fast); + auto av2 = av.Subspan(1); + for (int i = 0; i < 4; ++i) ASSERT_EQ(av2[i], i + 2); + } +} + +SPAN_TEST(at_call) { + int arr[4] = {1, 2, 3, 4}; + + { + Span s = arr; + ASSERT_EQ(s.at(0), 1); + CHECK_THROW(s.at(5), fail_fast); + } + + { + int arr2d[2] = {1, 6}; + Span s = arr2d; + ASSERT_EQ(s.at(0), 1); + ASSERT_EQ(s.at(1), 6); + CHECK_THROW(s.at(2), fail_fast); + } +} + +SPAN_TEST(operator_function_call) { + int arr[4] = {1, 2, 3, 4}; + + { + Span s = arr; + ASSERT_EQ(s(0), 1); + CHECK_THROW(s(5), fail_fast); + } + + { + int arr2d[2] = {1, 6}; + Span s = arr2d; + ASSERT_EQ(s(0), 1); + ASSERT_EQ(s(1), 6); + CHECK_THROW(s(2), fail_fast); + } +} + +SPAN_TEST(iterator_default_init) { + Span::iterator it1; + Span::iterator it2; + ASSERT_EQ(it1, it2); +} + +SPAN_TEST(const_iterator_default_init) { + Span::const_iterator it1; + Span::const_iterator it2; + ASSERT_EQ(it1, it2); +} + +SPAN_TEST(iterator_conversions) { + Span::iterator badIt; + Span::const_iterator badConstIt; + ASSERT_EQ(badIt, badConstIt); + + int a[] = {1, 2, 3, 4}; + Span s = a; + + auto it = s.begin(); + auto cit = s.cbegin(); + + ASSERT_EQ(it, cit); + ASSERT_EQ(cit, it); + + Span::const_iterator cit2 = it; + ASSERT_EQ(cit2, cit); + + Span::const_iterator cit3 = it + 4; + ASSERT_EQ(cit3, s.cend()); +} + +SPAN_TEST(iterator_comparisons) { + int a[] = {1, 2, 3, 4}; + { + Span s = a; + Span::iterator it = s.begin(); + auto it2 = it + 1; + Span::const_iterator cit = s.cbegin(); + + ASSERT_EQ(it, cit); + ASSERT_EQ(cit, it); + ASSERT_EQ(it, it); + ASSERT_EQ(cit, cit); + ASSERT_EQ(cit, s.begin()); + ASSERT_EQ(s.begin(), cit); + ASSERT_EQ(s.cbegin(), cit); + ASSERT_EQ(it, s.begin()); + ASSERT_EQ(s.begin(), it); + + ASSERT_NE(it, it2); + ASSERT_NE(it2, it); + ASSERT_NE(it, s.end()); + ASSERT_NE(it2, s.end()); + ASSERT_NE(s.end(), it); + ASSERT_NE(it2, cit); + ASSERT_NE(cit, it2); + + ASSERT_LT(it, it2); + ASSERT_LE(it, it2); + ASSERT_LE(it2, s.end()); + ASSERT_LT(it, s.end()); + ASSERT_LE(it, cit); + ASSERT_LE(cit, it); + ASSERT_LT(cit, it2); + ASSERT_LE(cit, it2); + ASSERT_LT(cit, s.end()); + ASSERT_LE(cit, s.end()); + + ASSERT_GT(it2, it); + ASSERT_GE(it2, it); + ASSERT_GT(s.end(), it2); + ASSERT_GE(s.end(), it2); + ASSERT_GT(it2, cit); + ASSERT_GE(it2, cit); + } +} + +SPAN_TEST(begin_end) { + { + int a[] = {1, 2, 3, 4}; + Span s = a; + + Span::iterator it = s.begin(); + Span::iterator it2 = std::begin(s); + ASSERT_EQ(it, it2); + + it = s.end(); + it2 = std::end(s); + ASSERT_EQ(it, it2); + } + + { + int a[] = {1, 2, 3, 4}; + Span s = a; + + auto it = s.begin(); + auto first = it; + ASSERT_EQ(it, first); + ASSERT_EQ(*it, 1); + + auto beyond = s.end(); + ASSERT_NE(it, beyond); + CHECK_THROW(*beyond, fail_fast); + + ASSERT_EQ(beyond - first, 4); + ASSERT_EQ(first - first, 0); + ASSERT_EQ(beyond - beyond, 0); + + ++it; + ASSERT_EQ(it - first, 1); + ASSERT_EQ(*it, 2); + *it = 22; + ASSERT_EQ(*it, 22); + ASSERT_EQ(beyond - it, 3); + + it = first; + ASSERT_EQ(it, first); + while (it != s.end()) { + *it = 5; + ++it; + } + + ASSERT_EQ(it, beyond); + ASSERT_EQ(it - beyond, 0); + + for (auto& n : s) { + ASSERT_EQ(n, 5); + } + } +} + +SPAN_TEST(cbegin_cend) { +#if 0 + { + int a[] = { 1, 2, 3, 4 }; + Span s = a; + + Span::const_iterator cit = s.cbegin(); + Span::const_iterator cit2 = std::cbegin(s); + ASSERT_EQ(cit , cit2); + + cit = s.cend(); + cit2 = std::cend(s); + ASSERT_EQ(cit , cit2); + } +#endif + { + int a[] = {1, 2, 3, 4}; + Span s = a; + + auto it = s.cbegin(); + auto first = it; + ASSERT_EQ(it, first); + ASSERT_EQ(*it, 1); + + auto beyond = s.cend(); + ASSERT_NE(it, beyond); + CHECK_THROW(*beyond, fail_fast); + + ASSERT_EQ(beyond - first, 4); + ASSERT_EQ(first - first, 0); + ASSERT_EQ(beyond - beyond, 0); + + ++it; + ASSERT_EQ(it - first, 1); + ASSERT_EQ(*it, 2); + ASSERT_EQ(beyond - it, 3); + + int last = 0; + it = first; + ASSERT_EQ(it, first); + while (it != s.cend()) { + ASSERT_EQ(*it, last + 1); + + last = *it; + ++it; + } + + ASSERT_EQ(it, beyond); + ASSERT_EQ(it - beyond, 0); + } +} + +SPAN_TEST(rbegin_rend) { + { + int a[] = {1, 2, 3, 4}; + Span s = a; + + auto it = s.rbegin(); + auto first = it; + ASSERT_EQ(it, first); + ASSERT_EQ(*it, 4); + + auto beyond = s.rend(); + ASSERT_NE(it, beyond); + CHECK_THROW(*beyond, fail_fast); + + ASSERT_EQ(beyond - first, 4); + ASSERT_EQ(first - first, 0); + ASSERT_EQ(beyond - beyond, 0); + + ++it; + ASSERT_EQ(it - first, 1); + ASSERT_EQ(*it, 3); + *it = 22; + ASSERT_EQ(*it, 22); + ASSERT_EQ(beyond - it, 3); + + it = first; + ASSERT_EQ(it, first); + while (it != s.rend()) { + *it = 5; + ++it; + } + + ASSERT_EQ(it, beyond); + ASSERT_EQ(it - beyond, 0); + + for (auto& n : s) { + ASSERT_EQ(n, 5); + } + } +} + +SPAN_TEST(crbegin_crend) { + { + int a[] = {1, 2, 3, 4}; + Span s = a; + + auto it = s.crbegin(); + auto first = it; + ASSERT_EQ(it, first); + ASSERT_EQ(*it, 4); + + auto beyond = s.crend(); + ASSERT_NE(it, beyond); + CHECK_THROW(*beyond, fail_fast); + + ASSERT_EQ(beyond - first, 4); + ASSERT_EQ(first - first, 0); + ASSERT_EQ(beyond - beyond, 0); + + ++it; + ASSERT_EQ(it - first, 1); + ASSERT_EQ(*it, 3); + ASSERT_EQ(beyond - it, 3); + + it = first; + ASSERT_EQ(it, first); + int last = 5; + while (it != s.crend()) { + ASSERT_EQ(*it, last - 1); + last = *it; + + ++it; + } + + ASSERT_EQ(it, beyond); + ASSERT_EQ(it - beyond, 0); + } +} + +SPAN_TEST(comparison_operators) { + { + Span s1 = nullptr; + Span s2 = nullptr; + ASSERT_EQ(s1, s2); + ASSERT_FALSE(s1 != s2); + ASSERT_FALSE(s1 < s2); + ASSERT_LE(s1, s2); + ASSERT_FALSE(s1 > s2); + ASSERT_GE(s1, s2); + ASSERT_EQ(s2, s1); + ASSERT_FALSE(s2 != s1); + ASSERT_FALSE(s2 < s1); + ASSERT_LE(s2, s1); + ASSERT_FALSE(s2 > s1); + ASSERT_GE(s2, s1); + } + + { + int arr[] = {2, 1}; + Span s1 = arr; + Span s2 = arr; + + ASSERT_EQ(s1, s2); + ASSERT_FALSE(s1 != s2); + ASSERT_FALSE(s1 < s2); + ASSERT_LE(s1, s2); + ASSERT_FALSE(s1 > s2); + ASSERT_GE(s1, s2); + ASSERT_EQ(s2, s1); + ASSERT_FALSE(s2 != s1); + ASSERT_FALSE(s2 < s1); + ASSERT_LE(s2, s1); + ASSERT_FALSE(s2 > s1); + ASSERT_GE(s2, s1); + } + + { + int arr[] = {2, 1}; // bigger + + Span s1 = nullptr; + Span s2 = arr; + + ASSERT_NE(s1, s2); + ASSERT_NE(s2, s1); + ASSERT_NE(s1, s2); + ASSERT_NE(s2, s1); + ASSERT_LT(s1, s2); + ASSERT_FALSE(s2 < s1); + ASSERT_LE(s1, s2); + ASSERT_FALSE(s2 <= s1); + ASSERT_GT(s2, s1); + ASSERT_FALSE(s1 > s2); + ASSERT_GE(s2, s1); + ASSERT_FALSE(s1 >= s2); + } + + { + int arr1[] = {1, 2}; + int arr2[] = {1, 2}; + Span s1 = arr1; + Span s2 = arr2; + + ASSERT_EQ(s1, s2); + ASSERT_FALSE(s1 != s2); + ASSERT_FALSE(s1 < s2); + ASSERT_LE(s1, s2); + ASSERT_FALSE(s1 > s2); + ASSERT_GE(s1, s2); + ASSERT_EQ(s2, s1); + ASSERT_FALSE(s2 != s1); + ASSERT_FALSE(s2 < s1); + ASSERT_LE(s2, s1); + ASSERT_FALSE(s2 > s1); + ASSERT_GE(s2, s1); + } + + { + int arr[] = {1, 2, 3}; + + AssertSpanOfThreeInts(arr); + + Span s1 = {&arr[0], 2}; // shorter + Span s2 = arr; // longer + + ASSERT_NE(s1, s2); + ASSERT_NE(s2, s1); + ASSERT_NE(s1, s2); + ASSERT_NE(s2, s1); + ASSERT_LT(s1, s2); + ASSERT_FALSE(s2 < s1); + ASSERT_LE(s1, s2); + ASSERT_FALSE(s2 <= s1); + ASSERT_GT(s2, s1); + ASSERT_FALSE(s1 > s2); + ASSERT_GE(s2, s1); + ASSERT_FALSE(s1 >= s2); + } + + { + int arr1[] = {1, 2}; // smaller + int arr2[] = {2, 1}; // bigger + + Span s1 = arr1; + Span s2 = arr2; + + ASSERT_NE(s1, s2); + ASSERT_NE(s2, s1); + ASSERT_NE(s1, s2); + ASSERT_NE(s2, s1); + ASSERT_LT(s1, s2); + ASSERT_FALSE(s2 < s1); + ASSERT_LE(s1, s2); + ASSERT_FALSE(s2 <= s1); + ASSERT_GT(s2, s1); + ASSERT_FALSE(s1 > s2); + ASSERT_GE(s2, s1); + ASSERT_FALSE(s1 >= s2); + } +} + +SPAN_TEST(as_bytes) { + int a[] = {1, 2, 3, 4}; + + { + Span s = a; + ASSERT_EQ(s.Length(), 4U); + Span bs = AsBytes(s); + ASSERT_EQ(static_cast(bs.data()), + static_cast(s.data())); + ASSERT_EQ(bs.Length(), s.LengthBytes()); + } + + { + Span s; + auto bs = AsBytes(s); + ASSERT_EQ(bs.Length(), s.Length()); + ASSERT_EQ(bs.Length(), 0U); + ASSERT_EQ(bs.size_bytes(), 0U); + ASSERT_EQ(static_cast(bs.data()), + static_cast(s.data())); + ASSERT_EQ(bs.data(), reinterpret_cast(SLICE_INT_PTR)); + } + + { + Span s = a; + auto bs = AsBytes(s); + ASSERT_EQ(static_cast(bs.data()), + static_cast(s.data())); + ASSERT_EQ(bs.Length(), s.LengthBytes()); + } +} + +SPAN_TEST(as_writable_bytes) { + int a[] = {1, 2, 3, 4}; + + { +#ifdef CONFIRM_COMPILATION_ERRORS + // you should not be able to get writeable bytes for const objects + Span s = a; + ASSERT_EQ(s.Length(), 4U); + Span bs = AsWritableBytes(s); + ASSERT_EQ(static_cast(bs.data()), static_cast(s.data())); + ASSERT_EQ(bs.Length(), s.LengthBytes()); +#endif + } + + { + Span s; + auto bs = AsWritableBytes(s); + ASSERT_EQ(bs.Length(), s.Length()); + ASSERT_EQ(bs.Length(), 0U); + ASSERT_EQ(bs.size_bytes(), 0U); + ASSERT_EQ(static_cast(bs.data()), static_cast(s.data())); + ASSERT_EQ(bs.data(), reinterpret_cast(SLICE_INT_PTR)); + } + + { + Span s = a; + auto bs = AsWritableBytes(s); + ASSERT_EQ(static_cast(bs.data()), static_cast(s.data())); + ASSERT_EQ(bs.Length(), s.LengthBytes()); + } +} + +SPAN_TEST(as_chars) { + const uint8_t a[] = {1, 2, 3, 4}; + Span u = Span(a); + Span c = AsChars(u); + ASSERT_EQ(static_cast(u.data()), + static_cast(c.data())); + ASSERT_EQ(u.size(), c.size()); +} + +SPAN_TEST(as_writable_chars) { + uint8_t a[] = {1, 2, 3, 4}; + Span u = Span(a); + Span c = AsWritableChars(u); + ASSERT_EQ(static_cast(u.data()), static_cast(c.data())); + ASSERT_EQ(u.size(), c.size()); +} + +SPAN_TEST(fixed_size_conversions) { + int arr[] = {1, 2, 3, 4}; + + // converting to an Span from an equal size array is ok + Span s4 = arr; + ASSERT_EQ(s4.Length(), 4U); + + // converting to dynamic_range is always ok + { + Span s = s4; + ASSERT_EQ(s.Length(), s4.Length()); + static_cast(s); + } + +// initialization or assignment to static Span that REDUCES size is NOT ok +#ifdef CONFIRM_COMPILATION_ERRORS + { Span s = arr; } + { + Span s2 = s4; + static_cast(s2); + } +#endif + +#if 0 + // even when done dynamically + { + Span s = arr; + auto f = [&]() { + Span s2 = s; + static_cast(s2); + }; + CHECK_THROW(f(), fail_fast); + } +#endif + + // but doing so explicitly is ok + + // you can convert statically + { + Span s2 = {arr, 2}; + static_cast(s2); + } + { + Span s1 = s4.First<1>(); + static_cast(s1); + } + + // ...or dynamically + { + // NB: implicit conversion to Span from Span + Span s1 = s4.First(1); + static_cast(s1); + } + +#if 0 + // initialization or assignment to static Span that requires size INCREASE is not ok. + int arr2[2] = {1, 2}; +#endif + +#ifdef CONFIRM_COMPILATION_ERRORS + { Span s3 = arr2; } + { + Span s2 = arr2; + Span s4a = s2; + } +#endif + +#if 0 + { + auto f = [&]() { + Span _s4 = {arr2, 2}; + static_cast(_s4); + }; + CHECK_THROW(f(), fail_fast); + } + + // this should fail - we are trying to assign a small dynamic Span to a fixed_size larger one + Span av = arr2; + auto f = [&]() { + Span _s4 = av; + static_cast(_s4); + }; + CHECK_THROW(f(), fail_fast); +#endif +} + +#if 0 + SPAN_TEST(interop_with_std_regex) + { + char lat[] = { '1', '2', '3', '4', '5', '6', 'E', 'F', 'G' }; + Span s = lat; + auto f_it = s.begin() + 7; + + std::match_results::iterator> match; + + std::regex_match(s.begin(), s.end(), match, std::regex(".*")); + ASSERT_EQ(match.ready()); + ASSERT_TRUE(!match.empty()); + ASSERT_TRUE(match[0].matched); + ASSERT_TRUE(match[0].first , s.begin()); + ASSERT_EQ(match[0].second , s.end()); + + std::regex_search(s.begin(), s.end(), match, std::regex("F")); + ASSERT_TRUE(match.ready()); + ASSERT_TRUE(!match.empty()); + ASSERT_TRUE(match[0].matched); + ASSERT_EQ(match[0].first , f_it); + ASSERT_EQ(match[0].second , (f_it + 1)); + } + +SPAN_TEST(interop_with_gsl_at) +{ + int arr[5] = { 1, 2, 3, 4, 5 }; + Span s{ arr }; + ASSERT_EQ(at(s, 0) , 1 ); +ASSERT_EQ(at(s, 1) , 2U); +} +#endif + +SPAN_TEST(default_constructible) { + ASSERT_TRUE((std::is_default_constructible>::value)); + ASSERT_TRUE((std::is_default_constructible>::value)); + ASSERT_TRUE((!std::is_default_constructible>::value)); +} + +SPAN_TEST(type_inference) { + static constexpr int arr[5] = {1, 2, 3, 4, 5}; + constexpr auto s = Span{arr}; + static_assert(std::is_same_v, decltype(s)>); + static_assert(arr == s.Elements()); +} + +SPAN_TEST(split_at_dynamic_with_dynamic_extent) { + static constexpr int arr[5] = {1, 2, 3, 4, 5}; + constexpr Span s = Span{arr}; + + { // Split at begin. + constexpr auto splitAt0Result = s.SplitAt(0); + static_assert( + std::is_same_v, decltype(splitAt0Result.first)>); + static_assert( + std::is_same_v, decltype(splitAt0Result.second)>); + ASSERT_EQ(s.Elements(), splitAt0Result.second.Elements()); + ASSERT_EQ(0u, splitAt0Result.first.Length()); + ASSERT_EQ(5u, splitAt0Result.second.Length()); + } + + { // Split at end. + constexpr auto splitAt5Result = s.SplitAt(s.Length()); + static_assert( + std::is_same_v, decltype(splitAt5Result.first)>); + static_assert( + std::is_same_v, decltype(splitAt5Result.second)>); + ASSERT_EQ(s.Elements(), splitAt5Result.first.Elements()); + ASSERT_EQ(5u, splitAt5Result.first.Length()); + ASSERT_EQ(0u, splitAt5Result.second.Length()); + } + + { + // Split inside. + constexpr auto splitAt3Result = s.SplitAt(3); + static_assert( + std::is_same_v, decltype(splitAt3Result.first)>); + static_assert( + std::is_same_v, decltype(splitAt3Result.second)>); + ASSERT_EQ(s.Elements(), splitAt3Result.first.Elements()); + ASSERT_EQ(s.Elements() + 3, splitAt3Result.second.Elements()); + ASSERT_EQ(3u, splitAt3Result.first.Length()); + ASSERT_EQ(2u, splitAt3Result.second.Length()); + } +} + +SPAN_TEST(split_at_dynamic_with_static_extent) { + static constexpr int arr[5] = {1, 2, 3, 4, 5}; + constexpr auto s = Span{arr}; + + { + // Split at begin. + constexpr auto splitAt0Result = s.SplitAt(0); + static_assert( + std::is_same_v, decltype(splitAt0Result.first)>); + static_assert( + std::is_same_v, decltype(splitAt0Result.second)>); + ASSERT_EQ(s.Elements(), splitAt0Result.second.Elements()); + } + + { + // Split at end. + constexpr auto splitAt5Result = s.SplitAt(s.Length()); + static_assert( + std::is_same_v, decltype(splitAt5Result.first)>); + static_assert( + std::is_same_v, decltype(splitAt5Result.second)>); + ASSERT_EQ(s.Elements(), splitAt5Result.first.Elements()); + } + + { + // Split inside. + constexpr auto splitAt3Result = s.SplitAt(3); + static_assert( + std::is_same_v, decltype(splitAt3Result.first)>); + static_assert( + std::is_same_v, decltype(splitAt3Result.second)>); + ASSERT_EQ(s.Elements(), splitAt3Result.first.Elements()); + ASSERT_EQ(s.Elements() + 3, splitAt3Result.second.Elements()); + } +} + +SPAN_TEST(split_at_static) { + static constexpr int arr[5] = {1, 2, 3, 4, 5}; + constexpr auto s = Span{arr}; + + // Split at begin. + constexpr auto splitAt0Result = s.SplitAt<0>(); + static_assert( + std::is_same_v, decltype(splitAt0Result.first)>); + static_assert( + std::is_same_v, decltype(splitAt0Result.second)>); + static_assert(splitAt0Result.second.Elements() == s.Elements()); + + // Split at end. + constexpr auto splitAt5Result = s.SplitAt(); + static_assert(std::is_same_v, + decltype(splitAt5Result.first)>); + static_assert( + std::is_same_v, decltype(splitAt5Result.second)>); + static_assert(splitAt5Result.first.Elements() == s.Elements()); + + // Split inside. + constexpr auto splitAt3Result = s.SplitAt<3>(); + static_assert( + std::is_same_v, decltype(splitAt3Result.first)>); + static_assert( + std::is_same_v, decltype(splitAt3Result.second)>); + static_assert(splitAt3Result.first.Elements() == s.Elements()); + static_assert(splitAt3Result.second.Elements() == s.Elements() + 3); +} + +SPAN_TEST(as_const_dynamic) { + static int arr[5] = {1, 2, 3, 4, 5}; + auto span = Span{arr, 5}; + auto constSpan = span.AsConst(); + static_assert(std::is_same_v, decltype(constSpan)>); +} + +SPAN_TEST(as_const_static) { + { + static constexpr int constArr[5] = {1, 2, 3, 4, 5}; + constexpr auto span = Span{constArr}; // is already a Span + constexpr auto constSpan = span.AsConst(); + + static_assert( + std::is_same_v, decltype(constSpan)>); + } + + { + static int arr[5] = {1, 2, 3, 4, 5}; + auto span = Span{arr}; + auto constSpan = span.AsConst(); + static_assert(std::is_same_v, decltype(constSpan)>); + } +} + +SPAN_TEST(construct_from_iterators_dynamic) { + const int constArr[5] = {1, 2, 3, 4, 5}; + auto constSpan = Span{constArr}; + + // const from const + { + const auto wholeSpan = Span{constSpan.cbegin(), constSpan.cend()}; + static_assert(std::is_same_v>); + ASSERT_TRUE(constSpan == wholeSpan); + + const auto emptyBeginSpan = Span{constSpan.cbegin(), constSpan.cbegin()}; + ASSERT_TRUE(emptyBeginSpan.IsEmpty()); + + const auto emptyEndSpan = Span{constSpan.cend(), constSpan.cend()}; + ASSERT_TRUE(emptyEndSpan.IsEmpty()); + + const auto subSpan = Span{constSpan.cbegin() + 1, constSpan.cend() - 1}; + ASSERT_EQ(constSpan.Length() - 2, subSpan.Length()); + ASSERT_EQ(constSpan.Elements() + 1, subSpan.Elements()); + } + + int arr[5] = {1, 2, 3, 4, 5}; + auto span = Span{arr}; + + // const from non-const + { + const auto wholeSpan = Span{span.cbegin(), span.cend()}; + static_assert(std::is_same_v>); + // XXX Can't use span == wholeSpan because of difference in constness. + ASSERT_EQ(span.Elements(), wholeSpan.Elements()); + ASSERT_EQ(span.Length(), wholeSpan.Length()); + + const auto emptyBeginSpan = Span{span.cbegin(), span.cbegin()}; + ASSERT_TRUE(emptyBeginSpan.IsEmpty()); + + const auto emptyEndSpan = Span{span.cend(), span.cend()}; + ASSERT_TRUE(emptyEndSpan.IsEmpty()); + + const auto subSpan = Span{span.cbegin() + 1, span.cend() - 1}; + ASSERT_EQ(span.Length() - 2, subSpan.Length()); + ASSERT_EQ(span.Elements() + 1, subSpan.Elements()); + } + + // non-const from non-const + { + const auto wholeSpan = Span{span.begin(), span.end()}; + static_assert(std::is_same_v>); + ASSERT_TRUE(span == wholeSpan); + + const auto emptyBeginSpan = Span{span.begin(), span.begin()}; + ASSERT_TRUE(emptyBeginSpan.IsEmpty()); + + const auto emptyEndSpan = Span{span.end(), span.end()}; + ASSERT_TRUE(emptyEndSpan.IsEmpty()); + + const auto subSpan = Span{span.begin() + 1, span.end() - 1}; + ASSERT_EQ(span.Length() - 2, subSpan.Length()); + } +} + +SPAN_TEST(construct_from_iterators_static) { + static constexpr int arr[5] = {1, 2, 3, 4, 5}; + constexpr auto constSpan = Span{arr}; + + // const + { + const auto wholeSpan = Span{constSpan.cbegin(), constSpan.cend()}; + static_assert(std::is_same_v>); + ASSERT_TRUE(constSpan == wholeSpan); + + const auto emptyBeginSpan = Span{constSpan.cbegin(), constSpan.cbegin()}; + ASSERT_TRUE(emptyBeginSpan.IsEmpty()); + + const auto emptyEndSpan = Span{constSpan.cend(), constSpan.cend()}; + ASSERT_TRUE(emptyEndSpan.IsEmpty()); + + const auto subSpan = Span{constSpan.cbegin() + 1, constSpan.cend() - 1}; + ASSERT_EQ(constSpan.Length() - 2, subSpan.Length()); + ASSERT_EQ(constSpan.Elements() + 1, subSpan.Elements()); + } +} + +SPAN_TEST(construct_from_container_with_type_deduction) { + std::vector vec = {1, 2, 3, 4, 5}; + + // from const + { + const auto& constVecRef = vec; + + auto span = Span{constVecRef}; + static_assert(std::is_same_v>); + } + + // from non-const + { + auto span = Span{vec}; + static_assert(std::is_same_v>); + } +} diff --git a/mfbt/tests/gtest/TestTainting.cpp b/mfbt/tests/gtest/TestTainting.cpp new file mode 100644 index 0000000000..0025819c06 --- /dev/null +++ b/mfbt/tests/gtest/TestTainting.cpp @@ -0,0 +1,485 @@ +/* -*- 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 "gtest/gtest.h" +#include + +#include "mozilla/Array.h" +#include "mozilla/Assertions.h" +#include "mozilla/Range.h" +#include "mozilla/Tainting.h" +#include "nsTHashtable.h" +#include "nsHashKeys.h" +#include "nsTArray.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using mozilla::Tainted; + +#define EXPECTED_INT 10 +#define EXPECTED_CHAR 'z' + +static bool externalFunction(int arg) { return arg > 2; } + +// ================================================================== +// MOZ_VALIDATE_AND_GET ============================================= +TEST(Tainting, moz_validate_and_get) +{ + int bar; + int comparisonVariable = 20; + Tainted foo = Tainted(EXPECTED_INT); + + bar = MOZ_VALIDATE_AND_GET(foo, foo < 20); + ASSERT_EQ(bar, EXPECTED_INT); + + // This test is for comparison to an external variable, testing the + // default capture mode of the lambda used inside the macro. + bar = MOZ_VALIDATE_AND_GET(foo, foo < comparisonVariable); + ASSERT_EQ(bar, EXPECTED_INT); + + bar = MOZ_VALIDATE_AND_GET( + foo, foo < 20, + "foo must be less than 20 because higher values represent decibel" + "levels greater than a a jet engine inside your ear."); + ASSERT_EQ(bar, EXPECTED_INT); + + // Test an external variable with a comment. + bar = MOZ_VALIDATE_AND_GET(foo, foo < comparisonVariable, "Test comment"); + ASSERT_EQ(bar, EXPECTED_INT); + + // Test an external function with a comment. + bar = MOZ_VALIDATE_AND_GET(foo, externalFunction(foo), "Test comment"); + ASSERT_EQ(bar, EXPECTED_INT); + + // Lambda Tests + bar = + MOZ_VALIDATE_AND_GET(foo, ([&foo]() { return externalFunction(foo); }())); + ASSERT_EQ(bar, EXPECTED_INT); + + // This test is for the lambda variant with a supplied assertion + // string. + bar = + MOZ_VALIDATE_AND_GET(foo, ([&foo]() { return externalFunction(foo); }()), + "This tests a comment"); + ASSERT_EQ(bar, EXPECTED_INT); + + // This test is for the lambda variant with a captured variable + bar = MOZ_VALIDATE_AND_GET(foo, ([&foo, &comparisonVariable] { + bool intermediateResult = externalFunction(foo); + return intermediateResult || + comparisonVariable < 4; + }()), + "This tests a comment"); + ASSERT_EQ(bar, EXPECTED_INT); + + // This test is for the lambda variant with full capture mode + bar = MOZ_VALIDATE_AND_GET(foo, ([&] { + bool intermediateResult = externalFunction(foo); + return intermediateResult || + comparisonVariable < 4; + }()), + "This tests a comment"); + ASSERT_EQ(bar, EXPECTED_INT); + + // External lambdas + auto lambda1 = [](int foo) { return externalFunction(foo); }; + + auto lambda2 = [&](int foo) { + bool intermediateResult = externalFunction(foo); + return intermediateResult || comparisonVariable < 4; + }; + + // Test with an explicit capture + auto lambda3 = [&comparisonVariable](int foo) { + bool intermediateResult = externalFunction(foo); + return intermediateResult || comparisonVariable < 4; + }; + + bar = MOZ_VALIDATE_AND_GET(foo, lambda1(foo)); + ASSERT_EQ(bar, EXPECTED_INT); + + // Test with a comment + bar = MOZ_VALIDATE_AND_GET(foo, lambda1(foo), "Test comment."); + ASSERT_EQ(bar, EXPECTED_INT); + + // Test with a default capture mode + bar = MOZ_VALIDATE_AND_GET(foo, lambda2(foo), "Test comment."); + ASSERT_EQ(bar, EXPECTED_INT); + + bar = MOZ_VALIDATE_AND_GET(foo, lambda3(foo), "Test comment."); + ASSERT_EQ(bar, EXPECTED_INT); + + // We can't test MOZ_VALIDATE_AND_GET failing, because that triggers + // a release assert. +} + +// ================================================================== +// MOZ_IS_VALID ===================================================== +TEST(Tainting, moz_is_valid) +{ + int comparisonVariable = 20; + Tainted foo = Tainted(EXPECTED_INT); + + ASSERT_TRUE(MOZ_IS_VALID(foo, foo < 20)); + + ASSERT_FALSE(MOZ_IS_VALID(foo, foo > 20)); + + ASSERT_TRUE(MOZ_IS_VALID(foo, foo < comparisonVariable)); + + ASSERT_TRUE( + MOZ_IS_VALID(foo, ([&foo]() { return externalFunction(foo); }()))); + + ASSERT_TRUE(MOZ_IS_VALID(foo, ([&foo, &comparisonVariable]() { + bool intermediateResult = externalFunction(foo); + return intermediateResult || + comparisonVariable < 4; + }()))); + + // External lambdas + auto lambda1 = [](int foo) { return externalFunction(foo); }; + + auto lambda2 = [&](int foo) { + bool intermediateResult = externalFunction(foo); + return intermediateResult || comparisonVariable < 4; + }; + + // Test with an explicit capture + auto lambda3 = [&comparisonVariable](int foo) { + bool intermediateResult = externalFunction(foo); + return intermediateResult || comparisonVariable < 4; + }; + + ASSERT_TRUE(MOZ_IS_VALID(foo, lambda1(foo))); + + ASSERT_TRUE(MOZ_IS_VALID(foo, lambda2(foo))); + + ASSERT_TRUE(MOZ_IS_VALID(foo, lambda3(foo))); +} + +// ================================================================== +// MOZ_VALIDATE_OR ================================================== +TEST(Tainting, moz_validate_or) +{ + int result; + int comparisonVariable = 20; + Tainted foo = Tainted(EXPECTED_INT); + + result = MOZ_VALIDATE_OR(foo, foo < 20, 100); + ASSERT_EQ(result, EXPECTED_INT); + + result = MOZ_VALIDATE_OR(foo, foo > 20, 100); + ASSERT_EQ(result, 100); + + result = MOZ_VALIDATE_OR(foo, foo < comparisonVariable, 100); + ASSERT_EQ(result, EXPECTED_INT); + + // External lambdas + auto lambda1 = [](int foo) { return externalFunction(foo); }; + + auto lambda2 = [&](int foo) { + bool intermediateResult = externalFunction(foo); + return intermediateResult || comparisonVariable < 4; + }; + + // Test with an explicit capture + auto lambda3 = [&comparisonVariable](int foo) { + bool intermediateResult = externalFunction(foo); + return intermediateResult || comparisonVariable < 4; + }; + + result = MOZ_VALIDATE_OR(foo, lambda1(foo), 100); + ASSERT_EQ(result, EXPECTED_INT); + + result = MOZ_VALIDATE_OR(foo, lambda2(foo), 100); + ASSERT_EQ(result, EXPECTED_INT); + + result = MOZ_VALIDATE_OR(foo, lambda3(foo), 100); + ASSERT_EQ(result, EXPECTED_INT); + + result = + MOZ_VALIDATE_OR(foo, ([&foo]() { return externalFunction(foo); }()), 100); + ASSERT_EQ(result, EXPECTED_INT); + + // This test is for the lambda variant with a supplied assertion + // string. + result = + MOZ_VALIDATE_OR(foo, ([&foo] { return externalFunction(foo); }()), 100); + ASSERT_EQ(result, EXPECTED_INT); + + // This test is for the lambda variant with a captured variable + result = + MOZ_VALIDATE_OR(foo, ([&foo, &comparisonVariable] { + bool intermediateResult = externalFunction(foo); + return intermediateResult || comparisonVariable < 4; + }()), + 100); + ASSERT_EQ(result, EXPECTED_INT); + + // This test is for the lambda variant with full capture mode + result = + MOZ_VALIDATE_OR(foo, ([&] { + bool intermediateResult = externalFunction(foo); + return intermediateResult || comparisonVariable < 4; + }()), + 100); + ASSERT_EQ(result, EXPECTED_INT); +} + +// ================================================================== +// MOZ_FIND_AND_VALIDATE ============================================ +TEST(Tainting, moz_find_and_validate) +{ + Tainted foo = Tainted(EXPECTED_INT); + Tainted baz = Tainted(EXPECTED_CHAR); + + //------------------------------- + const mozilla::Array mozarrayWithFoo(0, 5, EXPECTED_INT, 15, 20, 25); + const mozilla::Array mozarrayWithoutFoo(0, 5, 15, 20, 25); + + ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, mozarrayWithFoo) == + mozarrayWithFoo[2]); + + ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, + mozarrayWithoutFoo) == nullptr); + + //------------------------------- + class TestClass { + public: + int a; + int b; + + TestClass(int a, int b) { + this->a = a; + this->b = b; + } + + bool operator==(const TestClass& other) const { + return this->a == other.a && this->b == other.b; + } + }; + + const mozilla::Array mozarrayOfClassesWithFoo( + TestClass(0, 1), TestClass(2, 3), TestClass(EXPECTED_INT, EXPECTED_INT), + TestClass(4, 5), TestClass(6, 7)); + + ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE( + foo, foo == list_item.a && foo == list_item.b, + mozarrayOfClassesWithFoo) == mozarrayOfClassesWithFoo[2]); + + ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE( + foo, (foo == list_item.a && foo == list_item.b), + mozarrayOfClassesWithFoo) == mozarrayOfClassesWithFoo[2]); + + ASSERT_TRUE( + *MOZ_FIND_AND_VALIDATE( + foo, + (foo == list_item.a && foo == list_item.b && externalFunction(foo)), + mozarrayOfClassesWithFoo) == mozarrayOfClassesWithFoo[2]); + + ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE( + foo, ([](int tainted_val, TestClass list_item) { + return tainted_val == list_item.a && + tainted_val == list_item.b; + }(foo, list_item)), + mozarrayOfClassesWithFoo) == mozarrayOfClassesWithFoo[2]); + + auto lambda4 = [](int tainted_val, TestClass list_item) { + return tainted_val == list_item.a && tainted_val == list_item.b; + }; + + ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, lambda4(foo, list_item), + mozarrayOfClassesWithFoo) == + mozarrayOfClassesWithFoo[2]); + + //------------------------------- + const char m[] = "m"; + const char o[] = "o"; + const char z[] = {EXPECTED_CHAR, '\0'}; + const char l[] = "l"; + const char a[] = "a"; + + nsTHashtable hashtableWithBaz; + hashtableWithBaz.PutEntry(m); + hashtableWithBaz.PutEntry(o); + hashtableWithBaz.PutEntry(z); + hashtableWithBaz.PutEntry(l); + hashtableWithBaz.PutEntry(a); + nsTHashtable hashtableWithoutBaz; + hashtableWithoutBaz.PutEntry(m); + hashtableWithoutBaz.PutEntry(o); + hashtableWithoutBaz.PutEntry(l); + hashtableWithoutBaz.PutEntry(a); + + ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(baz, *list_item.GetKey() == baz, + hashtableWithBaz) == + hashtableWithBaz.GetEntry(z)); + + ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(baz, *list_item.GetKey() == baz, + hashtableWithoutBaz) == nullptr); + + //------------------------------- + const nsTArray nsTArrayWithFoo = {0, 5, EXPECTED_INT, 15, 20, 25}; + const nsTArray nsTArrayWithoutFoo = {0, 5, 15, 20, 25}; + + ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, nsTArrayWithFoo) == + nsTArrayWithFoo[2]); + + ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, + nsTArrayWithoutFoo) == nullptr); + + //------------------------------- + const std::array arrayWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; + const std::array arrayWithoutFoo{0, 5, 15, 20, 25}; + + ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, arrayWithFoo) == + arrayWithFoo[2]); + + ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, arrayWithoutFoo) == + nullptr); + + //------------------------------- + const std::deque dequeWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; + const std::deque dequeWithoutFoo{0, 5, 15, 20, 25}; + + ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, dequeWithFoo) == + dequeWithFoo[2]); + + ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, dequeWithoutFoo) == + nullptr); + + //------------------------------- + const std::forward_list forwardWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; + const std::forward_list forwardWithoutFoo{0, 5, 15, 20, 25}; + + auto forwardListIt = forwardWithFoo.begin(); + std::advance(forwardListIt, 2); + + ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, forwardWithFoo) == + *forwardListIt); + + ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, forwardWithoutFoo) == + nullptr); + + //------------------------------- + const std::list listWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; + const std::list listWithoutFoo{0, 5, 15, 20, 25}; + + auto listIt = listWithFoo.begin(); + std::advance(listIt, 2); + + ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, listWithFoo) == + *listIt); + + ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, listWithoutFoo) == + nullptr); + + //------------------------------- + const std::map mapWithFoo{{ + {"zero", 0}, + {"five", 5}, + {"ten", EXPECTED_INT}, + {"fifteen", 15}, + {"twenty", 20}, + {"twenty-five", 25}, + }}; + const std::map mapWithoutFoo{{ + {"zero", 0}, + {"five", 5}, + {"fifteen", 15}, + {"twenty", 20}, + {"twenty-five", 25}, + }}; + + const auto map_it = mapWithFoo.find("ten"); + + ASSERT_TRUE( + MOZ_FIND_AND_VALIDATE(foo, list_item.second == foo, mapWithFoo)->second == + map_it->second); + + ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item.second == foo, + mapWithoutFoo) == nullptr); + + //------------------------------- + const std::set setWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; + const std::set setWithoutFoo{0, 5, 15, 20, 25}; + + auto setIt = setWithFoo.find(EXPECTED_INT); + + ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, setWithFoo) == + *setIt); + + ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, setWithoutFoo) == + nullptr); + + //------------------------------- + const std::unordered_map unordermapWithFoo = { + {"zero", 0}, {"five", 5}, {"ten", EXPECTED_INT}, + {"fifteen", 15}, {"twenty", 20}, {"twenty-five", 25}, + }; + const std::unordered_map unordermapWithoutFoo{{ + {"zero", 0}, + {"five", 5}, + {"fifteen", 15}, + {"twenty", 20}, + {"twenty-five", 25}, + }}; + + auto unorderedMapIt = unordermapWithFoo.find("ten"); + + ASSERT_TRUE( + MOZ_FIND_AND_VALIDATE(foo, list_item.second == foo, unordermapWithFoo) + ->second == unorderedMapIt->second); + + ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item.second == foo, + unordermapWithoutFoo) == nullptr); + + //------------------------------- + const std::unordered_set unorderedsetWithFoo{0, 5, EXPECTED_INT, + 15, 20, 25}; + const std::unordered_set unorderedsetWithoutFoo{0, 5, 15, 20, 25}; + + auto unorderedSetIt = unorderedsetWithFoo.find(EXPECTED_INT); + + ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, + unorderedsetWithFoo) == *unorderedSetIt); + + ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, + unorderedsetWithoutFoo) == nullptr); + + //------------------------------- + const std::vector vectorWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; + const std::vector vectorWithoutFoo{0, 5, 15, 20, 25}; + + ASSERT_TRUE(*MOZ_FIND_AND_VALIDATE(foo, list_item == foo, vectorWithFoo) == + vectorWithFoo[2]); + + ASSERT_TRUE(MOZ_FIND_AND_VALIDATE(foo, list_item == foo, vectorWithoutFoo) == + nullptr); +} + +// ================================================================== +// MOZ_NO_VALIDATE ================================================== +TEST(Tainting, moz_no_validate) +{ + int result; + Tainted foo = Tainted(EXPECTED_INT); + + result = MOZ_NO_VALIDATE( + foo, + "Value is used to match against a dictionary key in the parent." + "If there's no key present, there won't be a match." + "There is no risk of grabbing a cross-origin value from the dictionary," + "because the IPC actor is instatiated per-content-process and the " + "dictionary is not shared between actors."); + ASSERT_TRUE(result == EXPECTED_INT); +} diff --git a/mfbt/tests/gtest/moz.build b/mfbt/tests/gtest/moz.build new file mode 100644 index 0000000000..0af8d1ea75 --- /dev/null +++ b/mfbt/tests/gtest/moz.build @@ -0,0 +1,32 @@ +# -*- 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/. + +UNIFIED_SOURCES += [ + "TestBuffer.cpp", + "TestLinkedList.cpp", + "TestReverseIterator.cpp", + "TestSpan.cpp", + "TestTainting.cpp", +] + +SOURCES += [ + "TestAlgorithm.cpp", + "TestInitializedOnce.cpp", + "TestMainThreadWeakPtr.cpp", + "TestResultExtensions.cpp", +] + +if not CONFIG["MOZILLA_OFFICIAL"]: + UNIFIED_SOURCES += [ + # MOZ_DBG is not defined in MOZILLA_OFFICIAL builds. + "TestMozDbg.cpp", + ] + +# LOCAL_INCLUDES += [ +# "../../base", +# ] + +FINAL_LIBRARY = "xul-gtest" -- cgit v1.2.3