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