Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/tests/gtest/TestHashtables.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsTHashtable.h"
8
#include "nsBaseHashtable.h"
9
#include "nsDataHashtable.h"
10
#include "nsInterfaceHashtable.h"
11
#include "nsClassHashtable.h"
12
13
#include "nsCOMPtr.h"
14
#include "nsISupports.h"
15
#include "nsCOMArray.h"
16
#include "mozilla/Attributes.h"
17
18
#include "gtest/gtest.h"
19
20
namespace TestHashtables {
21
22
class TestUniChar // for nsClassHashtable
23
{
24
public:
25
  explicit TestUniChar(uint32_t aWord)
26
0
  {
27
0
    mWord = aWord;
28
0
  }
29
30
  ~TestUniChar() = default;
31
32
0
  uint32_t GetChar() const { return mWord; }
33
34
private:
35
  uint32_t mWord;
36
};
37
38
struct EntityNode {
39
  const char*   mStr; // never owns buffer
40
  uint32_t       mUnicode;
41
};
42
43
EntityNode gEntities[] = {
44
  {"nbsp",160},
45
  {"iexcl",161},
46
  {"cent",162},
47
  {"pound",163},
48
  {"curren",164},
49
  {"yen",165},
50
  {"brvbar",166},
51
  {"sect",167},
52
  {"uml",168},
53
  {"copy",169},
54
  {"ordf",170},
55
  {"laquo",171},
56
  {"not",172},
57
  {"shy",173},
58
  {"reg",174},
59
  {"macr",175}
60
};
61
62
0
#define ENTITY_COUNT (unsigned(sizeof(gEntities)/sizeof(EntityNode)))
63
64
class EntityToUnicodeEntry : public PLDHashEntryHdr
65
{
66
public:
67
  typedef const char* KeyType;
68
  typedef const char* KeyTypePointer;
69
70
0
  explicit EntityToUnicodeEntry(const char* aKey) { mNode = nullptr; }
71
0
  EntityToUnicodeEntry(const EntityToUnicodeEntry& aEntry) { mNode = aEntry.mNode; }
72
  ~EntityToUnicodeEntry() = default;
73
74
0
  bool KeyEquals(const char* aEntity) const { return !strcmp(mNode->mStr, aEntity); }
75
0
  static const char* KeyToPointer(const char* aEntity) { return aEntity; }
76
0
  static PLDHashNumber HashKey(const char* aEntity) { return mozilla::HashString(aEntity); }
77
  enum { ALLOW_MEMMOVE = true };
78
79
  const EntityNode* mNode;
80
};
81
82
static uint32_t
83
nsTIterPrint(nsTHashtable<EntityToUnicodeEntry>& hash)
84
0
{
85
0
  uint32_t n = 0;
86
0
  for (auto iter = hash.Iter(); !iter.Done(); iter.Next()) {
87
0
    n++;
88
0
  }
89
0
  return n;
90
0
}
91
92
static uint32_t
93
nsTIterPrintRemove(nsTHashtable<EntityToUnicodeEntry>& hash)
94
0
{
95
0
  uint32_t n = 0;
96
0
  for (auto iter = hash.Iter(); !iter.Done(); iter.Next()) {
97
0
    iter.Remove();
98
0
    n++;
99
0
  }
100
0
  return n;
101
0
}
102
103
void
104
0
testTHashtable(nsTHashtable<EntityToUnicodeEntry>& hash, uint32_t numEntries) {
105
0
  uint32_t i;
106
0
  for (i = 0; i < numEntries; ++i) {
107
0
    EntityToUnicodeEntry* entry =
108
0
      hash.PutEntry(gEntities[i].mStr);
109
0
110
0
    EXPECT_TRUE(entry);
111
0
112
0
    EXPECT_FALSE(entry->mNode);
113
0
    entry->mNode = &gEntities[i];
114
0
  }
115
0
116
0
  for (i = 0; i < numEntries; ++i) {
117
0
    EntityToUnicodeEntry* entry =
118
0
      hash.GetEntry(gEntities[i].mStr);
119
0
120
0
    EXPECT_TRUE(entry);
121
0
  }
122
0
123
0
  EntityToUnicodeEntry* entry =
124
0
    hash.GetEntry("xxxy");
125
0
126
0
  EXPECT_FALSE(entry);
127
0
128
0
  uint32_t count = nsTIterPrint(hash);
129
0
  EXPECT_EQ(count, numEntries);
130
0
}
131
132
//
133
// all this nsIFoo stuff was copied wholesale from TestCOMPtr.cpp
134
//
135
136
#define NS_IFOO_IID \
137
{ 0x6f7652e0,  0xee43, 0x11d1, \
138
 { 0x9c, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3 } }
139
140
class IFoo final : public nsISupports
141
  {
142
    public:
143
      NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFOO_IID)
144
145
      IFoo();
146
147
      NS_IMETHOD_(MozExternalRefCountType) AddRef() override;
148
      NS_IMETHOD_(MozExternalRefCountType) Release() override;
149
      NS_IMETHOD QueryInterface( const nsIID&, void** ) override;
150
151
      NS_IMETHOD SetString(const nsACString& /*in*/ aString);
152
      NS_IMETHOD GetString(nsACString& /*out*/ aString);
153
154
      static void print_totals();
155
156
    private:
157
      ~IFoo();
158
159
      unsigned int refcount_;
160
161
      static unsigned int total_constructions_;
162
      static unsigned int total_destructions_;
163
      nsCString mString;
164
  };
