michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: // vim:cindent:ts=4:et:sw=4: michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "TestHarness.h" michael@0: #include "nsCOMArray.h" michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: // {9e70a320-be02-11d1-8031-006008159b5a} michael@0: #define NS_IFOO_IID \ michael@0: {0x9e70a320, 0xbe02, 0x11d1, \ michael@0: {0x80, 0x31, 0x00, 0x60, 0x08, 0x15, 0x9b, 0x5a}} michael@0: michael@0: class IFoo : public nsISupports { michael@0: public: michael@0: michael@0: NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID) michael@0: michael@0: NS_IMETHOD_(MozExternalRefCountType) RefCnt() = 0; michael@0: NS_IMETHOD_(int32_t) ID() = 0; michael@0: }; michael@0: michael@0: NS_DEFINE_STATIC_IID_ACCESSOR(IFoo, NS_IFOO_IID) michael@0: michael@0: class Foo MOZ_FINAL : public IFoo { michael@0: public: michael@0: michael@0: Foo(int32_t aID); michael@0: ~Foo(); michael@0: michael@0: // nsISupports implementation michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: // IFoo implementation michael@0: NS_IMETHOD_(MozExternalRefCountType) RefCnt() { return mRefCnt; } michael@0: NS_IMETHOD_(int32_t) ID() { return mID; } michael@0: michael@0: static int32_t gCount; michael@0: michael@0: int32_t mID; michael@0: }; michael@0: michael@0: int32_t Foo::gCount = 0; michael@0: michael@0: Foo::Foo(int32_t aID) michael@0: { michael@0: mID = aID; michael@0: ++gCount; michael@0: } michael@0: michael@0: Foo::~Foo() michael@0: { michael@0: --gCount; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(Foo, IFoo) michael@0: michael@0: michael@0: typedef nsCOMArray Array; michael@0: michael@0: michael@0: // {0e70a320-be02-11d1-8031-006008159b5a} michael@0: #define NS_IBAR_IID \ michael@0: {0x0e70a320, 0xbe02, 0x11d1, \ michael@0: {0x80, 0x31, 0x00, 0x60, 0x08, 0x15, 0x9b, 0x5a}} michael@0: michael@0: class IBar : public nsISupports { michael@0: public: michael@0: michael@0: NS_DECLARE_STATIC_IID_ACCESSOR(NS_IBAR_IID) michael@0: }; michael@0: michael@0: NS_DEFINE_STATIC_IID_ACCESSOR(IBar, NS_IBAR_IID) michael@0: michael@0: class Bar MOZ_FINAL : public IBar { michael@0: public: michael@0: michael@0: explicit Bar(nsCOMArray& aArray); michael@0: ~Bar(); michael@0: michael@0: // nsISupports implementation michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: static int32_t sReleaseCalled; michael@0: michael@0: private: michael@0: nsCOMArray& mArray; michael@0: }; michael@0: michael@0: int32_t Bar::sReleaseCalled = 0; michael@0: michael@0: typedef nsCOMArray Array2; michael@0: michael@0: Bar::Bar(Array2& aArray) michael@0: : mArray(aArray) michael@0: { michael@0: } michael@0: michael@0: Bar::~Bar() michael@0: { michael@0: if (mArray.RemoveObject(this)) { michael@0: fail("We should never manage to remove the object here"); michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_ADDREF(Bar) michael@0: NS_IMPL_QUERY_INTERFACE(Bar, IBar) michael@0: michael@0: NS_IMETHODIMP_(MozExternalRefCountType) michael@0: Bar::Release(void) michael@0: { michael@0: ++Bar::sReleaseCalled; michael@0: MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); michael@0: NS_ASSERT_OWNINGTHREAD(_class); michael@0: --mRefCnt; michael@0: NS_LOG_RELEASE(this, mRefCnt, "Bar"); michael@0: if (mRefCnt == 0) { michael@0: mRefCnt = 1; /* stabilize */ michael@0: delete this; michael@0: return 0; michael@0: } michael@0: return mRefCnt; michael@0: } michael@0: michael@0: michael@0: int main(int argc, char **argv) michael@0: { michael@0: ScopedXPCOM xpcom("nsCOMArrayTests"); michael@0: if (xpcom.failed()) { michael@0: return 1; michael@0: } michael@0: michael@0: int rv = 0; michael@0: michael@0: Array arr; michael@0: michael@0: for (int32_t i = 0; i < 20; ++i) { michael@0: nsCOMPtr foo = new Foo(i); michael@0: arr.AppendObject(foo); michael@0: } michael@0: michael@0: if (arr.Count() != 20 || Foo::gCount != 20) { michael@0: fail("nsCOMArray::AppendObject failed"); michael@0: rv = 1; michael@0: } michael@0: michael@0: arr.TruncateLength(10); michael@0: michael@0: if (arr.Count() != 10 || Foo::gCount != 10) { michael@0: fail("nsCOMArray::TruncateLength shortening of array failed"); michael@0: rv = 1; michael@0: } michael@0: michael@0: arr.SetCount(30); michael@0: michael@0: if (arr.Count() != 30 || Foo::gCount != 10) { michael@0: fail("nsCOMArray::SetCount lengthening of array failed"); michael@0: rv = 1; michael@0: } michael@0: michael@0: for (int32_t i = 0; i < 10; ++i) { michael@0: if (arr[i] == nullptr) { michael@0: fail("nsCOMArray elements should be non-null"); michael@0: rv = 1; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: for (int32_t i = 10; i < 30; ++i) { michael@0: if (arr[i] != nullptr) { michael@0: fail("nsCOMArray elements should be null"); michael@0: rv = 1; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: int32_t base; michael@0: { michael@0: Array2 arr2; michael@0: michael@0: IBar *thirdObject = nullptr, michael@0: *fourthObject = nullptr, michael@0: *fifthObject = nullptr, michael@0: *ninthObject = nullptr; michael@0: for (int32_t i = 0; i < 20; ++i) { michael@0: nsCOMPtr bar = new Bar(arr2); michael@0: switch (i) { michael@0: case 2: michael@0: thirdObject = bar; break; michael@0: case 3: michael@0: fourthObject = bar; break; michael@0: case 4: michael@0: fifthObject = bar; break; michael@0: case 8: michael@0: ninthObject = bar; break; michael@0: } michael@0: arr2.AppendObject(bar); michael@0: } michael@0: michael@0: base = Bar::sReleaseCalled; michael@0: michael@0: arr2.SetCount(10); michael@0: if (Bar::sReleaseCalled != base + 10) { michael@0: fail("Release called multiple times for SetCount"); michael@0: } michael@0: if (arr2.Count() != 10) { michael@0: fail("SetCount(10) should remove exactly ten objects"); michael@0: } michael@0: michael@0: arr2.RemoveObjectAt(9); michael@0: if (Bar::sReleaseCalled != base + 11) { michael@0: fail("Release called multiple times for RemoveObjectAt"); michael@0: } michael@0: if (arr2.Count() != 9) { michael@0: fail("RemoveObjectAt should remove exactly one object"); michael@0: } michael@0: michael@0: arr2.RemoveObject(ninthObject); michael@0: if (Bar::sReleaseCalled != base + 12) { michael@0: fail("Release called multiple times for RemoveObject"); michael@0: } michael@0: if (arr2.Count() != 8) { michael@0: fail("RemoveObject should remove exactly one object"); michael@0: } michael@0: michael@0: arr2.RemoveObjectsAt(2, 3); michael@0: if (Bar::sReleaseCalled != base + 15) { michael@0: fail("Release called more or less than three times for RemoveObjectsAt"); michael@0: } michael@0: if (arr2.Count() != 5) { michael@0: fail("RemoveObjectsAt should remove exactly three objects"); michael@0: } michael@0: for (int32_t j = 0; j < arr2.Count(); ++j) { michael@0: if (arr2.ObjectAt(j) == thirdObject) { michael@0: fail("RemoveObjectsAt should have removed thirdObject"); michael@0: } michael@0: if (arr2.ObjectAt(j) == fourthObject) { michael@0: fail("RemoveObjectsAt should have removed fourthObject"); michael@0: } michael@0: if (arr2.ObjectAt(j) == fifthObject) { michael@0: fail("RemoveObjectsAt should have removed fifthObject"); michael@0: } michael@0: } michael@0: michael@0: arr2.RemoveObjectsAt(4, 1); michael@0: if (Bar::sReleaseCalled != base + 16) { michael@0: fail("Release called more or less than one time for RemoveObjectsAt"); michael@0: } michael@0: if (arr2.Count() != 4) { michael@0: fail("RemoveObjectsAt should work for removing the last element"); michael@0: } michael@0: michael@0: arr2.Clear(); michael@0: if (Bar::sReleaseCalled != base + 20) { michael@0: fail("Release called multiple times for Clear"); michael@0: } michael@0: } michael@0: michael@0: { michael@0: Array2 arr2; michael@0: michael@0: IBar *thirdElement = nullptr, michael@0: *fourthElement = nullptr, michael@0: *fifthElement = nullptr, michael@0: *ninthElement = nullptr; michael@0: for (int32_t i = 0; i < 20; ++i) { michael@0: nsCOMPtr bar = new Bar(arr2); michael@0: switch (i) { michael@0: case 2: michael@0: thirdElement = bar; break; michael@0: case 3: michael@0: fourthElement = bar; break; michael@0: case 4: michael@0: fifthElement = bar; break; michael@0: case 8: michael@0: ninthElement = bar; break; michael@0: } michael@0: arr2.AppendElement(bar); michael@0: } michael@0: michael@0: base = Bar::sReleaseCalled; michael@0: michael@0: arr2.TruncateLength(10); michael@0: if (Bar::sReleaseCalled != base + 10) { michael@0: fail("Release called multiple times for TruncateLength"); michael@0: } michael@0: if (arr2.Length() != 10) { michael@0: fail("TruncateLength(10) should remove exactly ten objects"); michael@0: } michael@0: michael@0: arr2.RemoveElementAt(9); michael@0: if (Bar::sReleaseCalled != base + 11) { michael@0: fail("Release called multiple times for RemoveElementAt"); michael@0: } michael@0: if (arr2.Length() != 9) { michael@0: fail("RemoveElementAt should remove exactly one object"); michael@0: } michael@0: michael@0: arr2.RemoveElement(ninthElement); michael@0: if (Bar::sReleaseCalled != base + 12) { michael@0: fail("Release called multiple times for RemoveElement"); michael@0: } michael@0: if (arr2.Length() != 8) { michael@0: fail("RemoveElement should remove exactly one object"); michael@0: } michael@0: michael@0: arr2.RemoveElementsAt(2, 3); michael@0: if (Bar::sReleaseCalled != base + 15) { michael@0: fail("Release called more or less than three times for RemoveElementsAt"); michael@0: } michael@0: if (arr2.Length() != 5) { michael@0: fail("RemoveElementsAt should remove exactly three objects"); michael@0: } michael@0: for (uint32_t j = 0; j < arr2.Length(); ++j) { michael@0: if (arr2.ElementAt(j) == thirdElement) { michael@0: fail("RemoveElementsAt should have removed thirdElement"); michael@0: } michael@0: if (arr2.ElementAt(j) == fourthElement) { michael@0: fail("RemoveElementsAt should have removed fourthElement"); michael@0: } michael@0: if (arr2.ElementAt(j) == fifthElement) { michael@0: fail("RemoveElementsAt should have removed fifthElement"); michael@0: } michael@0: } michael@0: michael@0: arr2.RemoveElementsAt(4, 1); michael@0: if (Bar::sReleaseCalled != base + 16) { michael@0: fail("Release called more or less than one time for RemoveElementsAt"); michael@0: } michael@0: if (arr2.Length() != 4) { michael@0: fail("RemoveElementsAt should work for removing the last element"); michael@0: } michael@0: michael@0: arr2.Clear(); michael@0: if (Bar::sReleaseCalled != base + 20) { michael@0: fail("Release called multiple times for Clear"); michael@0: } michael@0: } michael@0: michael@0: Bar::sReleaseCalled = 0; michael@0: michael@0: { michael@0: Array2 arr2; michael@0: michael@0: for (int32_t i = 0; i < 20; ++i) { michael@0: nsCOMPtr bar = new Bar(arr2); michael@0: arr2.AppendObject(bar); michael@0: } michael@0: michael@0: base = Bar::sReleaseCalled; michael@0: michael@0: // Let arr2 be destroyed michael@0: } michael@0: if (Bar::sReleaseCalled != base + 20) { michael@0: fail("Release called multiple times for nsCOMArray::~nsCOMArray"); michael@0: } michael@0: michael@0: return rv; michael@0: }