summaryrefslogtreecommitdiffstats
path: root/xpcom/tests/gtest/TestNsRefPtr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/tests/gtest/TestNsRefPtr.cpp')
-rw-r--r--xpcom/tests/gtest/TestNsRefPtr.cpp479
1 files changed, 479 insertions, 0 deletions
diff --git a/xpcom/tests/gtest/TestNsRefPtr.cpp b/xpcom/tests/gtest/TestNsRefPtr.cpp
new file mode 100644
index 000000000..a085c2966
--- /dev/null
+++ b/xpcom/tests/gtest/TestNsRefPtr.cpp
@@ -0,0 +1,479 @@
+/* -*- 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 "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "nsISupports.h"
+#include "nsQueryObject.h"
+#include "mozilla/Unused.h"
+
+#include "gtest/gtest.h"
+
+namespace TestNsRefPtr
+{
+
+#define NS_FOO_IID \
+{ 0x6f7652e0, 0xee43, 0x11d1, \
+ { 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 } }
+
+class Foo : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_FOO_IID)
+
+public:
+ Foo();
+ // virtual dtor because Bar uses our Release()
+ virtual ~Foo();
+
+ NS_IMETHOD_(MozExternalRefCountType) AddRef();
+ NS_IMETHOD_(MozExternalRefCountType) Release();
+ NS_IMETHOD QueryInterface( const nsIID&, void** );
+ void MemberFunction( int, int*, int& );
+ virtual void VirtualMemberFunction( int, int*, int& );
+ virtual void VirtualConstMemberFunction( int, int*, int& ) const;
+
+ void NonconstMethod() {}
+ void ConstMethod() const {}
+
+ int refcount_;
+
+ static int total_constructions_;
+ static int total_destructions_;
+ static int total_addrefs_;
+ static int total_queries_;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(Foo, NS_FOO_IID)
+
+int Foo::total_constructions_;
+int Foo::total_destructions_;
+int Foo::total_addrefs_;
+int Foo::total_queries_;
+
+Foo::Foo()
+ : refcount_(0)
+{
+ ++total_constructions_;
+}
+
+Foo::~Foo()
+{
+ ++total_destructions_;
+}
+
+MozExternalRefCountType
+Foo::AddRef()
+{
+ ++refcount_;
+ ++total_addrefs_;
+ return refcount_;
+}
+
+MozExternalRefCountType
+Foo::Release()
+{
+ int newcount = --refcount_;
+ if ( newcount == 0 )
+ {
+ delete this;
+ }
+
+ return newcount;
+}
+
+nsresult
+Foo::QueryInterface( const nsIID& aIID, void** aResult )
+{
+ ++total_queries_;
+
+ nsISupports* rawPtr = 0;
+ nsresult status = NS_OK;
+
+ if ( aIID.Equals(NS_GET_IID(Foo)) )
+ rawPtr = this;
+ else
+ {
+ nsID iid_of_ISupports = NS_ISUPPORTS_IID;
+ if ( aIID.Equals(iid_of_ISupports) )
+ rawPtr = static_cast<nsISupports*>(this);
+ else
+ status = NS_ERROR_NO_INTERFACE;
+ }
+
+ NS_IF_ADDREF(rawPtr);
+ *aResult = rawPtr;
+
+ return status;
+}
+
+void
+Foo::MemberFunction( int aArg1, int* aArgPtr, int& aArgRef )
+{
+}
+
+void
+Foo::VirtualMemberFunction( int aArg1, int* aArgPtr, int& aArgRef )
+{
+}
+
+void
+Foo::VirtualConstMemberFunction( int aArg1, int* aArgPtr, int& aArgRef ) const
+{
+}
+
+nsresult
+CreateFoo( void** result )
+ // a typical factory function (that calls AddRef)
+{
+ Foo* foop = new Foo;
+
+ foop->AddRef();
+ *result = foop;
+
+ return NS_OK;
+}
+
+void
+set_a_Foo( RefPtr<Foo>* result )
+{
+ assert(result);
+
+ RefPtr<Foo> foop( do_QueryObject(new Foo) );
+ *result = foop;
+}
+
+RefPtr<Foo>
+return_a_Foo()
+{
+ RefPtr<Foo> foop( do_QueryObject(new Foo) );
+ return foop;
+}
+
+#define NS_BAR_IID \
+{ 0x6f7652e1, 0xee43, 0x11d1, \
+ { 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 } }
+
+class Bar : public Foo
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_BAR_IID)
+
+public:
+ Bar();
+ virtual ~Bar();
+
+ NS_IMETHOD QueryInterface( const nsIID&, void** ) override;
+
+ virtual void VirtualMemberFunction( int, int*, int& ) override;
+ virtual void VirtualConstMemberFunction( int, int*, int& ) const override;
+
+ static int total_constructions_;
+ static int total_destructions_;
+ static int total_queries_;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(Bar, NS_BAR_IID)
+
+int Bar::total_constructions_;
+int Bar::total_destructions_;
+int Bar::total_queries_;
+
+Bar::Bar()
+{
+ ++total_constructions_;
+}
+
+Bar::~Bar()
+{
+ ++total_destructions_;
+}
+
+nsresult
+Bar::QueryInterface( const nsID& aIID, void** aResult )
+{
+ ++total_queries_;
+
+ nsISupports* rawPtr = 0;
+ nsresult status = NS_OK;
+
+ if ( aIID.Equals(NS_GET_IID(Bar)) )
+ rawPtr = this;
+ else if ( aIID.Equals(NS_GET_IID(Foo)) )
+ rawPtr = static_cast<Foo*>(this);
+ else
+ {
+ nsID iid_of_ISupports = NS_ISUPPORTS_IID;
+ if ( aIID.Equals(iid_of_ISupports) )
+ rawPtr = static_cast<nsISupports*>(this);
+ else
+ status = NS_ERROR_NO_INTERFACE;
+ }
+
+ NS_IF_ADDREF(rawPtr);
+ *aResult = rawPtr;
+
+ return status;
+}
+
+void
+Bar::VirtualMemberFunction( int aArg1, int* aArgPtr, int& aArgRef )
+{
+}
+void
+Bar::VirtualConstMemberFunction( int aArg1, int* aArgPtr, int& aArgRef ) const
+{
+}
+
+} // namespace TestNsRefPtr
+
+using namespace TestNsRefPtr;
+
+TEST(nsRefPtr, AddRefAndRelease)
+{
+ Foo::total_constructions_ = 0;
+ Foo::total_destructions_ = 0;
+
+ {
+ RefPtr<Foo> foop( do_QueryObject(new Foo) );
+ ASSERT_EQ(Foo::total_constructions_, 1);
+ ASSERT_EQ(Foo::total_destructions_, 0);
+ ASSERT_EQ(foop->refcount_, 1);
+
+ foop = do_QueryObject(new Foo);
+ ASSERT_EQ(Foo::total_constructions_, 2);
+ ASSERT_EQ(Foo::total_destructions_, 1);
+
+ // [Shouldn't compile] Is it a compile time error to try to |AddRef| by hand?
+ //foop->AddRef();
+
+ // [Shouldn't compile] Is it a compile time error to try to |Release| be hand?
+ //foop->Release();
+
+ // [Shouldn't compile] Is it a compile time error to try to |delete| an |nsCOMPtr|?
+ //delete foop;
+
+ static_cast<Foo*>(foop)->AddRef();
+ ASSERT_EQ(foop->refcount_, 2);
+
+ static_cast<Foo*>(foop)->Release();
+ ASSERT_EQ(foop->refcount_, 1);
+ }
+
+ ASSERT_EQ(Foo::total_destructions_, 2);
+
+ {
+ RefPtr<Foo> fooP( do_QueryObject(new Foo) );
+ ASSERT_EQ(Foo::total_constructions_, 3);
+ ASSERT_EQ(Foo::total_destructions_, 2);
+ ASSERT_EQ(fooP->refcount_, 1);
+
+ Foo::total_addrefs_ = 0;
+ RefPtr<Foo> fooP2( fooP.forget() );
+ ASSERT_EQ(Foo::total_addrefs_, 0);
+ }
+}
+
+TEST(nsRefPtr, VirtualDestructor)
+{
+ Bar::total_destructions_ = 0;
+
+ {
+ RefPtr<Foo> foop( do_QueryObject(new Bar) );
+ mozilla::Unused << foop;
+ }
+
+ ASSERT_EQ(Bar::total_destructions_, 1);
+}
+
+TEST(nsRefPtr, Equality)
+{
+ Foo::total_constructions_ = 0;
+ Foo::total_destructions_ = 0;
+
+ {
+ RefPtr<Foo> foo1p( do_QueryObject(new Foo) );
+ RefPtr<Foo> foo2p( do_QueryObject(new Foo) );
+
+ ASSERT_EQ(Foo::total_constructions_, 2);
+ ASSERT_EQ(Foo::total_destructions_, 0);
+
+ ASSERT_NE(foo1p, foo2p);
+
+ ASSERT_NE(foo1p, nullptr);
+ ASSERT_NE(nullptr, foo1p);
+ ASSERT_FALSE(foo1p == nullptr);
+ ASSERT_FALSE(nullptr == foo1p);
+
+ ASSERT_NE(foo1p, foo2p.get());
+
+ foo1p = foo2p;
+
+ ASSERT_EQ(Foo::total_constructions_, 2);
+ ASSERT_EQ(Foo::total_destructions_, 1);
+ ASSERT_EQ(foo1p, foo2p);
+
+ ASSERT_EQ(foo2p, foo2p.get());
+
+ ASSERT_EQ(RefPtr<Foo>(foo2p.get()), foo2p);
+
+ ASSERT_TRUE(foo1p);
+ }
+
+ ASSERT_EQ(Foo::total_constructions_, 2);
+ ASSERT_EQ(Foo::total_destructions_, 2);
+}
+
+TEST(nsRefPtr, AddRefHelpers)
+{
+ Foo::total_addrefs_ = 0;
+
+ {
+ Foo* raw_foo1p = new Foo;
+ raw_foo1p->AddRef();
+
+ Foo* raw_foo2p = new Foo;
+ raw_foo2p->AddRef();
+
+ ASSERT_EQ(Foo::total_addrefs_, 2);
+
+ RefPtr<Foo> foo1p( dont_AddRef(raw_foo1p) );
+
+ ASSERT_EQ(Foo::total_addrefs_, 2);
+
+ RefPtr<Foo> foo2p;
+ foo2p = dont_AddRef(raw_foo2p);
+
+ ASSERT_EQ(Foo::total_addrefs_, 2);
+ }
+
+ {
+ // Test that various assignment helpers compile.
+ RefPtr<Foo> foop;
+ CreateFoo( RefPtrGetterAddRefs<Foo>(foop) );
+ CreateFoo( getter_AddRefs(foop) );
+ set_a_Foo(address_of(foop));
+ foop = return_a_Foo();
+ }
+}
+
+TEST(nsRefPtr, QueryInterface)
+{
+ Foo::total_queries_ = 0;
+ Bar::total_queries_ = 0;
+
+ {
+ RefPtr<Foo> fooP;
+ fooP = do_QueryObject(new Foo);
+ ASSERT_EQ(Foo::total_queries_, 1);
+ }
+
+ {
+ RefPtr<Foo> fooP;
+ fooP = do_QueryObject(new Foo);
+ ASSERT_EQ(Foo::total_queries_, 2);
+
+ RefPtr<Foo> foo2P;
+ foo2P = fooP;
+ ASSERT_EQ(Foo::total_queries_, 2);
+ }
+
+ {
+ RefPtr<Bar> barP( do_QueryObject(new Bar) );
+ ASSERT_EQ(Bar::total_queries_, 1);
+
+ RefPtr<Foo> fooP( do_QueryObject(barP) );
+ ASSERT_TRUE(fooP);
+ ASSERT_EQ(Foo::total_queries_, 2);
+ ASSERT_EQ(Bar::total_queries_, 2);
+ }
+}
+
+// -------------------------------------------------------------------------
+// TODO(ER): The following tests should be moved to MFBT.
+
+#define NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING(_class) \
+public: \
+NS_METHOD_(MozExternalRefCountType) AddRef(void) const { \
+ MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
+ MOZ_RELEASE_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ nsrefcnt count = ++mRefCnt; \
+ return (nsrefcnt) count; \
+} \
+NS_METHOD_(MozExternalRefCountType) Release(void) const { \
+ MOZ_RELEASE_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ nsrefcnt count = --mRefCnt; \
+ if (count == 0) { \
+ delete (this); \
+ return 0; \
+ } \
+ return count; \
+} \
+protected: \
+mutable ::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
+public:
+
+class ObjectForConstPtr
+{
+ private:
+ // Reference-counted classes cannot have public destructors.
+ ~ObjectForConstPtr()
+ {
+ }
+ public:
+ NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING(ObjectForConstPtr)
+ void ConstMemberFunction( int aArg1, int* aArgPtr, int& aArgRef ) const
+ {
+ }
+};
+#undef NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING
+
+namespace TestNsRefPtr
+{
+void AnFooPtrPtrContext(Foo**) { }
+void AVoidPtrPtrContext(void**) { }
+} // namespace TestNsRefPtr
+
+TEST(nsRefPtr, RefPtrCompilationTests)
+{
+
+ {
+ RefPtr<Foo> fooP;
+
+ AnFooPtrPtrContext( getter_AddRefs(fooP) );
+ AVoidPtrPtrContext( getter_AddRefs(fooP) );
+ }
+
+ {
+ RefPtr<Foo> fooP(new Foo);
+ RefPtr<const Foo> constFooP = fooP;
+ constFooP->ConstMethod();
+
+ // [Shouldn't compile] Is it a compile time error to call a non-const method on an |RefPtr<const T>|?
+ //constFooP->NonconstMethod();
+
+ // [Shouldn't compile] Is it a compile time error to construct an |RefPtr<T> from an |RefPtr<const T>|?
+ //RefPtr<Foo> otherFooP(constFooP);
+ }
+
+ {
+ RefPtr<Foo> foop = new Foo;
+ RefPtr<Foo> foop2 = new Bar;
+ RefPtr<const ObjectForConstPtr> foop3 = new ObjectForConstPtr;
+ int test = 1;
+ void (Foo::*fPtr)( int, int*, int& ) = &Foo::MemberFunction;
+ void (Foo::*fVPtr)( int, int*, int& ) = &Foo::VirtualMemberFunction;
+ void (Foo::*fVCPtr)( int, int*, int& ) const = &Foo::VirtualConstMemberFunction;
+ void (ObjectForConstPtr::*fCPtr2)( int, int*, int& ) const = &ObjectForConstPtr::ConstMemberFunction;
+
+ (foop->*fPtr)(test, &test, test);
+ (foop2->*fVPtr)(test, &test, test);
+ (foop2->*fVCPtr)(test, &test, test);
+ (foop3->*fCPtr2)(test, &test, test);
+ }
+
+ // Looks like everything ran.
+ ASSERT_TRUE(true);
+}