summaryrefslogtreecommitdiffstats
path: root/mfbt/tests/gtest
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /mfbt/tests/gtest
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mfbt/tests/gtest')
-rw-r--r--mfbt/tests/gtest/TestAlgorithm.cpp191
-rw-r--r--mfbt/tests/gtest/TestBuffer.cpp96
-rw-r--r--mfbt/tests/gtest/TestInitializedOnce.cpp200
-rw-r--r--mfbt/tests/gtest/TestLinkedList.cpp78
-rw-r--r--mfbt/tests/gtest/TestMainThreadWeakPtr.cpp42
-rw-r--r--mfbt/tests/gtest/TestMozDbg.cpp170
-rw-r--r--mfbt/tests/gtest/TestResultExtensions.cpp579
-rw-r--r--mfbt/tests/gtest/TestReverseIterator.cpp104
-rw-r--r--mfbt/tests/gtest/TestSpan.cpp2355
-rw-r--r--mfbt/tests/gtest/TestTainting.cpp485
-rw-r--r--mfbt/tests/gtest/moz.build32
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..fb7db0d158
--- /dev/null
+++ b/mfbt/tests/gtest/TestSpan.cpp
@@ -0,0 +1,2355 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// Adapted from
+// https://github.com/Microsoft/GSL/blob/3819df6e378ffccf0e29465afe99c3b324c2aa70/tests/Span_tests.cpp
+
+#include "gtest/gtest.h"
+
+#include "mozilla/Array.h"
+#include "mozilla/Span.h"
+
+#include "nsString.h"
+#include "nsTArray.h"
+#include "mozilla/Range.h"
+
+#include <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, 4);
+ ASSERT_EQ(first - first, 0);
+ ASSERT_EQ(beyond - beyond, 0);
+
+ ++it;
+ ASSERT_EQ(it - first, 1);
+ ASSERT_EQ(*it, 2);
+ *it = 22;
+ ASSERT_EQ(*it, 22);
+ ASSERT_EQ(beyond - it, 3);
+
+ it = first;
+ ASSERT_EQ(it, first);
+ while (it != s.end()) {
+ *it = 5;
+ ++it;
+ }
+
+ ASSERT_EQ(it, beyond);
+ ASSERT_EQ(it - beyond, 0);
+
+ for (auto& n : s) {
+ ASSERT_EQ(n, 5);
+ }
+ }
+}
+
+SPAN_TEST(cbegin_cend) {
+#if 0
+ {
+ int a[] = { 1, 2, 3, 4 };
+ Span<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, 4);
+ ASSERT_EQ(first - first, 0);
+ ASSERT_EQ(beyond - beyond, 0);
+
+ ++it;
+ ASSERT_EQ(it - first, 1);
+ ASSERT_EQ(*it, 2);
+ ASSERT_EQ(beyond - it, 3);
+
+ int last = 0;
+ it = first;
+ ASSERT_EQ(it, first);
+ while (it != s.cend()) {
+ ASSERT_EQ(*it, last + 1);
+
+ last = *it;
+ ++it;
+ }
+
+ ASSERT_EQ(it, beyond);
+ ASSERT_EQ(it - beyond, 0);
+ }
+}
+
+SPAN_TEST(rbegin_rend) {
+ {
+ int a[] = {1, 2, 3, 4};
+ Span<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, 4);
+ ASSERT_EQ(first - first, 0);
+ ASSERT_EQ(beyond - beyond, 0);
+
+ ++it;
+ ASSERT_EQ(it - first, 1);
+ ASSERT_EQ(*it, 3);
+ *it = 22;
+ ASSERT_EQ(*it, 22);
+ ASSERT_EQ(beyond - it, 3);
+
+ it = first;
+ ASSERT_EQ(it, first);
+ while (it != s.rend()) {
+ *it = 5;
+ ++it;
+ }
+
+ ASSERT_EQ(it, beyond);
+ ASSERT_EQ(it - beyond, 0);
+
+ for (auto& n : s) {
+ ASSERT_EQ(n, 5);
+ }
+ }
+}
+
+SPAN_TEST(crbegin_crend) {
+ {
+ int a[] = {1, 2, 3, 4};
+ Span<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, 4);
+ ASSERT_EQ(first - first, 0);
+ ASSERT_EQ(beyond - beyond, 0);
+
+ ++it;
+ ASSERT_EQ(it - first, 1);
+ ASSERT_EQ(*it, 3);
+ ASSERT_EQ(beyond - it, 3);
+
+ it = first;
+ ASSERT_EQ(it, first);
+ int last = 5;
+ while (it != s.crend()) {
+ ASSERT_EQ(*it, last - 1);
+ last = *it;
+
+ ++it;
+ }
+
+ ASSERT_EQ(it, beyond);
+ ASSERT_EQ(it - beyond, 0);
+ }
+}
+
+SPAN_TEST(comparison_operators) {
+ {
+ Span<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..0025819c06
--- /dev/null
+++ b/mfbt/tests/gtest/TestTainting.cpp
@@ -0,0 +1,485 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include <math.h>
+
+#include "mozilla/Array.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Range.h"
+#include "mozilla/Tainting.h"
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+#include "nsTArray.h"
+#include <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..0af8d1ea75
--- /dev/null
+++ b/mfbt/tests/gtest/moz.build
@@ -0,0 +1,32 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ "TestBuffer.cpp",
+ "TestLinkedList.cpp",
+ "TestReverseIterator.cpp",
+ "TestSpan.cpp",
+ "TestTainting.cpp",
+]
+
+SOURCES += [
+ "TestAlgorithm.cpp",
+ "TestInitializedOnce.cpp",
+ "TestMainThreadWeakPtr.cpp",
+ "TestResultExtensions.cpp",
+]
+
+if not CONFIG["MOZILLA_OFFICIAL"]:
+ UNIFIED_SOURCES += [
+ # MOZ_DBG is not defined in MOZILLA_OFFICIAL builds.
+ "TestMozDbg.cpp",
+ ]
+
+# LOCAL_INCLUDES += [
+# "../../base",
+# ]
+
+FINAL_LIBRARY = "xul-gtest"