165
166
NS_DEFINE_STATIC_IID_ACCESSOR(IFoo, NS_IFOO_IID)
167
168
unsigned int IFoo::total_constructions_;
169
unsigned int IFoo::total_destructions_;
170
171
void
172
IFoo::print_totals()
173
0
  {
174
0
  }
175
176
IFoo::IFoo()
177
    : refcount_(0)
178
0
  {
179
0
    ++total_constructions_;
180
0
  }
181
182
IFoo::~IFoo()
183
0
  {
184
0
    ++total_destructions_;
185
0
  }
186
187
MozExternalRefCountType
188
IFoo::AddRef()
189
0
  {
190
0
    ++refcount_;
191
0
    return refcount_;
192
0
  }
193
194
MozExternalRefCountType
195
IFoo::Release()
196
0
  {
197
0
    int newcount = --refcount_;
198
0
    if ( newcount == 0 )
199
0
      {
200
0
        delete this;
201
0
      }
202
0
203
0
    return newcount;
204
0
  }
205
206
nsresult
207
IFoo::QueryInterface( const nsIID& aIID, void** aResult )
208
0
  {
209
0
    nsISupports* rawPtr = 0;
210
0
    nsresult status = NS_OK;
211
0
212
0
    if ( aIID.Equals(NS_GET_IID(IFoo)) )
213
0
      rawPtr = this;
214
0
    else
215
0
      {
216
0
        nsID iid_of_ISupports = NS_ISUPPORTS_IID;
217
0
        if ( aIID.Equals(iid_of_ISupports) )
218
0
          rawPtr = static_cast<nsISupports*>(this);
219
0
        else
220
0
          status = NS_ERROR_NO_INTERFACE;
221
0
      }
222
0
223
0
    NS_IF_ADDREF(rawPtr);
224
0
    *aResult = rawPtr;
225
0
226
0
    return status;
227
0
  }
228
229
nsresult
230
IFoo::SetString(const nsACString& aString)
231
0
{
232
0
  mString = aString;
233
0
  return NS_OK;
234
0
}
235
236
nsresult
237
IFoo::GetString(nsACString& aString)
238
0
{
239
0
  aString = mString;
240
0
  return NS_OK;
241
0
}
242
243
nsresult
244
CreateIFoo( IFoo** result )
245
    // a typical factory function (that calls AddRef)
