1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "nsCOMPtr.h"
7 #include "gtest/gtest.h"
8
9 #include "mozilla/Unused.h"
10
11 #define NS_IFOO_IID \
12 { \
13 0x6f7652e0, 0xee43, 0x11d1, { \
14 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 \
15 } \
16 }
17
18 namespace TestCOMPtr {
19
20 class IFoo : public nsISupports {
21 public:
22 NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
23
24 public:
25 IFoo();
26 // virtual dtor because IBar uses our Release()
27 virtual ~IFoo();
28
29 NS_IMETHOD_(MozExternalRefCountType) AddRef() override;
30 NS_IMETHOD_(MozExternalRefCountType) Release() override;
31 NS_IMETHOD QueryInterface(const nsIID&, void**) override;
32
33 unsigned int refcount_;
34
35 static int total_constructions_;
36 static int total_destructions_;
37 static int total_queries_;
38 };
39
40 NS_DEFINE_STATIC_IID_ACCESSOR(IFoo, NS_IFOO_IID)
41
42 int IFoo::total_constructions_;
43 int IFoo::total_destructions_;
44 int IFoo::total_queries_;
45
IFoo()46 IFoo::IFoo() : refcount_(0) { ++total_constructions_; }
47
~IFoo()48 IFoo::~IFoo() { ++total_destructions_; }
49
AddRef()50 MozExternalRefCountType IFoo::AddRef() {
51 ++refcount_;
52 return refcount_;
53 }
54
Release()55 MozExternalRefCountType IFoo::Release() {
56 int newcount = --refcount_;
57
58 if (newcount == 0) {
59 delete this;
60 }
61
62 return newcount;
63 }
64
QueryInterface(const nsIID & aIID,void ** aResult)65 nsresult IFoo::QueryInterface(const nsIID& aIID, void** aResult) {
66 total_queries_++;
67
68 nsISupports* rawPtr = 0;
69 nsresult status = NS_OK;
70
71 if (aIID.Equals(NS_GET_IID(IFoo)))
72 rawPtr = this;
73 else {
74 nsID iid_of_ISupports = NS_ISUPPORTS_IID;
75 if (aIID.Equals(iid_of_ISupports))
76 rawPtr = static_cast<nsISupports*>(this);
77 else
78 status = NS_ERROR_NO_INTERFACE;
79 }
80
81 NS_IF_ADDREF(rawPtr);
82 *aResult = rawPtr;
83
84 return status;
85 }
86
CreateIFoo(void ** result)87 static nsresult CreateIFoo(void** result)
88 // a typical factory function (that calls AddRef)
89 {
90 auto* foop = new IFoo;
91
92 foop->AddRef();
93 *result = foop;
94
95 return NS_OK;
96 }
97
set_a_IFoo(nsCOMPtr<IFoo> * result)98 static void set_a_IFoo(nsCOMPtr<IFoo>* result) {
99 // Various places in this file do a static_cast to nsISupports* in order to
100 // make the QI non-trivial, to avoid hitting a static assert.
101 nsCOMPtr<IFoo> foop(do_QueryInterface(static_cast<nsISupports*>(new IFoo)));
102 *result = foop;
103 }
104
return_a_IFoo()105 static nsCOMPtr<IFoo> return_a_IFoo() {
106 nsCOMPtr<IFoo> foop(do_QueryInterface(static_cast<nsISupports*>(new IFoo)));
107 return foop;
108 }
109
110 #define NS_IBAR_IID \
111 { \
112 0x6f7652e1, 0xee43, 0x11d1, { \
113 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 \
114 } \
115 }
116
117 class IBar : public IFoo {
118 public:
119 NS_DECLARE_STATIC_IID_ACCESSOR(NS_IBAR_IID)
120
121 public:
122 IBar();
123 ~IBar() override;
124
125 NS_IMETHOD QueryInterface(const nsIID&, void**) override;
126
127 static int total_destructions_;
128 static int total_queries_;
129 };
130
131 NS_DEFINE_STATIC_IID_ACCESSOR(IBar, NS_IBAR_IID)
132
133 int IBar::total_destructions_;
134 int IBar::total_queries_;
135
136 IBar::IBar() = default;
137
~IBar()138 IBar::~IBar() { total_destructions_++; }
139
QueryInterface(const nsID & aIID,void ** aResult)140 nsresult IBar::QueryInterface(const nsID& aIID, void** aResult) {
141 total_queries_++;
142
143 nsISupports* rawPtr = 0;
144 nsresult status = NS_OK;
145
146 if (aIID.Equals(NS_GET_IID(IBar)))
147 rawPtr = this;
148 else if (aIID.Equals(NS_GET_IID(IFoo)))
149 rawPtr = static_cast<IFoo*>(this);
150 else {
151 nsID iid_of_ISupports = NS_ISUPPORTS_IID;
152 if (aIID.Equals(iid_of_ISupports))
153 rawPtr = static_cast<nsISupports*>(this);
154 else
155 status = NS_ERROR_NO_INTERFACE;
156 }
157
158 NS_IF_ADDREF(rawPtr);
159 *aResult = rawPtr;
160
161 return status;
162 }
163
CreateIBar(void ** result)164 static nsresult CreateIBar(void** result)
165 // a typical factory function (that calls AddRef)
166 {
167 auto* barp = new IBar;
168
169 barp->AddRef();
170 *result = barp;
171
172 return NS_OK;
173 }
174
AnIFooPtrPtrContext(IFoo **)175 static void AnIFooPtrPtrContext(IFoo**) {}
176
AVoidPtrPtrContext(void **)177 static void AVoidPtrPtrContext(void**) {}
178
AnISupportsPtrPtrContext(nsISupports **)179 static void AnISupportsPtrPtrContext(nsISupports**) {}
180
181 } // namespace TestCOMPtr
182
183 using namespace TestCOMPtr;
184
TEST(COMPtr,Bloat_Raw_Unsafe)185 TEST(COMPtr, Bloat_Raw_Unsafe)
186 {
187 // ER: I'm not sure what this is testing...
188 IBar* barP = 0;
189 nsresult rv = CreateIBar(reinterpret_cast<void**>(&barP));
190 ASSERT_TRUE(NS_SUCCEEDED(rv));
191 ASSERT_TRUE(barP);
192
193 IFoo* fooP = 0;
194 rv = barP->QueryInterface(NS_GET_IID(IFoo), reinterpret_cast<void**>(&fooP));
195 ASSERT_TRUE(NS_SUCCEEDED(rv));
196 ASSERT_TRUE(fooP);
197
198 NS_RELEASE(fooP);
199 NS_RELEASE(barP);
200 }
201
TEST(COMPtr,Bloat_Smart)202 TEST(COMPtr, Bloat_Smart)
203 {
204 // ER: I'm not sure what this is testing...
205 nsCOMPtr<IBar> barP;
206 nsresult rv = CreateIBar(getter_AddRefs(barP));
207 ASSERT_TRUE(NS_SUCCEEDED(rv));
208 ASSERT_TRUE(barP);
209
210 nsCOMPtr<IFoo> fooP(do_QueryInterface(static_cast<nsISupports*>(barP), &rv));
211 ASSERT_TRUE(NS_SUCCEEDED(rv));
212 ASSERT_TRUE(fooP);
213 }
214
TEST(COMPtr,AddRefAndRelease)215 TEST(COMPtr, AddRefAndRelease)
216 {
217 IFoo::total_constructions_ = 0;
218 IFoo::total_destructions_ = 0;
219 IBar::total_destructions_ = 0;
220
221 {
222 nsCOMPtr<IFoo> foop(do_QueryInterface(static_cast<nsISupports*>(new IFoo)));
223 ASSERT_EQ(foop->refcount_, (unsigned int)1);
224 ASSERT_EQ(IFoo::total_constructions_, 1);
225 ASSERT_EQ(IFoo::total_destructions_, 0);
226
227 foop = do_QueryInterface(static_cast<nsISupports*>(new IFoo));
228 ASSERT_EQ(foop->refcount_, (unsigned int)1);
229 ASSERT_EQ(IFoo::total_constructions_, 2);
230 ASSERT_EQ(IFoo::total_destructions_, 1);
231
232 // [Shouldn't compile] Is it a compile time error to try to |AddRef| by
233 // hand?
234 // foop->AddRef();
235
236 // [Shouldn't compile] Is it a compile time error to try to |Release| be
237 // hand?
238 // foop->Release();
239
240 // [Shouldn't compile] Is it a compile time error to try to |delete| an
241 // |nsCOMPtr|?
242 // delete foop;
243
244 static_cast<IFoo*>(foop)->AddRef();
245 ASSERT_EQ(foop->refcount_, (unsigned int)2);
246 ASSERT_EQ(IFoo::total_constructions_, 2);
247 ASSERT_EQ(IFoo::total_destructions_, 1);
248
249 static_cast<IFoo*>(foop)->Release();
250 ASSERT_EQ(foop->refcount_, (unsigned int)1);
251 ASSERT_EQ(IFoo::total_constructions_, 2);
252 ASSERT_EQ(IFoo::total_destructions_, 1);
253 }
254
255 ASSERT_EQ(IFoo::total_constructions_, 2);
256 ASSERT_EQ(IFoo::total_destructions_, 2);
257
258 {
259 nsCOMPtr<IFoo> foop(do_QueryInterface(static_cast<nsISupports*>(new IBar)));
260 mozilla::Unused << foop;
261 }
262
263 ASSERT_EQ(IBar::total_destructions_, 1);
264 }
265
TEST(COMPtr,Comparison)266 TEST(COMPtr, Comparison)
267 {
268 IFoo::total_constructions_ = 0;
269 IFoo::total_destructions_ = 0;
270
271 {
272 nsCOMPtr<IFoo> foo1p(
273 do_QueryInterface(static_cast<nsISupports*>(new IFoo)));
274 nsCOMPtr<IFoo> foo2p(
275 do_QueryInterface(static_cast<nsISupports*>(new IFoo)));
276
277 ASSERT_EQ(IFoo::total_constructions_, 2);
278
279 // Test != operator
280 ASSERT_NE(foo1p, foo2p);
281 ASSERT_NE(foo1p, foo2p.get());
282
283 // Test == operator
284 foo1p = foo2p;
285
286 ASSERT_EQ(IFoo::total_destructions_, 1);
287
288 ASSERT_EQ(foo1p, foo2p);
289 ASSERT_EQ(foo2p, foo2p.get());
290 ASSERT_EQ(foo2p.get(), foo2p);
291
292 // Test () operator
293 ASSERT_TRUE(foo1p);
294
295 ASSERT_EQ(foo1p->refcount_, (unsigned int)2);
296 ASSERT_EQ(foo2p->refcount_, (unsigned int)2);
297 }
298
299 ASSERT_EQ(IFoo::total_destructions_, 2);
300 }
301
TEST(COMPtr,DontAddRef)302 TEST(COMPtr, DontAddRef)
303 {
304 {
305 auto* raw_foo1p = new IFoo;
306 raw_foo1p->AddRef();
307
308 auto* raw_foo2p = new IFoo;
309 raw_foo2p->AddRef();
310
311 nsCOMPtr<IFoo> foo1p(dont_AddRef(raw_foo1p));
312 ASSERT_EQ(raw_foo1p, foo1p);
313 ASSERT_EQ(foo1p->refcount_, (unsigned int)1);
314
315 nsCOMPtr<IFoo> foo2p;
316 foo2p = dont_AddRef(raw_foo2p);
317 ASSERT_EQ(raw_foo2p, foo2p);
318 ASSERT_EQ(foo2p->refcount_, (unsigned int)1);
319 }
320 }
321
TEST(COMPtr,AssignmentHelpers)322 TEST(COMPtr, AssignmentHelpers)
323 {
324 IFoo::total_constructions_ = 0;
325 IFoo::total_destructions_ = 0;
326
327 {
328 nsCOMPtr<IFoo> foop;
329 ASSERT_FALSE(foop);
330 CreateIFoo(nsGetterAddRefs<IFoo>(foop));
331 ASSERT_TRUE(foop);
332 }
333
334 ASSERT_EQ(IFoo::total_constructions_, 1);
335 ASSERT_EQ(IFoo::total_destructions_, 1);
336
337 {
338 nsCOMPtr<IFoo> foop;
339 ASSERT_FALSE(foop);
340 CreateIFoo(getter_AddRefs(foop));
341 ASSERT_TRUE(foop);
342 }
343
344 ASSERT_EQ(IFoo::total_constructions_, 2);
345 ASSERT_EQ(IFoo::total_destructions_, 2);
346
347 {
348 nsCOMPtr<IFoo> foop;
349 ASSERT_FALSE(foop);
350 set_a_IFoo(address_of(foop));
351 ASSERT_TRUE(foop);
352
353 ASSERT_EQ(IFoo::total_constructions_, 3);
354 ASSERT_EQ(IFoo::total_destructions_, 2);
355
356 foop = return_a_IFoo();
357 ASSERT_TRUE(foop);
358
359 ASSERT_EQ(IFoo::total_constructions_, 4);
360 ASSERT_EQ(IFoo::total_destructions_, 3);
361 }
362
363 ASSERT_EQ(IFoo::total_constructions_, 4);
364 ASSERT_EQ(IFoo::total_destructions_, 4);
365
366 {
367 nsCOMPtr<IFoo> fooP(do_QueryInterface(static_cast<nsISupports*>(new IFoo)));
368 ASSERT_TRUE(fooP);
369
370 ASSERT_EQ(IFoo::total_constructions_, 5);
371 ASSERT_EQ(IFoo::total_destructions_, 4);
372
373 nsCOMPtr<IFoo> fooP2(std::move(fooP));
374 ASSERT_TRUE(fooP2);
375
376 ASSERT_EQ(IFoo::total_constructions_, 5);
377 ASSERT_EQ(IFoo::total_destructions_, 4);
378 }
379
380 ASSERT_EQ(IFoo::total_constructions_, 5);
381 ASSERT_EQ(IFoo::total_destructions_, 5);
382 }
383
TEST(COMPtr,QueryInterface)384 TEST(COMPtr, QueryInterface)
385 {
386 IFoo::total_queries_ = 0;
387 IBar::total_queries_ = 0;
388
389 {
390 nsCOMPtr<IFoo> fooP;
391 ASSERT_FALSE(fooP);
392 fooP = do_QueryInterface(static_cast<nsISupports*>(new IFoo));
393 ASSERT_TRUE(fooP);
394 ASSERT_EQ(IFoo::total_queries_, 1);
395
396 nsCOMPtr<IFoo> foo2P;
397
398 // Test that |QueryInterface| _not_ called when assigning a smart-pointer
399 // of the same type.);
400 foo2P = fooP;
401 ASSERT_EQ(IFoo::total_queries_, 1);
402 }
403
404 {
405 nsCOMPtr<IBar> barP(do_QueryInterface(static_cast<nsISupports*>(new IBar)));
406 ASSERT_EQ(IBar::total_queries_, 1);
407
408 // Test that |QueryInterface| is called when assigning a smart-pointer of
409 // a different type.
410 nsCOMPtr<IFoo> fooP(do_QueryInterface(static_cast<nsISupports*>(barP)));
411 ASSERT_EQ(IBar::total_queries_, 2);
412 ASSERT_EQ(IFoo::total_queries_, 1);
413 ASSERT_TRUE(fooP);
414 }
415 }
416
TEST(COMPtr,GetterConversions)417 TEST(COMPtr, GetterConversions)
418 {
419 // This is just a compilation test. We add a few asserts to keep gtest happy.
420 {
421 nsCOMPtr<IFoo> fooP;
422 ASSERT_FALSE(fooP);
423
424 AnIFooPtrPtrContext(getter_AddRefs(fooP));
425 AVoidPtrPtrContext(getter_AddRefs(fooP));
426 }
427
428 {
429 nsCOMPtr<nsISupports> supportsP;
430 ASSERT_FALSE(supportsP);
431
432 AVoidPtrPtrContext(getter_AddRefs(supportsP));
433 AnISupportsPtrPtrContext(getter_AddRefs(supportsP));
434 }
435 }
436