diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /mfbt/tests/gtest | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mfbt/tests/gtest')
-rw-r--r-- | mfbt/tests/gtest/TestAlgorithm.cpp | 191 | ||||
-rw-r--r-- | mfbt/tests/gtest/TestBuffer.cpp | 96 | ||||
-rw-r--r-- | mfbt/tests/gtest/TestInitializedOnce.cpp | 200 | ||||
-rw-r--r-- | mfbt/tests/gtest/TestLinkedList.cpp | 78 | ||||
-rw-r--r-- | mfbt/tests/gtest/TestMainThreadWeakPtr.cpp | 42 | ||||
-rw-r--r-- | mfbt/tests/gtest/TestMozDbg.cpp | 170 | ||||
-rw-r--r-- | mfbt/tests/gtest/TestResultExtensions.cpp | 579 | ||||
-rw-r--r-- | mfbt/tests/gtest/TestReverseIterator.cpp | 104 | ||||
-rw-r--r-- | mfbt/tests/gtest/TestSpan.cpp | 2354 | ||||
-rw-r--r-- | mfbt/tests/gtest/TestTainting.cpp | 484 | ||||
-rw-r--r-- | mfbt/tests/gtest/moz.build | 34 |
11 files changed, 4332 insertions, 0 deletions
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 <iterator> +#include <vector> + +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<int32_t> mValue; +}; + +struct TestError {}; + +constexpr static int32_t arr1[3] = {1, 2, 3}; +} // namespace + +TEST(MFBT_Algorithm_TransformAbortOnErr, NoError) +{ + std::vector<int64_t> out; + auto res = TransformAbortOnErr( + begin(arr1), end(arr1), std::back_inserter(out), + [](const int32_t value) -> Result<int64_t, TestError> { + return value * 10; + }); + ASSERT_TRUE(res.isOk()); + + const std::vector<int64_t> expected = {10, 20, 30}; + ASSERT_EQ(expected, out); +} + +TEST(MFBT_Algorithm_TransformAbortOnErr, NoError_Range) +{ + std::vector<int64_t> out; + auto res = TransformAbortOnErr( + arr1, std::back_inserter(out), + [](const int32_t value) -> Result<int64_t, TestError> { + return value * 10; + }); + ASSERT_TRUE(res.isOk()); + + const std::vector<int64_t> expected = {10, 20, 30}; + ASSERT_EQ(expected, out); +} + +TEST(MFBT_Algorithm_TransformAbortOnErr, ErrorOnFirst) +{ + std::vector<int64_t> out; + auto res = TransformAbortOnErr( + begin(arr1), end(arr1), std::back_inserter(out), + [](const int32_t value) -> Result<int64_t, TestError> { + return Err(TestError{}); + }); + ASSERT_TRUE(res.isErr()); + ASSERT_TRUE(out.empty()); +} + +TEST(MFBT_Algorithm_TransformAbortOnErr, ErrorOnOther) +{ + std::vector<int64_t> out; + auto res = TransformAbortOnErr( + begin(arr1), end(arr1), std::back_inserter(out), + [](const int32_t value) -> Result<int64_t, TestError> { + 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<int64_t> expected = {10, 20}; + ASSERT_EQ(expected, out); +} + +TEST(MFBT_Algorithm_TransformAbortOnErr, ErrorOnOther_Move) +{ + MoveOnly in[3] = {MoveOnly{1}, MoveOnly{2}, MoveOnly{3}}; + std::vector<int64_t> out; + auto res = TransformAbortOnErr( + std::make_move_iterator(begin(in)), std::make_move_iterator(end(in)), + std::back_inserter(out), + [](MoveOnly value) -> Result<int64_t, TestError> { + 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<int64_t> expected = {10}; + ASSERT_EQ(expected, out); +} + +TEST(MFBT_Algorithm_TransformIfAbortOnErr, NoError) +{ + std::vector<int64_t> 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<int64_t, TestError> { + return value * 10; + }); + ASSERT_TRUE(res.isOk()); + + const std::vector<int64_t> expected = {10, 30}; + ASSERT_EQ(expected, out); +} + +TEST(MFBT_Algorithm_TransformIfAbortOnErr, NoError_Range) +{ + std::vector<int64_t> out; + auto res = TransformIfAbortOnErr( + arr1, std::back_inserter(out), + [](const int32_t value) { return value % 2 == 1; }, + [](const int32_t value) -> Result<int64_t, TestError> { + return value * 10; + }); + ASSERT_TRUE(res.isOk()); + + const std::vector<int64_t> expected = {10, 30}; + ASSERT_EQ(expected, out); +} + +TEST(MFBT_Algorithm_TransformIfAbortOnErr, ErrorOnOther) +{ + std::vector<int64_t> 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<int64_t, TestError> { + if (value > 2) { + return Err(TestError{}); + } + return value * 10; + }); + ASSERT_TRUE(res.isErr()); + + const std::vector<int64_t> expected = {10}; + ASSERT_EQ(expected, out); +} + +TEST(MFBT_Algorithm_TransformIfAbortOnErr, ErrorOnOther_Move) +{ + MoveOnly in[3] = {MoveOnly{1}, MoveOnly{2}, MoveOnly{3}}; + std::vector<int64_t> 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<int64_t, TestError> { + 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<int64_t> 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<int32_t, LEN> arr = {1, 2, 3, 4, 5, 6, 7, 8}; + Buffer<int32_t> 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<int32_t> 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<int32_t> another(std::move(buf)); + ASSERT_EQ(another[3], 42); + ASSERT_EQ(buf.Length(), 0U); +} + +TEST(Buffer, TestBufferFallible) +{ + const size_t LEN = 8; + Array<int32_t, LEN> arr = {1, 2, 3, 4, 5, 6, 7, 8}; + auto maybe = Buffer<int32_t>::CopyFrom(arr); + ASSERT_TRUE(maybe.isSome()); + Buffer<int32_t> 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<int32_t> 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<int32_t> another(std::move(buf)); + ASSERT_EQ(another[3], 42); + ASSERT_EQ(buf.Length(), 0U); +} + +TEST(Buffer, TestBufferElements) +{ + ASSERT_EQ(Buffer<int32_t>().Elements(), + reinterpret_cast<int32_t*>(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 <type_traits> + +using namespace mozilla; + +namespace { +template <typename T> +void AssertIsSome(const T& aVal) { + ASSERT_TRUE(aVal); + ASSERT_TRUE(aVal.isSome()); + ASSERT_FALSE(aVal.isNothing()); +} + +template <typename T> +void AssertIsNothing(const T& aVal) { + ASSERT_FALSE(aVal); + ASSERT_FALSE(aVal.isSome()); + ASSERT_TRUE(aVal.isNothing()); +} + +static_assert(std::is_trivially_destructible_v<InitializedOnce<const int>>); +static_assert(std::is_trivially_destructible_v<LazyInitializedOnce<const int>>); + +static_assert(!std::is_copy_constructible_v<InitializedOnce<const int>>); +static_assert(!std::is_copy_assignable_v<InitializedOnce<const int>>); + +static_assert(!std::is_default_constructible_v<InitializedOnce<const int>>); +static_assert(std::is_default_constructible_v<LazyInitializedOnce<const int>>); +static_assert(std::is_default_constructible_v< + LazyInitializedOnceEarlyDestructible<const int>>); + +// 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<const int>* kPtrInitializedOnceIntLazyInitForbid = + nullptr; +constexpr LazyInitializedOnce<const int>* kPtrInitializedOnceIntLazyInitAllow = + nullptr; +constexpr LazyInitializedOnceEarlyDestructible<const int>* + kPtrInitializedOnceIntLazyInitAllowResettable = nullptr; + +template <class T, typename = decltype(std::declval<T*>()->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 <class T, + typename = decltype(std::declval<T*>()->init(std::declval<int>()))> +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<const MoveOnly> 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<const MoveOnly> val{testValue}; + val.destroy(); + + AssertIsNothing(val); +} + +TEST(InitializedOnce, MoveConstruct) +{ + InitializedOnce<const MoveOnly> oldVal{testValue}; + InitializedOnce<const MoveOnly> val{std::move(oldVal)}; + + AssertIsNothing(oldVal); + AssertIsSome(val); +} + +TEST(InitializedOnceAllowLazy, DefaultCtor) +{ + LazyInitializedOnce<const MoveOnly> val; + + AssertIsNothing(val); +} + +TEST(InitializedOnceAllowLazy, Init) +{ + LazyInitializedOnce<const MoveOnly> 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<const MoveOnly> 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<const MoveOnly> val; + + AssertIsNothing(val); +} + +TEST(InitializedOnceAllowLazyResettable, Init) +{ + LazyInitializedOnceEarlyDestructible<const MoveOnly> 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<const MoveOnly> val; + val.init(testValue); + val.destroy(); + + AssertIsNothing(val); +} + +TEST(InitializedOnceAllowLazyResettable, MoveConstruct) +{ + LazyInitializedOnceEarlyDestructible<const MoveOnly> oldVal{testValue}; + LazyInitializedOnceEarlyDestructible<const MoveOnly> val{std::move(oldVal)}; + + AssertIsNothing(oldVal); + AssertIsSome(val); +} + +TEST(InitializedOnceAllowLazyResettable, MoveAssign) +{ + LazyInitializedOnceEarlyDestructible<const MoveOnly> oldVal{testValue}; + LazyInitializedOnceEarlyDestructible<const MoveOnly> 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<PtrClass> { + 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<PtrClass> 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<RefPtr<CountedClass>> { + public: + int mCount; + void AddRef() { mCount++; } + void Release() { mCount--; } + + CountedClass() : mCount(0) {} + ~CountedClass() { EXPECT_TRUE(mCount == 0); } +}; + +TEST(LinkedList, AutoCleanLinkedListRefPtr) +{ + RefPtr<CountedClass> elt1 = new CountedClass; + CountedClass* elt2 = new CountedClass; + { + AutoCleanLinkedList<RefPtr<CountedClass>> 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 <thread> + +using namespace mozilla; + +struct C : public SupportsWeakPtr { + int mNum{0}; +}; + +struct HasWeakPtrToC { + explicit HasWeakPtrToC(C* c) : mPtr(c) {} + + MainThreadWeakPtr<C> mPtr; + + ~HasWeakPtrToC() { + MOZ_RELEASE_ASSERT(!NS_IsMainThread(), "Should be released OMT"); + } +}; + +TEST(MFBT_MainThreadWeakPtr, Basic) +{ + auto c = MakeUnique<C>(); + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + auto weakRef = MakeUnique<HasWeakPtrToC>(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 <iostream> +#include <type_traits> + +#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<type_, decltype(MOZ_DBG(expression_))>, \ + "MOZ_DBG should return the indicated type") + +#define TEST_MOZ_DBG_TYPE_SAME(expression_...) \ + static_assert( \ + std::is_same_v<decltype((expression_)), decltype(MOZ_DBG(expression_))>, \ + "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 <functional> + +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<int, int>* aOut) { + *aOut = std::pair{kTestValue, kTestValue}; + return NS_OK; + } + nsresult NonOverloadedNoInputFailsComplex(std::pair<int, int>* 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<nsLocalFile>().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<mozilla::detail::DerefedType<RefCountedTestClass&>, + RefCountedTestClass>); +static_assert(std::is_same_v<mozilla::detail::DerefedType<RefCountedTestClass*>, + RefCountedTestClass>); +static_assert( + std::is_same_v<mozilla::detail::DerefedType<RefPtr<RefCountedTestClass>>, + RefCountedTestClass>); + +static_assert(std::is_same_v<mozilla::detail::DerefedType<nsIFile&>, nsIFile>); +static_assert(std::is_same_v<mozilla::detail::DerefedType<nsIFile*>, nsIFile>); +static_assert( + std::is_same_v<mozilla::detail::DerefedType<nsCOMPtr<nsIFile>>, nsIFile>); +} // namespace + +TEST(ResultExtensions_ToResultInvoke, Lambda_NoInput) +{ + TestClass foo; + + // success + { + auto valOrErr = ToResultInvoke<int>( + [&foo](int* out) { return foo.NonOverloadedNoInput(out); }); + static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = ToResultInvoke<int>( + [&foo](int* out) { return foo.NonOverloadedNoInputFails(out); }); + static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvoke, MemFn_NoInput) +{ + TestClass foo; + + // success + { + auto valOrErr = + ToResultInvoke<int>(std::mem_fn(&TestClass::NonOverloadedNoInput), foo); + static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = ToResultInvoke<int>( + std::mem_fn(&TestClass::NonOverloadedNoInputFails), foo); + static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvoke, MemFn_Polymorphic_NoInput) +{ + TestClass foo; + + // success + { + auto valOrErr = ToResultInvoke<nsCOMPtr<nsIFile>>( + std::mem_fn(&TestClass::PolymorphicNoInput), foo); + static_assert(std::is_same_v<decltype(valOrErr), + Result<nsCOMPtr<nsIFile>, nsresult>>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_NE(nullptr, valOrErr.inspect()); + + ASSERT_EQ(ToResultInvoke<nsString>(std::mem_fn(&nsIFile::GetPath), + *MakeRefPtr<nsLocalFile>()) + .inspect(), + ToResultInvoke<nsString>(std::mem_fn(&nsIFile::GetPath), + valOrErr.inspect()) + .inspect()); + } + + // failure + { + auto valOrErr = ToResultInvoke<nsCOMPtr<nsIFile>>( + std::mem_fn(&TestClass::PolymorphicNoInputFails), foo); + static_assert(std::is_same_v<decltype(valOrErr), + Result<nsCOMPtr<nsIFile>, 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<decltype(valOrErr), Result<int, nsresult>>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = + ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputFails); + static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>); + 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<decltype(valOrErr), Result<int, nsresult>>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = + ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputFailsConst); + static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>); + 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<decltype(valOrErr), Result<int, nsresult>>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = + ToResultInvokeMember(foo, &TestClass::NonOverloadedNoInputFailsRef); + static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>); + 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<decltype(valOrErr), + Result<std::pair<int, int>, 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<decltype(valOrErr), + Result<std::pair<int, int>, 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<decltype(valOrErr), Result<int, nsresult>>); + 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<decltype(valOrErr), Result<int, nsresult>>); + 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<decltype(valOrErr), Result<Ok, nsresult>>); + ASSERT_TRUE(valOrErr.isOk()); + } + + // failure + { + auto valOrErr = ToResultInvokeMember( + foo, &TestClass::NonOverloadedNoOutputFails, -TestClass::kTestValue); + static_assert(std::is_same_v<decltype(valOrErr), Result<Ok, nsresult>>); + 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<decltype(valOrErr), Result<int, nsresult>>); + 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<decltype(valOrErr), Result<int, nsresult>>); + 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<decltype(valOrErr), Result<int, nsresult>>); + 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<decltype(valOrErr), Result<int, nsresult>>); + 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<decltype(valOrErr), Result<int, nsresult>>); + 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<decltype(valOrErr), Result<int, nsresult>>); + 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<decltype(valOrErr), + Result<std::pair<int, int>, 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<decltype(valOrErr), + Result<std::pair<int, int>, 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<decltype(valOrErr), Result<int, nsresult>>); + 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<decltype(valOrErr), Result<int, nsresult>>); + 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<decltype(valOrErr), Result<Ok, nsresult>>); + ASSERT_TRUE(valOrErr.isOk()); + } + + // failure + { + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoOutputFails, + -TestClass::kTestValue); + static_assert(std::is_same_v<decltype(valOrErr), Result<Ok, nsresult>>); + 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<int, int>), foo, NonOverloadedNoInputComplex); + static_assert(std::is_same_v<decltype(valOrErr), + Result<std::pair<int, int>, 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<int, int>), foo, NonOverloadedNoInputFailsComplex); + static_assert(std::is_same_v<decltype(valOrErr), + Result<std::pair<int, int>, nsresult>>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, RefPtr_NoInput) +{ + auto foo = MakeRefPtr<RefCountedTestClass>(); + + // success + { + auto valOrErr = + ToResultInvokeMember(foo, &RefCountedTestClass::NonOverloadedNoInput); + static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(TestClass::kTestValue, valOrErr.unwrap()); + } + + // failure + { + auto valOrErr = ToResultInvokeMember( + foo, &RefCountedTestClass::NonOverloadedNoInputFails); + static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, RefPtr_NoInput_Macro) +{ + auto foo = MakeRefPtr<RefCountedTestClass>(); + + // success + { + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(foo, NonOverloadedNoInput); + static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>); + 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<decltype(valOrErr), Result<int, nsresult>>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, RawPtr_NoInput_Macro) +{ + auto foo = MakeRefPtr<RefCountedTestClass>(); + auto* fooPtr = foo.get(); + + // success + { + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(fooPtr, NonOverloadedNoInput); + static_assert(std::is_same_v<decltype(valOrErr), Result<int, nsresult>>); + 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<decltype(valOrErr), Result<int, nsresult>>); + ASSERT_TRUE(valOrErr.isErr()); + ASSERT_EQ(NS_ERROR_FAILURE, valOrErr.unwrapErr()); + } +} + +TEST(ResultExtensions_ToResultInvokeMember, nsCOMPtr_AbstractClass_WithInput) +{ + nsCOMPtr<nsIFile> file = MakeAndAddRef<nsLocalFile>(); + + auto valOrErr = ToResultInvokeMember(file, &nsIFile::Equals, file); + static_assert(std::is_same_v<decltype(valOrErr), Result<bool, nsresult>>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(true, valOrErr.unwrap()); +} + +TEST(ResultExtensions_ToResultInvokeMember, + RawPtr_AbstractClass_WithInput_Macro) +{ + nsCOMPtr<nsIFile> file = MakeAndAddRef<nsLocalFile>(); + auto* filePtr = file.get(); + + auto valOrErr = MOZ_TO_RESULT_INVOKE_MEMBER(filePtr, Equals, file); + static_assert(std::is_same_v<decltype(valOrErr), Result<bool, nsresult>>); + ASSERT_TRUE(valOrErr.isOk()); + ASSERT_EQ(true, valOrErr.unwrap()); +} + +TEST(ResultExtensions_ToResultInvokeMember, + RawPtr_AbstractClass_NoInput_Macro_Typed) +{ + nsCOMPtr<nsIFile> file = MakeAndAddRef<nsLocalFile>(); + auto* filePtr = file.get(); + + auto valOrErr = + MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCOMPtr<nsIFile>, filePtr, Clone); + static_assert( + std::is_same_v<decltype(valOrErr), Result<nsCOMPtr<nsIFile>, 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<int> in = {1, 2, 3, 4}; + const auto reversedRange = + detail::IteratorRange<ReverseIterator<std::vector<int>::const_iterator>>{ + ReverseIterator{in.end()}, ReverseIterator{in.begin()}}; + + const std::vector<int> expected = {4, 3, 2, 1}; + std::vector<int> out; + for (auto i : reversedRange) { + out.emplace_back(i); + } + + EXPECT_EQ(expected, out); +} + +TEST(ReverseIterator, NonConst_RangeBasedFor) +{ + std::vector<int> in = {1, 2, 3, 4}; + auto reversedRange = + detail::IteratorRange<ReverseIterator<std::vector<int>::iterator>>{ + ReverseIterator{in.end()}, ReverseIterator{in.begin()}}; + + const std::vector<int> expected = {-1, -2, -3, -4}; + for (auto& i : reversedRange) { + i = -i; + } + + EXPECT_EQ(expected, in); +} + +TEST(ReverseIterator, Difference) +{ + const std::vector<int> in = {1, 2, 3, 4}; + using reverse_iterator = ReverseIterator<std::vector<int>::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<int> in = {1, 2, 3, 4}; + using reverse_iterator = ReverseIterator<std::vector<int>::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..405a87ee2c --- /dev/null +++ b/mfbt/tests/gtest/TestSpan.cpp @@ -0,0 +1,2354 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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/Span.h" + +#include "nsString.h" +#include "nsTArray.h" +#include "mozilla/Range.h" + +#include <type_traits> + +#define SPAN_TEST(name) TEST(SpanTest, name) +#define CHECK_THROW(a, b) + +using namespace mozilla; + +static_assert(std::is_convertible_v<Range<int>, Span<const int>>, + "Range should convert into const"); +static_assert(std::is_convertible_v<Range<const int>, Span<const int>>, + "const Range should convert into const"); +static_assert(!std::is_convertible_v<Range<const int>, Span<int>>, + "Range should not drop const in conversion"); +static_assert(std::is_convertible_v<Span<int>, Range<const int>>, + "Span should convert into const"); +static_assert(std::is_convertible_v<Span<const int>, Range<const int>>, + "const Span should convert into const"); +static_assert(!std::is_convertible_v<Span<const int>, Range<int>>, + "Span should not drop const in conversion"); +static_assert(std::is_convertible_v<Span<const int>, Span<const int>>, + "const Span should convert into const"); +static_assert(std::is_convertible_v<Span<int>, Span<const int>>, + "Span should convert into const"); +static_assert(!std::is_convertible_v<Span<const int>, Span<int>>, + "Span should not drop const in conversion"); +static_assert(std::is_convertible_v<const nsTArray<int>, Span<const int>>, + "const nsTArray should convert into const"); +static_assert(std::is_convertible_v<nsTArray<int>, Span<const int>>, + "nsTArray should convert into const"); +static_assert(!std::is_convertible_v<const nsTArray<int>, Span<int>>, + "nsTArray should not drop const in conversion"); +static_assert(std::is_convertible_v<nsTArray<const int>, Span<const int>>, + "nsTArray should convert into const"); +static_assert(!std::is_convertible_v<nsTArray<const int>, Span<int>>, + "nsTArray should not drop const in conversion"); + +static_assert(std::is_convertible_v<const std::vector<int>, Span<const int>>, + "const std::vector should convert into const"); +static_assert(std::is_convertible_v<std::vector<int>, Span<const int>>, + "std::vector should convert into const"); +static_assert(!std::is_convertible_v<const std::vector<int>, Span<int>>, + "std::vector should not drop const in conversion"); + +/** + * Rust slice-compatible nullptr replacement value. + */ +#define SLICE_CONST_INT_PTR reinterpret_cast<const int*>(alignof(const int)) + +/** + * Rust slice-compatible nullptr replacement value. + */ +#define SLICE_INT_PTR reinterpret_cast<int*>(alignof(int)) + +/** + * Rust slice-compatible nullptr replacement value. + */ +#define SLICE_CONST_INT_PTR_PTR \ + reinterpret_cast<const int**>(alignof(const int*)) + +/** + * Rust slice-compatible nullptr replacement value. + */ +#define SLICE_INT_PTR_PTR reinterpret_cast<int**>(alignof(int*)) + +namespace { +struct BaseClass {}; +struct DerivedClass : BaseClass {}; +} // namespace + +void AssertSpanOfThreeInts(Span<const int> s) { + ASSERT_EQ(s.size(), 3U); + ASSERT_EQ(s[0], 1); + ASSERT_EQ(s[1], 2); + ASSERT_EQ(s[2], 3); +} + +void AssertSpanOfThreeChars(Span<const char> s) { + ASSERT_EQ(s.size(), 3U); + ASSERT_EQ(s[0], 'a'); + ASSERT_EQ(s[1], 'b'); + ASSERT_EQ(s[2], 'c'); +} + +void AssertSpanOfThreeChar16s(Span<const char16_t> 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<int> s; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + + Span<const int> cs; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR); + } + + { + Span<int, 0> s; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + + Span<const int, 0> cs; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + Span<int, 1> s; + ASSERT_EQ(s.Length(), 1U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); // explains why it can't compile +#endif + } + + { + Span<int> s{}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + + Span<const int> cs{}; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR); + } +} + +SPAN_TEST(size_optimization) { + { + Span<int> s; + ASSERT_EQ(sizeof(s), sizeof(int*) + sizeof(size_t)); + } + + { + Span<int, 0> s; + ASSERT_EQ(sizeof(s), sizeof(int*)); + } +} + +SPAN_TEST(from_nullptr_constructor) { + { + Span<int> s = nullptr; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + + Span<const int> cs = nullptr; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR); + } + + { + Span<int, 0> s = nullptr; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + + Span<const int, 0> cs = nullptr; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + Span<int, 1> s = nullptr; + ASSERT_EQ(s.Length(), 1U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); // explains why it can't compile +#endif + } + + { + Span<int> s{nullptr}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + + Span<const int> cs{nullptr}; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR); + } + + { + Span<int*> s{nullptr}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR_PTR); + + Span<const int*> cs{nullptr}; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR_PTR); + } +} + +SPAN_TEST(from_nullptr_length_constructor) { + { + Span<int> s{nullptr, static_cast<Span<int>::index_type>(0)}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + + Span<const int> cs{nullptr, static_cast<Span<int>::index_type>(0)}; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR); + } + + { + Span<int, 0> s{nullptr, static_cast<Span<int>::index_type>(0)}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + + Span<const int, 0> cs{nullptr, static_cast<Span<int>::index_type>(0)}; + ASSERT_EQ(cs.Length(), 0U); + ASSERT_EQ(cs.data(), SLICE_CONST_INT_PTR); + } + +#if 0 + { + auto workaround_macro = []() { Span<int, 1> s{ nullptr, static_cast<Span<int>::index_type>(0) }; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + { + auto workaround_macro = []() { Span<int> s{nullptr, 1}; }; + CHECK_THROW(workaround_macro(), fail_fast); + + auto const_workaround_macro = []() { Span<const int> cs{nullptr, 1}; }; + CHECK_THROW(const_workaround_macro(), fail_fast); + } + + { + auto workaround_macro = []() { Span<int, 0> s{nullptr, 1}; }; + CHECK_THROW(workaround_macro(), fail_fast); + + auto const_workaround_macro = []() { Span<const int, 0> s{nullptr, 1}; }; + CHECK_THROW(const_workaround_macro(), fail_fast); + } +#endif + { + Span<int*> s{nullptr, static_cast<Span<int>::index_type>(0)}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR_PTR); + + Span<const int*> cs{nullptr, static_cast<Span<int>::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<int> 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<int, 2> 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<int> s{p, static_cast<Span<int>::index_type>(0)}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + } + +#if 0 + { + int* p = nullptr; + auto workaround_macro = [=]() { Span<int> 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<Span<int>::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<int> 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<int, 2> 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<int> s{&arr[0], &arr[0]}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), &arr[0]); + } + + { + Span<int, 0> 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<int> 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<int> s{&arr[0], p}; }; + // CHECK_THROW(workaround_macro(), fail_fast); + //} + + { + int* p = nullptr; + Span<int> s{p, p}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), SLICE_INT_PTR); + } + + { + int* p = nullptr; + Span<int, 0> 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<int> 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<int> s{arr}; + ASSERT_EQ(s.Length(), 5U); + ASSERT_EQ(s.data(), &arr[0]); + } + + { + Span<int, 5> 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<int, 6> s{arr}; } + + { + Span<int, 0> s{arr}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), &arr[0]); + } + + { + Span<int> 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<int, 0> s{arr2d}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), &arr2d[0][0]); + } + + { Span<int, 6> s{arr2d}; } +#endif + { + Span<int[3]> 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<int> 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<int, 0> s{arr3d}; + ASSERT_EQ(s.Length(), 0U); + ASSERT_EQ(s.data(), &arr3d[0][0][0]); + } + + { Span<int, 11> s{arr3d}; } + + { + Span<int, 12> 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<int[3][2]> 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<double> 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<int, 4> arr = {{1, 2, 3, 4}}; + + { + Span<int> s{arr}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + + Span<const int> cs{arr}; + ASSERT_EQ(cs.size(), narrow_cast<size_t>(arr.size())); + ASSERT_EQ(cs.data(), arr.data()); + } + + { + Span<int, 4> s{arr}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + + Span<const int, 4> cs{arr}; + ASSERT_EQ(cs.size(), narrow_cast<size_t>(arr.size())); + ASSERT_EQ(cs.data(), arr.data()); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + Span<int, 2> s{arr}; + ASSERT_EQ(s.size(), 2U); + ASSERT_EQ(s.data(), arr.data()); + + Span<const int, 2> cs{arr}; + ASSERT_EQ(cs.size(), 2U); + ASSERT_EQ(cs.data(), arr.data()); + } + + { + Span<int, 0> s{arr}; + ASSERT_EQ(s.size(), 0U); + ASSERT_EQ(s.data(), arr.data()); + + Span<const int, 0> cs{arr}; + ASSERT_EQ(cs.size(), 0U); + ASSERT_EQ(cs.data(), arr.data()); + } + + { Span<int, 5> s{arr}; } + + { + auto get_an_array = []() -> std::array<int, 4> { return {1, 2, 3, 4}; }; + auto take_a_Span = [](Span<int> s) { static_cast<void>(s); }; + // try to take a temporary std::array + take_a_Span(get_an_array()); + } +#endif + + { + auto get_an_array = []() -> std::array<int, 4> { return {{1, 2, 3, 4}}; }; + auto take_a_Span = [](Span<const int> s) { static_cast<void>(s); }; + // try to take a temporary std::array + take_a_Span(get_an_array()); + } + + { + auto s = Span(arr); + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + } +} + +SPAN_TEST(from_const_std_array_constructor) { + const std::array<int, 4> arr = {{1, 2, 3, 4}}; + + { + Span<const int> s{arr}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + } + + { + Span<const int, 4> s{arr}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + Span<const int, 2> s{arr}; + ASSERT_EQ(s.size(), 2U); + ASSERT_EQ(s.data(), arr.data()); + } + + { + Span<const int, 0> s{arr}; + ASSERT_EQ(s.size(), 0U); + ASSERT_EQ(s.data(), arr.data()); + } + + { Span<const int, 5> s{arr}; } +#endif + + { + auto get_an_array = []() -> const std::array<int, 4> { + return {{1, 2, 3, 4}}; + }; + auto take_a_Span = [](Span<const int> s) { static_cast<void>(s); }; + // try to take a temporary std::array + take_a_Span(get_an_array()); + } + + { + auto s = Span(arr); + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + } +} + +SPAN_TEST(from_std_array_const_constructor) { + std::array<const int, 4> arr = {{1, 2, 3, 4}}; + + { + Span<const int> s{arr}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + } + + { + Span<const int, 4> s{arr}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + Span<const int, 2> s{arr}; + ASSERT_EQ(s.size(), 2U); + ASSERT_EQ(s.data(), arr.data()); + } + + { + Span<const int, 0> s{arr}; + ASSERT_EQ(s.size(), 0U); + ASSERT_EQ(s.data(), arr.data()); + } + + { Span<const int, 5> s{arr}; } + + { Span<int, 4> s{arr}; } +#endif + + { + auto s = Span(arr); + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size())); + ASSERT_EQ(s.data(), arr.data()); + } +} + +SPAN_TEST(from_mozilla_array_constructor) { + mozilla::Array<int, 4> arr(1, 2, 3, 4); + + { + Span<int> s{arr}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + + Span<const int> cs{arr}; + ASSERT_EQ(cs.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin())); + ASSERT_EQ(cs.data(), &arr[0]); + } + + { + Span<int, 4> s{arr}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + + Span<const int, 4> cs{arr}; + ASSERT_EQ(cs.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin())); + ASSERT_EQ(cs.data(), &arr[0]); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + Span<int, 2> s{arr}; + ASSERT_EQ(s.size(), 2U); + ASSERT_EQ(s.data(), &arr[0]); + + Span<const int, 2> cs{arr}; + ASSERT_EQ(cs.size(), 2U); + ASSERT_EQ(cs.data(), &arr[0]); + } + + { + Span<int, 0> s{arr}; + ASSERT_EQ(s.size(), 0U); + ASSERT_EQ(s.data(), &arr[0]); + + Span<const int, 0> cs{arr}; + ASSERT_EQ(cs.size(), 0U); + ASSERT_EQ(cs.data(), &arr[0]); + } + + { Span<int, 5> s{arr}; } + + { + auto get_an_array = []() -> mozilla::Array<int, 4> { return {1, 2, 3, 4}; }; + auto take_a_Span = [](Span<int> s) { static_cast<void>(s); }; + // try to take a temporary mozilla::Array + take_a_Span(get_an_array()); + } +#endif + + { + auto get_an_array = []() -> mozilla::Array<int, 4> { return {1, 2, 3, 4}; }; + auto take_a_Span = [](Span<const int> s) { static_cast<void>(s); }; + // try to take a temporary mozilla::Array + take_a_Span(get_an_array()); + } + + { + auto s = Span(arr); + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + } +} + +SPAN_TEST(from_const_mozilla_array_constructor) { + const mozilla::Array<int, 4> arr(1, 2, 3, 4); + + { + Span<const int> s{arr}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + } + + { + Span<const int, 4> s{arr}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + Span<const int, 2> s{arr}; + ASSERT_EQ(s.size(), 2U); + ASSERT_EQ(s.data(), &arr[0]); + } + + { + Span<const int, 0> s{arr}; + ASSERT_EQ(s.size(), 0U); + ASSERT_EQ(s.data(), &arr[0]); + } + + { Span<const int, 5> s{arr}; } +#endif + +#if 0 + { + auto get_an_array = []() -> const mozilla::Array<int, 4> { + return { 1, 2, 3, 4 }; + }; + auto take_a_Span = [](Span<const int> s) { static_cast<void>(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<size_t>(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + } +} + +SPAN_TEST(from_mozilla_array_const_constructor) { + mozilla::Array<const int, 4> arr(1, 2, 3, 4); + + { + Span<const int> s{arr}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + } + + { + Span<const int, 4> s{arr}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + Span<const int, 2> s{arr}; + ASSERT_EQ(s.size(), 2U); + ASSERT_EQ(s.data(), &arr[0]); + } + + { + Span<const int, 0> s{arr}; + ASSERT_EQ(s.size(), 0U); + ASSERT_EQ(s.data(), &arr[0]); + } + + { Span<const int, 5> s{arr}; } + + { Span<int, 4> s{arr}; } +#endif + + { + auto s = Span(arr); + ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin())); + ASSERT_EQ(s.data(), &arr[0]); + } +} + +SPAN_TEST(from_container_constructor) { + std::vector<int> v = {1, 2, 3}; + const std::vector<int> cv = v; + + { + AssertSpanOfThreeInts(v); + + Span<int> s{v}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(v.size())); + ASSERT_EQ(s.data(), v.data()); + + Span<const int> cs{v}; + ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.size())); + ASSERT_EQ(cs.data(), v.data()); + } + + std::string str = "hello"; + const std::string cstr = "hello"; + + { +#ifdef CONFIRM_COMPILATION_ERRORS + Span<char> s{str}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(str.size())); + ASSERT_EQ(s.data(), str.data()); +#endif + Span<const char> cs{str}; + ASSERT_EQ(cs.size(), narrow_cast<size_t>(str.size())); + ASSERT_EQ(cs.data(), str.data()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + Span<char> s{cstr}; +#endif + Span<const char> cs{cstr}; + ASSERT_EQ(cs.size(), narrow_cast<size_t>(cstr.size())); + ASSERT_EQ(cs.data(), cstr.data()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_vector = []() -> std::vector<int> { return {}; }; + auto use_Span = [](Span<int> s) { static_cast<void>(s); }; + use_Span(get_temp_vector()); +#endif + } + + { + auto get_temp_vector = []() -> std::vector<int> { return {}; }; + auto use_Span = [](Span<const int> s) { static_cast<void>(s); }; + use_Span(get_temp_vector()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_string = []() -> std::string { return {}; }; + auto use_Span = [](Span<char> s) { static_cast<void>(s); }; + use_Span(get_temp_string()); +#endif + } + + { + auto get_temp_string = []() -> std::string { return {}; }; + auto use_Span = [](Span<const char> s) { static_cast<void>(s); }; + use_Span(get_temp_string()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_vector = []() -> const std::vector<int> { return {}; }; + auto use_Span = [](Span<const char> s) { static_cast<void>(s); }; + use_Span(get_temp_vector()); +#endif + } + + { + auto get_temp_string = []() -> const std::string { return {}; }; + auto use_Span = [](Span<const char> s) { static_cast<void>(s); }; + use_Span(get_temp_string()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::map<int, int> m; + Span<int> s{m}; +#endif + } + + { + auto s = Span(v); + ASSERT_EQ(s.size(), narrow_cast<size_t>(v.size())); + ASSERT_EQ(s.data(), v.data()); + + auto cs = Span(cv); + ASSERT_EQ(cs.size(), narrow_cast<size_t>(cv.size())); + ASSERT_EQ(cs.data(), cv.data()); + } +} + +SPAN_TEST(from_xpcom_collections) { + { + nsTArray<int> v; + v.AppendElement(1); + v.AppendElement(2); + v.AppendElement(3); + + AssertSpanOfThreeInts(v); + + Span<int> s{v}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length())); + ASSERT_EQ(s.data(), v.Elements()); + ASSERT_EQ(s[2], 3); + + Span<const int> cs{v}; + ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.Length())); + ASSERT_EQ(cs.data(), v.Elements()); + ASSERT_EQ(cs[2], 3); + } + { + nsTArray<int> v; + v.AppendElement(1); + v.AppendElement(2); + v.AppendElement(3); + + AssertSpanOfThreeInts(v); + + auto s = Span(v); + ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length())); + ASSERT_EQ(s.data(), v.Elements()); + ASSERT_EQ(s[2], 3); + } + { + AutoTArray<int, 5> v; + v.AppendElement(1); + v.AppendElement(2); + v.AppendElement(3); + + AssertSpanOfThreeInts(v); + + Span<int> s{v}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length())); + ASSERT_EQ(s.data(), v.Elements()); + ASSERT_EQ(s[2], 3); + + Span<const int> cs{v}; + ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.Length())); + ASSERT_EQ(cs.data(), v.Elements()); + ASSERT_EQ(cs[2], 3); + } + { + AutoTArray<int, 5> v; + v.AppendElement(1); + v.AppendElement(2); + v.AppendElement(3); + + AssertSpanOfThreeInts(v); + + auto s = Span(v); + ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length())); + ASSERT_EQ(s.data(), v.Elements()); + ASSERT_EQ(s[2], 3); + } + { + FallibleTArray<int> v; + *(v.AppendElement(fallible)) = 1; + *(v.AppendElement(fallible)) = 2; + *(v.AppendElement(fallible)) = 3; + + AssertSpanOfThreeInts(v); + + Span<int> s{v}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length())); + ASSERT_EQ(s.data(), v.Elements()); + ASSERT_EQ(s[2], 3); + + Span<const int> cs{v}; + ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.Length())); + ASSERT_EQ(cs.data(), v.Elements()); + ASSERT_EQ(cs[2], 3); + } + { + FallibleTArray<int> 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<size_t>(v.Length())); + ASSERT_EQ(s.data(), v.Elements()); + ASSERT_EQ(s[2], 3); + } + { + nsAutoString str; + str.AssignLiteral(u"abc"); + + AssertSpanOfThreeChar16s(str); + AssertSpanOfThreeChar16sViaString(str); + + Span<char16_t> s{str.GetMutableData()}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(str.Length())); + ASSERT_EQ(s.data(), str.BeginWriting()); + ASSERT_EQ(s[2], 'c'); + + Span<const char16_t> cs{str}; + ASSERT_EQ(cs.size(), narrow_cast<size_t>(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<size_t>(str.Length())); + ASSERT_EQ(s.data(), str.BeginReading()); + ASSERT_EQ(s[2], 'c'); + } + { + nsAutoCString str; + str.AssignLiteral("abc"); + + AssertSpanOfThreeChars(str); + AssertSpanOfThreeCharsViaString(str); + + Span<const uint8_t> cs{str}; + ASSERT_EQ(cs.size(), narrow_cast<size_t>(str.Length())); + ASSERT_EQ(cs.data(), reinterpret_cast<const uint8_t*>(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<size_t>(str.Length())); + ASSERT_EQ(s.data(), str.BeginReading()); + ASSERT_EQ(s[2], 'c'); + } + { + nsTArray<int> v; + v.AppendElement(1); + v.AppendElement(2); + v.AppendElement(3); + + Range<int> r(v.Elements(), v.Length()); + + AssertSpanOfThreeInts(r); + + Span<int> s{r}; + ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length())); + ASSERT_EQ(s.data(), v.Elements()); + ASSERT_EQ(s[2], 3); + + Span<const int> cs{r}; + ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.Length())); + ASSERT_EQ(cs.data(), v.Elements()); + ASSERT_EQ(cs[2], 3); + } + { + nsTArray<int> v; + v.AppendElement(1); + v.AppendElement(2); + v.AppendElement(3); + + Range<int> r(v.Elements(), v.Length()); + + AssertSpanOfThreeInts(r); + + auto s = Span(r); + ASSERT_EQ(s.size(), narrow_cast<size_t>(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<const char> scccl("literal"); // error + + Span<const char> 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<char> scca(arr); // error + Span<const char> sccca(arr); // error + + Span<const char> 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<char16_t> scca(arr); // error + + Span<const char16_t> scccea; + scccea = arr; // error + + Span<const char16_t> scccl(u"literal"); // error + + Span<const char16_t>* sccel; + *sccel = u"literal"; // error + + cs = Span(u"literal"); // error +#endif + } +} + +SPAN_TEST(from_convertible_Span_constructor){{Span<DerivedClass> avd; +Span<const DerivedClass> avcd = avd; +static_cast<void>(avcd); +} + +{ +#ifdef CONFIRM_COMPILATION_ERRORS + Span<DerivedClass> avd; + Span<BaseClass> avb = avd; + static_cast<void>(avb); +#endif +} + +#ifdef CONFIRM_COMPILATION_ERRORS +{ + Span<int> s; + Span<unsigned int> s2 = s; + static_cast<void>(s2); +} + +{ + Span<int> s; + Span<const unsigned int> s2 = s; + static_cast<void>(s2); +} + +{ + Span<int> s; + Span<short> s2 = s; + static_cast<void>(s2); +} +#endif +} + +SPAN_TEST(copy_move_and_assignment) { + Span<int> s1; + ASSERT_TRUE(s1.empty()); + + int arr[] = {3, 4, 5}; + + Span<const int> s2 = arr; + ASSERT_EQ(s2.Length(), 3U); + ASSERT_EQ(s2.data(), &arr[0]); + + s2 = s1; + ASSERT_TRUE(s2.empty()); + + auto get_temp_Span = [&]() -> Span<int> { return {&arr[1], 2}; }; + auto use_Span = [&](Span<const int> 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<int, 5> av = arr; + ASSERT_EQ(av.First<2>().Length(), 2U); + ASSERT_EQ(av.First(2).Length(), 2U); + } + + { + Span<int, 5> av = arr; + ASSERT_EQ(av.First<0>().Length(), 0U); + ASSERT_EQ(av.First(0).Length(), 0U); + } + + { + Span<int, 5> av = arr; + ASSERT_EQ(av.First<5>().Length(), 5U); + ASSERT_EQ(av.First(5).Length(), 5U); + } + +#if 0 + { + Span<int, 5> 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<int> 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<int, 5> av = arr; + ASSERT_EQ(av.Last<2>().Length(), 2U); + ASSERT_EQ(av.Last(2).Length(), 2U); + } + + { + Span<int, 5> av = arr; + ASSERT_EQ(av.Last<0>().Length(), 0U); + ASSERT_EQ(av.Last(0).Length(), 0U); + } + + { + Span<int, 5> av = arr; + ASSERT_EQ(av.Last<5>().Length(), 5U); + ASSERT_EQ(av.Last(5).Length(), 5U); + } + +#if 0 + { + Span<int, 5> av = arr; +# ifdef CONFIRM_COMPILATION_ERRORS + ASSERT_EQ(av.Last<6>().Length() , 6U); +# endif + CHECK_THROW(av.Last(6).Length(), fail_fast); + } +#endif + + { + Span<int> 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<int, 5> av = arr; + ASSERT_EQ(av.From(3).Length(), 2U); + ASSERT_EQ(av.From(2)[1], 4); + } + + { + Span<int, 5> av = arr; + ASSERT_EQ(av.From(5).Length(), 0U); + } + + { + Span<int, 5> av = arr; + ASSERT_EQ(av.From(0).Length(), 5U); + } + + { + Span<int, 5> av = arr; + ASSERT_EQ(av.To(3).Length(), 3U); + ASSERT_EQ(av.To(3)[1], 2); + } + + { + Span<int, 5> av = arr; + ASSERT_EQ(av.To(0).Length(), 0U); + } + + { + Span<int, 5> av = arr; + ASSERT_EQ(av.To(5).Length(), 5U); + } + + { + Span<int, 5> av = arr; + ASSERT_EQ(av.FromTo(1, 4).Length(), 3U); + ASSERT_EQ(av.FromTo(1, 4)[1], 3); + } + + { + Span<int, 5> av = arr; + ASSERT_EQ(av.FromTo(2, 2).Length(), 0U); + } + + { + Span<int, 5> av = arr; + ASSERT_EQ(av.FromTo(0, 5).Length(), 5U); + } +} + +SPAN_TEST(Subspan) { + int arr[5] = {1, 2, 3, 4, 5}; + + { + Span<int, 5> 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<int, 5> av = arr; + ASSERT_EQ((av.Subspan<0, 0>().Length()), 0U); + ASSERT_EQ(av.Subspan(0, 0).Length(), 0U); + } + + { + Span<int, 5> 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<int, 5> 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<int> 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<int> av; + ASSERT_EQ(av.Subspan(0).Length(), 0U); + CHECK_THROW(av.Subspan(1).Length(), fail_fast); + } + + { + Span<int> 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<int, 5> 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<int> s = arr; + ASSERT_EQ(s.at(0), 1); + CHECK_THROW(s.at(5), fail_fast); + } + + { + int arr2d[2] = {1, 6}; + Span<int, 2> 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<int> s = arr; + ASSERT_EQ(s(0), 1); + CHECK_THROW(s(5), fail_fast); + } + + { + int arr2d[2] = {1, 6}; + Span<int, 2> s = arr2d; + ASSERT_EQ(s(0), 1); + ASSERT_EQ(s(1), 6); + CHECK_THROW(s(2), fail_fast); + } +} + +SPAN_TEST(iterator_default_init) { + Span<int>::iterator it1; + Span<int>::iterator it2; + ASSERT_EQ(it1, it2); +} + +SPAN_TEST(const_iterator_default_init) { + Span<int>::const_iterator it1; + Span<int>::const_iterator it2; + ASSERT_EQ(it1, it2); +} + +SPAN_TEST(iterator_conversions) { + Span<int>::iterator badIt; + Span<int>::const_iterator badConstIt; + ASSERT_EQ(badIt, badConstIt); + + int a[] = {1, 2, 3, 4}; + Span<int> s = a; + + auto it = s.begin(); + auto cit = s.cbegin(); + + ASSERT_EQ(it, cit); + ASSERT_EQ(cit, it); + + Span<int>::const_iterator cit2 = it; + ASSERT_EQ(cit2, cit); + + Span<int>::const_iterator cit3 = it + 4; + ASSERT_EQ(cit3, s.cend()); +} + +SPAN_TEST(iterator_comparisons) { + int a[] = {1, 2, 3, 4}; + { + Span<int> s = a; + Span<int>::iterator it = s.begin(); + auto it2 = it + 1; + Span<int>::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<int> s = a; + + Span<int>::iterator it = s.begin(); + Span<int>::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<int> 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, 4U); + ASSERT_EQ(first - first, 0U); + ASSERT_EQ(beyond - beyond, 0U); + + ++it; + ASSERT_EQ(it - first, 1U); + ASSERT_EQ(*it, 2); + *it = 22; + ASSERT_EQ(*it, 22); + ASSERT_EQ(beyond - it, 3U); + + it = first; + ASSERT_EQ(it, first); + while (it != s.end()) { + *it = 5; + ++it; + } + + ASSERT_EQ(it, beyond); + ASSERT_EQ(it - beyond, 0U); + + for (auto& n : s) { + ASSERT_EQ(n, 5); + } + } +} + +SPAN_TEST(cbegin_cend) { +#if 0 + { + int a[] = { 1, 2, 3, 4 }; + Span<int> s = a; + + Span<int>::const_iterator cit = s.cbegin(); + Span<int>::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<int> 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, 4U); + ASSERT_EQ(first - first, 0U); + ASSERT_EQ(beyond - beyond, 0U); + + ++it; + ASSERT_EQ(it - first, 1U); + ASSERT_EQ(*it, 2); + ASSERT_EQ(beyond - it, 3U); + + 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, 0U); + } +} + +SPAN_TEST(rbegin_rend) { + { + int a[] = {1, 2, 3, 4}; + Span<int> 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, 4U); + ASSERT_EQ(first - first, 0U); + ASSERT_EQ(beyond - beyond, 0U); + + ++it; + ASSERT_EQ(it - first, 1U); + ASSERT_EQ(*it, 3); + *it = 22; + ASSERT_EQ(*it, 22); + ASSERT_EQ(beyond - it, 3U); + + it = first; + ASSERT_EQ(it, first); + while (it != s.rend()) { + *it = 5; + ++it; + } + + ASSERT_EQ(it, beyond); + ASSERT_EQ(it - beyond, 0U); + + for (auto& n : s) { + ASSERT_EQ(n, 5); + } + } +} + +SPAN_TEST(crbegin_crend) { + { + int a[] = {1, 2, 3, 4}; + Span<int> 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, 4U); + ASSERT_EQ(first - first, 0U); + ASSERT_EQ(beyond - beyond, 0U); + + ++it; + ASSERT_EQ(it - first, 1U); + ASSERT_EQ(*it, 3); + ASSERT_EQ(beyond - it, 3U); + + 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, 0U); + } +} + +SPAN_TEST(comparison_operators) { + { + Span<int> s1 = nullptr; + Span<int> 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<int> s1 = arr; + Span<int> 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<int> s1 = nullptr; + Span<int> 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<int> s1 = arr1; + Span<int> 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<int> s1 = {&arr[0], 2}; // shorter + Span<int> 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<int> s1 = arr1; + Span<int> 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<const int> s = a; + ASSERT_EQ(s.Length(), 4U); + Span<const uint8_t> bs = AsBytes(s); + ASSERT_EQ(static_cast<const void*>(bs.data()), + static_cast<const void*>(s.data())); + ASSERT_EQ(bs.Length(), s.LengthBytes()); + } + + { + Span<int> 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<const void*>(bs.data()), + static_cast<const void*>(s.data())); + ASSERT_EQ(bs.data(), reinterpret_cast<const uint8_t*>(SLICE_INT_PTR)); + } + + { + Span<int> s = a; + auto bs = AsBytes(s); + ASSERT_EQ(static_cast<const void*>(bs.data()), + static_cast<const void*>(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<const int> s = a; + ASSERT_EQ(s.Length(), 4U); + Span<const byte> bs = AsWritableBytes(s); + ASSERT_EQ(static_cast<void*>(bs.data()), static_cast<void*>(s.data())); + ASSERT_EQ(bs.Length(), s.LengthBytes()); +#endif + } + + { + Span<int> 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<void*>(bs.data()), static_cast<void*>(s.data())); + ASSERT_EQ(bs.data(), reinterpret_cast<uint8_t*>(SLICE_INT_PTR)); + } + + { + Span<int> s = a; + auto bs = AsWritableBytes(s); + ASSERT_EQ(static_cast<void*>(bs.data()), static_cast<void*>(s.data())); + ASSERT_EQ(bs.Length(), s.LengthBytes()); + } +} + +SPAN_TEST(as_chars) { + const uint8_t a[] = {1, 2, 3, 4}; + Span<const uint8_t> u = Span(a); + Span<const char> c = AsChars(u); + ASSERT_EQ(static_cast<const void*>(u.data()), + static_cast<const void*>(c.data())); + ASSERT_EQ(u.size(), c.size()); +} + +SPAN_TEST(as_writable_chars) { + uint8_t a[] = {1, 2, 3, 4}; + Span<uint8_t> u = Span(a); + Span<char> c = AsWritableChars(u); + ASSERT_EQ(static_cast<void*>(u.data()), static_cast<void*>(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<int, 4> s4 = arr; + ASSERT_EQ(s4.Length(), 4U); + + // converting to dynamic_range is always ok + { + Span<int> s = s4; + ASSERT_EQ(s.Length(), s4.Length()); + static_cast<void>(s); + } + +// initialization or assignment to static Span that REDUCES size is NOT ok +#ifdef CONFIRM_COMPILATION_ERRORS + { Span<int, 2> s = arr; } + { + Span<int, 2> s2 = s4; + static_cast<void>(s2); + } +#endif + +#if 0 + // even when done dynamically + { + Span<int> s = arr; + auto f = [&]() { + Span<int, 2> s2 = s; + static_cast<void>(s2); + }; + CHECK_THROW(f(), fail_fast); + } +#endif + + // but doing so explicitly is ok + + // you can convert statically + { + Span<int, 2> s2 = {arr, 2}; + static_cast<void>(s2); + } + { + Span<int, 1> s1 = s4.First<1>(); + static_cast<void>(s1); + } + + // ...or dynamically + { + // NB: implicit conversion to Span<int,1> from Span<int> + Span<int, 1> s1 = s4.First(1); + static_cast<void>(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<int, 4> s3 = arr2; } + { + Span<int, 2> s2 = arr2; + Span<int, 4> s4a = s2; + } +#endif + +#if 0 + { + auto f = [&]() { + Span<int, 4> _s4 = {arr2, 2}; + static_cast<void>(_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<int> av = arr2; + auto f = [&]() { + Span<int, 4> _s4 = av; + static_cast<void>(_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<char> s = lat; + auto f_it = s.begin() + 7; + + std::match_results<Span<char>::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<int> 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<Span<int>>::value)); + ASSERT_TRUE((std::is_default_constructible<Span<int, 0>>::value)); + ASSERT_TRUE((!std::is_default_constructible<Span<int, 42>>::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<const Span<const int, 5>, 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<const int> s = Span{arr}; + + { // Split at begin. + constexpr auto splitAt0Result = s.SplitAt(0); + static_assert( + std::is_same_v<Span<const int>, decltype(splitAt0Result.first)>); + static_assert( + std::is_same_v<Span<const int>, 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<Span<const int>, decltype(splitAt5Result.first)>); + static_assert( + std::is_same_v<Span<const int>, 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<Span<const int>, decltype(splitAt3Result.first)>); + static_assert( + std::is_same_v<Span<const int>, 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<Span<const int>, decltype(splitAt0Result.first)>); + static_assert( + std::is_same_v<Span<const int>, 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<Span<const int>, decltype(splitAt5Result.first)>); + static_assert( + std::is_same_v<Span<const int>, decltype(splitAt5Result.second)>); + ASSERT_EQ(s.Elements(), splitAt5Result.first.Elements()); + } + + { + // Split inside. + constexpr auto splitAt3Result = s.SplitAt(3); + static_assert( + std::is_same_v<Span<const int>, decltype(splitAt3Result.first)>); + static_assert( + std::is_same_v<Span<const int>, 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<Span<const int, 0>, decltype(splitAt0Result.first)>); + static_assert( + std::is_same_v<Span<const int, 5>, decltype(splitAt0Result.second)>); + static_assert(splitAt0Result.second.Elements() == s.Elements()); + + // Split at end. + constexpr auto splitAt5Result = s.SplitAt<s.Length()>(); + static_assert(std::is_same_v<Span<const int, s.Length()>, + decltype(splitAt5Result.first)>); + static_assert( + std::is_same_v<Span<const int, 0>, decltype(splitAt5Result.second)>); + static_assert(splitAt5Result.first.Elements() == s.Elements()); + + // Split inside. + constexpr auto splitAt3Result = s.SplitAt<3>(); + static_assert( + std::is_same_v<Span<const int, 3>, decltype(splitAt3Result.first)>); + static_assert( + std::is_same_v<Span<const int, 2>, 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<Span<const int>, 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<const int> + constexpr auto constSpan = span.AsConst(); + + static_assert( + std::is_same_v<const Span<const int, 5>, 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<Span<const int, 5>, 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<decltype(wholeSpan), const Span<const int>>); + 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<decltype(wholeSpan), const Span<const int>>); + // 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<decltype(wholeSpan), const Span<int>>); + 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<decltype(wholeSpan), const Span<const int>>); + 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<int> vec = {1, 2, 3, 4, 5}; + + // from const + { + const auto& constVecRef = vec; + + auto span = Span{constVecRef}; + static_assert(std::is_same_v<decltype(span), Span<const int>>); + } + + // from non-const + { + auto span = Span{vec}; + static_assert(std::is_same_v<decltype(span), Span<int>>); + } +} diff --git a/mfbt/tests/gtest/TestTainting.cpp b/mfbt/tests/gtest/TestTainting.cpp new file mode 100644 index 0000000000..ed9ba1e160 --- /dev/null +++ b/mfbt/tests/gtest/TestTainting.cpp @@ -0,0 +1,484 @@ +/* -*- 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 <math.h> + +#include "mozilla/Assertions.h" +#include "mozilla/Range.h" +#include "mozilla/Tainting.h" +#include "nsTHashtable.h" +#include "nsHashKeys.h" +#include "nsTArray.h" +#include <array> +#include <deque> +#include <forward_list> +#include <list> +#include <map> +#include <set> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +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<int> foo = Tainted<int>(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<int> foo = Tainted<int>(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<int> foo = Tainted<int>(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<int> foo = Tainted<int>(EXPECTED_INT); + Tainted<char> baz = Tainted<char>(EXPECTED_CHAR); + + //------------------------------- + const mozilla::Array<int, 6> mozarrayWithFoo(0, 5, EXPECTED_INT, 15, 20, 25); + const mozilla::Array<int, 5> 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<TestClass, 5> 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<nsCharPtrHashKey> hashtableWithBaz; + hashtableWithBaz.PutEntry(m); + hashtableWithBaz.PutEntry(o); + hashtableWithBaz.PutEntry(z); + hashtableWithBaz.PutEntry(l); + hashtableWithBaz.PutEntry(a); + nsTHashtable<nsCharPtrHashKey> 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<int> nsTArrayWithFoo = {0, 5, EXPECTED_INT, 15, 20, 25}; + const nsTArray<int> 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<int, 6> arrayWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; + const std::array<int, 5> 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<int> dequeWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; + const std::deque<int> 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<int> forwardWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; + const std::forward_list<int> 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<int> listWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; + const std::list<int> 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<std::string, int> mapWithFoo{{ + {"zero", 0}, + {"five", 5}, + {"ten", EXPECTED_INT}, + {"fifteen", 15}, + {"twenty", 20}, + {"twenty-five", 25}, + }}; + const std::map<std::string, int> 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<int> setWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; + const std::set<int> 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<std::string, int> unordermapWithFoo = { + {"zero", 0}, {"five", 5}, {"ten", EXPECTED_INT}, + {"fifteen", 15}, {"twenty", 20}, {"twenty-five", 25}, + }; + const std::unordered_map<std::string, int> 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<int> unorderedsetWithFoo{0, 5, EXPECTED_INT, + 15, 20, 25}; + const std::unordered_set<int> 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<int> vectorWithFoo{0, 5, EXPECTED_INT, 15, 20, 25}; + const std::vector<int> 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<int> foo = Tainted<int>(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..5ded0f4391 --- /dev/null +++ b/mfbt/tests/gtest/moz.build @@ -0,0 +1,34 @@ +# -*- 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" + +REQUIRES_UNIFIED_BUILD = True |