246
0
  {
247
0
    auto* foop = new IFoo();
248
0
249
0
    foop->AddRef();
250
0
    *result = foop;
251
0
252
0
    return NS_OK;
253
0
  }
254
255
} // namespace TestHashtables
256
257
using namespace TestHashtables;
258
259
TEST(Hashtable, THashtable)
260
0
{
261
0
  // check an nsTHashtable
262
0
  nsTHashtable<EntityToUnicodeEntry> EntityToUnicode(ENTITY_COUNT);
263
0
264
0
  testTHashtable(EntityToUnicode, 5);
265
0
266
0
  uint32_t count = nsTIterPrintRemove(EntityToUnicode);
267
0
  ASSERT_EQ(count, uint32_t(5));
268
0
269
0
  count = nsTIterPrint(EntityToUnicode);
270
0
  ASSERT_EQ(count, uint32_t(0));
271
0
272
0
  testTHashtable(EntityToUnicode, ENTITY_COUNT);
273
0
274
0
  EntityToUnicode.Clear();
275
0
276
0
  count = nsTIterPrint(EntityToUnicode);
277
0
  ASSERT_EQ(count, uint32_t(0));
278
0
}
279
280
TEST(Hashtable, Move)
281
0
{
282
0
  const void* kPtr = reinterpret_cast<void*>(static_cast<uintptr_t>(0xbadc0de));
283
0
284
0
  nsTHashtable<nsPtrHashKey<const void>> table;
285
0
  table.PutEntry(kPtr);
286
0
287
0
  nsTHashtable<nsPtrHashKey<const void>> moved = std::move(table);
288
0
  ASSERT_EQ(table.Count(), 0u);
289
0
  ASSERT_EQ(moved.Count(), 1u);
290
0
291
0
  EXPECT_TRUE(moved.Contains(kPtr));
292
0
  EXPECT_FALSE(table.Contains(kPtr));
293
0
}
294
295
TEST(Hashtables, DataHashtable)
296
0
{
297
0
  // check a data-hashtable
298
0
  nsDataHashtable<nsUint32HashKey,const char*> UniToEntity(ENTITY_COUNT);
299
0
300
0
  for (auto& entity : gEntities) {
301
0
    UniToEntity.Put(entity.mUnicode, entity.mStr);
302
0
  }
303
0
304
0
  const char* str;
305
0
306
0
  for (auto& entity : gEntities) {
307
0
    ASSERT_TRUE(UniToEntity.Get(entity.mUnicode, &str));
308
0
  }
309
0
310
0
  ASSERT_FALSE(UniToEntity.Get(99446, &str));
311
0
312
0
  uint32_t count = 0;
313
0
  for (auto iter = UniToEntity.Iter(); !iter.Done(); iter.Next()) {
314
0
    count++;
315
0
  }
316
0
  ASSERT_EQ(count, ENTITY_COUNT);
317
0
318
0
  UniToEntity.Clear();
319
0
320
0
  count = 0;
321
0
  for (auto iter = UniToEntity.Iter(); !iter.Done(); iter.Next()) {
322
0
    printf("  enumerated %u = \"%s\"\n", iter.Key(), iter.Data());
323
0
    count++;
324
0
  }
325
0
  ASSERT_EQ(count, uint32_t(0));
326
0
}
327
328
TEST(Hashtables, ClassHashtable)
329
0
{
330
0
  // check a class-hashtable
331
0
  nsClassHashtable<nsCStringHashKey,TestUniChar> EntToUniClass(ENTITY_COUNT);
332
0
333
0
  for (auto& entity : gEntities) {
334
0
    auto* temp = new TestUniChar(entity.mUnicode);
335
0
    EntToUniClass.Put(nsDependentCString(entity.mStr), temp);
336
0
  }
337
0
338
0
  TestUniChar* myChar;
339
0
340
0
  for (auto& entity : gEntities) {
341
0
    ASSERT_TRUE(EntToUniClass.Get(nsDependentCString(entity.mStr), &myChar));
342
0
  }
343
0
344
0
  ASSERT_FALSE(EntToUniClass.Get(NS_LITERAL_CSTRING("xxxx"), &myChar));
345
0
346
0
  uint32_t count = 0;
347
0
  for (auto iter = EntToUniClass.Iter(); !iter.Done(); iter.Next()) {
348
0
    count++;
349
0
  }
350
0
  ASSERT_EQ(count, ENTITY_COUNT);
351
0
352
0
  EntToUniClass.Clear();
353
0
354
0
  count = 0;
355
0
  for (auto iter = EntToUniClass.Iter(); !iter.Done(); iter.Next()) {
356
0
    count++;
357
0
  }
358
0
  ASSERT_EQ(count, uint32_t(0));
359
0
}
360
361
TEST(Hashtables, DataHashtableWithInterfaceKey)
362
0
{
363
0
  // check a data-hashtable with an interface key
364
0
  nsDataHashtable<nsISupportsHashKey,uint32_t> EntToUniClass2(ENTITY_COUNT);
365
0
366
0
  nsCOMArray<IFoo> fooArray;
367
0
368
0
  for (uint32_t i = 0; i < ENTITY_COUNT; ++i) {
369
0
    nsCOMPtr<IFoo> foo;
370
0
    CreateIFoo(getter_AddRefs(foo));
371
0
    foo->SetString(nsDependentCString(gEntities[i].mStr));
372
0
373
0
    fooArray.InsertObjectAt(foo, i);
374
0
375
0
    EntToUniClass2.Put(foo, gEntities[i].mUnicode);
376
0
  }
377
0
378
0
  uint32_t myChar2;
379
0
380
0
  for (uint32_t i = 0; i < ENTITY_COUNT; ++i) {
381
0
    ASSERT_TRUE(EntToUniClass2.Get(fooArray[i], &myChar2));
382
0
  }
383
0
384
0
  ASSERT_FALSE(EntToUniClass2.Get((nsISupports*) 0x55443316, &myChar2));
385
0
386
0
  uint32_t count = 0;
387
0
  for (auto iter = EntToUniClass2.Iter(); !iter.Done(); iter.Next()) {
388
0
    nsAutoCString s;
389
0
    nsCOMPtr<IFoo> foo = do_QueryInterface(iter.Key());
390
0
    foo->GetString(s);
391
0
    count++;
392
0
  }
393
0
  ASSERT_EQ(count, ENTITY_COUNT);
394
0
395
0
  EntToUniClass2.Clear();
396
0
397
0
  count = 0;
398
0
  for (auto iter = EntToUniClass2.Iter(); !iter.Done(); iter.Next()) {
399
0
    nsAutoCString s;
400
0
    nsCOMPtr<IFoo> foo = do_QueryInterface(iter.Key());
401
0
    foo->GetString(s);
402
0
    count++;
403
0
  }
404
0
  ASSERT_EQ(count, uint32_t(0));
405
0
}
406
407
TEST(Hashtables, InterfaceHashtable)
408
0
{
409
0
  // check an interface-hashtable with an uint32_t key
410
0
  nsInterfaceHashtable<nsUint32HashKey,IFoo> UniToEntClass2(ENTITY_COUNT);
411
0
412
0
  for (auto& entity : gEntities) {
413
0
    nsCOMPtr<IFoo> foo;
414
0
    CreateIFoo(getter_AddRefs(foo));
415
0
    foo->SetString(nsDependentCString(entity.mStr));
416
0
417
0
    UniToEntClass2.Put(entity.mUnicode, foo);
418
0
  }
419
0
420
0
  for (auto& entity : gEntities) {
421
0
    nsCOMPtr<IFoo> myEnt;
422
0
    ASSERT_TRUE(UniToEntClass2.Get(entity.mUnicode, getter_AddRefs(myEnt)));
423
0
424
0
    nsAutoCString myEntStr;
425
0
    myEnt->GetString(myEntStr);
426
0
  }
427
0
428
0
  nsCOMPtr<IFoo> myEnt;
429
0
  ASSERT_FALSE(UniToEntClass2.Get(9462, getter_AddRefs(myEnt)));
430
0
431
0
  uint32_t count = 0;
432
0
  for (auto iter = UniToEntClass2.Iter(); !iter.Done(); iter.Next()) {
433
0
    nsAutoCString s;
434
0
    iter.UserData()->GetString(s);
435
0
    count++;
436
0
  }
437
0
  ASSERT_EQ(count, ENTITY_COUNT);
438
0
439
0
  UniToEntClass2.Clear();
440
0
441
0
  count = 0;
442
0
  for (auto iter = UniToEntClass2.Iter(); !iter.Done(); iter.Next()) {
443
0
    nsAutoCString s;
444
0
    iter.Data()->GetString(s);
445
0
    count++;
446
0
  }
447
0
  ASSERT_EQ(count, uint32_t(0));
448
0
}
449
450
TEST(Hashtables, DataHashtable_LookupForAdd)
451
0
{
452
0
  // check LookupForAdd/OrInsert
453
0
  nsDataHashtable<nsUint32HashKey,const char*> UniToEntity(ENTITY_COUNT);
454
0
455
0
  for (auto& entity : gEntities) {
456
0
    auto entry = UniToEntity.LookupForAdd(entity.mUnicode);
457
0
    const char* val = entry.OrInsert([&entity] () { return entity.mStr; });
458
0
    ASSERT_FALSE(entry);
459
0
    ASSERT_TRUE(val == entity.mStr);
460
0
    ASSERT_TRUE(entry.Data() == entity.mStr);
461
0
  }
462
0
463
0
  for (auto& entity : gEntities) {
464
0
    ASSERT_TRUE(UniToEntity.LookupForAdd(entity.mUnicode));
465
0
  }
466
0
467
0
  // 0 should not be found
468
0
  size_t count = UniToEntity.Count();
469
0
  UniToEntity.Lookup(0U).Remove();
470
0
  ASSERT_TRUE(count == UniToEntity.Count());
471
0
472
0
  // Lookup should find all entries
473
0
  count = 0;
474
0
  for (auto& entity : gEntities) {
475
0
    if (UniToEntity.Lookup(entity.mUnicode)) {
476
0
      count++;
477
0
    }
478
0
  }
479
0
  ASSERT_TRUE(count == UniToEntity.Count());
480
0
481
0
  for (auto& entity : gEntities) {
482
0
    ASSERT_TRUE(UniToEntity.LookupForAdd(entity.mUnicode));
483
0
  }
484
0
485
0
  // Lookup().Remove() should remove all entries.
486
0
  for (auto& entity : gEntities) {
487
0
    if (auto entry = UniToEntity.Lookup(entity.mUnicode)) {
488
0
      entry.Remove();
489
0
    }
490
0
  }
491
0
  ASSERT_TRUE(0 == UniToEntity.Count());
492
0
493
0
  // Remove newly added entries via OrRemove.
494
0
  for (auto& entity : gEntities) {
495
0
    auto entry = UniToEntity.LookupForAdd(entity.mUnicode);
496
0
    ASSERT_FALSE(entry);
497
0
    entry.OrRemove();
498
0
  }
499
0
  ASSERT_TRUE(0 == UniToEntity.Count());
500
0
501
0
  // Remove existing entries via OrRemove.
502
0
  for (auto& entity : gEntities) {
503
0
    auto entry = UniToEntity.LookupForAdd(entity.mUnicode);
504
0
    const char* val = entry.OrInsert([&entity] () { return entity.mStr; });
505
0
    ASSERT_FALSE(entry);
506
0
    ASSERT_TRUE(val == entity.mStr);
507
0
    ASSERT_TRUE(entry.Data() == entity.mStr);
508
0
509
0
    auto entry2 = UniToEntity.LookupForAdd(entity.mUnicode);
510
0
    ASSERT_TRUE(entry2);
511
0
    entry2.OrRemove();
512
0
  }
513
0
  ASSERT_TRUE(0 == UniToEntity.Count());
514
0
}
515
516
TEST(Hashtables, ClassHashtable_LookupForAdd)
517
0
{
518
0
  // check a class-hashtable LookupForAdd with null values
519
0
  nsClassHashtable<nsCStringHashKey,TestUniChar> EntToUniClass(ENTITY_COUNT);
520
0
521
0
  for (auto& entity : gEntities) {
522
0
    auto entry = EntToUniClass.LookupForAdd(nsDependentCString(entity.mStr));
523
0
    const TestUniChar* val = entry.OrInsert([] () { return nullptr; });
524
0
    ASSERT_FALSE(entry);
525
0
    ASSERT_TRUE(val == nullptr);
526
0
    ASSERT_TRUE(entry.Data() == nullptr);
527
0
  }
528
0
529
0
  for (auto& entity : gEntities) {
530
0
    ASSERT_TRUE(EntToUniClass.LookupForAdd(nsDependentCString(entity.mStr)));
531
0
    ASSERT_TRUE(EntToUniClass.LookupForAdd(nsDependentCString(entity.mStr)).Data() == nullptr);
532
0
  }
533
0
534
0
  // "" should not be found
535
0
  size_t count = EntToUniClass.Count();
536
0
  EntToUniClass.Lookup(nsDependentCString("")).Remove();
537
0
  ASSERT_TRUE(count == EntToUniClass.Count());
538
0
539
0
  // Lookup should find all entries.
540
0
  count = 0;
541
0
  for (auto& entity : gEntities) {
542
0
    if (EntToUniClass.Lookup(nsDependentCString(entity.mStr))) {
543
0
      count++;
544
0
    }
545
0
  }
546
0
  ASSERT_TRUE(count == EntToUniClass.Count());
547
0
548
0
  for (auto& entity : gEntities) {
549
0
    ASSERT_TRUE(EntToUniClass.LookupForAdd(nsDependentCString(entity.mStr)));
550
0
  }
551
0
552
0
  // Lookup().Remove() should remove all entries.
553
0
  for (auto& entity : gEntities) {
554
0
    if (auto entry = EntToUniClass.Lookup(nsDependentCString(entity.mStr))) {
555
0
      entry.Remove();
556
0
    }
557
0
  }
558
0
  ASSERT_TRUE(0 == EntToUniClass.Count());
559
0
560
0
  // Remove newly added entries via OrRemove.
561
0
  for (auto& entity : gEntities) {
562
0
    auto entry = EntToUniClass.LookupForAdd(nsDependentCString(entity.mStr));
563
0
    ASSERT_FALSE(entry);
564
0
    entry.OrRemove();
565
0
  }
566
0
  ASSERT_TRUE(0 == EntToUniClass.Count());
567
0
568
0
  // Remove existing entries via OrRemove.
569
0
  for (auto& entity : gEntities) {
570
0
    auto entry = EntToUniClass.LookupForAdd(nsDependentCString(entity.mStr));
571
0
    const TestUniChar* val = entry.OrInsert([] () { return nullptr; });
572
0
    ASSERT_FALSE(entry);
573
0
    ASSERT_TRUE(val == nullptr);
574
0
    ASSERT_TRUE(entry.Data() == nullptr);
575
0
576
0
    auto entry2 = EntToUniClass.LookupForAdd(nsDependentCString(entity.mStr));
577
0
    ASSERT_TRUE(entry2);
578
0
    entry2.OrRemove();
579
0
  }
580
0
  ASSERT_TRUE(0 == EntToUniClass.Count());
581
0
}