/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
// vim:cindent:ts=4:et:sw=4:
/* 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 "nsCOMArray.h"
#include "nsCOMPtr.h"
#include "nsISupports.h"
#include "gtest/gtest.h"
// {9e70a320-be02-11d1-8031-006008159b5a}
#define NS_IFOO_IID \
{ \
0x9e70a320,
0xbe02,
0x11d1, { \
0x80,
0x31,
0x00,
0x60,
0x08,
0x15,
0x9b,
0x5a \
} \
}
class IFoo :
public nsISupports {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
NS_IMETHOD_(MozExternalRefCountType) RefCnt() =
0;
NS_IMETHOD_(int32_t) ID() =
0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(IFoo, NS_IFOO_IID)
class Foo final :
public IFoo {
~Foo();
public:
explicit Foo(int32_t aID);
// nsISupports implementation
NS_DECL_ISUPPORTS
// IFoo implementation
NS_IMETHOD_(MozExternalRefCountType) RefCnt() override {
return mRefCnt; }
NS_IMETHOD_(int32_t) ID() override {
return mID; }
static int32_t gCount;
int32_t mID;
};
int32_t Foo::gCount =
0;
Foo::Foo(int32_t aID) {
mID = aID;
++gCount;
}
Foo::~Foo() { --gCount; }
NS_IMPL_ISUPPORTS(Foo, IFoo)
// {0e70a320-be02-11d1-8031-006008159b5a}
#define NS_IBAR_IID \
{ \
0x0e70a320,
0xbe02,
0x11d1, { \
0x80,
0x31,
0x00,
0x60,
0x08,
0x15,
0x9b,
0x5a \
} \
}
class IBar :
public nsISupports {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IBAR_IID)
};
NS_DEFINE_STATIC_IID_ACCESSOR(IBar, NS_IBAR_IID)
class Bar final :
public IBar {
public:
explicit Bar(nsCOMArray<IBar>& aArray);
// nsISupports implementation
NS_DECL_ISUPPORTS
static int32_t sReleaseCalled;
private:
~Bar();
nsCOMArray<IBar>& mArray;
};
int32_t Bar::sReleaseCalled =
0;
typedef nsCOMArray<IBar> Array2;
Bar::Bar(Array2& aArray) : mArray(aArray) {}
Bar::~Bar() { EXPECT_FALSE(mArray.RemoveObject(
this)); }
NS_IMPL_ADDREF(Bar)
NS_IMPL_QUERY_INTERFACE(Bar, IBar)
NS_IMETHODIMP_(MozExternalRefCountType)
Bar::Release(
void) {
++Bar::sReleaseCalled;
EXPECT_GT(
int(mRefCnt),
0);
NS_ASSERT_OWNINGTHREAD(_
class);
--mRefCnt;
NS_LOG_RELEASE(
this, mRefCnt,
"Bar");
if (mRefCnt ==
0) {
mRefCnt =
1;
/* stabilize */
delete this;
return 0;
}
return mRefCnt;
}
TEST(COMArray, Sizing)
{
nsCOMArray<IFoo> arr;
for (int32_t i =
0; i <
20; ++i) {
nsCOMPtr<IFoo> foo =
new Foo(i);
arr.AppendObject(foo);
}
ASSERT_EQ(arr.Count(), int32_t(
20));
ASSERT_EQ(Foo::gCount, int32_t(
20));
arr.TruncateLength(
10);
ASSERT_EQ(arr.Count(), int32_t(
10));
ASSERT_EQ(Foo::gCount, int32_t(
10));
arr.SetCount(
30);
ASSERT_EQ(arr.Count(), int32_t(
30));
ASSERT_EQ(Foo::gCount, int32_t(
10));
for (int32_t i =
0; i <
10; ++i) {
ASSERT_NE(arr[i], nullptr);
}
for (int32_t i =
10; i <
30; ++i) {
ASSERT_EQ(arr[i], nullptr);
}
}
TEST(COMArray, ObjectFunctions)
{
int32_t base;
{
nsCOMArray<IBar> arr2;
IBar *thirdObject = nullptr, *fourthObject = nullptr,
*fifthObject = nullptr, *ninthObject = nullptr;
for (int32_t i =
0; i <
20; ++i) {
nsCOMPtr<IBar> bar =
new Bar(arr2);
switch (i) {
case 2:
thirdObject = bar;
break;
case 3:
fourthObject = bar;
break;
case 4:
fifthObject = bar;
break;
case 8:
ninthObject = bar;
break;
}
arr2.AppendObject(bar);
}
base = Bar::sReleaseCalled;
arr2.SetCount(
10);
ASSERT_EQ(Bar::sReleaseCalled, base +
10);
ASSERT_EQ(arr2.Count(), int32_t(
10));
arr2.RemoveObjectAt(
9);
ASSERT_EQ(Bar::sReleaseCalled, base +
11);
ASSERT_EQ(arr2.Count(), int32_t(
9));
arr2.RemoveObject(ninthObject);
ASSERT_EQ(Bar::sReleaseCalled, base +
12);
ASSERT_EQ(arr2.Count(), int32_t(
8));
arr2.RemoveObjectsAt(
2,
3);
ASSERT_EQ(Bar::sReleaseCalled, base +
15);
ASSERT_EQ(arr2.Count(), int32_t(
5));
for (int32_t j =
0; j < arr2.Count(); ++j) {
ASSERT_NE(arr2.ObjectAt(j), thirdObject);
ASSERT_NE(arr2.ObjectAt(j), fourthObject);
ASSERT_NE(arr2.ObjectAt(j), fifthObject);
}
arr2.RemoveObjectsAt(
4,
1);
ASSERT_EQ(Bar::sReleaseCalled, base +
16);
ASSERT_EQ(arr2.Count(), int32_t(
4));
arr2.Clear();
ASSERT_EQ(Bar::sReleaseCalled, base +
20);
}
}
TEST(COMArray, ElementFunctions)
{
int32_t base;
{
nsCOMArray<IBar> arr2;
IBar *thirdElement = nullptr, *fourthElement = nullptr,
*fifthElement = nullptr, *ninthElement = nullptr;
for (int32_t i =
0; i <
20; ++i) {
nsCOMPtr<IBar> bar =
new Bar(arr2);
switch (i) {
case 2:
thirdElement = bar;
break;
case 3:
fourthElement = bar;
break;
case 4:
fifthElement = bar;
break;
case 8:
ninthElement = bar;
break;
}
arr2.AppendElement(bar);
}
base = Bar::sReleaseCalled;
arr2.TruncateLength(
10);
ASSERT_EQ(Bar::sReleaseCalled, base +
10);
ASSERT_EQ(arr2.Length(), uint32_t(
10));
arr2.RemoveElementAt(
9);
ASSERT_EQ(Bar::sReleaseCalled, base +
11);
ASSERT_EQ(arr2.Length(), uint32_t(
9));
arr2.RemoveElement(ninthElement);
ASSERT_EQ(Bar::sReleaseCalled, base +
12);
ASSERT_EQ(arr2.Length(), uint32_t(
8));
arr2.RemoveElementsAt(
2,
3);
ASSERT_EQ(Bar::sReleaseCalled, base +
15);
ASSERT_EQ(arr2.Length(), uint32_t(
5));
for (uint32_t j =
0; j < arr2.Length(); ++j) {
ASSERT_NE(arr2.ElementAt(j), thirdElement);
ASSERT_NE(arr2.ElementAt(j), fourthElement);
ASSERT_NE(arr2.ElementAt(j), fifthElement);
}
arr2.RemoveElementsAt(
4,
1);
ASSERT_EQ(Bar::sReleaseCalled, base +
16);
ASSERT_EQ(arr2.Length(), uint32_t(
4));
arr2.Clear();
ASSERT_EQ(Bar::sReleaseCalled, base +
20);
}
}
TEST(COMArray, Destructor)
{
int32_t base;
Bar::sReleaseCalled =
0;
{
nsCOMArray<IBar> arr2;
for (int32_t i =
0; i <
20; ++i) {
nsCOMPtr<IBar> bar =
new Bar(arr2);
arr2.AppendObject(bar);
}
base = Bar::sReleaseCalled;
// Let arr2 be destroyed
}
ASSERT_EQ(Bar::sReleaseCalled, base +
20);
}
TEST(COMArray, ConvertIteratorToConstIterator)
{
nsCOMArray<IFoo> array;
for (int32_t i =
0; i <
20; ++i) {
nsCOMPtr<IFoo> foo =
new Foo(i);
array.AppendObject(foo);
}
nsCOMArray<IFoo>::const_iterator it = array.begin();
ASSERT_EQ(array.cbegin(), it);
}