Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/tests/gtest/TestNsRefPtr.cpp
Line
Count
Source (jump to first uncovered line)
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 "mozilla/RefPtr.h"
7
#include "nsCOMPtr.h"
8
#include "nsISupports.h"
9
#include "nsQueryObject.h"
10
#include "mozilla/Unused.h"
11
12
#include "gtest/gtest.h"
13
14
namespace TestNsRefPtr
15
{
16
17
#define NS_FOO_IID \
18
{ 0x6f7652e0,  0xee43, 0x11d1, \
19
  { 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 } }
20
21
class Foo : public nsISupports
22
{
23
public:
24
  NS_DECLARE_STATIC_IID_ACCESSOR(NS_FOO_IID)
25
26
public:
27
  Foo();
28
  // virtual dtor because Bar uses our Release()
29
  virtual ~Foo();
30
31
  NS_IMETHOD_(MozExternalRefCountType) AddRef() override;
32
  NS_IMETHOD_(MozExternalRefCountType) Release() override;
33
  NS_IMETHOD QueryInterface( const nsIID&, void** ) override;
34
  void MemberFunction( int, int*, int& );
35
  virtual void VirtualMemberFunction( int, int*, int& );
36
  virtual void VirtualConstMemberFunction( int, int*, int& ) const;
37
38
0
  void NonconstMethod() {}
39
0
  void ConstMethod() const {}
40
41
  int refcount_;
42
43
  static int total_constructions_;
44
  static int total_destructions_;
45
  static int total_addrefs_;
46
  static int total_queries_;
47
};
48
49
NS_DEFINE_STATIC_IID_ACCESSOR(Foo, NS_FOO_IID)
50
51
int Foo::total_constructions_;
52
int Foo::total_destructions_;
53
int Foo::total_addrefs_;
54
int Foo::total_queries_;
55
56
Foo::Foo()
57
  : refcount_(0)
58
0
{
59
0
  ++total_constructions_;
60
0
}
61
62
Foo::~Foo()
63
0
{
64
0
  ++total_destructions_;
65
0
}
66
67
MozExternalRefCountType
68
Foo::AddRef()
69
0
{
70
0
  ++refcount_;
71
0
  ++total_addrefs_;
72
0
  return refcount_;
73
0
}
74
75
MozExternalRefCountType
76
Foo::Release()
77
0
{
78
0
  int newcount = --refcount_;
79
0
  if ( newcount == 0 )
80
0
  {
81
0
    delete this;
82
0
  }
83
0
84
0
  return newcount;
85
0
}
86
87
nsresult
88
Foo::QueryInterface( const nsIID& aIID, void** aResult )
89
0
{
90
0
  ++total_queries_;
91
0
92
0
  nsISupports* rawPtr = 0;
93
0
  nsresult status = NS_OK;
94
0
95
0
  if ( aIID.Equals(NS_GET_IID(Foo)) )
96
0
    rawPtr = this;
97
0
  else
98
0
  {
99
0
    nsID iid_of_ISupports = NS_ISUPPORTS_IID;
100
0
    if ( aIID.Equals(iid_of_ISupports) )
101
0
      rawPtr = static_cast<nsISupports*>(this);
102
0
    else
103
0
      status = NS_ERROR_NO_INTERFACE;
104
0
  }
105
0
106
0
  NS_IF_ADDREF(rawPtr);
107
0
  *aResult = rawPtr;
108
0
109
0
  return status;
110
0
}
111
112
void
113
Foo::MemberFunction( int aArg1, int* aArgPtr, int& aArgRef )
114
0
{
115
0
}
116
117
void
118
Foo::VirtualMemberFunction( int aArg1, int* aArgPtr, int& aArgRef )
119
0
{
120
0
}
121
122
void
123
Foo::VirtualConstMemberFunction( int aArg1, int* aArgPtr, int& aArgRef ) const
124
0
{
125
0
}
126
127
nsresult
128
CreateFoo( void** result )
129
  // a typical factory function (that calls AddRef)
130
0
{
131
0
  auto* foop = new Foo;
132
0
133
0
  foop->AddRef();
134
0
  *result = foop;
135
0
136
0
  return NS_OK;
137
0
}
138
139
void
140
set_a_Foo( RefPtr<Foo>* result )
141
0
{
142
0
  assert(result);
143
0
144
0
  RefPtr<Foo> foop( do_QueryObject(new Foo) );
145
0
  *result = foop;
146
0
}
147
148
RefPtr<Foo>
149
return_a_Foo()
150
0
{
151
0
  RefPtr<Foo> foop( do_QueryObject(new Foo) );
152
0
  return foop;
153
0
}
154
155
#define NS_BAR_IID \
156
{ 0x6f7652e1,  0xee43, 0x11d1, \
157
  { 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 } }
158
159
class Bar : public Foo
160
{
161
public:
162
  NS_DECLARE_STATIC_IID_ACCESSOR(NS_BAR_IID)
163
164
public:
165
  Bar();
166
  ~Bar() override;
167
168
  NS_IMETHOD QueryInterface( const nsIID&, void** ) override;
169
170
  void VirtualMemberFunction( int, int*, int& ) override;
171
  void VirtualConstMemberFunction( int, int*, int& ) const override;
172
173
  static int total_constructions_;
174
  static int total_destructions_;
175
  static int total_queries_;
176
};
177
178
NS_DEFINE_STATIC_IID_ACCESSOR(Bar, NS_BAR_IID)
179
180
int Bar::total_constructions_;
181
int Bar::total_destructions_;
182
int Bar::total_queries_;
183
184
Bar::Bar()
185
0
{
186
0
  ++total_constructions_;
187
0
}
188
189
Bar::~Bar()
190
0
{
191
0
  ++total_destructions_;
192
0
}
193
194
nsresult
195
Bar::QueryInterface( const nsID& aIID, void** aResult )
196
0
{
197
0
  ++total_queries_;
198
0
199
0
  nsISupports* rawPtr = 0;
200
0
  nsresult status = NS_OK;
201
0
202
0
  if ( aIID.Equals(NS_GET_IID(Bar)) )
203
0
    rawPtr = this;
204
0
  else if ( aIID.Equals(NS_GET_IID(Foo)) )
205
0
    rawPtr = static_cast<Foo*>(this);
206
0
  else
207
0
  {
208
0
    nsID iid_of_ISupports = NS_ISUPPORTS_IID;
209
0
    if ( aIID.Equals(iid_of_ISupports) )
210
0
      rawPtr = static_cast<nsISupports*>(this);
211
0
    else
212
0
      status = NS_ERROR_NO_INTERFACE;
213
0
  }
214
0
215
0
  NS_IF_ADDREF(rawPtr);
216
0
  *aResult = rawPtr;
217
0
218
0
  return status;
219
0
}
220
221
void
222
Bar::VirtualMemberFunction( int aArg1, int* aArgPtr, int& aArgRef )
223
0
{
224
0
}
225
void
226
Bar::VirtualConstMemberFunction( int aArg1, int* aArgPtr, int& aArgRef ) const
227
0
{
228
0
}
229
230
} // namespace TestNsRefPtr
231
232
using namespace TestNsRefPtr;
233
234
TEST(nsRefPtr, AddRefAndRelease)
235
0
{
236
0
  Foo::total_constructions_ = 0;
237
0
  Foo::total_destructions_ = 0;
238
0
239
0
  {
240
0
    RefPtr<Foo> foop( do_QueryObject(new Foo) );
241
0
    ASSERT_EQ(Foo::total_constructions_, 1);
242
0
    ASSERT_EQ(Foo::total_destructions_, 0);
243
0
    ASSERT_EQ(foop->refcount_, 1);
244
0
245
0
    foop = do_QueryObject(new Foo);
246
0
    ASSERT_EQ(Foo::total_constructions_, 2);
247
0
    ASSERT_EQ(Foo::total_destructions_, 1);
248
0
249
0
    // [Shouldn't compile] Is it a compile time error to try to |AddRef| by hand?
250
0
    //foop->AddRef();
251
0
252
0
    // [Shouldn't compile] Is it a compile time error to try to |Release| be hand?
253
0
    //foop->Release();
254
0
255
0
    // [Shouldn't compile] Is it a compile time error to try to |delete| an |nsCOMPtr|?
256
0
    //delete foop;
257
0
258
0
    static_cast<Foo*>(foop)->AddRef();
259
0
    ASSERT_EQ(foop->refcount_, 2);
260
0
261
0
    static_cast<Foo*>(foop)->Release();
262
0
    ASSERT_EQ(foop->refcount_, 1);
263
0
  }
264
0
265
0
  ASSERT_EQ(Foo::total_destructions_, 2);
266
0
267
0
  {
268
0
    RefPtr<Foo> fooP( do_QueryObject(new Foo) );
269
0
    ASSERT_EQ(Foo::total_constructions_, 3);
270
0
    ASSERT_EQ(Foo::total_destructions_, 2);
271
0
    ASSERT_EQ(fooP->refcount_, 1);
272
0
273
0
    Foo::total_addrefs_ = 0;
274
0
    RefPtr<Foo> fooP2( fooP.forget() );
275
0
    ASSERT_EQ(Foo::total_addrefs_, 0);
276
0
  }
277
0
}
278
279
TEST(nsRefPtr, VirtualDestructor)
280
0
{
281
0
  Bar::total_destructions_ = 0;
282
0
283
0
  {
284
0
    RefPtr<Foo> foop( do_QueryObject(new Bar) );
285
0
    mozilla::Unused << foop;
286
0
  }
287
0
288
0
  ASSERT_EQ(Bar::total_destructions_, 1);
289
0
}
290
291
TEST(nsRefPtr, Equality)
292
0
{
293
0
  Foo::total_constructions_ = 0;
294
0
  Foo::total_destructions_ = 0;
295
0
296
0
  {
297
0
    RefPtr<Foo> foo1p( do_QueryObject(new Foo) );
298
0
    RefPtr<Foo> foo2p( do_QueryObject(new Foo) );
299
0
300
0
    ASSERT_EQ(Foo::total_constructions_, 2);
301
0
    ASSERT_EQ(Foo::total_destructions_, 0);
302
0
303
0
    ASSERT_NE(foo1p, foo2p);
304
0
305
0
    ASSERT_NE(foo1p, nullptr);
306
0
    ASSERT_NE(nullptr, foo1p);
307
0
    ASSERT_FALSE(foo1p == nullptr);
308
0
    ASSERT_FALSE(nullptr == foo1p);
309
0
310
0
    ASSERT_NE(foo1p, foo2p.get());
311
0
312
0
    foo1p = foo2p;
313
0
314
0
    ASSERT_EQ(Foo::total_constructions_, 2);
315
0
    ASSERT_EQ(Foo::total_destructions_, 1);
316
0
    ASSERT_EQ(foo1p, foo2p);
317
0
318
0
    ASSERT_EQ(foo2p, foo2p.get());
319
0
320
0
    ASSERT_EQ(RefPtr<Foo>(foo2p.get()), foo2p);
321
0
322
0
    ASSERT_TRUE(foo1p);
323
0
  }
324
0
325
0
  ASSERT_EQ(Foo::total_constructions_, 2);
326
0
  ASSERT_EQ(Foo::total_destructions_, 2);
327
0
}
328
329
TEST(nsRefPtr, AddRefHelpers)
330
0
{
331
0
  Foo::total_addrefs_ = 0;
332
0
333
0
  {
334
0
    auto* raw_foo1p = new Foo;
335
0
    raw_foo1p->AddRef();
336
0
337
0
    auto* raw_foo2p = new Foo;
338
0
    raw_foo2p->AddRef();
339
0
340
0
    ASSERT_EQ(Foo::total_addrefs_, 2);
341
0
342
0
    RefPtr<Foo> foo1p( dont_AddRef(raw_foo1p) );
343
0
344
0
    ASSERT_EQ(Foo::total_addrefs_, 2);
345
0
346
0
    RefPtr<Foo> foo2p;
347
0
    foo2p = dont_AddRef(raw_foo2p);
348
0
349
0
    ASSERT_EQ(Foo::total_addrefs_, 2);
350
0
  }
351
0
352
0
  {
353
0
    // Test that various assignment helpers compile.
354
0
    RefPtr<Foo> foop;
355
0
    CreateFoo( RefPtrGetterAddRefs<Foo>(foop) );
356
0
    CreateFoo( getter_AddRefs(foop) );
357
0
    set_a_Foo(address_of(foop));
358
0
    foop = return_a_Foo();
359
0
  }
360
0
}
361
362
TEST(nsRefPtr, QueryInterface)
363
0
{
364
0
  Foo::total_queries_ = 0;
365
0
  Bar::total_queries_ = 0;
366
0
367
0
  {
368
0
    RefPtr<Foo> fooP;
369
0
    fooP = do_QueryObject(new Foo);
370
0
    ASSERT_EQ(Foo::total_queries_, 1);
371
0
  }
372
0
373
0
  {
374
0
    RefPtr<Foo> fooP;
375
0
    fooP = do_QueryObject(new Foo);
376
0
    ASSERT_EQ(Foo::total_queries_, 2);
377
0
378
0
    RefPtr<Foo> foo2P;
379
0
    foo2P = fooP;
380
0
    ASSERT_EQ(Foo::total_queries_, 2);
381
0
  }
382
0
383
0
  {
384
0
    RefPtr<Bar> barP( do_QueryObject(new Bar) );
385
0
    ASSERT_EQ(Bar::total_queries_, 1);
386
0
387
0
    RefPtr<Foo> fooP( do_QueryObject(barP) );
388
0
    ASSERT_TRUE(fooP);
389
0
    ASSERT_EQ(Foo::total_queries_, 2);
390
0
    ASSERT_EQ(Bar::total_queries_, 2);
391
0
  }
392
0
}
393
394
// -------------------------------------------------------------------------
395
// TODO(ER): The following tests should be moved to MFBT.
396
397
#define NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING(_class)               \
398
public:                                                                     \
399
0
NS_METHOD_(MozExternalRefCountType) AddRef(void) const {                    \
400
0
  MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class)                                \
