/* 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/CheckedUnsafePtr.h" #include "gtest/gtest.h" #include #include #include #include "mozilla/fallible.h" using namespace mozilla; class NoCheckTestType : public SupportsCheckedUnsafePtr {}; #if __cplusplus < 202002L static_assert(std::is_literal_type_v>); #endif static_assert( std::is_trivially_copy_constructible_v>); static_assert( std::is_trivially_copy_assignable_v>); static_assert( std::is_trivially_move_constructible_v>); static_assert( std::is_trivially_move_assignable_v>); class TestCheckingPolicy : public CheckCheckedUnsafePtrs { protected: explicit TestCheckingPolicy(bool& aPassedCheck) : mPassedCheck(aPassedCheck) {} private: friend class mozilla::CheckingPolicyAccess; void NotifyCheckFailure() { mPassedCheck = false; } bool& mPassedCheck; }; struct BasePointee : public SupportsCheckedUnsafePtr { explicit BasePointee(bool& aCheckPassed) : SupportsCheckedUnsafePtr(aCheckPassed) {} }; struct DerivedPointee : public BasePointee { using BasePointee::BasePointee; }; class CheckedUnsafePtrTest : public ::testing::Test { protected: bool mPassedCheck = true; }; TEST_F(CheckedUnsafePtrTest, PointeeWithNoCheckedUnsafePtrs) { { DerivedPointee pointee{mPassedCheck}; } ASSERT_TRUE(mPassedCheck); } template class TypedCheckedUnsafePtrTest : public CheckedUnsafePtrTest {}; TYPED_TEST_SUITE_P(TypedCheckedUnsafePtrTest); TYPED_TEST_P(TypedCheckedUnsafePtrTest, PointeeWithOneCheckedUnsafePtr) { { DerivedPointee pointee{this->mPassedCheck}; CheckedUnsafePtr ptr = &pointee; } ASSERT_TRUE(this->mPassedCheck); } TYPED_TEST_P(TypedCheckedUnsafePtrTest, CheckedUnsafePtrCopyConstructed) { { DerivedPointee pointee{this->mPassedCheck}; CheckedUnsafePtr ptr1 = &pointee; CheckedUnsafePtr ptr2 = ptr1; } ASSERT_TRUE(this->mPassedCheck); } TYPED_TEST_P(TypedCheckedUnsafePtrTest, CheckedUnsafePtrCopyAssigned) { { DerivedPointee pointee{this->mPassedCheck}; CheckedUnsafePtr ptr1 = &pointee; CheckedUnsafePtr ptr2; ptr2 = ptr1; } ASSERT_TRUE(this->mPassedCheck); } TYPED_TEST_P(TypedCheckedUnsafePtrTest, PointeeWithOneDanglingCheckedUnsafePtr) { [this]() -> CheckedUnsafePtr { DerivedPointee pointee{this->mPassedCheck}; return &pointee; }(); ASSERT_FALSE(this->mPassedCheck); } TYPED_TEST_P(TypedCheckedUnsafePtrTest, PointeeWithOneCopiedDanglingCheckedUnsafePtr) { const auto dangling1 = [this]() -> CheckedUnsafePtr { DerivedPointee pointee{this->mPassedCheck}; return &pointee; }(); EXPECT_FALSE(this->mPassedCheck); // With AddressSanitizer we would hopefully detect if the copy constructor // tries to add dangling2 to the now-gone pointee's unsafe pointer array. No // promises though, since it might be optimized away. CheckedUnsafePtr dangling2{dangling1}; ASSERT_TRUE(dangling2); } TYPED_TEST_P(TypedCheckedUnsafePtrTest, PointeeWithOneCopyAssignedDanglingCheckedUnsafePtr) { const auto dangling1 = [this]() -> CheckedUnsafePtr { DerivedPointee pointee{this->mPassedCheck}; return &pointee; }(); EXPECT_FALSE(this->mPassedCheck); // With AddressSanitizer we would hopefully detect if the assignment tries to // add dangling2 to the now-gone pointee's unsafe pointer array. No promises // though, since it might be optimized away. CheckedUnsafePtr dangling2; dangling2 = dangling1; ASSERT_TRUE(dangling2); } REGISTER_TYPED_TEST_SUITE_P(TypedCheckedUnsafePtrTest, PointeeWithOneCheckedUnsafePtr, CheckedUnsafePtrCopyConstructed, CheckedUnsafePtrCopyAssigned, PointeeWithOneDanglingCheckedUnsafePtr, PointeeWithOneCopiedDanglingCheckedUnsafePtr, PointeeWithOneCopyAssignedDanglingCheckedUnsafePtr); using BothTypes = ::testing::Types; INSTANTIATE_TYPED_TEST_SUITE_P(InstantiationOf, TypedCheckedUnsafePtrTest, BothTypes);