/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/quota/QuotaCommon.h" #include "gtest/gtest.h" #include #include #include #include #include #include #include #include #include #include #include "ErrorList.h" #include "mozilla/Assertions.h" #include "mozilla/Result.h" #include "mozilla/ResultExtensions.h" #include "mozilla/ResultVariant.h" #include "mozilla/Unused.h" #include "mozilla/fallible.h" #include "mozilla/dom/quota/QuotaTestParent.h" #include "mozilla/dom/quota/ResultExtensions.h" #include "nsCOMPtr.h" #include "nsDirectoryServiceDefs.h" #include "nsDirectoryServiceUtils.h" #include "nsIFile.h" #include "nsLiteralString.h" #include "nsString.h" #include "nsStringFwd.h" #include "nsTLiteralString.h" class nsISupports; namespace mozilla::dom::quota { namespace { void CheckUnknownFileEntry(nsIFile& aBase, const nsAString& aName, const bool aWarnIfFile, const bool aWarnIfDir) { nsCOMPtr file; nsresult rv = aBase.Clone(getter_AddRefs(file)); ASSERT_EQ(rv, NS_OK); rv = file->Append(aName); ASSERT_EQ(rv, NS_OK); rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0600); ASSERT_EQ(rv, NS_OK); auto okOrErr = WARN_IF_FILE_IS_UNKNOWN(*file); ASSERT_TRUE(okOrErr.isOk()); #ifdef DEBUG EXPECT_TRUE(okOrErr.inspect() == aWarnIfFile); #else EXPECT_TRUE(okOrErr.inspect() == false); #endif rv = file->Remove(false); ASSERT_EQ(rv, NS_OK); rv = file->Create(nsIFile::DIRECTORY_TYPE, 0700); ASSERT_EQ(rv, NS_OK); okOrErr = WARN_IF_FILE_IS_UNKNOWN(*file); ASSERT_TRUE(okOrErr.isOk()); #ifdef DEBUG EXPECT_TRUE(okOrErr.inspect() == aWarnIfDir); #else EXPECT_TRUE(okOrErr.inspect() == false); #endif rv = file->Remove(false); ASSERT_EQ(rv, NS_OK); } } // namespace TEST(QuotaCommon_WarnIfFileIsUnknown, Basics) { nsCOMPtr base; nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(base)); ASSERT_EQ(rv, NS_OK); rv = base->Append(u"mozquotatests"_ns); ASSERT_EQ(rv, NS_OK); base->Remove(true); rv = base->Create(nsIFile::DIRECTORY_TYPE, 0700); ASSERT_EQ(rv, NS_OK); CheckUnknownFileEntry(*base, u"foo.bar"_ns, true, true); CheckUnknownFileEntry(*base, u".DS_Store"_ns, false, true); CheckUnknownFileEntry(*base, u".desktop"_ns, false, true); CheckUnknownFileEntry(*base, u"desktop.ini"_ns, false, true); CheckUnknownFileEntry(*base, u"DESKTOP.INI"_ns, false, true); CheckUnknownFileEntry(*base, u"thumbs.db"_ns, false, true); CheckUnknownFileEntry(*base, u"THUMBS.DB"_ns, false, true); CheckUnknownFileEntry(*base, u".xyz"_ns, false, true); rv = base->Remove(true); ASSERT_EQ(rv, NS_OK); } mozilla::ipc::IPCResult QuotaTestParent::RecvTry_Success_CustomErr_QmIpcFail( bool* aTryDidNotReturn) { QM_TRY(MOZ_TO_RESULT(NS_OK), QM_IPC_FAIL(this)); *aTryDidNotReturn = true; return IPC_OK(); } mozilla::ipc::IPCResult QuotaTestParent::RecvTry_Success_CustomErr_IpcFail( bool* aTryDidNotReturn) { QM_TRY(MOZ_TO_RESULT(NS_OK), IPC_FAIL(this, "Custom why")); *aTryDidNotReturn = true; return IPC_OK(); } mozilla::ipc::IPCResult QuotaTestParent::RecvTryInspect_Success_CustomErr_QmIpcFail( bool* aTryDidNotReturn) { QM_TRY_INSPECT(const auto& x, (mozilla::Result{42}), QM_IPC_FAIL(this)); Unused << x; *aTryDidNotReturn = true; return IPC_OK(); } mozilla::ipc::IPCResult QuotaTestParent::RecvTryInspect_Success_CustomErr_IpcFail( bool* aTryDidNotReturn) { QM_TRY_INSPECT(const auto& x, (mozilla::Result{42}), IPC_FAIL(this, "Custom why")); Unused << x; *aTryDidNotReturn = true; return IPC_OK(); } #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wunreachable-code" #endif TEST(QuotaCommon_Try, Success) { bool tryDidNotReturn = false; nsresult rv = [&tryDidNotReturn]() -> nsresult { QM_TRY(MOZ_TO_RESULT(NS_OK)); tryDidNotReturn = true; return NS_OK; }(); EXPECT_TRUE(tryDidNotReturn); EXPECT_EQ(rv, NS_OK); } TEST(QuotaCommon_Try, Success_CustomErr_QmIpcFail) { auto foo = MakeRefPtr(); bool tryDidNotReturn = false; auto res = foo->RecvTry_Success_CustomErr_QmIpcFail(&tryDidNotReturn); EXPECT_TRUE(tryDidNotReturn); EXPECT_TRUE(res); } TEST(QuotaCommon_Try, Success_CustomErr_IpcFail) { auto foo = MakeRefPtr(); bool tryDidNotReturn = false; auto res = foo->RecvTry_Success_CustomErr_IpcFail(&tryDidNotReturn); EXPECT_TRUE(tryDidNotReturn); EXPECT_TRUE(res); } #ifdef DEBUG TEST(QuotaCommon_Try, Success_CustomErr_AssertUnreachable) { bool tryDidNotReturn = false; nsresult rv = [&tryDidNotReturn]() -> nsresult { QM_TRY(MOZ_TO_RESULT(NS_OK), QM_ASSERT_UNREACHABLE); tryDidNotReturn = true; return NS_OK; }(); EXPECT_TRUE(tryDidNotReturn); EXPECT_EQ(rv, NS_OK); } TEST(QuotaCommon_Try, Success_NoErr_AssertUnreachable) { bool tryDidNotReturn = false; [&tryDidNotReturn]() -> void { QM_TRY(MOZ_TO_RESULT(NS_OK), QM_ASSERT_UNREACHABLE_VOID); tryDidNotReturn = true; }(); EXPECT_TRUE(tryDidNotReturn); } #else # if defined(QM_ASSERT_UNREACHABLE) || defined(QM_ASSERT_UNREACHABLE_VOID) #error QM_ASSERT_UNREACHABLE and QM_ASSERT_UNREACHABLE_VOID should not be defined. # endif #endif #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED TEST(QuotaCommon_Try, Success_CustomErr_DiagnosticAssertUnreachable) { bool tryDidNotReturn = false; nsresult rv = [&tryDidNotReturn]() -> nsresult { QM_TRY(MOZ_TO_RESULT(NS_OK), QM_DIAGNOSTIC_ASSERT_UNREACHABLE); tryDidNotReturn = true; return NS_OK; }(); EXPECT_TRUE(tryDidNotReturn); EXPECT_EQ(rv, NS_OK); } TEST(QuotaCommon_Try, Success_NoErr_DiagnosticAssertUnreachable) { bool tryDidNotReturn = false; [&tryDidNotReturn]() -> void { QM_TRY(MOZ_TO_RESULT(NS_OK), QM_DIAGNOSTIC_ASSERT_UNREACHABLE_VOID); tryDidNotReturn = true; }(); EXPECT_TRUE(tryDidNotReturn); } #else # if defined(QM_DIAGNOSTIC_ASSERT_UNREACHABLE) || \ defined(QM_DIAGNOSTIC_ASSERT_UNREACHABLE_VOID) #error QM_DIAGNOSTIC_ASSERT_UNREACHABLE and QM_DIAGNOSTIC_ASSERT_UNREACHABLE_VOID should not be defined. # endif #endif TEST(QuotaCommon_Try, Success_CustomErr_CustomLambda) { #define SUBTEST(...) \ { \ bool tryDidNotReturn = false; \ \ nsresult rv = [&tryDidNotReturn]() -> nsresult { \ QM_TRY(MOZ_TO_RESULT(NS_OK), [](__VA_ARGS__) { return aRv; }); \ \ tryDidNotReturn = true; \ \ return NS_OK; \ }(); \ \ EXPECT_TRUE(tryDidNotReturn); \ EXPECT_EQ(rv, NS_OK); \ } SUBTEST(const char*, nsresult aRv); SUBTEST(nsresult aRv); #undef SUBTEST } TEST(QuotaCommon_Try, Success_WithCleanup) { bool tryCleanupRan = false; bool tryDidNotReturn = false; nsresult rv = [&tryCleanupRan, &tryDidNotReturn]() -> nsresult { QM_TRY(MOZ_TO_RESULT(NS_OK), QM_PROPAGATE, [&tryCleanupRan](const auto&) { tryCleanupRan = true; }); tryDidNotReturn = true; return NS_OK; }(); EXPECT_FALSE(tryCleanupRan); EXPECT_TRUE(tryDidNotReturn); EXPECT_EQ(rv, NS_OK); } TEST(QuotaCommon_Try, Failure_PropagateErr) { bool tryDidNotReturn = false; nsresult rv = [&tryDidNotReturn]() -> nsresult { QM_TRY(MOZ_TO_RESULT(NS_ERROR_FAILURE)); tryDidNotReturn = true; return NS_OK; }(); EXPECT_FALSE(tryDidNotReturn); EXPECT_EQ(rv, NS_ERROR_FAILURE); } TEST(QuotaCommon_Try, Failure_CustomErr) { bool tryDidNotReturn = false; nsresult rv = [&tryDidNotReturn]() -> nsresult { QM_TRY(MOZ_TO_RESULT(NS_ERROR_FAILURE), NS_ERROR_UNEXPECTED); tryDidNotReturn = true; return NS_OK; }(); EXPECT_FALSE(tryDidNotReturn); EXPECT_EQ(rv, NS_ERROR_UNEXPECTED); } TEST(QuotaCommon_Try, Failure_CustomErr_CustomLambda) { #define SUBTEST(...) \ { \ bool tryDidNotReturn = false; \ \ nsresult rv = [&tryDidNotReturn]() -> nsresult { \ QM_TRY(MOZ_TO_RESULT(NS_ERROR_FAILURE), \ [](__VA_ARGS__) { return NS_ERROR_UNEXPECTED; }); \ \ tryDidNotReturn = true; \ \ return NS_OK; \ }(); \ \ EXPECT_FALSE(tryDidNotReturn); \ EXPECT_EQ(rv, NS_ERROR_UNEXPECTED); \ } SUBTEST(const char* aFunc, nsresult); SUBTEST(nsresult rv); #undef SUBTEST } TEST(QuotaCommon_Try, Failure_NoErr) { bool tryDidNotReturn = false; [&tryDidNotReturn]() -> void { QM_TRY(MOZ_TO_RESULT(NS_ERROR_FAILURE), QM_VOID); tryDidNotReturn = true; }(); EXPECT_FALSE(tryDidNotReturn); } TEST(QuotaCommon_Try, Failure_WithCleanup) { bool tryCleanupRan = false; bool tryDidNotReturn = false; nsresult rv = [&tryCleanupRan, &tryDidNotReturn]() -> nsresult { QM_TRY(MOZ_TO_RESULT(NS_ERROR_FAILURE), QM_PROPAGATE, [&tryCleanupRan](const auto& result) { EXPECT_EQ(result, NS_ERROR_FAILURE); tryCleanupRan = true; }); tryDidNotReturn = true; return NS_OK; }(); EXPECT_TRUE(tryCleanupRan); EXPECT_FALSE(tryDidNotReturn); EXPECT_EQ(rv, NS_ERROR_FAILURE); } TEST(QuotaCommon_Try, Failure_WithCleanup_UnwrapErr) { bool tryCleanupRan = false; bool tryDidNotReturn = false; nsresult rv; [&tryCleanupRan, &tryDidNotReturn](nsresult& aRv) -> void { QM_TRY(MOZ_TO_RESULT(NS_ERROR_FAILURE), QM_VOID, ([&tryCleanupRan, &aRv](auto& result) { EXPECT_EQ(result, NS_ERROR_FAILURE); aRv = result; tryCleanupRan = true; })); tryDidNotReturn = true; aRv = NS_OK; }(rv); EXPECT_TRUE(tryCleanupRan); EXPECT_FALSE(tryDidNotReturn); EXPECT_EQ(rv, NS_ERROR_FAILURE); } TEST(QuotaCommon_Try, Failure_WithCleanupAndPredicate) { auto predicate = []() { static bool calledOnce = false; const bool result = !calledOnce; calledOnce = true; return result; }; { bool tryDidNotReturn = false; nsresult rv = [&predicate, &tryDidNotReturn]() -> nsresult { QM_TRY(MOZ_TO_RESULT(NS_ERROR_FAILURE), QM_PROPAGATE, QM_NO_CLEANUP, predicate); tryDidNotReturn = true; return NS_OK; }(); EXPECT_FALSE(tryDidNotReturn); EXPECT_EQ(rv, NS_ERROR_FAILURE); } { bool tryDidNotReturn = false; nsresult rv = [&predicate, &tryDidNotReturn]() -> nsresult { QM_TRY(MOZ_TO_RESULT(NS_ERROR_FAILURE), QM_PROPAGATE, QM_NO_CLEANUP, predicate); tryDidNotReturn = true; return NS_OK; }(); EXPECT_FALSE(tryDidNotReturn); EXPECT_EQ(rv, NS_ERROR_FAILURE); } } TEST(QuotaCommon_Try, SameLine) { // clang-format off QM_TRY(MOZ_TO_RESULT(NS_OK), QM_VOID); QM_TRY(MOZ_TO_RESULT(NS_OK), QM_VOID); // clang-format on } TEST(QuotaCommon_Try, NestingMadness_Success) { bool nestedTryDidNotReturn = false; bool tryDidNotReturn = false; nsresult rv = [&nestedTryDidNotReturn, &tryDidNotReturn]() -> nsresult { QM_TRY(([&nestedTryDidNotReturn]() -> Result { QM_TRY(MOZ_TO_RESULT(NS_OK)); nestedTryDidNotReturn = true; return Ok(); }())); tryDidNotReturn = true; return NS_OK; }(); EXPECT_TRUE(nestedTryDidNotReturn); EXPECT_TRUE(tryDidNotReturn); EXPECT_EQ(rv, NS_OK); } TEST(QuotaCommon_Try, NestingMadness_Failure) { bool nestedTryDidNotReturn = false; bool tryDidNotReturn = false; nsresult rv = [&nestedTryDidNotReturn, &tryDidNotReturn]() -> nsresult { QM_TRY(([&nestedTryDidNotReturn]() -> Result { QM_TRY(MOZ_TO_RESULT(NS_ERROR_FAILURE)); nestedTryDidNotReturn = true; return Ok(); }())); tryDidNotReturn = true; return NS_OK; }(); EXPECT_FALSE(nestedTryDidNotReturn); EXPECT_FALSE(tryDidNotReturn); EXPECT_EQ(rv, NS_ERROR_FAILURE); } TEST(QuotaCommon_Try, NestingMadness_Multiple_Success) { bool nestedTry1DidNotReturn = false; bool nestedTry2DidNotReturn = false; bool tryDidNotReturn = false; nsresult rv = [&nestedTry1DidNotReturn, &nestedTry2DidNotReturn, &tryDidNotReturn]() -> nsresult { QM_TRY(([&nestedTry1DidNotReturn, &nestedTry2DidNotReturn]() -> Result { QM_TRY(MOZ_TO_RESULT(NS_OK)); nestedTry1DidNotReturn = true; QM_TRY(MOZ_TO_RESULT(NS_OK)); nestedTry2DidNotReturn = true; return Ok(); }())); tryDidNotReturn = true; return NS_OK; }(); EXPECT_TRUE(nestedTry1DidNotReturn); EXPECT_TRUE(nestedTry2DidNotReturn); EXPECT_TRUE(tryDidNotReturn); EXPECT_EQ(rv, NS_OK); } TEST(QuotaCommon_Try, NestingMadness_Multiple_Failure1) { bool nestedTry1DidNotReturn = false; bool nestedTry2DidNotReturn = false; bool tryDidNotReturn = false; nsresult rv = [&nestedTry1DidNotReturn, &nestedTry2DidNotReturn, &tryDidNotReturn]() -> nsresult { QM_TRY(([&nestedTry1DidNotReturn, &nestedTry2DidNotReturn]() -> Result { QM_TRY(MOZ_TO_RESULT(NS_ERROR_FAILURE)); nestedTry1DidNotReturn = true; QM_TRY(MOZ_TO_RESULT(NS_OK)); nestedTry2DidNotReturn = true; return Ok(); }())); tryDidNotReturn = true; return NS_OK; }(); EXPECT_FALSE(nestedTry1DidNotReturn); EXPECT_FALSE(nestedTry2DidNotReturn); EXPECT_FALSE(tryDidNotReturn); EXPECT_EQ(rv, NS_ERROR_FAILURE); } TEST(QuotaCommon_Try, NestingMadness_Multiple_Failure2) { bool nestedTry1DidNotReturn = false; bool nestedTry2DidNotReturn = false; bool tryDidNotReturn = false; nsresult rv = [&nestedTry1DidNotReturn, &nestedTry2DidNotReturn, &tryDidNotReturn]() -> nsresult { QM_TRY(([&nestedTry1DidNotReturn, &nestedTry2DidNotReturn]() -> Result { QM_TRY(MOZ_TO_RESULT(NS_OK)); nestedTry1DidNotReturn = true; QM_TRY(MOZ_TO_RESULT(NS_ERROR_FAILURE)); nestedTry2DidNotReturn = true; return Ok(); }())); tryDidNotReturn = true; return NS_OK; }(); EXPECT_TRUE(nestedTry1DidNotReturn); EXPECT_FALSE(nestedTry2DidNotReturn); EXPECT_FALSE(tryDidNotReturn); EXPECT_EQ(rv, NS_ERROR_FAILURE); } TEST(QuotaCommon_TryInspect, Success) { bool tryInspectDidNotReturn = false; nsresult rv = [&tryInspectDidNotReturn]() -> nsresult { QM_TRY_INSPECT(const auto& x, (Result{42})); EXPECT_EQ(x, 42); tryInspectDidNotReturn = true; return NS_OK; }(); EXPECT_TRUE(tryInspectDidNotReturn); EXPECT_EQ(rv, NS_OK); } TEST(QuotaCommon_TryInspect, Success_CustomErr_QmIpcFail) { auto foo = MakeRefPtr(); bool tryDidNotReturn = false; auto res = foo->RecvTryInspect_Success_CustomErr_QmIpcFail(&tryDidNotReturn); EXPECT_TRUE(tryDidNotReturn); EXPECT_TRUE(res); } TEST(QuotaCommon_TryInspect, Success_CustomErr_IpcFail) { auto foo = MakeRefPtr(); bool tryDidNotReturn = false; auto res = foo->RecvTryInspect_Success_CustomErr_IpcFail(&tryDidNotReturn); EXPECT_TRUE(tryDidNotReturn); EXPECT_TRUE(res); } #ifdef DEBUG TEST(QuotaCommon_TryInspect, Success_CustomErr_AssertUnreachable) { bool tryInspectDidNotReturn = false; nsresult rv = [&tryInspectDidNotReturn]() -> nsresult { QM_TRY_INSPECT(const auto& x, (Result{42}), QM_ASSERT_UNREACHABLE); EXPECT_EQ(x, 42); tryInspectDidNotReturn = true; return NS_OK; }(); EXPECT_TRUE(tryInspectDidNotReturn); EXPECT_EQ(rv, NS_OK); } TEST(QuotaCommon_TryInspect, Success_NoErr_AssertUnreachable) { bool tryInspectDidNotReturn = false; [&tryInspectDidNotReturn]() -> void { QM_TRY_INSPECT(const auto& x, (Result{42}), QM_ASSERT_UNREACHABLE_VOID); EXPECT_EQ(x, 42); tryInspectDidNotReturn = true; }(); EXPECT_TRUE(tryInspectDidNotReturn); } #endif TEST(QuotaCommon_TryInspect, Success_CustomErr_CustomLambda) { #define SUBTEST(...) \ { \ bool tryInspectDidNotReturn = false; \ \ nsresult rv = [&tryInspectDidNotReturn]() -> nsresult { \ QM_TRY_INSPECT(const auto& x, (Result{42}), \ [](__VA_ARGS__) { return aRv; }); \ EXPECT_EQ(x, 42); \ \ tryInspectDidNotReturn = true; \ \ return NS_OK; \ }(); \ \ EXPECT_TRUE(tryInspectDidNotReturn); \ EXPECT_EQ(rv, NS_OK); \ } SUBTEST(const char*, nsresult aRv); SUBTEST(nsresult aRv); #undef SUBTEST } TEST(QuotaCommon_TryInspect, Success_WithCleanup) { bool tryInspectCleanupRan = false; bool tryInspectDidNotReturn = false; nsresult rv = [&tryInspectCleanupRan, &tryInspectDidNotReturn]() -> nsresult { QM_TRY_INSPECT( const auto& x, (Result{42}), QM_PROPAGATE, [&tryInspectCleanupRan](const auto&) { tryInspectCleanupRan = true; }); EXPECT_EQ(x, 42); tryInspectDidNotReturn = true; return NS_OK; }(); EXPECT_FALSE(tryInspectCleanupRan); EXPECT_TRUE(tryInspectDidNotReturn); EXPECT_EQ(rv, NS_OK); } TEST(QuotaCommon_TryInspect, Failure_PropagateErr) { bool tryInspectDidNotReturn = false; nsresult rv = [&tryInspectDidNotReturn]() -> nsresult { QM_TRY_INSPECT(const auto& x, (Result{Err(NS_ERROR_FAILURE)})); Unused << x; tryInspectDidNotReturn = true; return NS_OK; }(); EXPECT_FALSE(tryInspectDidNotReturn); EXPECT_EQ(rv, NS_ERROR_FAILURE); } TEST(QuotaCommon_TryInspect, Failure_CustomErr) { bool tryInspectDidNotReturn = false; nsresult rv = [&tryInspectDidNotReturn]() -> nsresult { QM_TRY_INSPECT(const auto& x, (Result{Err(NS_ERROR_FAILURE)}), NS_ERROR_UNEXPECTED); Unused << x; tryInspectDidNotReturn = true; return NS_OK; }(); EXPECT_FALSE(tryInspectDidNotReturn); EXPECT_EQ(rv, NS_ERROR_UNEXPECTED); } TEST(QuotaCommon_TryInspect, Failure_CustomErr_CustomLambda) { #define SUBTEST(...) \ { \ bool tryInspectDidNotReturn = false; \ \ nsresult rv = [&tryInspectDidNotReturn]() -> nsresult { \ QM_TRY_INSPECT(const auto& x, \ (Result{Err(NS_ERROR_FAILURE)}), \ [](__VA_ARGS__) { return NS_ERROR_UNEXPECTED; }); \ Unused << x; \ \ tryInspectDidNotReturn = true; \ \ return NS_OK; \ }(); \ \ EXPECT_FALSE(tryInspectDidNotReturn); \ EXPECT_EQ(rv, NS_ERROR_UNEXPECTED); \ } SUBTEST(const char*, nsresult); SUBTEST(nsresult); #undef SUBTEST } TEST(QuotaCommon_TryInspect, Failure_NoErr) { bool tryInspectDidNotReturn = false; [&tryInspectDidNotReturn]() -> void { QM_TRY_INSPECT(const auto& x, (Result{Err(NS_ERROR_FAILURE)}), QM_VOID); Unused << x; tryInspectDidNotReturn = true; }(); EXPECT_FALSE(tryInspectDidNotReturn); } TEST(QuotaCommon_TryInspect, Failure_WithCleanup) { bool tryInspectCleanupRan = false; bool tryInspectDidNotReturn = false; nsresult rv = [&tryInspectCleanupRan, &tryInspectDidNotReturn]() -> nsresult { QM_TRY_INSPECT(const auto& x, (Result{Err(NS_ERROR_FAILURE)}), QM_PROPAGATE, [&tryInspectCleanupRan](const auto& result) { EXPECT_EQ(result, NS_ERROR_FAILURE); tryInspectCleanupRan = true; }); Unused << x; tryInspectDidNotReturn = true; return NS_OK; }(); EXPECT_TRUE(tryInspectCleanupRan); EXPECT_FALSE(tryInspectDidNotReturn); EXPECT_EQ(rv, NS_ERROR_FAILURE); } TEST(QuotaCommon_TryInspect, Failure_WithCleanup_UnwrapErr) { bool tryInspectCleanupRan = false; bool tryInspectDidNotReturn = false; nsresult rv; [&tryInspectCleanupRan, &tryInspectDidNotReturn](nsresult& aRv) -> void { QM_TRY_INSPECT(const auto& x, (Result{Err(NS_ERROR_FAILURE)}), QM_VOID, ([&tryInspectCleanupRan, &aRv](auto& result) { EXPECT_EQ(result, NS_ERROR_FAILURE); aRv = result; tryInspectCleanupRan = true; })); Unused << x; tryInspectDidNotReturn = true; aRv = NS_OK; }(rv); EXPECT_TRUE(tryInspectCleanupRan); EXPECT_FALSE(tryInspectDidNotReturn); EXPECT_EQ(rv, NS_ERROR_FAILURE); } TEST(QuotaCommon_TryInspect, ConstDecl) { QM_TRY_INSPECT(const int32_t& x, (Result{42}), QM_VOID); static_assert(std::is_same_v); EXPECT_EQ(x, 42); } TEST(QuotaCommon_TryInspect, SameScopeDecl) { QM_TRY_INSPECT(const int32_t& x, (Result{42}), QM_VOID); EXPECT_EQ(x, 42); QM_TRY_INSPECT(const int32_t& y, (Result{42}), QM_VOID); EXPECT_EQ(y, 42); } TEST(QuotaCommon_TryInspect, SameLine) { // clang-format off QM_TRY_INSPECT(const auto &x, (Result{42}), QM_VOID); QM_TRY_INSPECT(const auto &y, (Result{42}), QM_VOID); // clang-format on EXPECT_EQ(x, 42); EXPECT_EQ(y, 42); } TEST(QuotaCommon_TryInspect, NestingMadness_Success) { bool nestedTryInspectDidNotReturn = false; bool tryInspectDidNotReturn = false; nsresult rv = [&nestedTryInspectDidNotReturn, &tryInspectDidNotReturn]() -> nsresult { QM_TRY_INSPECT( const auto& x, ([&nestedTryInspectDidNotReturn]() -> Result { QM_TRY_INSPECT(const auto& x, (Result{42})); nestedTryInspectDidNotReturn = true; return x; }())); EXPECT_EQ(x, 42); tryInspectDidNotReturn = true; return NS_OK; }(); EXPECT_TRUE(nestedTryInspectDidNotReturn); EXPECT_TRUE(tryInspectDidNotReturn); EXPECT_EQ(rv, NS_OK); } TEST(QuotaCommon_TryInspect, NestingMadness_Failure) { bool nestedTryInspectDidNotReturn = false; bool tryInspectDidNotReturn = false; nsresult rv = [&nestedTryInspectDidNotReturn, &tryInspectDidNotReturn]() -> nsresult { QM_TRY_INSPECT( const auto& x, ([&nestedTryInspectDidNotReturn]() -> Result { QM_TRY_INSPECT(const auto& x, (Result{Err(NS_ERROR_FAILURE)})); nestedTryInspectDidNotReturn = true; return x; }())); Unused << x; tryInspectDidNotReturn = true; return NS_OK; }(); EXPECT_FALSE(nestedTryInspectDidNotReturn); EXPECT_FALSE(tryInspectDidNotReturn); EXPECT_EQ(rv, NS_ERROR_FAILURE); } TEST(QuotaCommon_TryInspect, NestingMadness_Multiple_Success) { bool nestedTryInspect1DidNotReturn = false; bool nestedTryInspect2DidNotReturn = false; bool tryInspectDidNotReturn = false; nsresult rv = [&nestedTryInspect1DidNotReturn, &nestedTryInspect2DidNotReturn, &tryInspectDidNotReturn]() -> nsresult { QM_TRY_INSPECT( const auto& z, ([&nestedTryInspect1DidNotReturn, &nestedTryInspect2DidNotReturn]() -> Result { QM_TRY_INSPECT(const auto& x, (Result{42})); nestedTryInspect1DidNotReturn = true; QM_TRY_INSPECT(const auto& y, (Result{42})); nestedTryInspect2DidNotReturn = true; return x + y; }())); EXPECT_EQ(z, 84); tryInspectDidNotReturn = true; return NS_OK; }(); EXPECT_TRUE(nestedTryInspect1DidNotReturn); EXPECT_TRUE(nestedTryInspect2DidNotReturn); EXPECT_TRUE(tryInspectDidNotReturn); EXPECT_EQ(rv, NS_OK); } TEST(QuotaCommon_TryInspect, NestingMadness_Multiple_Failure1) { bool nestedTryInspect1DidNotReturn = false; bool nestedTryInspect2DidNotReturn = false; bool tryInspectDidNotReturn = false; nsresult rv = [&nestedTryInspect1DidNotReturn, &nestedTryInspect2DidNotReturn, &tryInspectDidNotReturn]() -> nsresult { QM_TRY_INSPECT( const auto& z, ([&nestedTryInspect1DidNotReturn, &nestedTryInspect2DidNotReturn]() -> Result { QM_TRY_INSPECT(const auto& x, (Result{Err(NS_ERROR_FAILURE)})); nestedTryInspect1DidNotReturn = true; QM_TRY_INSPECT(const auto& y, (Result{42})); nestedTryInspect2DidNotReturn = true; return x + y; }())); Unused << z; tryInspectDidNotReturn = true; return NS_OK; }(); EXPECT_FALSE(nestedTryInspect1DidNotReturn); EXPECT_FALSE(nestedTryInspect2DidNotReturn); EXPECT_FALSE(tryInspectDidNotReturn); EXPECT_EQ(rv, NS_ERROR_FAILURE); } TEST(QuotaCommon_TryInspect, NestingMadness_Multiple_Failure2) { bool nestedTryInspect1DidNotReturn = false; bool nestedTryInspect2DidNotReturn = false; bool tryInspectDidNotReturn = false; nsresult rv = [&nestedTryInspect1DidNotReturn, &nestedTryInspect2DidNotReturn, &tryInspectDidNotReturn]() -> nsresult { QM_TRY_INSPECT( const auto& z, ([&nestedTryInspect1DidNotReturn, &nestedTryInspect2DidNotReturn]() -> Result { QM_TRY_INSPECT(const auto& x, (Result{42})); nestedTryInspect1DidNotReturn = true; QM_TRY_INSPECT(const auto& y, (Result{Err(NS_ERROR_FAILURE)})); nestedTryInspect2DidNotReturn = true; return x + y; }())); Unused << z; tryInspectDidNotReturn = true; return NS_OK; }(); EXPECT_TRUE(nestedTryInspect1DidNotReturn); EXPECT_FALSE(nestedTryInspect2DidNotReturn); EXPECT_FALSE(tryInspectDidNotReturn); EXPECT_EQ(rv, NS_ERROR_FAILURE); } // We are not repeating all QM_TRY_INSPECT test cases for QM_TRY_UNWRAP, since // they are largely based on the same implementation. We just add some where // inspecting and unwrapping differ. TEST(QuotaCommon_TryUnwrap, NonConstDecl) { QM_TRY_UNWRAP(int32_t x, (Result{42}), QM_VOID); static_assert(std::is_same_v); EXPECT_EQ(x, 42); } TEST(QuotaCommon_TryUnwrap, RvalueDecl) { QM_TRY_UNWRAP(int32_t && x, (Result{42}), QM_VOID); static_assert(std::is_same_v); EXPECT_EQ(x, 42); } TEST(QuotaCommon_TryUnwrap, ParenDecl) { QM_TRY_UNWRAP( (auto&& [x, y]), (Result, nsresult>{std::pair{42, true}}), QM_VOID); static_assert(std::is_same_v); static_assert(std::is_same_v); EXPECT_EQ(x, 42); EXPECT_EQ(y, true); } TEST(QuotaCommon_TryReturn, Success) { bool tryReturnDidNotReturn = false; auto res = [&tryReturnDidNotReturn] { QM_TRY_RETURN((Result{42})); tryReturnDidNotReturn = true; }(); EXPECT_FALSE(tryReturnDidNotReturn); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), 42); } TEST(QuotaCommon_TryReturn, Success_nsresult) { bool tryReturnDidNotReturn = false; auto res = [&tryReturnDidNotReturn] { QM_TRY_RETURN(MOZ_TO_RESULT(NS_OK)); tryReturnDidNotReturn = true; }(); EXPECT_FALSE(tryReturnDidNotReturn); EXPECT_TRUE(res.isOk()); } #ifdef DEBUG TEST(QuotaCommon_TryReturn, Success_CustomErr_AssertUnreachable) { bool tryReturnDidNotReturn = false; auto res = [&tryReturnDidNotReturn]() -> Result { QM_TRY_RETURN((Result{42}), QM_ASSERT_UNREACHABLE); tryReturnDidNotReturn = true; }(); EXPECT_FALSE(tryReturnDidNotReturn); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), 42); } #endif TEST(QuotaCommon_TryReturn, Success_CustomErr_CustomLambda) { #define SUBTEST(...) \ { \ bool tryReturnDidNotReturn = false; \ \ auto res = [&tryReturnDidNotReturn]() -> Result { \ QM_TRY_RETURN(MOZ_TO_RESULT(NS_OK), \ [](__VA_ARGS__) { return Err(aRv); }); \ \ tryReturnDidNotReturn = true; \ }(); \ \ EXPECT_FALSE(tryReturnDidNotReturn); \ EXPECT_TRUE(res.isOk()); \ } SUBTEST(const char*, nsresult aRv); SUBTEST(nsresult aRv); #undef SUBTEST } TEST(QuotaCommon_TryReturn, Success_WithCleanup) { bool tryReturnCleanupRan = false; bool tryReturnDidNotReturn = false; auto res = [&tryReturnCleanupRan, &tryReturnDidNotReturn]() -> Result { QM_TRY_RETURN( (Result{42}), QM_PROPAGATE, [&tryReturnCleanupRan](const auto&) { tryReturnCleanupRan = true; }); tryReturnDidNotReturn = true; }(); EXPECT_FALSE(tryReturnCleanupRan); EXPECT_FALSE(tryReturnDidNotReturn); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), 42); } TEST(QuotaCommon_TryReturn, Failure_PropagateErr) { bool tryReturnDidNotReturn = false; auto res = [&tryReturnDidNotReturn] { QM_TRY_RETURN((Result{Err(NS_ERROR_FAILURE)})); tryReturnDidNotReturn = true; }(); EXPECT_FALSE(tryReturnDidNotReturn); EXPECT_TRUE(res.isErr()); EXPECT_EQ(res.unwrapErr(), NS_ERROR_FAILURE); } TEST(QuotaCommon_TryReturn, Failure_PropagateErr_nsresult) { bool tryReturnDidNotReturn = false; auto res = [&tryReturnDidNotReturn] { QM_TRY_RETURN(MOZ_TO_RESULT(NS_ERROR_FAILURE)); tryReturnDidNotReturn = true; }(); EXPECT_FALSE(tryReturnDidNotReturn); EXPECT_TRUE(res.isErr()); EXPECT_EQ(res.unwrapErr(), NS_ERROR_FAILURE); } TEST(QuotaCommon_TryReturn, Failure_CustomErr) { bool tryReturnDidNotReturn = false; auto res = [&tryReturnDidNotReturn]() -> Result { QM_TRY_RETURN((Result{Err(NS_ERROR_FAILURE)}), Err(NS_ERROR_UNEXPECTED)); tryReturnDidNotReturn = true; }(); EXPECT_FALSE(tryReturnDidNotReturn); EXPECT_TRUE(res.isErr()); EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED); } TEST(QuotaCommon_TryReturn, Failure_CustomErr_CustomLambda) { #define SUBTEST(...) \ { \ bool tryReturnDidNotReturn = false; \ \ auto res = [&tryReturnDidNotReturn]() -> Result { \ QM_TRY_RETURN((Result{Err(NS_ERROR_FAILURE)}), \ [](__VA_ARGS__) { return Err(NS_ERROR_UNEXPECTED); }); \ \ tryReturnDidNotReturn = true; \ }(); \ \ EXPECT_FALSE(tryReturnDidNotReturn); \ EXPECT_TRUE(res.isErr()); \ EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED); \ } SUBTEST(const char*, nsresult); SUBTEST(nsresult); #undef SUBTEST } TEST(QuotaCommon_TryReturn, Failure_WithCleanup) { bool tryReturnCleanupRan = false; bool tryReturnDidNotReturn = false; auto res = [&tryReturnCleanupRan, &tryReturnDidNotReturn]() -> Result { QM_TRY_RETURN((Result{Err(NS_ERROR_FAILURE)}), QM_PROPAGATE, [&tryReturnCleanupRan](const auto& result) { EXPECT_EQ(result, NS_ERROR_FAILURE); tryReturnCleanupRan = true; }); tryReturnDidNotReturn = true; }(); EXPECT_TRUE(tryReturnCleanupRan); EXPECT_FALSE(tryReturnDidNotReturn); EXPECT_TRUE(res.isErr()); EXPECT_EQ(res.unwrapErr(), NS_ERROR_FAILURE); } TEST(QuotaCommon_TryReturn, SameLine) { // clang-format off auto res1 = [] { QM_TRY_RETURN((Result{42})); }(); auto res2 = []() -> Result { QM_TRY_RETURN((Result{42})); }(); // clang-format on EXPECT_TRUE(res1.isOk()); EXPECT_EQ(res1.unwrap(), 42); EXPECT_TRUE(res2.isOk()); EXPECT_EQ(res2.unwrap(), 42); } TEST(QuotaCommon_TryReturn, NestingMadness_Success) { bool nestedTryReturnDidNotReturn = false; bool tryReturnDidNotReturn = false; auto res = [&nestedTryReturnDidNotReturn, &tryReturnDidNotReturn] { QM_TRY_RETURN(([&nestedTryReturnDidNotReturn] { QM_TRY_RETURN((Result{42})); nestedTryReturnDidNotReturn = true; }())); tryReturnDidNotReturn = true; }(); EXPECT_FALSE(nestedTryReturnDidNotReturn); EXPECT_FALSE(tryReturnDidNotReturn); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), 42); } TEST(QuotaCommon_TryReturn, NestingMadness_Failure) { bool nestedTryReturnDidNotReturn = false; bool tryReturnDidNotReturn = false; auto res = [&nestedTryReturnDidNotReturn, &tryReturnDidNotReturn] { QM_TRY_RETURN(([&nestedTryReturnDidNotReturn] { QM_TRY_RETURN((Result{Err(NS_ERROR_FAILURE)})); nestedTryReturnDidNotReturn = true; }())); tryReturnDidNotReturn = true; }(); EXPECT_FALSE(nestedTryReturnDidNotReturn); EXPECT_FALSE(tryReturnDidNotReturn); EXPECT_TRUE(res.isErr()); EXPECT_EQ(res.unwrapErr(), NS_ERROR_FAILURE); } TEST(QuotaCommon_Fail, ReturnValue) { bool failDidNotReturn = false; nsresult rv = [&failDidNotReturn]() -> nsresult { QM_FAIL(NS_ERROR_FAILURE); failDidNotReturn = true; return NS_OK; }(); EXPECT_FALSE(failDidNotReturn); EXPECT_EQ(rv, NS_ERROR_FAILURE); } TEST(QuotaCommon_Fail, ReturnValue_WithCleanup) { bool failCleanupRan = false; bool failDidNotReturn = false; nsresult rv = [&failCleanupRan, &failDidNotReturn]() -> nsresult { QM_FAIL(NS_ERROR_FAILURE, [&failCleanupRan]() { failCleanupRan = true; }); failDidNotReturn = true; return NS_OK; }(); EXPECT_TRUE(failCleanupRan); EXPECT_FALSE(failDidNotReturn); EXPECT_EQ(rv, NS_ERROR_FAILURE); } TEST(QuotaCommon_WarnOnlyTry, Success) { bool warnOnlyTryDidNotReturn = false; const auto res = [&warnOnlyTryDidNotReturn]() -> mozilla::Result { QM_WARNONLY_TRY(OkIf(true)); warnOnlyTryDidNotReturn = true; return mozilla::Ok{}; }(); EXPECT_TRUE(res.isOk()); EXPECT_TRUE(warnOnlyTryDidNotReturn); } TEST(QuotaCommon_WarnOnlyTry, Success_WithCleanup) { bool warnOnlyTryCleanupRan = false; bool warnOnlyTryDidNotReturn = false; const auto res = [&warnOnlyTryCleanupRan, &warnOnlyTryDidNotReturn]() -> mozilla::Result { QM_WARNONLY_TRY(OkIf(true), [&warnOnlyTryCleanupRan](const auto&) { warnOnlyTryCleanupRan = true; }); warnOnlyTryDidNotReturn = true; return mozilla::Ok{}; }(); EXPECT_TRUE(res.isOk()); EXPECT_FALSE(warnOnlyTryCleanupRan); EXPECT_TRUE(warnOnlyTryDidNotReturn); } TEST(QuotaCommon_WarnOnlyTry, Failure) { bool warnOnlyTryDidNotReturn = false; const auto res = [&warnOnlyTryDidNotReturn]() -> mozilla::Result { QM_WARNONLY_TRY(OkIf(false)); warnOnlyTryDidNotReturn = true; return mozilla::Ok{}; }(); EXPECT_TRUE(res.isOk()); EXPECT_TRUE(warnOnlyTryDidNotReturn); } TEST(QuotaCommon_WarnOnlyTry, Failure_WithCleanup) { bool warnOnlyTryCleanupRan = false; bool warnOnlyTryDidNotReturn = false; const auto res = [&warnOnlyTryCleanupRan, &warnOnlyTryDidNotReturn]() -> mozilla::Result { QM_WARNONLY_TRY(OkIf(false), ([&warnOnlyTryCleanupRan](const auto&) { warnOnlyTryCleanupRan = true; })); warnOnlyTryDidNotReturn = true; return mozilla::Ok{}; }(); EXPECT_TRUE(res.isOk()); EXPECT_TRUE(warnOnlyTryCleanupRan); EXPECT_TRUE(warnOnlyTryDidNotReturn); } TEST(QuotaCommon_WarnOnlyTryUnwrap, Success) { bool warnOnlyTryUnwrapDidNotReturn = false; const auto res = [&warnOnlyTryUnwrapDidNotReturn]() -> mozilla::Result { QM_WARNONLY_TRY_UNWRAP(const auto x, (Result{42})); EXPECT_TRUE(x); EXPECT_EQ(*x, 42); warnOnlyTryUnwrapDidNotReturn = true; return mozilla::Ok{}; }(); EXPECT_TRUE(res.isOk()); EXPECT_TRUE(warnOnlyTryUnwrapDidNotReturn); } TEST(QuotaCommon_WarnOnlyTryUnwrap, Success_WithCleanup) { bool warnOnlyTryUnwrapCleanupRan = false; bool warnOnlyTryUnwrapDidNotReturn = false; const auto res = [&warnOnlyTryUnwrapCleanupRan, &warnOnlyTryUnwrapDidNotReturn]() -> mozilla::Result { QM_WARNONLY_TRY_UNWRAP(const auto x, (Result{42}), [&warnOnlyTryUnwrapCleanupRan](const auto&) { warnOnlyTryUnwrapCleanupRan = true; }); EXPECT_TRUE(x); EXPECT_EQ(*x, 42); warnOnlyTryUnwrapDidNotReturn = true; return mozilla::Ok{}; }(); EXPECT_TRUE(res.isOk()); EXPECT_FALSE(warnOnlyTryUnwrapCleanupRan); EXPECT_TRUE(warnOnlyTryUnwrapDidNotReturn); } TEST(QuotaCommon_WarnOnlyTryUnwrap, Failure) { bool warnOnlyTryUnwrapDidNotReturn = false; const auto res = [&warnOnlyTryUnwrapDidNotReturn]() -> mozilla::Result { QM_WARNONLY_TRY_UNWRAP(const auto x, (Result{Err(NotOk{})})); EXPECT_FALSE(x); warnOnlyTryUnwrapDidNotReturn = true; return mozilla::Ok{}; }(); EXPECT_TRUE(res.isOk()); EXPECT_TRUE(warnOnlyTryUnwrapDidNotReturn); } TEST(QuotaCommon_WarnOnlyTryUnwrap, Failure_WithCleanup) { bool warnOnlyTryUnwrapCleanupRan = false; bool warnOnlyTryUnwrapDidNotReturn = false; const auto res = [&warnOnlyTryUnwrapCleanupRan, &warnOnlyTryUnwrapDidNotReturn]() -> mozilla::Result { QM_WARNONLY_TRY_UNWRAP(const auto x, (Result{Err(NotOk{})}), [&warnOnlyTryUnwrapCleanupRan](const auto&) { warnOnlyTryUnwrapCleanupRan = true; }); EXPECT_FALSE(x); warnOnlyTryUnwrapDidNotReturn = true; return mozilla::Ok{}; }(); EXPECT_TRUE(res.isOk()); EXPECT_TRUE(warnOnlyTryUnwrapCleanupRan); EXPECT_TRUE(warnOnlyTryUnwrapDidNotReturn); } TEST(QuotaCommon_OrElseWarn, Success) { bool fallbackRun = false; bool tryContinued = false; const auto res = [&]() -> mozilla::Result { QM_TRY(QM_OR_ELSE_WARN(OkIf(true), ([&fallbackRun](const NotOk) { fallbackRun = true; return mozilla::Result{ mozilla::Ok{}}; }))); tryContinued = true; return mozilla::Ok{}; }(); EXPECT_TRUE(res.isOk()); EXPECT_FALSE(fallbackRun); EXPECT_TRUE(tryContinued); } TEST(QuotaCommon_OrElseWarn, Failure_MappedToSuccess) { bool fallbackRun = false; bool tryContinued = false; // XXX Consider allowing to set a custom error handler, so that we can // actually assert that a warning was emitted. const auto res = [&]() -> mozilla::Result { QM_TRY(QM_OR_ELSE_WARN(OkIf(false), ([&fallbackRun](const NotOk) { fallbackRun = true; return mozilla::Result{ mozilla::Ok{}}; }))); tryContinued = true; return mozilla::Ok{}; }(); EXPECT_TRUE(res.isOk()); EXPECT_TRUE(fallbackRun); EXPECT_TRUE(tryContinued); } TEST(QuotaCommon_OrElseWarn, Failure_MappedToError) { bool fallbackRun = false; bool tryContinued = false; // XXX Consider allowing to set a custom error handler, so that we can // actually assert that a warning was emitted. const auto res = [&]() -> mozilla::Result { QM_TRY(QM_OR_ELSE_WARN(OkIf(false), ([&fallbackRun](const NotOk) { fallbackRun = true; return mozilla::Result{ NotOk{}}; }))); tryContinued = true; return mozilla::Ok{}; }(); EXPECT_TRUE(res.isErr()); EXPECT_TRUE(fallbackRun); EXPECT_FALSE(tryContinued); } TEST(QuotaCommon_OrElseWarnIf, Success) { bool predicateRun = false; bool fallbackRun = false; bool tryContinued = false; const auto res = [&]() -> mozilla::Result { QM_TRY(QM_OR_ELSE_WARN_IF( OkIf(true), [&predicateRun](const NotOk) { predicateRun = true; return false; }, ([&fallbackRun](const NotOk) { fallbackRun = true; return mozilla::Result{mozilla::Ok{}}; }))); tryContinued = true; return mozilla::Ok{}; }(); EXPECT_TRUE(res.isOk()); EXPECT_FALSE(predicateRun); EXPECT_FALSE(fallbackRun); EXPECT_TRUE(tryContinued); } TEST(QuotaCommon_OrElseWarnIf, Failure_PredicateReturnsFalse) { bool predicateRun = false; bool fallbackRun = false; bool tryContinued = false; const auto res = [&]() -> mozilla::Result { QM_TRY(QM_OR_ELSE_WARN_IF( OkIf(false), [&predicateRun](const NotOk) { predicateRun = true; return false; }, ([&fallbackRun](const NotOk) { fallbackRun = true; return mozilla::Result{mozilla::Ok{}}; }))); tryContinued = true; return mozilla::Ok{}; }(); EXPECT_TRUE(res.isErr()); EXPECT_TRUE(predicateRun); EXPECT_FALSE(fallbackRun); EXPECT_FALSE(tryContinued); } TEST(QuotaCommon_OrElseWarnIf, Failure_PredicateReturnsTrue_MappedToSuccess) { bool predicateRun = false; bool fallbackRun = false; bool tryContinued = false; const auto res = [&]() -> mozilla::Result { QM_TRY(QM_OR_ELSE_WARN_IF( OkIf(false), [&predicateRun](const NotOk) { predicateRun = true; return true; }, ([&fallbackRun](const NotOk) { fallbackRun = true; return mozilla::Result{mozilla::Ok{}}; }))); tryContinued = true; return mozilla::Ok{}; }(); EXPECT_TRUE(res.isOk()); EXPECT_TRUE(predicateRun); EXPECT_TRUE(fallbackRun); EXPECT_TRUE(tryContinued); } TEST(QuotaCommon_OrElseWarnIf, Failure_PredicateReturnsTrue_MappedToError) { bool predicateRun = false; bool fallbackRun = false; bool tryContinued = false; const auto res = [&]() -> mozilla::Result { QM_TRY(QM_OR_ELSE_WARN_IF( OkIf(false), [&predicateRun](const NotOk) { predicateRun = true; return true; }, ([&fallbackRun](const NotOk) { fallbackRun = true; return mozilla::Result{mozilla::NotOk{}}; }))); tryContinued = true; return mozilla::Ok{}; }(); EXPECT_TRUE(res.isErr()); EXPECT_TRUE(predicateRun); EXPECT_TRUE(fallbackRun); EXPECT_FALSE(tryContinued); } TEST(QuotaCommon_OkIf, True) { auto res = OkIf(true); EXPECT_TRUE(res.isOk()); } TEST(QuotaCommon_OkIf, False) { auto res = OkIf(false); EXPECT_TRUE(res.isErr()); } TEST(QuotaCommon_OkToOk, Bool_True) { auto res = OkToOk(Ok()); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), true); } TEST(QuotaCommon_OkToOk, Bool_False) { auto res = OkToOk(Ok()); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), false); } TEST(QuotaCommon_OkToOk, Int_42) { auto res = OkToOk<42>(Ok()); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), 42); } TEST(QuotaCommon_ErrToOkOrErr, Bool_True) { auto res = ErrToOkOrErr(NS_ERROR_FAILURE); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), true); } TEST(QuotaCommon_ErrToOkOrErr, Bool_True_Err) { auto res = ErrToOkOrErr(NS_ERROR_UNEXPECTED); EXPECT_TRUE(res.isErr()); EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED); } TEST(QuotaCommon_ErrToOkOrErr, Bool_False) { auto res = ErrToOkOrErr(NS_ERROR_FAILURE); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), false); } TEST(QuotaCommon_ErrToOkOrErr, Bool_False_Err) { auto res = ErrToOkOrErr(NS_ERROR_UNEXPECTED); EXPECT_TRUE(res.isErr()); EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED); } TEST(QuotaCommon_ErrToOkOrErr, Int_42) { auto res = ErrToOkOrErr(NS_ERROR_FAILURE); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), 42); } TEST(QuotaCommon_ErrToOkOrErr, Int_42_Err) { auto res = ErrToOkOrErr(NS_ERROR_UNEXPECTED); EXPECT_TRUE(res.isErr()); EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED); } TEST(QuotaCommon_ErrToOkOrErr, NsCOMPtr_nullptr) { auto res = ErrToOkOrErr>( NS_ERROR_FAILURE); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), nullptr); } TEST(QuotaCommon_ErrToOkOrErr, NsCOMPtr_nullptr_Err) { auto res = ErrToOkOrErr>( NS_ERROR_UNEXPECTED); EXPECT_TRUE(res.isErr()); EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED); } TEST(QuotaCommon_ErrToDefaultOkOrErr, Ok) { auto res = ErrToDefaultOkOrErr(NS_ERROR_FAILURE); EXPECT_TRUE(res.isOk()); } TEST(QuotaCommon_ErrToDefaultOkOrErr, Ok_Err) { auto res = ErrToDefaultOkOrErr(NS_ERROR_UNEXPECTED); EXPECT_TRUE(res.isErr()); EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED); } TEST(QuotaCommon_ErrToDefaultOkOrErr, NsCOMPtr) { auto res = ErrToDefaultOkOrErr>( NS_ERROR_FAILURE); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), nullptr); } TEST(QuotaCommon_ErrToDefaultOkOrErr, NsCOMPtr_Err) { auto res = ErrToDefaultOkOrErr>( NS_ERROR_UNEXPECTED); EXPECT_TRUE(res.isErr()); EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED); } TEST(QuotaCommon_IsSpecificError, Match) { EXPECT_TRUE(IsSpecificError(NS_ERROR_FAILURE)); } TEST(QuotaCommon_IsSpecificError, Mismatch) { EXPECT_FALSE(IsSpecificError(NS_ERROR_UNEXPECTED)); } TEST(QuotaCommon_ErrToOk, Bool_True) { auto res = ErrToOk(NS_ERROR_FAILURE); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), true); } TEST(QuotaCommon_ErrToOk, Bool_False) { auto res = ErrToOk(NS_ERROR_FAILURE); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), false); } TEST(QuotaCommon_ErrToOk, Int_42) { auto res = ErrToOk<42>(NS_ERROR_FAILURE); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), 42); } TEST(QuotaCommon_ErrToOk, NsCOMPtr_nullptr) { auto res = ErrToOk>(NS_ERROR_FAILURE); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), nullptr); } TEST(QuotaCommon_ErrToDefaultOk, Ok) { auto res = ErrToDefaultOk(NS_ERROR_FAILURE); EXPECT_TRUE(res.isOk()); } TEST(QuotaCommon_ErrToDefaultOk, NsCOMPtr) { auto res = ErrToDefaultOk>(NS_ERROR_FAILURE); EXPECT_TRUE(res.isOk()); EXPECT_EQ(res.unwrap(), nullptr); } class StringPairParameterized : public ::testing::TestWithParam> {}; TEST_P(StringPairParameterized, AnonymizedOriginString) { const auto [in, expectedAnonymized] = GetParam(); const auto anonymized = AnonymizedOriginString(nsDependentCString(in)); EXPECT_STREQ(anonymized.get(), expectedAnonymized); } INSTANTIATE_TEST_SUITE_P( QuotaCommon, StringPairParameterized, ::testing::Values( // XXX Do we really want to anonymize about: origins? std::pair("about:home", "about:aaaa"), std::pair("https://foo.bar.com", "https://aaa.aaa.aaa"), std::pair("https://foo.bar.com:8000", "https://aaa.aaa.aaa:DDDD"), std::pair("file://UNIVERSAL_FILE_ORIGIN", "file://aaaaaaaaa_aaaa_aaaaaa"))); // BEGIN COPY FROM mfbt/tests/TestResult.cpp struct Failed {}; static GenericErrorResult Fail() { return Err(Failed()); } static Result Task1(bool pass) { if (!pass) { return Fail(); // implicit conversion from GenericErrorResult to Result } return Ok(); } // END COPY FROM mfbt/tests/TestResult.cpp static Result Condition(bool aNoError, bool aResult) { return Task1(aNoError).map([aResult](auto) { return aResult; }); } TEST(QuotaCommon_CollectWhileTest, NoFailures) { const size_t loopCount = 5; size_t conditionExecutions = 0; size_t bodyExecutions = 0; auto result = CollectWhile( [&conditionExecutions] { ++conditionExecutions; return Condition(true, conditionExecutions <= loopCount); }, [&bodyExecutions] { ++bodyExecutions; return Task1(true); }); static_assert(std::is_same_v>); MOZ_RELEASE_ASSERT(result.isOk()); MOZ_RELEASE_ASSERT(loopCount == bodyExecutions); MOZ_RELEASE_ASSERT(1 + loopCount == conditionExecutions); } TEST(QuotaCommon_CollectWhileTest, BodyFailsImmediately) { size_t conditionExecutions = 0; size_t bodyExecutions = 0; auto result = CollectWhile( [&conditionExecutions] { ++conditionExecutions; return Condition(true, true); }, [&bodyExecutions] { ++bodyExecutions; return Task1(false); }); static_assert(std::is_same_v>); MOZ_RELEASE_ASSERT(result.isErr()); MOZ_RELEASE_ASSERT(1 == bodyExecutions); MOZ_RELEASE_ASSERT(1 == conditionExecutions); } TEST(QuotaCommon_CollectWhileTest, BodyFailsOnSecondExecution) { size_t conditionExecutions = 0; size_t bodyExecutions = 0; auto result = CollectWhile( [&conditionExecutions] { ++conditionExecutions; return Condition(true, true); }, [&bodyExecutions] { ++bodyExecutions; return Task1(bodyExecutions < 2); }); static_assert(std::is_same_v>); MOZ_RELEASE_ASSERT(result.isErr()); MOZ_RELEASE_ASSERT(2 == bodyExecutions); MOZ_RELEASE_ASSERT(2 == conditionExecutions); } TEST(QuotaCommon_CollectWhileTest, ConditionFailsImmediately) { size_t conditionExecutions = 0; size_t bodyExecutions = 0; auto result = CollectWhile( [&conditionExecutions] { ++conditionExecutions; return Condition(false, true); }, [&bodyExecutions] { ++bodyExecutions; return Task1(true); }); static_assert(std::is_same_v>); MOZ_RELEASE_ASSERT(result.isErr()); MOZ_RELEASE_ASSERT(0 == bodyExecutions); MOZ_RELEASE_ASSERT(1 == conditionExecutions); } TEST(QuotaCommon_CollectWhileTest, ConditionFailsOnSecondExecution) { size_t conditionExecutions = 0; size_t bodyExecutions = 0; auto result = CollectWhile( [&conditionExecutions] { ++conditionExecutions; return Condition(conditionExecutions < 2, true); }, [&bodyExecutions] { ++bodyExecutions; return Task1(true); }); static_assert(std::is_same_v>); MOZ_RELEASE_ASSERT(result.isErr()); MOZ_RELEASE_ASSERT(1 == bodyExecutions); MOZ_RELEASE_ASSERT(2 == conditionExecutions); } TEST(QuotaCommon_CollectEachInRange, Success) { size_t bodyExecutions = 0; const auto result = CollectEachInRange( std::array{{1, 2, 3, 4, 5}}, [&bodyExecutions](const int val) -> Result { ++bodyExecutions; return Ok{}; }); MOZ_RELEASE_ASSERT(result.isOk()); MOZ_RELEASE_ASSERT(5 == bodyExecutions); } TEST(QuotaCommon_CollectEachInRange, FailureShortCircuit) { size_t bodyExecutions = 0; const auto result = CollectEachInRange( std::array{{1, 2, 3, 4, 5}}, [&bodyExecutions](const int val) -> Result { ++bodyExecutions; return val == 3 ? Err(NS_ERROR_FAILURE) : Result{Ok{}}; }); MOZ_RELEASE_ASSERT(result.isErr()); MOZ_RELEASE_ASSERT(NS_ERROR_FAILURE == result.inspectErr()); MOZ_RELEASE_ASSERT(3 == bodyExecutions); } TEST(QuotaCommon_ReduceEach, Success) { const auto result = ReduceEach( [i = int{0}]() mutable -> Result { if (i < 5) { return ++i; } return 0; }, 0, [](int val, int add) -> Result { return val + add; }); static_assert(std::is_same_v>); MOZ_RELEASE_ASSERT(result.isOk()); MOZ_RELEASE_ASSERT(15 == result.inspect()); } TEST(QuotaCommon_ReduceEach, StepError) { const auto result = ReduceEach( [i = int{0}]() mutable -> Result { if (i < 5) { return ++i; } return 0; }, 0, [](int val, int add) -> Result { if (val > 2) { return Err(Failed{}); } return val + add; }); static_assert(std::is_same_v>); MOZ_RELEASE_ASSERT(result.isErr()); } TEST(QuotaCommon_ReduceEach, GeneratorError) { size_t generatorExecutions = 0; const auto result = ReduceEach( [i = int{0}, &generatorExecutions]() mutable -> Result { ++generatorExecutions; if (i < 1) { return ++i; } return Err(Failed{}); }, 0, [](int val, int add) -> Result { if (val > 2) { return Err(Failed{}); } return val + add; }); static_assert(std::is_same_v>); MOZ_RELEASE_ASSERT(result.isErr()); MOZ_RELEASE_ASSERT(2 == generatorExecutions); } TEST(QuotaCommon_Reduce, Success) { const auto range = std::vector{0, 1, 2, 3, 4, 5}; const auto result = Reduce( range, 0, [](int val, Maybe add) -> Result { return val + add.ref(); }); static_assert(std::is_same_v>); MOZ_RELEASE_ASSERT(result.isOk()); MOZ_RELEASE_ASSERT(15 == result.inspect()); } TEST(QuotaCommon_CallWithDelayedRetriesIfAccessDenied, NoFailures) { uint32_t tries = 0; auto res = CallWithDelayedRetriesIfAccessDenied( [&tries]() -> Result { ++tries; return Ok{}; }, 10, 2); EXPECT_EQ(tries, 1u); EXPECT_TRUE(res.isOk()); } TEST(QuotaCommon_CallWithDelayedRetriesIfAccessDenied, PermanentFailures) { uint32_t tries = 0; auto res = CallWithDelayedRetriesIfAccessDenied( [&tries]() -> Result { ++tries; return Err(NS_ERROR_FILE_IS_LOCKED); }, 10, 2); EXPECT_EQ(tries, 11u); EXPECT_TRUE(res.isErr()); } TEST(QuotaCommon_CallWithDelayedRetriesIfAccessDenied, FailuresAndSuccess) { uint32_t tries = 0; auto res = CallWithDelayedRetriesIfAccessDenied( [&tries]() -> Result { if (++tries == 5) { return Ok{}; } return Err(NS_ERROR_FILE_ACCESS_DENIED); }, 10, 2); EXPECT_EQ(tries, 5u); EXPECT_TRUE(res.isOk()); } TEST(QuotaCommon_MakeSourceFileRelativePath, ThisSourceFile) { static constexpr auto thisSourceFileRelativePath = "dom/quota/test/gtest/TestQuotaCommon.cpp"_ns; const nsCString sourceFileRelativePath{ mozilla::dom::quota::detail::MakeSourceFileRelativePath( nsLiteralCString(__FILE__))}; EXPECT_STREQ(sourceFileRelativePath.get(), thisSourceFileRelativePath.get()); } static nsCString MakeTreePath(const nsACString& aBasePath, const nsACString& aRelativePath) { nsCString path{aBasePath}; path.Append("/"); path.Append(aRelativePath); return path; } static nsCString MakeSourceTreePath(const nsACString& aRelativePath) { return MakeTreePath(mozilla::dom::quota::detail::GetSourceTreeBase(), aRelativePath); } static nsCString MakeObjdirDistIncludeTreePath( const nsACString& aRelativePath) { return MakeTreePath( mozilla::dom::quota::detail::GetObjdirDistIncludeTreeBase(), aRelativePath); } TEST(QuotaCommon_MakeSourceFileRelativePath, DomQuotaSourceFile) { static constexpr auto domQuotaSourceFileRelativePath = "dom/quota/ActorsParent.cpp"_ns; const nsCString sourceFileRelativePath{ mozilla::dom::quota::detail::MakeSourceFileRelativePath( MakeSourceTreePath(domQuotaSourceFileRelativePath))}; EXPECT_STREQ(sourceFileRelativePath.get(), domQuotaSourceFileRelativePath.get()); } TEST(QuotaCommon_MakeSourceFileRelativePath, DomQuotaSourceFile_Exported) { static constexpr auto mozillaDomQuotaSourceFileRelativePath = "mozilla/dom/quota/QuotaCommon.h"_ns; static constexpr auto domQuotaSourceFileRelativePath = "dom/quota/QuotaCommon.h"_ns; const nsCString sourceFileRelativePath{ mozilla::dom::quota::detail::MakeSourceFileRelativePath( MakeObjdirDistIncludeTreePath( mozillaDomQuotaSourceFileRelativePath))}; EXPECT_STREQ(sourceFileRelativePath.get(), domQuotaSourceFileRelativePath.get()); } TEST(QuotaCommon_MakeSourceFileRelativePath, DomIndexedDBSourceFile) { static constexpr auto domIndexedDBSourceFileRelativePath = "dom/indexedDB/ActorsParent.cpp"_ns; const nsCString sourceFileRelativePath{ mozilla::dom::quota::detail::MakeSourceFileRelativePath( MakeSourceTreePath(domIndexedDBSourceFileRelativePath))}; EXPECT_STREQ(sourceFileRelativePath.get(), domIndexedDBSourceFileRelativePath.get()); } TEST(QuotaCommon_MakeSourceFileRelativePath, DomLocalstorageSourceFile_Exported_Mapped) { static constexpr auto mozillaDomSourceFileRelativePath = "mozilla/dom/LocalStorageCommon.h"_ns; static constexpr auto domLocalstorageSourceFileRelativePath = "dom/localstorage/LocalStorageCommon.h"_ns; const nsCString sourceFileRelativePath{ mozilla::dom::quota::detail::MakeSourceFileRelativePath( MakeObjdirDistIncludeTreePath(mozillaDomSourceFileRelativePath))}; EXPECT_STREQ(sourceFileRelativePath.get(), domLocalstorageSourceFileRelativePath.get()); } TEST(QuotaCommon_MakeSourceFileRelativePath, NonDomSourceFile) { static constexpr auto nonDomSourceFileRelativePath = "storage/mozStorageService.cpp"_ns; const nsCString sourceFileRelativePath{ mozilla::dom::quota::detail::MakeSourceFileRelativePath( MakeSourceTreePath(nonDomSourceFileRelativePath))}; EXPECT_STREQ(sourceFileRelativePath.get(), nonDomSourceFileRelativePath.get()); } TEST(QuotaCommon_MakeSourceFileRelativePath, OtherSourceFile) { constexpr auto otherSourceFilePath = "/foo/bar/Test.cpp"_ns; const nsCString sourceFileRelativePath{ mozilla::dom::quota::detail::MakeSourceFileRelativePath( otherSourceFilePath)}; EXPECT_STREQ(sourceFileRelativePath.get(), "Test.cpp"); } #ifdef __clang__ # pragma clang diagnostic pop #endif } // namespace mozilla::dom::quota