/* -*- 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/dom/SafeRefPtr.h" #include "mozilla/RefCounted.h" using namespace mozilla; class SafeBase : public SafeRefCounted { public: MOZ_DECLARE_REFCOUNTED_TYPENAME(SafeBase) SafeBase() : mDead(false) {} static inline int sNumDestroyed; ~SafeBase() { MOZ_RELEASE_ASSERT(!mDead); mDead = true; sNumDestroyed++; } private: bool mDead; }; struct SafeDerived : public SafeBase {}; class Base : public RefCounted { public: MOZ_DECLARE_REFCOUNTED_TYPENAME(Base) Base() : mDead(false) {} static inline int sNumDestroyed; ~Base() { MOZ_RELEASE_ASSERT(!mDead); mDead = true; sNumDestroyed++; } private: bool mDead; }; struct Derived : public Base {}; already_AddRefed NewFoo() { RefPtr ptr(new Base()); return ptr.forget(); } already_AddRefed NewBar() { RefPtr bar = new Derived(); return bar.forget(); } TEST(DOM_IndexedDB_SafeRefPtr, Construct_Default) { Base::sNumDestroyed = 0; { SafeRefPtr ptr; ASSERT_FALSE(ptr); ASSERT_EQ(0, Base::sNumDestroyed); } ASSERT_EQ(0, Base::sNumDestroyed); } TEST(DOM_IndexedDB_SafeRefPtr, Construct_FromNullPtr) { Base::sNumDestroyed = 0; { SafeRefPtr ptr = nullptr; ASSERT_FALSE(ptr); ASSERT_EQ(0, Base::sNumDestroyed); } ASSERT_EQ(0, Base::sNumDestroyed); } TEST(DOM_IndexedDB_SafeRefPtr, Construct_FromMakeSafeRefPtr_SafeRefCounted) { SafeBase::sNumDestroyed = 0; { SafeRefPtr ptr = MakeSafeRefPtr(); ASSERT_TRUE(ptr); ASSERT_EQ(1u, ptr->refCount()); } ASSERT_EQ(1, SafeBase::sNumDestroyed); } TEST(DOM_IndexedDB_SafeRefPtr, Construct_FromMakeSafeRefPtr_SafeRefCounted_DerivedType) { SafeBase::sNumDestroyed = 0; { SafeRefPtr ptr = MakeSafeRefPtr(); ASSERT_TRUE(ptr); ASSERT_EQ(1u, ptr->refCount()); } ASSERT_EQ(1, SafeBase::sNumDestroyed); } TEST(DOM_IndexedDB_SafeRefPtr, Construct_FromMakeSafeRefPtr_RefCounted) { Base::sNumDestroyed = 0; { SafeRefPtr ptr = MakeSafeRefPtr(); ASSERT_TRUE(ptr); ASSERT_EQ(1u, ptr->refCount()); } ASSERT_EQ(1, Base::sNumDestroyed); } TEST(DOM_IndexedDB_SafeRefPtr, Construct_FromRefPtr) { Base::sNumDestroyed = 0; { SafeRefPtr ptr = SafeRefPtr{MakeRefPtr()}; ASSERT_TRUE(ptr); ASSERT_EQ(1u, ptr->refCount()); } ASSERT_EQ(1, Base::sNumDestroyed); } TEST(DOM_IndexedDB_SafeRefPtr, Construct_FromRefPtr_DerivedType) { Base::sNumDestroyed = 0; { SafeRefPtr ptr = SafeRefPtr{MakeRefPtr()}; ASSERT_TRUE(ptr); ASSERT_EQ(1u, ptr->refCount()); } ASSERT_EQ(1, Base::sNumDestroyed); } TEST(DOM_IndexedDB_SafeRefPtr, Construct_FromAlreadyAddRefed) { Base::sNumDestroyed = 0; { SafeRefPtr ptr1 = SafeRefPtr{NewFoo()}; SafeRefPtr ptr2(NewFoo()); ASSERT_TRUE(ptr1); ASSERT_TRUE(ptr2); ASSERT_EQ(0, Base::sNumDestroyed); } ASSERT_EQ(2, Base::sNumDestroyed); } TEST(DOM_IndexedDB_SafeRefPtr, Construct_FromAlreadyAddRefed_DerivedType) { Base::sNumDestroyed = 0; { SafeRefPtr ptr = SafeRefPtr{NewBar()}; ASSERT_TRUE(ptr); ASSERT_EQ(1u, ptr->refCount()); ASSERT_EQ(0, Base::sNumDestroyed); } ASSERT_EQ(1, Base::sNumDestroyed); } TEST(DOM_IndexedDB_SafeRefPtr, Construct_FromRawPtr) { Base::sNumDestroyed = 0; { SafeRefPtr ptr = SafeRefPtr{new Base(), AcquireStrongRefFromRawPtr{}}; ASSERT_TRUE(ptr); ASSERT_EQ(1u, ptr->refCount()); ASSERT_EQ(0, Base::sNumDestroyed); } ASSERT_EQ(1, Base::sNumDestroyed); } TEST(DOM_IndexedDB_SafeRefPtr, Construct_FromRawPtr_Dervied) { Base::sNumDestroyed = 0; { SafeRefPtr ptr = SafeRefPtr{new Derived(), AcquireStrongRefFromRawPtr{}}; ASSERT_TRUE(ptr); ASSERT_EQ(1u, ptr->refCount()); ASSERT_EQ(0, Base::sNumDestroyed); } ASSERT_EQ(1, Base::sNumDestroyed); } TEST(DOM_IndexedDB_SafeRefPtr, ClonePtr) { Base::sNumDestroyed = 0; { SafeRefPtr ptr1; { ptr1 = MakeSafeRefPtr(); const SafeRefPtr ptr2 = ptr1.clonePtr(); SafeRefPtr f3 = ptr2.clonePtr(); ASSERT_EQ(3u, ptr1->refCount()); ASSERT_EQ(0, Base::sNumDestroyed); } ASSERT_EQ(1u, ptr1->refCount()); ASSERT_EQ(0, Base::sNumDestroyed); } ASSERT_EQ(1, Base::sNumDestroyed); } TEST(DOM_IndexedDB_SafeRefPtr, Forget) { Base::sNumDestroyed = 0; { SafeRefPtr ptr1 = MakeSafeRefPtr(); SafeRefPtr ptr2 = SafeRefPtr{ptr1.forget()}; ASSERT_FALSE(ptr1); ASSERT_TRUE(ptr2); ASSERT_EQ(1u, ptr2->refCount()); } ASSERT_EQ(1, Base::sNumDestroyed); } TEST(DOM_IndexedDB_SafeRefPtr, Downcast) { Base::sNumDestroyed = 0; { SafeRefPtr ptr1 = MakeSafeRefPtr(); SafeRefPtr ptr2 = std::move(ptr1).downcast(); ASSERT_FALSE(ptr1); ASSERT_TRUE(ptr2); ASSERT_EQ(1u, ptr2->refCount()); } ASSERT_EQ(1, Base::sNumDestroyed); } struct SafeTest final : SafeBase { template explicit SafeTest(Func aCallback) { aCallback(SafeRefPtrFromThis()); } }; TEST(DOM_IndexedDB_SafeRefPtr, SafeRefPtrFromThis_StoreFromCtor) { SafeBase::sNumDestroyed = 0; { SafeRefPtr ptr1; { SafeRefPtr ptr2 = MakeSafeRefPtr([&ptr1](SafeRefPtr ptr) { ptr1 = std::move(ptr); EXPECT_EQ(2u, ptr1->refCount()); }); ASSERT_EQ(2u, ptr2->refCount()); } ASSERT_EQ(0, SafeBase::sNumDestroyed); ASSERT_EQ(1u, ptr1->refCount()); } ASSERT_EQ(1, SafeBase::sNumDestroyed); } TEST(DOM_IndexedDB_SafeRefPtr, SafeRefPtrFromThis_DiscardInCtor) { SafeBase::sNumDestroyed = 0; { SafeRefPtr ptr = MakeSafeRefPtr( [](SafeRefPtr ptr) { EXPECT_EQ(2u, ptr->refCount()); }); ASSERT_EQ(1u, ptr->refCount()); ASSERT_EQ(0, SafeBase::sNumDestroyed); } ASSERT_EQ(1, SafeBase::sNumDestroyed); } static_assert( std::is_same_v, decltype(std::declval() ? MakeSafeRefPtr() : MakeSafeRefPtr())>);