| OLD | NEW |
| (Empty) |
| 1 // Copyright 2007-2009 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 | |
| 16 #include <vector> | |
| 17 #include "omaha/base/debug.h" | |
| 18 #include "omaha/base/scoped_ptr_cotask.h" | |
| 19 #include "omaha/testing/unit_test.h" | |
| 20 | |
| 21 // TestMallocSpy monitors CoTaskMemAlloc/Free, and records statistics about | |
| 22 // them. | |
| 23 | |
| 24 class TestMallocSpy : public IMallocSpy { | |
| 25 public: | |
| 26 struct Alloc { | |
| 27 size_t size; | |
| 28 void* ptr; | |
| 29 bool freed; | |
| 30 }; | |
| 31 | |
| 32 TestMallocSpy() : ref_(1) {} | |
| 33 | |
| 34 virtual ~TestMallocSpy() {} | |
| 35 | |
| 36 size_t NumAllocs() const { return allocs_.size(); } | |
| 37 | |
| 38 const Alloc* GetAlloc(size_t i) const { | |
| 39 if (i >= allocs_.size()) | |
| 40 return NULL; | |
| 41 return &allocs_[i]; | |
| 42 } | |
| 43 | |
| 44 size_t NumFrees() const { return frees_.size(); } | |
| 45 | |
| 46 const Alloc* GetFree(size_t i) const { | |
| 47 if (i >= frees_.size()) | |
| 48 return NULL; | |
| 49 ASSERT1(frees_[i] < allocs_.size()); | |
| 50 return &allocs_[frees_[i]]; | |
| 51 } | |
| 52 | |
| 53 // IUnknown methods | |
| 54 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) { | |
| 55 if (NULL == ppv) { | |
| 56 return E_POINTER; | |
| 57 } | |
| 58 if (::IsEqualIID(__uuidof(IUnknown), riid) || | |
| 59 ::IsEqualIID(__uuidof(IMallocSpy), riid)) { | |
| 60 AddRef(); | |
| 61 *ppv = static_cast<IUnknown*>(this); | |
| 62 return S_OK; | |
| 63 } | |
| 64 *ppv = NULL; | |
| 65 return E_NOINTERFACE; | |
| 66 } | |
| 67 virtual ULONG STDMETHODCALLTYPE AddRef() { | |
| 68 return ++ref_; | |
| 69 } | |
| 70 virtual ULONG STDMETHODCALLTYPE Release() { | |
| 71 ULONG r = --ref_; | |
| 72 if (0 == r) { | |
| 73 delete this; | |
| 74 } | |
| 75 return r; | |
| 76 } | |
| 77 | |
| 78 // IMallocSpy methods | |
| 79 virtual SIZE_T STDMETHODCALLTYPE PreAlloc(SIZE_T request_size) { | |
| 80 Alloc a = { request_size, NULL, false }; | |
| 81 allocs_.push_back(a); | |
| 82 return request_size; | |
| 83 } | |
| 84 virtual void* STDMETHODCALLTYPE PostAlloc(void* actual) { | |
| 85 ASSERT1(!allocs_.empty()); | |
| 86 ASSERT1(NULL == allocs_.back().ptr); | |
| 87 allocs_.back().ptr = actual; | |
| 88 return actual; | |
| 89 } | |
| 90 virtual void* STDMETHODCALLTYPE PreFree(void* request, BOOL spyed) { | |
| 91 if (spyed) { | |
| 92 bool found = false; | |
| 93 for (size_t i = 0; i < allocs_.size(); ++i) { | |
| 94 if ((allocs_[i].ptr == request) && !allocs_[i].freed) { | |
| 95 allocs_[i].freed = true; | |
| 96 frees_.push_back(i); | |
| 97 found = true; | |
| 98 break; | |
| 99 } | |
| 100 } | |
| 101 ASSERT1(found); | |
| 102 } | |
| 103 return request; | |
| 104 } | |
| 105 virtual void STDMETHODCALLTYPE PostFree(BOOL) {} | |
| 106 virtual SIZE_T STDMETHODCALLTYPE PreRealloc(void*, | |
| 107 SIZE_T request_size, | |
| 108 void**, | |
| 109 BOOL) { | |
| 110 return request_size; | |
| 111 } | |
| 112 virtual void* STDMETHODCALLTYPE PostRealloc(void* actual, BOOL) { | |
| 113 return actual; | |
| 114 } | |
| 115 virtual void* STDMETHODCALLTYPE PreGetSize(void* request, BOOL) { | |
| 116 return request; | |
| 117 } | |
| 118 virtual SIZE_T STDMETHODCALLTYPE PostGetSize(SIZE_T actual_size, BOOL) { | |
| 119 return actual_size; | |
| 120 } | |
| 121 virtual void* STDMETHODCALLTYPE PreDidAlloc(void* request, BOOL) { | |
| 122 return request; | |
| 123 } | |
| 124 virtual int STDMETHODCALLTYPE PostDidAlloc(void*, BOOL, int fActual) { | |
| 125 return fActual; | |
| 126 } | |
| 127 virtual void STDMETHODCALLTYPE PreHeapMinimize() {} | |
| 128 virtual void STDMETHODCALLTYPE PostHeapMinimize() {} | |
| 129 | |
| 130 private: | |
| 131 ULONG ref_; | |
| 132 std::vector<Alloc> allocs_; | |
| 133 std::vector<size_t> frees_; | |
| 134 }; | |
| 135 | |
| 136 // MallocTest runs tests with a TestMallocSpy installed. | |
| 137 | |
| 138 class MallocTest : public testing::Test { | |
| 139 public: | |
| 140 virtual void SetUp() { | |
| 141 spy_.Attach(new TestMallocSpy); | |
| 142 ASSERT_SUCCEEDED(::CoRegisterMallocSpy(spy_.p)); | |
| 143 EXPECT_EQ(0, spy()->NumAllocs()); | |
| 144 } | |
| 145 virtual void TearDown() { | |
| 146 EXPECT_EQ(spy()->NumAllocs(), spy()->NumFrees()); | |
| 147 ASSERT_SUCCEEDED(::CoRevokeMallocSpy()); | |
| 148 } | |
| 149 TestMallocSpy* spy() { return spy_.p; } | |
| 150 | |
| 151 private: | |
| 152 CComPtr<TestMallocSpy> spy_; | |
| 153 }; | |
| 154 | |
| 155 TEST_F(MallocTest, StrDupCoTask) { | |
| 156 const char kNarrowString[] = "Hello"; | |
| 157 const size_t kNarrowLen = strlen(kNarrowString); | |
| 158 const wchar_t kWideString[] = L"World"; | |
| 159 const size_t kWideLen = wcslen(kWideString); | |
| 160 | |
| 161 // Test StrDupCoTask with narrow strings. | |
| 162 char* narrow_copy = StrDupCoTask(kNarrowString, kNarrowLen); | |
| 163 | |
| 164 ASSERT_EQ(1, spy()->NumAllocs()); | |
| 165 EXPECT_EQ(0, spy()->NumFrees()); | |
| 166 EXPECT_EQ((kNarrowLen + 1) * sizeof(char), spy()->GetAlloc(0)->size); | |
| 167 EXPECT_EQ(narrow_copy, spy()->GetAlloc(0)->ptr); | |
| 168 EXPECT_FALSE(spy()->GetAlloc(0)->freed); | |
| 169 | |
| 170 ::CoTaskMemFree(narrow_copy); | |
| 171 | |
| 172 ASSERT_EQ(1, spy()->NumFrees()); | |
| 173 EXPECT_EQ(spy()->GetAlloc(0), spy()->GetFree(0)); | |
| 174 EXPECT_EQ(narrow_copy, spy()->GetFree(0)->ptr); | |
| 175 EXPECT_TRUE(spy()->GetFree(0)->freed); | |
| 176 | |
| 177 // Test StrDupCoTask with wide strings. | |
| 178 wchar_t* wide_copy = StrDupCoTask(kWideString, kWideLen); | |
| 179 | |
| 180 ASSERT_EQ(2, spy()->NumAllocs()); | |
| 181 EXPECT_EQ(1, spy()->NumFrees()); | |
| 182 EXPECT_EQ((kWideLen + 1) * sizeof(wchar_t), spy()->GetAlloc(1)->size); | |
| 183 EXPECT_EQ(wide_copy, spy()->GetAlloc(1)->ptr); | |
| 184 EXPECT_FALSE(spy()->GetAlloc(1)->freed); | |
| 185 | |
| 186 ::CoTaskMemFree(wide_copy); | |
| 187 | |
| 188 ASSERT_EQ(2, spy()->NumFrees()); | |
| 189 EXPECT_EQ(spy()->GetAlloc(1), spy()->GetFree(1)); | |
| 190 EXPECT_EQ(wide_copy, spy()->GetFree(1)->ptr); | |
| 191 EXPECT_TRUE(spy()->GetFree(1)->freed); | |
| 192 } | |
| 193 | |
| 194 TEST_F(MallocTest, scoped_ptr_cotask) { | |
| 195 scoped_ptr_cotask<wchar_t>* string_ptr; | |
| 196 | |
| 197 // Creating an empty ptr does no additional allocations. | |
| 198 string_ptr = new scoped_ptr_cotask<wchar_t>; | |
| 199 ASSERT_EQ(0, spy()->NumAllocs()); | |
| 200 EXPECT_EQ(0, spy()->NumFrees()); | |
| 201 | |
| 202 // Assigning a string does not additional allocations. | |
| 203 string_ptr->reset(StrDupCoTask(L"hi", 2)); | |
| 204 ASSERT_EQ(1, spy()->NumAllocs()); | |
| 205 EXPECT_EQ(0, spy()->NumFrees()); | |
| 206 EXPECT_EQ(3 * sizeof(wchar_t), spy()->GetAlloc(0)->size); | |
| 207 EXPECT_FALSE(spy()->GetAlloc(0)->freed); | |
| 208 | |
| 209 EXPECT_EQ(0, memcmp(string_ptr->get(), L"hi", 3 * sizeof(wchar_t))); | |
| 210 | |
| 211 // Replacing the string frees the old memory. | |
| 212 string_ptr->reset(StrDupCoTask(L"there", 5)); | |
| 213 ASSERT_EQ(2, spy()->NumAllocs()); | |
| 214 EXPECT_EQ(1, spy()->NumFrees()); | |
| 215 EXPECT_EQ(6 * sizeof(wchar_t), spy()->GetAlloc(1)->size); | |
| 216 EXPECT_TRUE(spy()->GetAlloc(0)->freed); | |
| 217 EXPECT_FALSE(spy()->GetAlloc(1)->freed); | |
| 218 | |
| 219 // Deleting the string frees the memory. | |
| 220 delete string_ptr; | |
| 221 ASSERT_EQ(2, spy()->NumAllocs()); | |
| 222 EXPECT_EQ(2, spy()->NumFrees()); | |
| 223 EXPECT_TRUE(spy()->GetAlloc(1)->freed); | |
| 224 } | |
| 225 | |
| 226 TEST_F(MallocTest, scoped_array_cotask) { | |
| 227 const size_t kSize = 5; | |
| 228 scoped_array_cotask<wchar_t*>* string_array; | |
| 229 | |
| 230 // Allocate an array of 5 empty elements. | |
| 231 string_array = new scoped_array_cotask<wchar_t*>(kSize); | |
| 232 ASSERT_EQ(kSize, string_array->size()); | |
| 233 ASSERT_EQ(1, spy()->NumAllocs()); | |
| 234 EXPECT_EQ(0, spy()->NumFrees()); | |
| 235 EXPECT_EQ(kSize * sizeof(wchar_t*), spy()->GetAlloc(0)->size); | |
| 236 | |
| 237 // Populate array elements. | |
| 238 for (size_t i = 0; i < kSize; ++i) { | |
| 239 EXPECT_TRUE(NULL == (*string_array)[i]); | |
| 240 (*string_array)[i] = StrDupCoTask(L"hi", 2); | |
| 241 } | |
| 242 EXPECT_EQ(1 + kSize, spy()->NumAllocs()); | |
| 243 EXPECT_EQ(0, spy()->NumFrees()); | |
| 244 | |
| 245 // Get is idempotent. | |
| 246 wchar_t** ptr = string_array->get(); | |
| 247 EXPECT_EQ(ptr, string_array->get()); | |
| 248 EXPECT_EQ(ptr, spy()->GetAlloc(0)->ptr); | |
| 249 EXPECT_EQ(0, spy()->NumFrees()); | |
| 250 | |
| 251 // Release is not idempotent, but does not free memory. | |
| 252 ptr = string_array->release(); | |
| 253 EXPECT_TRUE(NULL == string_array->release()); | |
| 254 EXPECT_EQ(ptr, spy()->GetAlloc(0)->ptr); | |
| 255 EXPECT_EQ(0, spy()->NumFrees()); | |
| 256 | |
| 257 // Deleting a released array does not free memory. | |
| 258 delete string_array; | |
| 259 EXPECT_EQ(0, spy()->NumFrees()); | |
| 260 | |
| 261 // Constructing an array from existing memory, does not cause allocations. | |
| 262 string_array = new scoped_array_cotask<wchar_t*>(kSize, ptr); | |
| 263 EXPECT_EQ(1 + kSize, spy()->NumAllocs()); | |
| 264 EXPECT_EQ(0, spy()->NumFrees()); | |
| 265 | |
| 266 // Deleting an array frees all elements and the array. | |
| 267 delete string_array; | |
| 268 ASSERT_EQ(1 + kSize, spy()->NumAllocs()); | |
| 269 EXPECT_EQ(1 + kSize, spy()->NumFrees()); | |
| 270 for (size_t i = 0; i < spy()->NumAllocs(); ++i) { | |
| 271 EXPECT_TRUE(spy()->GetAlloc(i)->freed); | |
| 272 } | |
| 273 } | |
| 274 | |
| 275 TEST_F(MallocTest, scoped_array_cotask_reset) { | |
| 276 // This test exposes a former bug, where reset did not reallocate a new | |
| 277 // array after being released. | |
| 278 | |
| 279 // Allocate an empty array. | |
| 280 const size_t kSize = 5; | |
| 281 scoped_array_cotask<int*>* array = new scoped_array_cotask<int*>(kSize); | |
| 282 ASSERT_EQ(1, spy()->NumAllocs()); | |
| 283 | |
| 284 // Release the array, to verify it was allocated. | |
| 285 int** first_raw_array = array->release(); | |
| 286 EXPECT_TRUE(NULL != first_raw_array); | |
| 287 EXPECT_FALSE(spy()->GetAlloc(0)->freed); | |
| 288 | |
| 289 // Allocate another empty array. | |
| 290 array->reset(kSize); | |
| 291 ASSERT_EQ(2, spy()->NumAllocs()); | |
| 292 | |
| 293 // Release the second array, to verify it was allocated. | |
| 294 int** second_raw_array = array->release(); | |
| 295 EXPECT_TRUE(NULL != second_raw_array); | |
| 296 EXPECT_FALSE(spy()->GetAlloc(1)->freed); | |
| 297 | |
| 298 // Use the scoped_array_cotask object to dispose of the allocated arrays. | |
| 299 array->reset(kSize, first_raw_array); | |
| 300 array->reset(kSize, second_raw_array); | |
| 301 delete array; | |
| 302 | |
| 303 // Check the final conditions. | |
| 304 ASSERT_EQ(2, spy()->NumAllocs()); | |
| 305 for (size_t i = 0; i < spy()->NumAllocs(); ++i) { | |
| 306 EXPECT_TRUE(spy()->GetAlloc(i)->freed); | |
| 307 } | |
| 308 } | |
| OLD | NEW |