michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ 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 "mozilla/ArrayUtils.h" michael@0: michael@0: #include michael@0: #include michael@0: #include "nsTArray.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsStringAPI.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsDirectoryServiceUtils.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsXPCOM.h" michael@0: #include "nsIFile.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: namespace TestTArray { michael@0: michael@0: // Define this so we can use test_basic_array in test_comptr_array michael@0: template michael@0: inline bool operator<(const nsCOMPtr& lhs, const nsCOMPtr& rhs) { michael@0: return lhs.get() < rhs.get(); michael@0: } michael@0: michael@0: //---- michael@0: michael@0: template michael@0: static bool test_basic_array(ElementType *data, michael@0: uint32_t dataLen, michael@0: const ElementType& extra) { michael@0: nsTArray ary; michael@0: ary.AppendElements(data, dataLen); michael@0: if (ary.Length() != dataLen) { michael@0: return false; michael@0: } michael@0: if (!(ary == ary)) { michael@0: return false; michael@0: } michael@0: uint32_t i; michael@0: for (i = 0; i < ary.Length(); ++i) { michael@0: if (ary[i] != data[i]) michael@0: return false; michael@0: } michael@0: for (i = 0; i < ary.Length(); ++i) { michael@0: if (ary.SafeElementAt(i, extra) != data[i]) michael@0: return false; michael@0: } michael@0: if (ary.SafeElementAt(ary.Length(), extra) != extra || michael@0: ary.SafeElementAt(ary.Length() * 10, extra) != extra) michael@0: return false; michael@0: // ensure sort results in ascending order michael@0: ary.Sort(); michael@0: uint32_t j = 0, k = ary.IndexOfFirstElementGt(extra); michael@0: if (k != 0 && ary[k-1] == extra) michael@0: return false; michael@0: for (i = 0; i < ary.Length(); ++i) { michael@0: k = ary.IndexOfFirstElementGt(ary[i]); michael@0: if (k == 0 || ary[k-1] != ary[i]) michael@0: return false; michael@0: if (k < j) michael@0: return false; michael@0: j = k; michael@0: } michael@0: for (i = ary.Length(); --i; ) { michael@0: if (ary[i] < ary[i - 1]) michael@0: return false; michael@0: if (ary[i] == ary[i - 1]) michael@0: ary.RemoveElementAt(i); michael@0: } michael@0: if (!(ary == ary)) { michael@0: return false; michael@0: } michael@0: for (i = 0; i < ary.Length(); ++i) { michael@0: if (ary.BinaryIndexOf(ary[i]) != i) michael@0: return false; michael@0: } michael@0: if (ary.BinaryIndexOf(extra) != ary.NoIndex) michael@0: return false; michael@0: uint32_t oldLen = ary.Length(); michael@0: ary.RemoveElement(data[dataLen / 2]); michael@0: if (ary.Length() != (oldLen - 1)) michael@0: return false; michael@0: if (!(ary == ary)) michael@0: return false; michael@0: michael@0: uint32_t index = ary.Length() / 2; michael@0: if (!ary.InsertElementAt(index, extra)) michael@0: return false; michael@0: if (!(ary == ary)) michael@0: return false; michael@0: if (ary[index] != extra) michael@0: return false; michael@0: if (ary.IndexOf(extra) == UINT32_MAX) michael@0: return false; michael@0: if (ary.LastIndexOf(extra) == UINT32_MAX) michael@0: return false; michael@0: // ensure proper searching michael@0: if (ary.IndexOf(extra) > ary.LastIndexOf(extra)) michael@0: return false; michael@0: if (ary.IndexOf(extra, index) != ary.LastIndexOf(extra, index)) michael@0: return false; michael@0: michael@0: nsTArray copy(ary); michael@0: if (!(ary == copy)) michael@0: return false; michael@0: for (i = 0; i < copy.Length(); ++i) { michael@0: if (ary[i] != copy[i]) michael@0: return false; michael@0: } michael@0: if (!ary.AppendElements(copy)) michael@0: return false; michael@0: uint32_t cap = ary.Capacity(); michael@0: ary.RemoveElementsAt(copy.Length(), copy.Length()); michael@0: ary.Compact(); michael@0: if (ary.Capacity() == cap) michael@0: return false; michael@0: michael@0: ary.Clear(); michael@0: if (ary.IndexOf(extra) != UINT32_MAX) michael@0: return false; michael@0: if (ary.LastIndexOf(extra) != UINT32_MAX) michael@0: return false; michael@0: michael@0: ary.Clear(); michael@0: if (!ary.IsEmpty() || ary.Elements() == nullptr) michael@0: return false; michael@0: if (!(ary == nsTArray())) michael@0: return false; michael@0: if (ary == copy) michael@0: return false; michael@0: if (ary.SafeElementAt(0, extra) != extra || michael@0: ary.SafeElementAt(10, extra) != extra) michael@0: return false; michael@0: michael@0: ary = copy; michael@0: if (!(ary == copy)) michael@0: return false; michael@0: for (i = 0; i < copy.Length(); ++i) { michael@0: if (ary[i] != copy[i]) michael@0: return false; michael@0: } michael@0: michael@0: if (!ary.InsertElementsAt(0, copy)) michael@0: return false; michael@0: if (ary == copy) michael@0: return false; michael@0: ary.RemoveElementsAt(0, copy.Length()); michael@0: for (i = 0; i < copy.Length(); ++i) { michael@0: if (ary[i] != copy[i]) michael@0: return false; michael@0: } michael@0: michael@0: // These shouldn't crash! michael@0: nsTArray empty; michael@0: ary.AppendElements(reinterpret_cast(0), 0); michael@0: ary.AppendElements(empty); michael@0: michael@0: // See bug 324981 michael@0: ary.RemoveElement(extra); michael@0: ary.RemoveElement(extra); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static bool test_int_array() { michael@0: int data[] = {4,6,8,2,4,1,5,7,3}; michael@0: return test_basic_array(data, ArrayLength(data), int(14)); michael@0: } michael@0: michael@0: static bool test_int64_array() { michael@0: int64_t data[] = {4,6,8,2,4,1,5,7,3}; michael@0: return test_basic_array(data, ArrayLength(data), int64_t(14)); michael@0: } michael@0: michael@0: static bool test_char_array() { michael@0: char data[] = {4,6,8,2,4,1,5,7,3}; michael@0: return test_basic_array(data, ArrayLength(data), char(14)); michael@0: } michael@0: michael@0: static bool test_uint32_array() { michael@0: uint32_t data[] = {4,6,8,2,4,1,5,7,3}; michael@0: return test_basic_array(data, ArrayLength(data), uint32_t(14)); michael@0: } michael@0: michael@0: //---- michael@0: michael@0: class Object { michael@0: public: michael@0: Object() : mNum(0) { michael@0: } michael@0: Object(const char *str, uint32_t num) : mStr(str), mNum(num) { michael@0: } michael@0: Object(const Object& other) : mStr(other.mStr), mNum(other.mNum) { michael@0: } michael@0: ~Object() {} michael@0: michael@0: Object& operator=(const Object& other) { michael@0: mStr = other.mStr; michael@0: mNum = other.mNum; michael@0: return *this; michael@0: } michael@0: michael@0: bool operator==(const Object& other) const { michael@0: return mStr == other.mStr && mNum == other.mNum; michael@0: } michael@0: michael@0: bool operator<(const Object& other) const { michael@0: // sort based on mStr only michael@0: return mStr.Compare(other.mStr) < 0; michael@0: } michael@0: michael@0: const char *Str() const { return mStr.get(); } michael@0: uint32_t Num() const { return mNum; } michael@0: michael@0: private: michael@0: nsCString mStr; michael@0: uint32_t mNum; michael@0: }; michael@0: michael@0: static bool test_object_array() { michael@0: nsTArray objArray; michael@0: const char kdata[] = "hello world"; michael@0: uint32_t i; michael@0: for (i = 0; i < ArrayLength(kdata); ++i) { michael@0: char x[] = {kdata[i],'\0'}; michael@0: if (!objArray.AppendElement(Object(x, i))) michael@0: return false; michael@0: } michael@0: for (i = 0; i < ArrayLength(kdata); ++i) { michael@0: if (objArray[i].Str()[0] != kdata[i]) michael@0: return false; michael@0: if (objArray[i].Num() != i) michael@0: return false; michael@0: } michael@0: objArray.Sort(); michael@0: const char ksorted[] = "\0 dehllloorw"; michael@0: for (i = 0; i < ArrayLength(kdata)-1; ++i) { michael@0: if (objArray[i].Str()[0] != ksorted[i]) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // nsTArray> is not supported michael@0: #if 0 michael@0: static bool test_autoptr_array() { michael@0: nsTArray< nsAutoPtr > objArray; michael@0: const char kdata[] = "hello world"; michael@0: for (uint32_t i = 0; i < ArrayLength(kdata); ++i) { michael@0: char x[] = {kdata[i],'\0'}; michael@0: nsAutoPtr obj(new Object(x,i)); michael@0: if (!objArray.AppendElement(obj)) // XXX does not call copy-constructor for nsAutoPtr!!! michael@0: return false; michael@0: if (obj.get() == nullptr) michael@0: return false; michael@0: obj.forget(); // the array now owns the reference michael@0: } michael@0: for (uint32_t i = 0; i < ArrayLength(kdata); ++i) { michael@0: if (objArray[i]->Str()[0] != kdata[i]) michael@0: return false; michael@0: if (objArray[i]->Num() != i) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: #endif michael@0: michael@0: //---- michael@0: michael@0: static bool test_string_array() { michael@0: nsTArray strArray; michael@0: const char kdata[] = "hello world"; michael@0: uint32_t i; michael@0: for (i = 0; i < ArrayLength(kdata); ++i) { michael@0: nsCString str; michael@0: str.Assign(kdata[i]); michael@0: if (!strArray.AppendElement(str)) michael@0: return false; michael@0: } michael@0: for (i = 0; i < ArrayLength(kdata); ++i) { michael@0: if (strArray[i].CharAt(0) != kdata[i]) michael@0: return false; michael@0: } michael@0: michael@0: const char kextra[] = "foo bar"; michael@0: uint32_t oldLen = strArray.Length(); michael@0: if (!strArray.AppendElement(kextra)) michael@0: return false; michael@0: strArray.RemoveElement(kextra); michael@0: if (oldLen != strArray.Length()) michael@0: return false; michael@0: michael@0: if (strArray.IndexOf("e") != 1) michael@0: return false; michael@0: michael@0: strArray.Sort(); michael@0: const char ksorted[] = "\0 dehllloorw"; michael@0: for (i = ArrayLength(kdata); i--; ) { michael@0: if (strArray[i].CharAt(0) != ksorted[i]) michael@0: return false; michael@0: if (i > 0 && strArray[i] == strArray[i - 1]) michael@0: strArray.RemoveElementAt(i); michael@0: } michael@0: for (i = 0; i < strArray.Length(); ++i) { michael@0: if (strArray.BinaryIndexOf(strArray[i]) != i) michael@0: return false; michael@0: } michael@0: if (strArray.BinaryIndexOf(EmptyCString()) != strArray.NoIndex) michael@0: return false; michael@0: michael@0: nsCString rawArray[MOZ_ARRAY_LENGTH(kdata) - 1]; michael@0: for (i = 0; i < ArrayLength(rawArray); ++i) michael@0: rawArray[i].Assign(kdata + i); // substrings of kdata michael@0: return test_basic_array(rawArray, ArrayLength(rawArray), michael@0: nsCString("foopy")); michael@0: } michael@0: michael@0: //---- michael@0: michael@0: typedef nsCOMPtr FilePointer; michael@0: michael@0: class nsFileNameComparator { michael@0: public: michael@0: bool Equals(const FilePointer &a, const char *b) const { michael@0: nsAutoCString name; michael@0: a->GetNativeLeafName(name); michael@0: return name.Equals(b); michael@0: } michael@0: }; michael@0: michael@0: static bool test_comptr_array() { michael@0: FilePointer tmpDir; michael@0: NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpDir)); michael@0: if (!tmpDir) michael@0: return false; michael@0: const char *kNames[] = { michael@0: "foo.txt", "bar.html", "baz.gif" michael@0: }; michael@0: nsTArray fileArray; michael@0: uint32_t i; michael@0: for (i = 0; i < ArrayLength(kNames); ++i) { michael@0: FilePointer f; michael@0: tmpDir->Clone(getter_AddRefs(f)); michael@0: if (!f) michael@0: return false; michael@0: if (NS_FAILED(f->AppendNative(nsDependentCString(kNames[i])))) michael@0: return false; michael@0: fileArray.AppendElement(f); michael@0: } michael@0: michael@0: if (fileArray.IndexOf(kNames[1], 0, nsFileNameComparator()) != 1) michael@0: return false; michael@0: michael@0: // It's unclear what 'operator<' means for nsCOMPtr, but whatever... michael@0: return test_basic_array(fileArray.Elements(), fileArray.Length(), michael@0: tmpDir); michael@0: } michael@0: michael@0: //---- michael@0: michael@0: class RefcountedObject { michael@0: public: michael@0: RefcountedObject() : rc(0) {} michael@0: void AddRef() { michael@0: ++rc; michael@0: } michael@0: void Release() { michael@0: if (--rc == 0) michael@0: delete this; michael@0: } michael@0: ~RefcountedObject() {} michael@0: private: michael@0: int32_t rc; michael@0: }; michael@0: michael@0: static bool test_refptr_array() { michael@0: bool rv = true; michael@0: michael@0: nsTArray< nsRefPtr > objArray; michael@0: michael@0: RefcountedObject *a = new RefcountedObject(); a->AddRef(); michael@0: RefcountedObject *b = new RefcountedObject(); b->AddRef(); michael@0: RefcountedObject *c = new RefcountedObject(); c->AddRef(); michael@0: michael@0: objArray.AppendElement(a); michael@0: objArray.AppendElement(b); michael@0: objArray.AppendElement(c); michael@0: michael@0: if (objArray.IndexOf(b) != 1) michael@0: rv = false; michael@0: michael@0: a->Release(); michael@0: b->Release(); michael@0: c->Release(); michael@0: return rv; michael@0: } michael@0: michael@0: //---- michael@0: michael@0: static bool test_ptrarray() { michael@0: nsTArray ary; michael@0: if (ary.SafeElementAt(0) != nullptr) michael@0: return false; michael@0: if (ary.SafeElementAt(1000) != nullptr) michael@0: return false; michael@0: uint32_t a = 10; michael@0: ary.AppendElement(&a); michael@0: if (*ary[0] != a) michael@0: return false; michael@0: if (*ary.SafeElementAt(0) != a) michael@0: return false; michael@0: michael@0: nsTArray cary; michael@0: if (cary.SafeElementAt(0) != nullptr) michael@0: return false; michael@0: if (cary.SafeElementAt(1000) != nullptr) michael@0: return false; michael@0: const uint32_t b = 14; michael@0: cary.AppendElement(&a); michael@0: cary.AppendElement(&b); michael@0: if (*cary[0] != a || *cary[1] != b) michael@0: return false; michael@0: if (*cary.SafeElementAt(0) != a || *cary.SafeElementAt(1) != b) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: //---- michael@0: michael@0: // This test relies too heavily on the existence of DebugGetHeader to be michael@0: // useful in non-debug builds. michael@0: #ifdef DEBUG michael@0: static bool test_autoarray() { michael@0: uint32_t data[] = {4,6,8,2,4,1,5,7,3}; michael@0: nsAutoTArray array; michael@0: michael@0: void* hdr = array.DebugGetHeader(); michael@0: if (hdr == nsTArray().DebugGetHeader()) michael@0: return false; michael@0: if (hdr == nsAutoTArray().DebugGetHeader()) michael@0: return false; michael@0: michael@0: array.AppendElement(1u); michael@0: if (hdr != array.DebugGetHeader()) michael@0: return false; michael@0: michael@0: array.RemoveElement(1u); michael@0: array.AppendElements(data, ArrayLength(data)); michael@0: if (hdr != array.DebugGetHeader()) michael@0: return false; michael@0: michael@0: array.AppendElement(2u); michael@0: if (hdr == array.DebugGetHeader()) michael@0: return false; michael@0: michael@0: array.Clear(); michael@0: array.Compact(); michael@0: if (hdr != array.DebugGetHeader()) michael@0: return false; michael@0: array.AppendElements(data, ArrayLength(data)); michael@0: if (hdr != array.DebugGetHeader()) michael@0: return false; michael@0: michael@0: nsTArray array2; michael@0: void* emptyHdr = array2.DebugGetHeader(); michael@0: array.SwapElements(array2); michael@0: if (emptyHdr == array.DebugGetHeader()) michael@0: return false; michael@0: if (hdr == array2.DebugGetHeader()) michael@0: return false; michael@0: uint32_t i; michael@0: for (i = 0; i < ArrayLength(data); ++i) { michael@0: if (array2[i] != data[i]) michael@0: return false; michael@0: } michael@0: if (!array.IsEmpty()) michael@0: return false; michael@0: michael@0: array.Compact(); michael@0: array.AppendElements(data, ArrayLength(data)); michael@0: uint32_t data3[] = {5, 7, 11}; michael@0: nsAutoTArray array3; michael@0: array3.AppendElements(data3, ArrayLength(data3)); michael@0: array.SwapElements(array3); michael@0: for (i = 0; i < ArrayLength(data); ++i) { michael@0: if (array3[i] != data[i]) michael@0: return false; michael@0: } michael@0: for (i = 0; i < ArrayLength(data3); ++i) { michael@0: if (array[i] != data3[i]) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: #endif michael@0: michael@0: //---- michael@0: michael@0: // IndexOf used to potentially scan beyond the end of the array. Test for michael@0: // this incorrect behavior by adding a value (5), removing it, then seeing michael@0: // if IndexOf finds it. michael@0: static bool test_indexof() { michael@0: nsTArray array; michael@0: array.AppendElement(0); michael@0: // add and remove the 5 michael@0: array.AppendElement(5); michael@0: array.RemoveElementAt(1); michael@0: // we should not find the 5! michael@0: return array.IndexOf(5, 1) == array.NoIndex; michael@0: } michael@0: michael@0: //---- michael@0: michael@0: template michael@0: static bool is_heap(const Array& ary, uint32_t len) { michael@0: uint32_t index = 1; michael@0: while (index < len) { michael@0: if (ary[index] > ary[(index - 1) >> 1]) michael@0: return false; michael@0: index++; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static bool test_heap() { michael@0: const int data[] = {4,6,8,2,4,1,5,7,3}; michael@0: nsTArray ary; michael@0: ary.AppendElements(data, ArrayLength(data)); michael@0: // make a heap and make sure it's a heap michael@0: ary.MakeHeap(); michael@0: if (!is_heap(ary, ArrayLength(data))) michael@0: return false; michael@0: // pop the root and make sure it's still a heap michael@0: int root = ary[0]; michael@0: ary.PopHeap(); michael@0: if (!is_heap(ary, ArrayLength(data) - 1)) michael@0: return false; michael@0: // push the previously poped value back on and make sure it's still a heap michael@0: ary.PushHeap(root); michael@0: if (!is_heap(ary, ArrayLength(data))) michael@0: return false; michael@0: // make sure the heap looks like what we expect michael@0: const int expected_data[] = {8,7,5,6,4,1,4,2,3}; michael@0: uint32_t index; michael@0: for (index = 0; index < ArrayLength(data); index++) michael@0: if (ary[index] != expected_data[index]) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: //---- michael@0: michael@0: // An array |arr| is using its auto buffer if |&arr < arr.Elements()| and michael@0: // |arr.Elements() - &arr| is small. michael@0: michael@0: #define IS_USING_AUTO(arr) \ michael@0: ((uintptr_t) &(arr) < (uintptr_t) arr.Elements() && \ michael@0: ((ptrdiff_t)arr.Elements() - (ptrdiff_t)&arr) <= 16) michael@0: michael@0: #define CHECK_IS_USING_AUTO(arr) \ michael@0: do { \ michael@0: if (!(IS_USING_AUTO(arr))) { \ michael@0: printf("%s:%d CHECK_IS_USING_AUTO(%s) failed.\n", \ michael@0: __FILE__, __LINE__, #arr); \ michael@0: return false; \ michael@0: } \ michael@0: } while(0) michael@0: michael@0: #define CHECK_NOT_USING_AUTO(arr) \ michael@0: do { \ michael@0: if (IS_USING_AUTO(arr)) { \ michael@0: printf("%s:%d CHECK_NOT_USING_AUTO(%s) failed.\n", \ michael@0: __FILE__, __LINE__, #arr); \ michael@0: return false; \ michael@0: } \ michael@0: } while(0) michael@0: michael@0: #define CHECK_USES_SHARED_EMPTY_HDR(arr) \ michael@0: do { \ michael@0: nsTArray _empty; \ michael@0: if (_empty.Elements() != arr.Elements()) { \ michael@0: printf("%s:%d CHECK_USES_EMPTY_HDR(%s) failed.\n", \ michael@0: __FILE__, __LINE__, #arr); \ michael@0: return false; \ michael@0: } \ michael@0: } while(0) michael@0: michael@0: #define CHECK_EQ_INT(actual, expected) \ michael@0: do { \ michael@0: if ((actual) != (expected)) { \ michael@0: printf("%s:%d CHECK_EQ_INT(%s=%u, %s=%u) failed.\n", \ michael@0: __FILE__, __LINE__, #actual, (actual), #expected, (expected)); \ michael@0: return false; \ michael@0: } \ michael@0: } while(0) michael@0: michael@0: #define CHECK_ARRAY(arr, data) \ michael@0: do { \ michael@0: CHECK_EQ_INT((arr).Length(), (uint32_t)ArrayLength(data)); \ michael@0: for (uint32_t _i = 0; _i < ArrayLength(data); _i++) { \ michael@0: CHECK_EQ_INT((arr)[_i], (data)[_i]); \ michael@0: } \ michael@0: } while(0) michael@0: michael@0: static bool test_swap() { michael@0: // Test nsTArray::SwapElements. Unfortunately there are many cases. michael@0: int data1[] = {8, 6, 7, 5}; michael@0: int data2[] = {3, 0, 9}; michael@0: michael@0: // Swap two auto arrays. michael@0: { michael@0: nsAutoTArray a; michael@0: nsAutoTArray b; michael@0: michael@0: a.AppendElements(data1, ArrayLength(data1)); michael@0: b.AppendElements(data2, ArrayLength(data2)); michael@0: CHECK_IS_USING_AUTO(a); michael@0: CHECK_IS_USING_AUTO(b); michael@0: michael@0: a.SwapElements(b); michael@0: michael@0: CHECK_IS_USING_AUTO(a); michael@0: CHECK_IS_USING_AUTO(b); michael@0: CHECK_ARRAY(a, data2); michael@0: CHECK_ARRAY(b, data1); michael@0: } michael@0: michael@0: // Swap two auto arrays -- one whose data lives on the heap, the other whose michael@0: // data lives on the stack -- which each fits into the other's auto storage. michael@0: { michael@0: nsAutoTArray a; michael@0: nsAutoTArray b; michael@0: michael@0: a.AppendElements(data1, ArrayLength(data1)); michael@0: a.RemoveElementAt(3); michael@0: b.AppendElements(data2, ArrayLength(data2)); michael@0: michael@0: // Here and elsewhere, we assert that if we start with an auto array michael@0: // capable of storing N elements, we store N+1 elements into the array, and michael@0: // then we remove one element, that array is still not using its auto michael@0: // buffer. michael@0: // michael@0: // This isn't at all required by the TArray API. It would be fine if, when michael@0: // we shrink back to N elements, the TArray frees its heap storage and goes michael@0: // back to using its stack storage. But we assert here as a check that the michael@0: // test does what we expect. If the TArray implementation changes, just michael@0: // change the failing assertions. michael@0: CHECK_NOT_USING_AUTO(a); michael@0: michael@0: // This check had better not change, though. michael@0: CHECK_IS_USING_AUTO(b); michael@0: michael@0: a.SwapElements(b); michael@0: michael@0: CHECK_IS_USING_AUTO(b); michael@0: CHECK_ARRAY(a, data2); michael@0: int expectedB[] = {8, 6, 7}; michael@0: CHECK_ARRAY(b, expectedB); michael@0: } michael@0: michael@0: // Swap two auto arrays which are using heap storage such that one fits into michael@0: // the other's auto storage, but the other needs to stay on the heap. michael@0: { michael@0: nsAutoTArray a; michael@0: nsAutoTArray b; michael@0: a.AppendElements(data1, ArrayLength(data1)); michael@0: a.RemoveElementAt(3); michael@0: michael@0: b.AppendElements(data2, ArrayLength(data2)); michael@0: b.RemoveElementAt(2); michael@0: michael@0: CHECK_NOT_USING_AUTO(a); michael@0: CHECK_NOT_USING_AUTO(b); michael@0: michael@0: a.SwapElements(b); michael@0: michael@0: CHECK_NOT_USING_AUTO(b); michael@0: michael@0: int expected1[] = {3, 0}; michael@0: int expected2[] = {8, 6, 7}; michael@0: michael@0: CHECK_ARRAY(a, expected1); michael@0: CHECK_ARRAY(b, expected2); michael@0: } michael@0: michael@0: // Swap two arrays, neither of which fits into the other's auto-storage. michael@0: { michael@0: nsAutoTArray a; michael@0: nsAutoTArray b; michael@0: michael@0: a.AppendElements(data1, ArrayLength(data1)); michael@0: b.AppendElements(data2, ArrayLength(data2)); michael@0: michael@0: a.SwapElements(b); michael@0: michael@0: CHECK_ARRAY(a, data2); michael@0: CHECK_ARRAY(b, data1); michael@0: } michael@0: michael@0: // Swap an empty nsTArray with a non-empty nsAutoTArray. michael@0: { michael@0: nsTArray a; michael@0: nsAutoTArray b; michael@0: michael@0: b.AppendElements(data2, ArrayLength(data2)); michael@0: CHECK_IS_USING_AUTO(b); michael@0: michael@0: a.SwapElements(b); michael@0: michael@0: CHECK_ARRAY(a, data2); michael@0: CHECK_EQ_INT(b.Length(), 0); michael@0: CHECK_IS_USING_AUTO(b); michael@0: } michael@0: michael@0: // Swap two big auto arrays. michael@0: { michael@0: const unsigned size = 8192; michael@0: nsAutoTArray a; michael@0: nsAutoTArray b; michael@0: michael@0: for (unsigned i = 0; i < size; i++) { michael@0: a.AppendElement(i); michael@0: b.AppendElement(i + 1); michael@0: } michael@0: michael@0: CHECK_IS_USING_AUTO(a); michael@0: CHECK_IS_USING_AUTO(b); michael@0: michael@0: a.SwapElements(b); michael@0: michael@0: CHECK_IS_USING_AUTO(a); michael@0: CHECK_IS_USING_AUTO(b); michael@0: michael@0: CHECK_EQ_INT(a.Length(), size); michael@0: CHECK_EQ_INT(b.Length(), size); michael@0: michael@0: for (unsigned i = 0; i < size; i++) { michael@0: CHECK_EQ_INT(a[i], i + 1); michael@0: CHECK_EQ_INT(b[i], i); michael@0: } michael@0: } michael@0: michael@0: // Swap two arrays and make sure that their capacities don't increase michael@0: // unnecessarily. michael@0: { michael@0: nsTArray a; michael@0: nsTArray b; michael@0: b.AppendElements(data2, ArrayLength(data2)); michael@0: michael@0: CHECK_EQ_INT(a.Capacity(), 0); michael@0: uint32_t bCapacity = b.Capacity(); michael@0: michael@0: a.SwapElements(b); michael@0: michael@0: // Make sure that we didn't increase the capacity of either array. michael@0: CHECK_ARRAY(a, data2); michael@0: CHECK_EQ_INT(b.Length(), 0); michael@0: CHECK_EQ_INT(b.Capacity(), 0); michael@0: CHECK_EQ_INT(a.Capacity(), bCapacity); michael@0: } michael@0: michael@0: // Swap an auto array with a TArray, then clear the auto array and make sure michael@0: // it doesn't forget the fact that it has an auto buffer. michael@0: { michael@0: nsTArray a; michael@0: nsAutoTArray b; michael@0: michael@0: a.AppendElements(data1, ArrayLength(data1)); michael@0: michael@0: a.SwapElements(b); michael@0: michael@0: CHECK_EQ_INT(a.Length(), 0); michael@0: CHECK_ARRAY(b, data1); michael@0: michael@0: b.Clear(); michael@0: michael@0: CHECK_USES_SHARED_EMPTY_HDR(a); michael@0: CHECK_IS_USING_AUTO(b); michael@0: } michael@0: michael@0: // Same thing as the previous test, but with more auto arrays. michael@0: { michael@0: nsAutoTArray a; michael@0: nsAutoTArray b; michael@0: michael@0: a.AppendElements(data1, ArrayLength(data1)); michael@0: michael@0: a.SwapElements(b); michael@0: michael@0: CHECK_EQ_INT(a.Length(), 0); michael@0: CHECK_ARRAY(b, data1); michael@0: michael@0: b.Clear(); michael@0: michael@0: CHECK_IS_USING_AUTO(a); michael@0: CHECK_IS_USING_AUTO(b); michael@0: } michael@0: michael@0: // Swap an empty nsTArray and an empty nsAutoTArray. michael@0: { michael@0: nsAutoTArray a; michael@0: nsTArray b; michael@0: michael@0: a.SwapElements(b); michael@0: michael@0: CHECK_IS_USING_AUTO(a); michael@0: CHECK_NOT_USING_AUTO(b); michael@0: CHECK_EQ_INT(a.Length(), 0); michael@0: CHECK_EQ_INT(b.Length(), 0); michael@0: } michael@0: michael@0: // Swap empty auto array with non-empty nsAutoTArray using malloc'ed storage. michael@0: // I promise, all these tests have a point. michael@0: { michael@0: nsAutoTArray a; michael@0: nsAutoTArray b; michael@0: michael@0: a.AppendElements(data1, ArrayLength(data1)); michael@0: michael@0: a.SwapElements(b); michael@0: michael@0: CHECK_IS_USING_AUTO(a); michael@0: CHECK_NOT_USING_AUTO(b); michael@0: CHECK_ARRAY(b, data1); michael@0: CHECK_EQ_INT(a.Length(), 0); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static bool test_fallible() michael@0: { michael@0: // Test that FallibleTArray works properly; that is, it never OOMs, but michael@0: // instead eventually returns false. michael@0: // michael@0: // This test is only meaningful on 32-bit systems. On a 64-bit system, we michael@0: // might never OOM. michael@0: if (sizeof(void*) > 4) { michael@0: return true; michael@0: } michael@0: michael@0: // Allocate a bunch of 128MB arrays. Larger allocations will fail on some michael@0: // platforms without actually hitting OOM. michael@0: // michael@0: // 36 * 128MB > 4GB, so we should definitely OOM by the 36th array. michael@0: const unsigned numArrays = 36; michael@0: FallibleTArray arrays[numArrays]; michael@0: for (uint32_t i = 0; i < numArrays; i++) { michael@0: bool success = arrays[i].SetCapacity(128 * 1024 * 1024); michael@0: if (!success) { michael@0: // We got our OOM. Check that it didn't come too early. michael@0: if (i < 8) { michael@0: printf("test_fallible: Got OOM on iteration %d. Too early!\n", i); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: // No OOM? That's...weird. michael@0: printf("test_fallible: Didn't OOM or crash? nsTArray::SetCapacity " michael@0: "must be lying.\n"); michael@0: return false; michael@0: } michael@0: michael@0: static bool test_conversion_operator() { michael@0: FallibleTArray f; michael@0: const FallibleTArray fconst; michael@0: AutoFallibleTArray fauto; michael@0: const AutoFallibleTArray fautoconst; michael@0: michael@0: InfallibleTArray i; michael@0: const InfallibleTArray iconst; michael@0: AutoInfallibleTArray iauto; michael@0: const AutoInfallibleTArray iautoconst; michael@0: michael@0: nsTArray t; michael@0: const nsTArray tconst; michael@0: nsAutoTArray tauto; michael@0: const nsAutoTArray tautoconst; michael@0: michael@0: #define CHECK_ARRAY_CAST(type) \ michael@0: do { \ michael@0: const type& z1 = f; \ michael@0: if ((void*)&z1 != (void*)&f) return false; \ michael@0: const type& z2 = fconst; \ michael@0: if ((void*)&z2 != (void*)&fconst) return false; \ michael@0: const type& z3 = fauto; \ michael@0: if ((void*)&z3 != (void*)&fauto) return false; \ michael@0: const type& z4 = fautoconst; \ michael@0: if ((void*)&z4 != (void*)&fautoconst) return false; \ michael@0: const type& z5 = i; \ michael@0: if ((void*)&z5 != (void*)&i) return false; \ michael@0: const type& z6 = iconst; \ michael@0: if ((void*)&z6 != (void*)&iconst) return false; \ michael@0: const type& z7 = iauto; \ michael@0: if ((void*)&z7 != (void*)&iauto) return false; \ michael@0: const type& z8 = iautoconst; \ michael@0: if ((void*)&z8 != (void*)&iautoconst) return false; \ michael@0: const type& z9 = t; \ michael@0: if ((void*)&z9 != (void*)&t) return false; \ michael@0: const type& z10 = tconst; \ michael@0: if ((void*)&z10 != (void*)&tconst) return false; \ michael@0: const type& z11 = tauto; \ michael@0: if ((void*)&z11 != (void*)&tauto) return false; \ michael@0: const type& z12 = tautoconst; \ michael@0: if ((void*)&z12 != (void*)&tautoconst) return false; \ michael@0: } while (0) michael@0: michael@0: CHECK_ARRAY_CAST(FallibleTArray); michael@0: CHECK_ARRAY_CAST(InfallibleTArray); michael@0: CHECK_ARRAY_CAST(nsTArray); michael@0: michael@0: #undef CHECK_ARRAY_CAST michael@0: michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: struct BufAccessor : public T michael@0: { michael@0: void* GetHdr() { return T::mHdr; } michael@0: }; michael@0: michael@0: static bool test_SetLengthAndRetainStorage_no_ctor() { michael@0: // 1050 because sizeof(int)*1050 is more than a page typically. michael@0: const int N = 1050; michael@0: FallibleTArray f; michael@0: AutoFallibleTArray fauto; michael@0: michael@0: InfallibleTArray i; michael@0: AutoInfallibleTArray iauto; michael@0: michael@0: nsTArray t; michael@0: nsAutoTArray tauto; michael@0: michael@0: #define LPAREN ( michael@0: #define RPAREN ) michael@0: #define FOR_EACH(pre, post) \ michael@0: do { \ michael@0: pre f post; \ michael@0: pre fauto post; \ michael@0: pre i post; \ michael@0: pre iauto post; \ michael@0: pre t post; \ michael@0: pre tauto post; \ michael@0: } while (0) michael@0: michael@0: // Setup test arrays. michael@0: FOR_EACH(;, .SetLength(N)); michael@0: for (int n = 0; n < N; ++n) { michael@0: FOR_EACH(;, [n] = n); michael@0: } michael@0: michael@0: void* initial_Hdrs[] = { michael@0: static_cast >&>(f).GetHdr(), michael@0: static_cast >&>(fauto).GetHdr(), michael@0: static_cast >&>(i).GetHdr(), michael@0: static_cast >&>(iauto).GetHdr(), michael@0: static_cast >&>(t).GetHdr(), michael@0: static_cast >&>(tauto).GetHdr(), michael@0: nullptr michael@0: }; michael@0: michael@0: // SetLengthAndRetainStorage(n), should NOT overwrite memory when T hasn't michael@0: // a default constructor. michael@0: FOR_EACH(;, .SetLengthAndRetainStorage(8)); michael@0: FOR_EACH(;, .SetLengthAndRetainStorage(12)); michael@0: for (int n = 0; n < 12; ++n) { michael@0: FOR_EACH(if LPAREN, [n] != n RPAREN return false); michael@0: } michael@0: FOR_EACH(;, .SetLengthAndRetainStorage(0)); michael@0: FOR_EACH(;, .SetLengthAndRetainStorage(N)); michael@0: for (int n = 0; n < N; ++n) { michael@0: FOR_EACH(if LPAREN, [n] != n RPAREN return false); michael@0: } michael@0: michael@0: void* current_Hdrs[] = { michael@0: static_cast >&>(f).GetHdr(), michael@0: static_cast >&>(fauto).GetHdr(), michael@0: static_cast >&>(i).GetHdr(), michael@0: static_cast >&>(iauto).GetHdr(), michael@0: static_cast >&>(t).GetHdr(), michael@0: static_cast >&>(tauto).GetHdr(), michael@0: nullptr michael@0: }; michael@0: michael@0: // SetLengthAndRetainStorage(n) should NOT have reallocated the internal michael@0: // memory. michael@0: if (sizeof(initial_Hdrs) != sizeof(current_Hdrs)) return false; michael@0: for (size_t n = 0; n < sizeof(current_Hdrs) / sizeof(current_Hdrs[0]); ++n) { michael@0: if (current_Hdrs[n] != initial_Hdrs[n]) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: michael@0: #undef FOR_EACH michael@0: #undef LPAREN michael@0: #undef RPAREN michael@0: michael@0: return true; michael@0: } michael@0: michael@0: //---- michael@0: michael@0: typedef bool (*TestFunc)(); michael@0: #define DECL_TEST(name) { #name, name } michael@0: michael@0: static const struct Test { michael@0: const char* name; michael@0: TestFunc func; michael@0: } tests[] = { michael@0: DECL_TEST(test_int_array), michael@0: DECL_TEST(test_int64_array), michael@0: DECL_TEST(test_char_array), michael@0: DECL_TEST(test_uint32_array), michael@0: DECL_TEST(test_object_array), michael@0: DECL_TEST(test_string_array), michael@0: DECL_TEST(test_comptr_array), michael@0: DECL_TEST(test_refptr_array), michael@0: DECL_TEST(test_ptrarray), michael@0: #ifdef DEBUG michael@0: DECL_TEST(test_autoarray), michael@0: #endif michael@0: DECL_TEST(test_indexof), michael@0: DECL_TEST(test_heap), michael@0: DECL_TEST(test_swap), michael@0: DECL_TEST(test_fallible), michael@0: DECL_TEST(test_conversion_operator), michael@0: DECL_TEST(test_SetLengthAndRetainStorage_no_ctor), michael@0: { nullptr, nullptr } michael@0: }; michael@0: michael@0: } michael@0: michael@0: using namespace TestTArray; michael@0: michael@0: int main(int argc, char **argv) { michael@0: int count = 1; michael@0: if (argc > 1) michael@0: count = atoi(argv[1]); michael@0: michael@0: if (NS_FAILED(NS_InitXPCOM2(nullptr, nullptr, nullptr))) michael@0: return -1; michael@0: michael@0: bool success = true; michael@0: while (count--) { michael@0: for (const Test* t = tests; t->name != nullptr; ++t) { michael@0: bool test_result = t->func(); michael@0: printf("%25s : %s\n", t->name, test_result ? "SUCCESS" : "FAILURE"); michael@0: if (!test_result) michael@0: success = false; michael@0: } michael@0: } michael@0: michael@0: NS_ShutdownXPCOM(nullptr); michael@0: return success ? 0 : -1; michael@0: }