401
0
  MOZ_RELEASE_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");              \
402
0
  nsrefcnt count = ++mRefCnt;                                               \
403
0
  return (nsrefcnt) count;                                                  \
404
0
}                                                                           \
405
0
NS_METHOD_(MozExternalRefCountType) Release(void) const {                   \
406
0
  MOZ_RELEASE_ASSERT(int32_t(mRefCnt) > 0, "dup release");                  \
407
0
  nsrefcnt count = --mRefCnt;                                               \
408
0
  if (count == 0) {                                                         \
409
0
    delete (this);                                                          \
410
0
    return 0;                                                               \
411
0
  }                                                                         \
412
0
  return count;                                                             \
413
0
}                                                                           \
414
protected:                                                                  \
415
mutable ::mozilla::ThreadSafeAutoRefCnt mRefCnt;                            \
416
public:
417
418
class ObjectForConstPtr
419
{
420
  private:
421
    // Reference-counted classes cannot have public destructors.
422
    ~ObjectForConstPtr() = default;
423
  public:
424
    NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING(ObjectForConstPtr)
425
      void ConstMemberFunction( int aArg1, int* aArgPtr, int& aArgRef ) const
426
0
      {
427
0
      }
428
};
429
#undef NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING
430
431
namespace TestNsRefPtr
432
{
433
0
void AnFooPtrPtrContext(Foo**) { }
434
0
void AVoidPtrPtrContext(void**) { }
435
} // namespace TestNsRefPtr
436
437
TEST(nsRefPtr, RefPtrCompilationTests)
438
0
{
439
0
440
0
  {
441
0
    RefPtr<Foo> fooP;
442
0
443
0
    AnFooPtrPtrContext( getter_AddRefs(fooP) );
444
0
    AVoidPtrPtrContext( getter_AddRefs(fooP) );
445
0
  }
446
0
447
0
  {
448
0
    RefPtr<Foo> fooP(new Foo);
449
0
    RefPtr<const Foo> constFooP = fooP;
450
0
    constFooP->ConstMethod();
451
0
452
0
    // [Shouldn't compile] Is it a compile time error to call a non-const method on an |RefPtr<const T>|?
453
0
    //constFooP->NonconstMethod();
454
0
455
0
    // [Shouldn't compile] Is it a compile time error to construct an |RefPtr<T> from an |RefPtr<const T>|?
456
0
    //RefPtr<Foo> otherFooP(constFooP);
457
0
  }
458
0
459
0
  {
460
0
    RefPtr<Foo> foop = new Foo;
461
0
    RefPtr<Foo> foop2 = new Bar;
462
0
    RefPtr<const ObjectForConstPtr> foop3 = new ObjectForConstPtr;
463
0
    int test = 1;
464
0
    void (Foo::*fPtr)( int, int*, int& ) = &Foo::MemberFunction;
465
0
    void (Foo::*fVPtr)( int, int*, int& ) = &Foo::VirtualMemberFunction;
466
0
    void (Foo::*fVCPtr)( int, int*, int& ) const = &Foo::VirtualConstMemberFunction;
467
0
    void (ObjectForConstPtr::*fCPtr2)( int, int*, int& ) const = &ObjectForConstPtr::ConstMemberFunction;
468
0
469
0
    (foop->*fPtr)(test, &test, test);
470
0
    (foop2->*fVPtr)(test, &test, test);
471
0
    (foop2->*fVCPtr)(test, &test, test);
472
0
    (foop3->*fCPtr2)(test, &test, test);
473
0
  }
474
0
475
0
  // Looks like everything ran.
476
0
  ASSERT_TRUE(true);
477
0
}