summaryrefslogtreecommitdiffstats
path: root/mfbt/tests/TestMaybe.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mfbt/tests/TestMaybe.cpp')
-rw-r--r--mfbt/tests/TestMaybe.cpp860
1 files changed, 860 insertions, 0 deletions
diff --git a/mfbt/tests/TestMaybe.cpp b/mfbt/tests/TestMaybe.cpp
new file mode 100644
index 000000000..5817ab428
--- /dev/null
+++ b/mfbt/tests/TestMaybe.cpp
@@ -0,0 +1,860 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <utility>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Compiler.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Move.h"
+#include "mozilla/Types.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/UniquePtr.h"
+
+using mozilla::IsSame;
+using mozilla::Maybe;
+using mozilla::Move;
+using mozilla::Nothing;
+using mozilla::Some;
+using mozilla::Swap;
+using mozilla::ToMaybe;
+using mozilla::UniquePtr;
+
+#if MOZ_IS_MSVC
+ template<typename T> struct Identity { typedef T type; };
+# define DECLTYPE(EXPR) Identity<decltype(EXPR)>::type
+#else
+# define DECLTYPE(EXPR) decltype(EXPR)
+#endif
+
+#define RUN_TEST(t) \
+ do { \
+ bool cond = (t()); \
+ if (!cond) \
+ return 1; \
+ cond = AllDestructorsWereCalled(); \
+ MOZ_ASSERT(cond, "Failed to destroy all objects during test: " #t); \
+ if (!cond) \
+ return 1; \
+ } while (false)
+
+enum Status
+{
+ eWasDefaultConstructed,
+ eWasConstructed,
+ eWasCopyConstructed,
+ eWasMoveConstructed,
+ eWasCopyAssigned,
+ eWasMoveAssigned,
+ eWasMovedFrom
+};
+
+static size_t sUndestroyedObjects = 0;
+
+static bool AllDestructorsWereCalled()
+{
+ return sUndestroyedObjects == 0;
+}
+
+struct BasicValue
+{
+ BasicValue()
+ : mStatus(eWasDefaultConstructed)
+ , mTag(0)
+ {
+ ++sUndestroyedObjects;
+ }
+
+ explicit BasicValue(int aTag)
+ : mStatus(eWasConstructed)
+ , mTag(aTag)
+ {
+ ++sUndestroyedObjects;
+ }
+
+ BasicValue(const BasicValue& aOther)
+ : mStatus(eWasCopyConstructed)
+ , mTag(aOther.mTag)
+ {
+ ++sUndestroyedObjects;
+ }
+
+ BasicValue(BasicValue&& aOther)
+ : mStatus(eWasMoveConstructed)
+ , mTag(aOther.mTag)
+ {
+ ++sUndestroyedObjects;
+ aOther.mStatus = eWasMovedFrom;
+ aOther.mTag = 0;
+ }
+
+ ~BasicValue() { --sUndestroyedObjects; }
+
+ BasicValue& operator=(const BasicValue& aOther)
+ {
+ mStatus = eWasCopyAssigned;
+ mTag = aOther.mTag;
+ return *this;
+ }
+
+ BasicValue& operator=(BasicValue&& aOther)
+ {
+ mStatus = eWasMoveAssigned;
+ mTag = aOther.mTag;
+ aOther.mStatus = eWasMovedFrom;
+ aOther.mTag = 0;
+ return *this;
+ }
+
+ bool operator==(const BasicValue& aOther) const
+ {
+ return mTag == aOther.mTag;
+ }
+
+ bool operator<(const BasicValue& aOther) const
+ {
+ return mTag < aOther.mTag;
+ }
+
+ Status GetStatus() const { return mStatus; }
+ void SetTag(int aValue) { mTag = aValue; }
+ int GetTag() const { return mTag; }
+
+private:
+ Status mStatus;
+ int mTag;
+};
+
+struct UncopyableValue
+{
+ UncopyableValue()
+ : mStatus(eWasDefaultConstructed)
+ {
+ ++sUndestroyedObjects;
+ }
+
+ UncopyableValue(UncopyableValue&& aOther)
+ : mStatus(eWasMoveConstructed)
+ {
+ ++sUndestroyedObjects;
+ aOther.mStatus = eWasMovedFrom;
+ }
+
+ ~UncopyableValue() { --sUndestroyedObjects; }
+
+ UncopyableValue& operator=(UncopyableValue&& aOther)
+ {
+ mStatus = eWasMoveAssigned;
+ aOther.mStatus = eWasMovedFrom;
+ return *this;
+ }
+
+ Status GetStatus() { return mStatus; }
+
+private:
+ UncopyableValue(const UncopyableValue& aOther) = delete;
+ UncopyableValue& operator=(const UncopyableValue& aOther) = delete;
+
+ Status mStatus;
+};
+
+struct UnmovableValue
+{
+ UnmovableValue()
+ : mStatus(eWasDefaultConstructed)
+ {
+ ++sUndestroyedObjects;
+ }
+
+ UnmovableValue(const UnmovableValue& aOther)
+ : mStatus(eWasCopyConstructed)
+ {
+ ++sUndestroyedObjects;
+ }
+
+ ~UnmovableValue() { --sUndestroyedObjects; }
+
+ UnmovableValue& operator=(const UnmovableValue& aOther)
+ {
+ mStatus = eWasCopyAssigned;
+ return *this;
+ }
+
+ Status GetStatus() { return mStatus; }
+
+private:
+ UnmovableValue(UnmovableValue&& aOther) = delete;
+ UnmovableValue& operator=(UnmovableValue&& aOther) = delete;
+
+ Status mStatus;
+};
+
+struct UncopyableUnmovableValue
+{
+ UncopyableUnmovableValue()
+ : mStatus(eWasDefaultConstructed)
+ {
+ ++sUndestroyedObjects;
+ }
+
+ explicit UncopyableUnmovableValue(int)
+ : mStatus(eWasConstructed)
+ {
+ ++sUndestroyedObjects;
+ }
+
+ ~UncopyableUnmovableValue() { --sUndestroyedObjects; }
+
+ Status GetStatus() { return mStatus; }
+
+private:
+ UncopyableUnmovableValue(const UncopyableUnmovableValue& aOther) = delete;
+ UncopyableUnmovableValue& operator=(const UncopyableUnmovableValue& aOther) = delete;
+ UncopyableUnmovableValue(UncopyableUnmovableValue&& aOther) = delete;
+ UncopyableUnmovableValue& operator=(UncopyableUnmovableValue&& aOther) = delete;
+
+ Status mStatus;
+};
+
+static bool
+TestBasicFeatures()
+{
+ // Check that a Maybe<T> is initialized to Nothing.
+ Maybe<BasicValue> mayValue;
+ static_assert(IsSame<BasicValue, DECLTYPE(mayValue)::ValueType>::value,
+ "Should have BasicValue ValueType");
+ MOZ_RELEASE_ASSERT(!mayValue);
+ MOZ_RELEASE_ASSERT(!mayValue.isSome());
+ MOZ_RELEASE_ASSERT(mayValue.isNothing());
+
+ // Check that emplace() default constructs and the accessors work.
+ mayValue.emplace();
+ MOZ_RELEASE_ASSERT(mayValue);
+ MOZ_RELEASE_ASSERT(mayValue.isSome());
+ MOZ_RELEASE_ASSERT(!mayValue.isNothing());
+ MOZ_RELEASE_ASSERT(*mayValue == BasicValue());
+ MOZ_RELEASE_ASSERT(mayValue.value() == BasicValue());
+ static_assert(IsSame<BasicValue, DECLTYPE(mayValue.value())>::value,
+ "value() should return a BasicValue");
+ MOZ_RELEASE_ASSERT(mayValue.ref() == BasicValue());
+ static_assert(IsSame<BasicValue&, DECLTYPE(mayValue.ref())>::value,
+ "ref() should return a BasicValue&");
+ MOZ_RELEASE_ASSERT(mayValue.ptr() != nullptr);
+ static_assert(IsSame<BasicValue*, DECLTYPE(mayValue.ptr())>::value,
+ "ptr() should return a BasicValue*");
+ MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasDefaultConstructed);
+
+ // Check that reset() works.
+ mayValue.reset();
+ MOZ_RELEASE_ASSERT(!mayValue);
+ MOZ_RELEASE_ASSERT(!mayValue.isSome());
+ MOZ_RELEASE_ASSERT(mayValue.isNothing());
+
+ // Check that emplace(T1) calls the correct constructor.
+ mayValue.emplace(1);
+ MOZ_RELEASE_ASSERT(mayValue);
+ MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasConstructed);
+ MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1);
+ mayValue.reset();
+ MOZ_RELEASE_ASSERT(!mayValue);
+
+ // Check that Some() and Nothing() work.
+ mayValue = Some(BasicValue(2));
+ MOZ_RELEASE_ASSERT(mayValue);
+ MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasMoveConstructed);
+ MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
+ mayValue = Nothing();
+ MOZ_RELEASE_ASSERT(!mayValue);
+
+ // Check that the accessors work through a const ref.
+ mayValue.emplace();
+ const Maybe<BasicValue>& mayValueCRef = mayValue;
+ MOZ_RELEASE_ASSERT(mayValueCRef);
+ MOZ_RELEASE_ASSERT(mayValueCRef.isSome());
+ MOZ_RELEASE_ASSERT(!mayValueCRef.isNothing());
+ MOZ_RELEASE_ASSERT(*mayValueCRef == BasicValue());
+ MOZ_RELEASE_ASSERT(mayValueCRef.value() == BasicValue());
+ static_assert(IsSame<BasicValue, DECLTYPE(mayValueCRef.value())>::value,
+ "value() should return a BasicValue");
+ MOZ_RELEASE_ASSERT(mayValueCRef.ref() == BasicValue());
+ static_assert(IsSame<const BasicValue&,
+ DECLTYPE(mayValueCRef.ref())>::value,
+ "ref() should return a const BasicValue&");
+ MOZ_RELEASE_ASSERT(mayValueCRef.ptr() != nullptr);
+ static_assert(IsSame<const BasicValue*,
+ DECLTYPE(mayValueCRef.ptr())>::value,
+ "ptr() should return a const BasicValue*");
+ MOZ_RELEASE_ASSERT(mayValueCRef->GetStatus() == eWasDefaultConstructed);
+ mayValue.reset();
+
+ return true;
+}
+
+static bool
+TestCopyAndMove()
+{
+ // Check that we get moves when possible for types that can support both moves
+ // and copies.
+ Maybe<BasicValue> mayBasicValue = Some(BasicValue(1));
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveConstructed);
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 1);
+ mayBasicValue = Some(BasicValue(2));
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveAssigned);
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 2);
+ mayBasicValue.reset();
+ mayBasicValue.emplace(BasicValue(3));
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveConstructed);
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 3);
+
+ // Check that we get copies when moves aren't possible.
+ Maybe<BasicValue> mayBasicValue2 = Some(*mayBasicValue);
+ MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyConstructed);
+ MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 3);
+ mayBasicValue->SetTag(4);
+ mayBasicValue2 = mayBasicValue;
+ // This test should work again when we fix bug 1052940.
+ //MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyAssigned);
+ MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 4);
+ mayBasicValue->SetTag(5);
+ mayBasicValue2.reset();
+ mayBasicValue2.emplace(*mayBasicValue);
+ MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyConstructed);
+ MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 5);
+
+ // Check that Move() works. (Another sanity check for move support.)
+ Maybe<BasicValue> mayBasicValue3 = Some(Move(*mayBasicValue));
+ MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMoveConstructed);
+ MOZ_RELEASE_ASSERT(mayBasicValue3->GetTag() == 5);
+ MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMovedFrom);
+ mayBasicValue2->SetTag(6);
+ mayBasicValue3 = Some(Move(*mayBasicValue2));
+ MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMoveAssigned);
+ MOZ_RELEASE_ASSERT(mayBasicValue3->GetTag() == 6);
+ MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasMovedFrom);
+ Maybe<BasicValue> mayBasicValue4;
+ mayBasicValue4.emplace(Move(*mayBasicValue3));
+ MOZ_RELEASE_ASSERT(mayBasicValue4->GetStatus() == eWasMoveConstructed);
+ MOZ_RELEASE_ASSERT(mayBasicValue4->GetTag() == 6);
+ MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMovedFrom);
+
+ // Check that we always get copies for types that don't support moves.
+ // XXX(seth): These tests fail but probably shouldn't. For now we'll just
+ // consider using Maybe with types that allow copies but have deleted or
+ // private move constructors, or which do not support copy assignment, to
+ // be supported only to the extent that we need for existing code to work.
+ // These tests should work again when we fix bug 1052940.
+ /*
+ Maybe<UnmovableValue> mayUnmovableValue = Some(UnmovableValue());
+ MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyConstructed);
+ mayUnmovableValue = Some(UnmovableValue());
+ MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyAssigned);
+ mayUnmovableValue.reset();
+ mayUnmovableValue.emplace(UnmovableValue());
+ MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyConstructed);
+ */
+
+ // Check that types that only support moves, but not copies, work.
+ Maybe<UncopyableValue> mayUncopyableValue = Some(UncopyableValue());
+ MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() == eWasMoveConstructed);
+ mayUncopyableValue = Some(UncopyableValue());
+ MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() == eWasMoveAssigned);
+ mayUncopyableValue.reset();
+ mayUncopyableValue.emplace(UncopyableValue());
+ MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() == eWasMoveConstructed);
+
+ // Check that types that support neither moves or copies work.
+ Maybe<UncopyableUnmovableValue> mayUncopyableUnmovableValue;
+ mayUncopyableUnmovableValue.emplace();
+ MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValue->GetStatus() == eWasDefaultConstructed);
+ mayUncopyableUnmovableValue.reset();
+ mayUncopyableUnmovableValue.emplace(0);
+ MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValue->GetStatus() == eWasConstructed);
+
+ return true;
+}
+
+static BasicValue* sStaticBasicValue = nullptr;
+
+static BasicValue
+MakeBasicValue()
+{
+ return BasicValue(9);
+}
+
+static BasicValue&
+MakeBasicValueRef()
+{
+ return *sStaticBasicValue;
+}
+
+static BasicValue*
+MakeBasicValuePtr()
+{
+ return sStaticBasicValue;
+}
+
+static bool
+TestFunctionalAccessors()
+{
+ BasicValue value(9);
+ sStaticBasicValue = new BasicValue(9);
+
+ // Check that the 'some' case of functional accessors works.
+ Maybe<BasicValue> someValue = Some(BasicValue(3));
+ MOZ_RELEASE_ASSERT(someValue.valueOr(value) == BasicValue(3));
+ static_assert(IsSame<BasicValue,
+ DECLTYPE(someValue.valueOr(value))>::value,
+ "valueOr should return a BasicValue");
+ MOZ_RELEASE_ASSERT(someValue.valueOrFrom(&MakeBasicValue) == BasicValue(3));
+ static_assert(IsSame<BasicValue,
+ DECLTYPE(someValue.valueOrFrom(&MakeBasicValue))>::value,
+ "valueOrFrom should return a BasicValue");
+ MOZ_RELEASE_ASSERT(someValue.ptrOr(&value) != &value);
+ static_assert(IsSame<BasicValue*,
+ DECLTYPE(someValue.ptrOr(&value))>::value,
+ "ptrOr should return a BasicValue*");
+ MOZ_RELEASE_ASSERT(*someValue.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(3));
+ static_assert(IsSame<BasicValue*,
+ DECLTYPE(someValue.ptrOrFrom(&MakeBasicValuePtr))>::value,
+ "ptrOrFrom should return a BasicValue*");
+ MOZ_RELEASE_ASSERT(someValue.refOr(value) == BasicValue(3));
+ static_assert(IsSame<BasicValue&,
+ DECLTYPE(someValue.refOr(value))>::value,
+ "refOr should return a BasicValue&");
+ MOZ_RELEASE_ASSERT(someValue.refOrFrom(&MakeBasicValueRef) == BasicValue(3));
+ static_assert(IsSame<BasicValue&,
+ DECLTYPE(someValue.refOrFrom(&MakeBasicValueRef))>::value,
+ "refOrFrom should return a BasicValue&");
+
+ // Check that the 'some' case works through a const reference.
+ const Maybe<BasicValue>& someValueCRef = someValue;
+ MOZ_RELEASE_ASSERT(someValueCRef.valueOr(value) == BasicValue(3));
+ static_assert(IsSame<BasicValue,
+ DECLTYPE(someValueCRef.valueOr(value))>::value,
+ "valueOr should return a BasicValue");
+ MOZ_RELEASE_ASSERT(someValueCRef.valueOrFrom(&MakeBasicValue) == BasicValue(3));
+ static_assert(IsSame<BasicValue,
+ DECLTYPE(someValueCRef.valueOrFrom(&MakeBasicValue))>::value,
+ "valueOrFrom should return a BasicValue");
+ MOZ_RELEASE_ASSERT(someValueCRef.ptrOr(&value) != &value);
+ static_assert(IsSame<const BasicValue*,
+ DECLTYPE(someValueCRef.ptrOr(&value))>::value,
+ "ptrOr should return a const BasicValue*");
+ MOZ_RELEASE_ASSERT(*someValueCRef.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(3));
+ static_assert(IsSame<const BasicValue*,
+ DECLTYPE(someValueCRef.ptrOrFrom(&MakeBasicValuePtr))>::value,
+ "ptrOrFrom should return a const BasicValue*");
+ MOZ_RELEASE_ASSERT(someValueCRef.refOr(value) == BasicValue(3));
+ static_assert(IsSame<const BasicValue&,
+ DECLTYPE(someValueCRef.refOr(value))>::value,
+ "refOr should return a const BasicValue&");
+ MOZ_RELEASE_ASSERT(someValueCRef.refOrFrom(&MakeBasicValueRef) == BasicValue(3));
+ static_assert(IsSame<const BasicValue&,
+ DECLTYPE(someValueCRef.refOrFrom(&MakeBasicValueRef))>::value,
+ "refOrFrom should return a const BasicValue&");
+
+ // Check that the 'none' case of functional accessors works.
+ Maybe<BasicValue> noneValue;
+ MOZ_RELEASE_ASSERT(noneValue.valueOr(value) == BasicValue(9));
+ static_assert(IsSame<BasicValue,
+ DECLTYPE(noneValue.valueOr(value))>::value,
+ "valueOr should return a BasicValue");
+ MOZ_RELEASE_ASSERT(noneValue.valueOrFrom(&MakeBasicValue) == BasicValue(9));
+ static_assert(IsSame<BasicValue,
+ DECLTYPE(noneValue.valueOrFrom(&MakeBasicValue))>::value,
+ "valueOrFrom should return a BasicValue");
+ MOZ_RELEASE_ASSERT(noneValue.ptrOr(&value) == &value);
+ static_assert(IsSame<BasicValue*,
+ DECLTYPE(noneValue.ptrOr(&value))>::value,
+ "ptrOr should return a BasicValue*");
+ MOZ_RELEASE_ASSERT(*noneValue.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(9));
+ static_assert(IsSame<BasicValue*,
+ DECLTYPE(noneValue.ptrOrFrom(&MakeBasicValuePtr))>::value,
+ "ptrOrFrom should return a BasicValue*");
+ MOZ_RELEASE_ASSERT(noneValue.refOr(value) == BasicValue(9));
+ static_assert(IsSame<BasicValue&,
+ DECLTYPE(noneValue.refOr(value))>::value,
+ "refOr should return a BasicValue&");
+ MOZ_RELEASE_ASSERT(noneValue.refOrFrom(&MakeBasicValueRef) == BasicValue(9));
+ static_assert(IsSame<BasicValue&,
+ DECLTYPE(noneValue.refOrFrom(&MakeBasicValueRef))>::value,
+ "refOrFrom should return a BasicValue&");
+
+ // Check that the 'none' case works through a const reference.
+ const Maybe<BasicValue>& noneValueCRef = noneValue;
+ MOZ_RELEASE_ASSERT(noneValueCRef.valueOr(value) == BasicValue(9));
+ static_assert(IsSame<BasicValue,
+ DECLTYPE(noneValueCRef.valueOr(value))>::value,
+ "valueOr should return a BasicValue");
+ MOZ_RELEASE_ASSERT(noneValueCRef.valueOrFrom(&MakeBasicValue) == BasicValue(9));
+ static_assert(IsSame<BasicValue,
+ DECLTYPE(noneValueCRef.valueOrFrom(&MakeBasicValue))>::value,
+ "valueOrFrom should return a BasicValue");
+ MOZ_RELEASE_ASSERT(noneValueCRef.ptrOr(&value) == &value);
+ static_assert(IsSame<const BasicValue*,
+ DECLTYPE(noneValueCRef.ptrOr(&value))>::value,
+ "ptrOr should return a const BasicValue*");
+ MOZ_RELEASE_ASSERT(*noneValueCRef.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(9));
+ static_assert(IsSame<const BasicValue*,
+ DECLTYPE(noneValueCRef.ptrOrFrom(&MakeBasicValuePtr))>::value,
+ "ptrOrFrom should return a const BasicValue*");
+ MOZ_RELEASE_ASSERT(noneValueCRef.refOr(value) == BasicValue(9));
+ static_assert(IsSame<const BasicValue&,
+ DECLTYPE(noneValueCRef.refOr(value))>::value,
+ "refOr should return a const BasicValue&");
+ MOZ_RELEASE_ASSERT(noneValueCRef.refOrFrom(&MakeBasicValueRef) == BasicValue(9));
+ static_assert(IsSame<const BasicValue&,
+ DECLTYPE(noneValueCRef.refOrFrom(&MakeBasicValueRef))>::value,
+ "refOrFrom should return a const BasicValue&");
+
+ // Clean up so the undestroyed objects count stays accurate.
+ delete sStaticBasicValue;
+ sStaticBasicValue = nullptr;
+
+ return true;
+}
+
+static bool gFunctionWasApplied = false;
+
+static void
+IncrementTag(BasicValue& aValue)
+{
+ gFunctionWasApplied = true;
+ aValue.SetTag(aValue.GetTag() + 1);
+}
+
+static void
+AccessValue(const BasicValue&)
+{
+ gFunctionWasApplied = true;
+}
+
+struct IncrementTagFunctor
+{
+ IncrementTagFunctor() : mBy(1) { }
+
+ void operator()(BasicValue& aValue)
+ {
+ aValue.SetTag(aValue.GetTag() + mBy.GetTag());
+ }
+
+ BasicValue mBy;
+};
+
+static bool
+TestApply()
+{
+ // Check that apply handles the 'Nothing' case.
+ gFunctionWasApplied = false;
+ Maybe<BasicValue> mayValue;
+ mayValue.apply(&IncrementTag);
+ mayValue.apply(&AccessValue);
+ MOZ_RELEASE_ASSERT(!gFunctionWasApplied);
+
+ // Check that apply handles the 'Some' case.
+ mayValue = Some(BasicValue(1));
+ mayValue.apply(&IncrementTag);
+ MOZ_RELEASE_ASSERT(gFunctionWasApplied);
+ MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
+ gFunctionWasApplied = false;
+ mayValue.apply(&AccessValue);
+ MOZ_RELEASE_ASSERT(gFunctionWasApplied);
+
+ // Check that apply works with a const reference.
+ const Maybe<BasicValue>& mayValueCRef = mayValue;
+ gFunctionWasApplied = false;
+ mayValueCRef.apply(&AccessValue);
+ MOZ_RELEASE_ASSERT(gFunctionWasApplied);
+
+ // Check that apply works with functors.
+ IncrementTagFunctor tagIncrementer;
+ MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed);
+ mayValue = Some(BasicValue(1));
+ mayValue.apply(tagIncrementer);
+ MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
+ MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed);
+
+ // Check that apply works with lambda expressions.
+ int32_t two = 2;
+ gFunctionWasApplied = false;
+ mayValue = Some(BasicValue(2));
+ mayValue.apply([&](BasicValue& aVal) { aVal.SetTag(aVal.GetTag() * two); });
+ MOZ_RELEASE_ASSERT(mayValue->GetTag() == 4);
+ mayValue.apply([=](BasicValue& aVal) { aVal.SetTag(aVal.GetTag() * two); });
+ MOZ_RELEASE_ASSERT(mayValue->GetTag() == 8);
+ mayValueCRef.apply([&](const BasicValue& aVal) { gFunctionWasApplied = true; });
+ MOZ_RELEASE_ASSERT(gFunctionWasApplied == true);
+
+ return true;
+}
+
+static int
+TimesTwo(const BasicValue& aValue)
+{
+ return aValue.GetTag() * 2;
+}
+
+static int
+TimesTwoAndResetOriginal(BasicValue& aValue)
+{
+ int tag = aValue.GetTag();
+ aValue.SetTag(1);
+ return tag * 2;
+}
+
+struct MultiplyTagFunctor
+{
+ MultiplyTagFunctor() : mBy(2) { }
+
+ int operator()(BasicValue& aValue)
+ {
+ return aValue.GetTag() * mBy.GetTag();
+ }
+
+ BasicValue mBy;
+};
+
+static bool
+TestMap()
+{
+ // Check that map handles the 'Nothing' case.
+ Maybe<BasicValue> mayValue;
+ MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwo) == Nothing());
+ static_assert(IsSame<Maybe<int>,
+ DECLTYPE(mayValue.map(&TimesTwo))>::value,
+ "map(TimesTwo) should return a Maybe<int>");
+ MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwoAndResetOriginal) == Nothing());
+
+ // Check that map handles the 'Some' case.
+ mayValue = Some(BasicValue(2));
+ MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwo) == Some(4));
+ MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwoAndResetOriginal) == Some(4));
+ MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1);
+ mayValue = Some(BasicValue(2));
+
+ // Check that map works with a const reference.
+ mayValue->SetTag(2);
+ const Maybe<BasicValue>& mayValueCRef = mayValue;
+ MOZ_RELEASE_ASSERT(mayValueCRef.map(&TimesTwo) == Some(4));
+ static_assert(IsSame<Maybe<int>,
+ DECLTYPE(mayValueCRef.map(&TimesTwo))>::value,
+ "map(TimesTwo) should return a Maybe<int>");
+
+ // Check that map works with functors.
+ MultiplyTagFunctor tagMultiplier;
+ MOZ_RELEASE_ASSERT(tagMultiplier.mBy.GetStatus() == eWasConstructed);
+ MOZ_RELEASE_ASSERT(mayValue.map(tagMultiplier) == Some(4));
+ MOZ_RELEASE_ASSERT(tagMultiplier.mBy.GetStatus() == eWasConstructed);
+
+ // Check that map works with lambda expressions.
+ int two = 2;
+ mayValue = Some(BasicValue(2));
+ Maybe<int> mappedValue =
+ mayValue.map([&](const BasicValue& aVal) {
+ return aVal.GetTag() * two;
+ });
+ MOZ_RELEASE_ASSERT(mappedValue == Some(4));
+ mappedValue =
+ mayValue.map([=](const BasicValue& aVal) {
+ return aVal.GetTag() * two;
+ });
+ MOZ_RELEASE_ASSERT(mappedValue == Some(4));
+ mappedValue =
+ mayValueCRef.map([&](const BasicValue& aVal) {
+ return aVal.GetTag() * two;
+ });
+ MOZ_RELEASE_ASSERT(mappedValue == Some(4));
+
+ return true;
+}
+
+static bool
+TestToMaybe()
+{
+ BasicValue value(1);
+ BasicValue* nullPointer = nullptr;
+
+ // Check that a non-null pointer translates into a Some value.
+ Maybe<BasicValue> mayValue = ToMaybe(&value);
+ static_assert(IsSame<Maybe<BasicValue>, DECLTYPE(ToMaybe(&value))>::value,
+ "ToMaybe should return a Maybe<BasicValue>");
+ MOZ_RELEASE_ASSERT(mayValue.isSome());
+ MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1);
+ MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasCopyConstructed);
+ MOZ_RELEASE_ASSERT(value.GetStatus() != eWasMovedFrom);
+
+ // Check that a null pointer translates into a Nothing value.
+ mayValue = ToMaybe(nullPointer);
+ static_assert(IsSame<Maybe<BasicValue>, DECLTYPE(ToMaybe(nullPointer))>::value,
+ "ToMaybe should return a Maybe<BasicValue>");
+ MOZ_RELEASE_ASSERT(mayValue.isNothing());
+
+ return true;
+}
+
+static bool
+TestComparisonOperators()
+{
+ Maybe<BasicValue> nothingValue = Nothing();
+ Maybe<BasicValue> anotherNothingValue = Nothing();
+ Maybe<BasicValue> oneValue = Some(BasicValue(1));
+ Maybe<BasicValue> anotherOneValue = Some(BasicValue(1));
+ Maybe<BasicValue> twoValue = Some(BasicValue(2));
+
+ // Check equality.
+ MOZ_RELEASE_ASSERT(nothingValue == anotherNothingValue);
+ MOZ_RELEASE_ASSERT(oneValue == anotherOneValue);
+
+ // Check inequality.
+ MOZ_RELEASE_ASSERT(nothingValue != oneValue);
+ MOZ_RELEASE_ASSERT(oneValue != nothingValue);
+ MOZ_RELEASE_ASSERT(oneValue != twoValue);
+
+ // Check '<'.
+ MOZ_RELEASE_ASSERT(nothingValue < oneValue);
+ MOZ_RELEASE_ASSERT(oneValue < twoValue);
+
+ // Check '<='.
+ MOZ_RELEASE_ASSERT(nothingValue <= anotherNothingValue);
+ MOZ_RELEASE_ASSERT(nothingValue <= oneValue);
+ MOZ_RELEASE_ASSERT(oneValue <= oneValue);
+ MOZ_RELEASE_ASSERT(oneValue <= twoValue);
+
+ // Check '>'.
+ MOZ_RELEASE_ASSERT(oneValue > nothingValue);
+ MOZ_RELEASE_ASSERT(twoValue > oneValue);
+
+ // Check '>='.
+ MOZ_RELEASE_ASSERT(nothingValue >= anotherNothingValue);
+ MOZ_RELEASE_ASSERT(oneValue >= nothingValue);
+ MOZ_RELEASE_ASSERT(oneValue >= oneValue);
+ MOZ_RELEASE_ASSERT(twoValue >= oneValue);
+
+ return true;
+}
+
+// Check that Maybe<> can wrap a superclass that happens to also be a concrete
+// class (i.e. that the compiler doesn't warn when we invoke the superclass's
+// destructor explicitly in |reset()|.
+class MySuperClass {
+ virtual void VirtualMethod() { /* do nothing */ }
+};
+
+class MyDerivedClass : public MySuperClass {
+ void VirtualMethod() override { /* do nothing */ }
+};
+
+static bool
+TestVirtualFunction() {
+ Maybe<MySuperClass> super;
+ super.emplace();
+ super.reset();
+
+ Maybe<MyDerivedClass> derived;
+ derived.emplace();
+ derived.reset();
+
+ // If this compiles successfully, we've passed.
+ return true;
+}
+
+static Maybe<int*>
+ReturnSomeNullptr()
+{
+ return Some(nullptr);
+}
+
+struct D
+{
+ explicit D(Maybe<int*>) {}
+};
+
+static bool
+TestSomeNullptrConversion()
+{
+ Maybe<int*> m1 = Some(nullptr);
+ MOZ_RELEASE_ASSERT(m1.isSome());
+ MOZ_RELEASE_ASSERT(m1);
+ MOZ_RELEASE_ASSERT(!*m1);
+
+ auto m2 = ReturnSomeNullptr();
+ MOZ_RELEASE_ASSERT(m2.isSome());
+ MOZ_RELEASE_ASSERT(m2);
+ MOZ_RELEASE_ASSERT(!*m2);
+
+ Maybe<decltype(nullptr)> m3 = Some(nullptr);
+ MOZ_RELEASE_ASSERT(m3.isSome());
+ MOZ_RELEASE_ASSERT(m3);
+ MOZ_RELEASE_ASSERT(*m3 == nullptr);
+
+ D d(Some(nullptr));
+
+ return true;
+}
+
+struct Base {};
+struct Derived : Base {};
+
+static Maybe<Base*>
+ReturnDerivedPointer()
+{
+ Derived* d = nullptr;
+ return Some(d);
+}
+
+struct ExplicitConstructorBasePointer
+{
+ explicit ExplicitConstructorBasePointer(Maybe<Base*>) {}
+};
+
+static bool
+TestSomePointerConversion()
+{
+ Base base;
+ Derived derived;
+
+ Maybe<Base*> m1 = Some(&derived);
+ MOZ_RELEASE_ASSERT(m1.isSome());
+ MOZ_RELEASE_ASSERT(m1);
+ MOZ_RELEASE_ASSERT(*m1 == &derived);
+
+ auto m2 = ReturnDerivedPointer();
+ MOZ_RELEASE_ASSERT(m2.isSome());
+ MOZ_RELEASE_ASSERT(m2);
+ MOZ_RELEASE_ASSERT(*m2 == nullptr);
+
+ Maybe<Base*> m3 = Some(&base);
+ MOZ_RELEASE_ASSERT(m3.isSome());
+ MOZ_RELEASE_ASSERT(m3);
+ MOZ_RELEASE_ASSERT(*m3 == &base);
+
+ auto s1 = Some(&derived);
+ Maybe<Base*> c1(s1);
+ MOZ_RELEASE_ASSERT(c1.isSome());
+ MOZ_RELEASE_ASSERT(c1);
+ MOZ_RELEASE_ASSERT(*c1 == &derived);
+
+ ExplicitConstructorBasePointer ecbp(Some(&derived));
+
+ return true;
+}
+
+int
+main()
+{
+ RUN_TEST(TestBasicFeatures);
+ RUN_TEST(TestCopyAndMove);
+ RUN_TEST(TestFunctionalAccessors);
+ RUN_TEST(TestApply);
+ RUN_TEST(TestMap);
+ RUN_TEST(TestToMaybe);
+ RUN_TEST(TestComparisonOperators);
+ RUN_TEST(TestVirtualFunction);
+ RUN_TEST(TestSomeNullptrConversion);
+ RUN_TEST(TestSomePointerConversion);
+
+ return 0;
+}