Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/common-tests/string_pool_tests.cpp
10595 views
1
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "common/string_pool.h"
5
6
#include <gtest/gtest.h>
7
8
// ---- BumpStringPool ----
9
10
TEST(BumpStringPool, InitiallyEmpty)
11
{
12
BumpStringPool pool;
13
EXPECT_TRUE(pool.IsEmpty());
14
EXPECT_EQ(pool.GetSize(), 0u);
15
}
16
17
TEST(BumpStringPool, AddSingleString)
18
{
19
BumpStringPool pool;
20
auto offset = pool.AddString("hello");
21
EXPECT_NE(offset, BumpStringPool::InvalidOffset);
22
EXPECT_FALSE(pool.IsEmpty());
23
EXPECT_EQ(pool.GetString(offset), "hello");
24
}
25
26
TEST(BumpStringPool, AddEmptyStringReturnsInvalid)
27
{
28
BumpStringPool pool;
29
auto offset = pool.AddString("");
30
EXPECT_EQ(offset, BumpStringPool::InvalidOffset);
31
EXPECT_TRUE(pool.IsEmpty());
32
}
33
34
TEST(BumpStringPool, AddEmptyStringViewReturnsInvalid)
35
{
36
BumpStringPool pool;
37
auto offset = pool.AddString(std::string_view{});
38
EXPECT_EQ(offset, BumpStringPool::InvalidOffset);
39
EXPECT_TRUE(pool.IsEmpty());
40
}
41
42
TEST(BumpStringPool, AddMultipleStrings)
43
{
44
BumpStringPool pool;
45
auto o1 = pool.AddString("alpha");
46
auto o2 = pool.AddString("beta");
47
auto o3 = pool.AddString("gamma");
48
49
EXPECT_NE(o1, o2);
50
EXPECT_NE(o2, o3);
51
EXPECT_EQ(pool.GetString(o1), "alpha");
52
EXPECT_EQ(pool.GetString(o2), "beta");
53
EXPECT_EQ(pool.GetString(o3), "gamma");
54
}
55
56
TEST(BumpStringPool, DuplicateStringsStored)
57
{
58
BumpStringPool pool;
59
auto o1 = pool.AddString("same");
60
const size_t size1 = pool.GetSize();
61
62
auto o2 = pool.AddString("same");
63
EXPECT_NE(o1, o2);
64
EXPECT_GT(pool.GetSize(), size1);
65
EXPECT_EQ(pool.GetString(o1), "same");
66
EXPECT_EQ(pool.GetString(o2), "same");
67
}
68
69
TEST(BumpStringPool, GetStringWithOffset)
70
{
71
BumpStringPool pool;
72
auto offset = pool.AddString("world");
73
EXPECT_EQ(pool.GetString(offset), "world");
74
}
75
76
TEST(BumpStringPool, GetStringWithOffsetAndLength)
77
{
78
BumpStringPool pool;
79
auto offset = pool.AddString("hello world");
80
EXPECT_EQ(pool.GetString(offset, 5), "hello");
81
EXPECT_EQ(pool.GetString(offset, 11), "hello world");
82
}
83
84
TEST(BumpStringPool, GetStringInvalidOffset)
85
{
86
BumpStringPool pool;
87
EXPECT_EQ(pool.GetString(BumpStringPool::InvalidOffset), std::string_view{});
88
}
89
90
TEST(BumpStringPool, GetStringOutOfBounds)
91
{
92
BumpStringPool pool;
93
std::ignore = pool.AddString("test");
94
EXPECT_EQ(pool.GetString(9999), std::string_view{});
95
}
96
97
TEST(BumpStringPool, GetStringWithLengthOutOfBounds)
98
{
99
BumpStringPool pool;
100
auto offset = pool.AddString("short");
101
EXPECT_EQ(pool.GetString(offset, 9999), std::string_view{});
102
}
103
104
TEST(BumpStringPool, SizeIncludesNullTerminators)
105
{
106
BumpStringPool pool;
107
std::ignore = pool.AddString("abc"); // 3 chars + 1 null = 4
108
EXPECT_EQ(pool.GetSize(), 4u);
109
110
std::ignore = pool.AddString("de"); // 2 chars + 1 null = 3
111
EXPECT_EQ(pool.GetSize(), 7u);
112
}
113
114
TEST(BumpStringPool, Clear)
115
{
116
BumpStringPool pool;
117
std::ignore = pool.AddString("one");
118
std::ignore = pool.AddString("two");
119
EXPECT_FALSE(pool.IsEmpty());
120
121
pool.Clear();
122
EXPECT_TRUE(pool.IsEmpty());
123
EXPECT_EQ(pool.GetSize(), 0u);
124
}
125
126
TEST(BumpStringPool, ClearThenReuse)
127
{
128
BumpStringPool pool;
129
std::ignore = pool.AddString("before");
130
pool.Clear();
131
132
auto offset = pool.AddString("after");
133
EXPECT_NE(offset, BumpStringPool::InvalidOffset);
134
EXPECT_EQ(pool.GetString(offset), "after");
135
}
136
137
TEST(BumpStringPool, ReserveDoesNotChangeState)
138
{
139
BumpStringPool pool;
140
pool.Reserve(4096);
141
EXPECT_TRUE(pool.IsEmpty());
142
EXPECT_EQ(pool.GetSize(), 0u);
143
}
144
145
TEST(BumpStringPool, UnicodeStrings)
146
{
147
BumpStringPool pool;
148
auto jp = pool.AddString("日本語テスト");
149
auto emoji = pool.AddString("🎮🕹️");
150
151
EXPECT_EQ(pool.GetString(jp), "日本語テスト");
152
EXPECT_EQ(pool.GetString(emoji), "🎮🕹️");
153
}
154
155
TEST(BumpStringPool, SingleCharacterString)
156
{
157
BumpStringPool pool;
158
auto offset = pool.AddString("x");
159
EXPECT_EQ(pool.GetString(offset), "x");
160
EXPECT_EQ(pool.GetSize(), 2u); // 1 char + null
161
}
162
163
TEST(BumpStringPool, VeryLongString)
164
{
165
BumpStringPool pool;
166
std::string long_str(10000, 'z');
167
auto offset = pool.AddString(long_str);
168
EXPECT_EQ(pool.GetString(offset), long_str);
169
EXPECT_EQ(pool.GetSize(), 10001u);
170
}
171
172
TEST(BumpStringPool, ManyStrings)
173
{
174
BumpStringPool pool;
175
pool.Reserve(2000);
176
177
for (int i = 0; i < 200; i++)
178
{
179
std::string s = "str_" + std::to_string(i);
180
auto offset = pool.AddString(s);
181
EXPECT_EQ(pool.GetString(offset), s);
182
}
183
}
184
185
TEST(BumpStringPool, SpecialCharacters)
186
{
187
BumpStringPool pool;
188
auto tab = pool.AddString("a\tb");
189
auto newline = pool.AddString("c\nd");
190
191
EXPECT_EQ(pool.GetString(tab), "a\tb");
192
EXPECT_EQ(pool.GetString(newline), "c\nd");
193
}
194
195
// ---- BumpUniqueStringPool ----
196
197
TEST(BumpUniqueStringPool, InitiallyEmpty)
198
{
199
BumpUniqueStringPool pool;
200
EXPECT_TRUE(pool.IsEmpty());
201
EXPECT_EQ(pool.GetSize(), 0u);
202
EXPECT_EQ(pool.GetCount(), 0u);
203
}
204
205
TEST(BumpUniqueStringPool, AddSingleString)
206
{
207
BumpUniqueStringPool pool;
208
auto offset = pool.AddString("hello");
209
EXPECT_NE(offset, BumpUniqueStringPool::InvalidOffset);
210
EXPECT_EQ(pool.GetCount(), 1u);
211
EXPECT_EQ(pool.GetString(offset), "hello");
212
}
213
214
TEST(BumpUniqueStringPool, AddEmptyStringReturnsInvalid)
215
{
216
BumpUniqueStringPool pool;
217
auto offset = pool.AddString("");
218
EXPECT_EQ(offset, BumpUniqueStringPool::InvalidOffset);
219
EXPECT_TRUE(pool.IsEmpty());
220
EXPECT_EQ(pool.GetCount(), 0u);
221
}
222
223
TEST(BumpUniqueStringPool, AddEmptyStringViewReturnsInvalid)
224
{
225
BumpUniqueStringPool pool;
226
auto offset = pool.AddString(std::string_view{});
227
EXPECT_EQ(offset, BumpUniqueStringPool::InvalidOffset);
228
EXPECT_TRUE(pool.IsEmpty());
229
}
230
231
TEST(BumpUniqueStringPool, DeduplicatesSameString)
232
{
233
BumpUniqueStringPool pool;
234
auto offset1 = pool.AddString("duplicate");
235
const size_t size_after_first = pool.GetSize();
236
const size_t count_after_first = pool.GetCount();
237
238
auto offset2 = pool.AddString("duplicate");
239
EXPECT_EQ(offset1, offset2);
240
EXPECT_EQ(pool.GetSize(), size_after_first);
241
EXPECT_EQ(pool.GetCount(), count_after_first);
242
}
243
244
TEST(BumpUniqueStringPool, DeduplicateMultipleAdds)
245
{
246
BumpUniqueStringPool pool;
247
auto offset = pool.AddString("test");
248
const size_t size_after = pool.GetSize();
249
250
for (int i = 0; i < 100; i++)
251
{
252
EXPECT_EQ(pool.AddString("test"), offset);
253
}
254
255
EXPECT_EQ(pool.GetSize(), size_after);
256
EXPECT_EQ(pool.GetCount(), 1u);
257
}
258
259
TEST(BumpUniqueStringPool, DistinguishesDifferentStrings)
260
{
261
BumpUniqueStringPool pool;
262
auto offset1 = pool.AddString("alpha");
263
auto offset2 = pool.AddString("beta");
264
EXPECT_NE(offset1, offset2);
265
EXPECT_EQ(pool.GetCount(), 2u);
266
EXPECT_EQ(pool.GetString(offset1), "alpha");
267
EXPECT_EQ(pool.GetString(offset2), "beta");
268
}
269
270
TEST(BumpUniqueStringPool, SizeDoesNotGrowOnDuplicate)
271
{
272
BumpUniqueStringPool pool;
273
std::ignore = pool.AddString("aaa");
274
std::ignore = pool.AddString("bbb");
275
std::ignore = pool.AddString("ccc");
276
const size_t size_before = pool.GetSize();
277
const size_t count_before = pool.GetCount();
278
279
std::ignore = pool.AddString("aaa");
280
std::ignore = std::ignore = pool.AddString("bbb");
281
std::ignore = pool.AddString("ccc");
282
EXPECT_EQ(pool.GetSize(), size_before);
283
EXPECT_EQ(pool.GetCount(), count_before);
284
}
285
286
TEST(BumpUniqueStringPool, SizeGrowsOnlyForNewStrings)
287
{
288
BumpUniqueStringPool pool;
289
std::ignore = pool.AddString("aaa");
290
const size_t size1 = pool.GetSize();
291
292
std::ignore = pool.AddString("aaa"); // duplicate
293
EXPECT_EQ(pool.GetSize(), size1);
294
295
std::ignore = pool.AddString("bbb"); // new
296
EXPECT_GT(pool.GetSize(), size1);
297
const size_t size2 = pool.GetSize();
298
299
std::ignore = pool.AddString("bbb"); // duplicate
300
EXPECT_EQ(pool.GetSize(), size2);
301
}
302
303
TEST(BumpUniqueStringPool, GetStringWithOffset)
304
{
305
BumpUniqueStringPool pool;
306
auto offset = pool.AddString("world");
307
EXPECT_EQ(pool.GetString(offset), "world");
308
}
309
310
TEST(BumpUniqueStringPool, GetStringWithOffsetAndLength)
311
{
312
BumpUniqueStringPool pool;
313
auto offset = pool.AddString("hello world");
314
EXPECT_EQ(pool.GetString(offset, 5), "hello");
315
EXPECT_EQ(pool.GetString(offset, 11), "hello world");
316
}
317
318
TEST(BumpUniqueStringPool, GetStringInvalidOffset)
319
{
320
BumpUniqueStringPool pool;
321
EXPECT_EQ(pool.GetString(BumpUniqueStringPool::InvalidOffset), std::string_view{});
322
}
323
324
TEST(BumpUniqueStringPool, GetStringOutOfBounds)
325
{
326
BumpUniqueStringPool pool;
327
std::ignore = pool.AddString("test");
328
EXPECT_EQ(pool.GetString(9999), std::string_view{});
329
}
330
331
TEST(BumpUniqueStringPool, GetStringWithLengthOutOfBounds)
332
{
333
BumpUniqueStringPool pool;
334
auto offset = pool.AddString("short");
335
EXPECT_EQ(pool.GetString(offset, 9999), std::string_view{});
336
}
337
338
TEST(BumpUniqueStringPool, Clear)
339
{
340
BumpUniqueStringPool pool;
341
std::ignore = pool.AddString("one");
342
std::ignore = pool.AddString("two");
343
std::ignore = pool.AddString("three");
344
EXPECT_FALSE(pool.IsEmpty());
345
EXPECT_EQ(pool.GetCount(), 3u);
346
347
pool.Clear();
348
EXPECT_TRUE(pool.IsEmpty());
349
EXPECT_EQ(pool.GetSize(), 0u);
350
EXPECT_EQ(pool.GetCount(), 0u);
351
}
352
353
TEST(BumpUniqueStringPool, ClearThenReuse)
354
{
355
BumpUniqueStringPool pool;
356
std::ignore = pool.AddString("before");
357
pool.Clear();
358
359
auto offset = pool.AddString("after");
360
EXPECT_NE(offset, BumpUniqueStringPool::InvalidOffset);
361
EXPECT_EQ(pool.GetString(offset), "after");
362
EXPECT_EQ(pool.GetCount(), 1u);
363
}
364
365
TEST(BumpUniqueStringPool, ReserveDoesNotChangeState)
366
{
367
BumpUniqueStringPool pool;
368
pool.Reserve(100, 4096);
369
EXPECT_TRUE(pool.IsEmpty());
370
EXPECT_EQ(pool.GetSize(), 0u);
371
EXPECT_EQ(pool.GetCount(), 0u);
372
}
373
374
TEST(BumpUniqueStringPool, ManyUniqueStrings)
375
{
376
BumpUniqueStringPool pool;
377
pool.Reserve(200, 2000);
378
379
for (int i = 0; i < 200; i++)
380
{
381
std::string s = "string_" + std::to_string(i);
382
std::ignore = pool.AddString(s);
383
}
384
385
EXPECT_EQ(pool.GetCount(), 200u);
386
387
// Verify all are retrievable and deduplicated.
388
for (int i = 0; i < 200; i++)
389
{
390
std::string s = "string_" + std::to_string(i);
391
auto offset = pool.AddString(s);
392
EXPECT_EQ(pool.GetString(offset), s);
393
}
394
395
// Count should not have grown.
396
EXPECT_EQ(pool.GetCount(), 200u);
397
}
398
399
TEST(BumpUniqueStringPool, CaseSensitive)
400
{
401
BumpUniqueStringPool pool;
402
auto lower = pool.AddString("hello");
403
auto upper = pool.AddString("Hello");
404
auto allupper = pool.AddString("HELLO");
405
406
EXPECT_NE(lower, upper);
407
EXPECT_NE(upper, allupper);
408
EXPECT_NE(lower, allupper);
409
EXPECT_EQ(pool.GetCount(), 3u);
410
}
411
412
TEST(BumpUniqueStringPool, SubstringNotDeduplicated)
413
{
414
BumpUniqueStringPool pool;
415
auto full = pool.AddString("hello world");
416
auto sub = pool.AddString("hello");
417
EXPECT_NE(full, sub);
418
EXPECT_EQ(pool.GetCount(), 2u);
419
EXPECT_EQ(pool.GetString(full), "hello world");
420
EXPECT_EQ(pool.GetString(sub), "hello");
421
}
422
423
TEST(BumpUniqueStringPool, PrefixSuffixDistinct)
424
{
425
BumpUniqueStringPool pool;
426
auto a = pool.AddString("abc");
427
auto b = pool.AddString("abcd");
428
auto c = pool.AddString("ab");
429
EXPECT_NE(a, b);
430
EXPECT_NE(a, c);
431
EXPECT_NE(b, c);
432
EXPECT_EQ(pool.GetCount(), 3u);
433
}
434
435
TEST(BumpUniqueStringPool, UnicodeStrings)
436
{
437
BumpUniqueStringPool pool;
438
auto jp = pool.AddString("日本語テスト");
439
auto cn = pool.AddString("中文测试");
440
auto emoji = pool.AddString("🎮🕹️");
441
442
EXPECT_EQ(pool.GetString(jp), "日本語テスト");
443
EXPECT_EQ(pool.GetString(cn), "中文测试");
444
EXPECT_EQ(pool.GetString(emoji), "🎮🕹️");
445
EXPECT_EQ(pool.GetCount(), 3u);
446
}
447
448
TEST(BumpUniqueStringPool, UnicodeDeduplicated)
449
{
450
BumpUniqueStringPool pool;
451
auto offset1 = pool.AddString("日本語");
452
const size_t size_after = pool.GetSize();
453
454
auto offset2 = pool.AddString("日本語");
455
EXPECT_EQ(offset1, offset2);
456
EXPECT_EQ(pool.GetSize(), size_after);
457
EXPECT_EQ(pool.GetCount(), 1u);
458
}
459
460
TEST(BumpUniqueStringPool, SpecialCharacters)
461
{
462
BumpUniqueStringPool pool;
463
auto tab = pool.AddString("hello\tworld");
464
auto newline = pool.AddString("hello\nworld");
465
auto null_inside = pool.AddString(std::string_view("a\0b", 3));
466
467
EXPECT_EQ(pool.GetString(tab), "hello\tworld");
468
EXPECT_EQ(pool.GetString(newline), "hello\nworld");
469
// Embedded null: GetString(offset) stops at null, but GetString(offset, length) returns full view.
470
EXPECT_EQ(pool.GetString(null_inside, 3), std::string_view("a\0b", 3));
471
EXPECT_EQ(pool.GetCount(), 3u);
472
}
473
474
TEST(BumpUniqueStringPool, SingleCharacterStrings)
475
{
476
BumpUniqueStringPool pool;
477
auto a = pool.AddString("a");
478
auto b = pool.AddString("b");
479
auto a2 = pool.AddString("a");
480
481
EXPECT_NE(a, b);
482
EXPECT_EQ(a, a2);
483
EXPECT_EQ(pool.GetCount(), 2u);
484
}
485
486
TEST(BumpUniqueStringPool, VeryLongString)
487
{
488
BumpUniqueStringPool pool;
489
std::string long_str(10000, 'x');
490
auto offset1 = pool.AddString(long_str);
491
const size_t size_after = pool.GetSize();
492
493
auto offset2 = pool.AddString(long_str);
494
EXPECT_EQ(offset1, offset2);
495
EXPECT_EQ(pool.GetSize(), size_after);
496
EXPECT_EQ(pool.GetString(offset1), long_str);
497
}
498
499
TEST(BumpUniqueStringPool, InterleaveAddAndLookup)
500
{
501
BumpUniqueStringPool pool;
502
auto a = pool.AddString("zebra");
503
auto b = pool.AddString("apple");
504
auto c = pool.AddString("mango");
505
506
// All original offsets still valid after sorted insertions.
507
EXPECT_EQ(pool.GetString(a), "zebra");
508
EXPECT_EQ(pool.GetString(b), "apple");
509
EXPECT_EQ(pool.GetString(c), "mango");
510
511
// Duplicates return same offsets.
512
EXPECT_EQ(pool.AddString("mango"), c);
513
EXPECT_EQ(pool.AddString("apple"), b);
514
EXPECT_EQ(pool.AddString("zebra"), a);
515
EXPECT_EQ(pool.GetCount(), 3u);
516
}
517
518
TEST(BumpUniqueStringPool, CountMatchesUniqueStrings)
519
{
520
BumpUniqueStringPool pool;
521
std::ignore = pool.AddString("a");
522
std::ignore = pool.AddString("b");
523
std::ignore = pool.AddString("c");
524
std::ignore = pool.AddString("a");
525
std::ignore = pool.AddString("b");
526
std::ignore = pool.AddString("d");
527
std::ignore = pool.AddString("c");
528
529
EXPECT_EQ(pool.GetCount(), 4u); // a, b, c, d
530
}
531
532
// ---- StringPool ----
533
534
TEST(StringPool, InitiallyEmpty)
535
{
536
StringPool pool;
537
EXPECT_TRUE(pool.IsEmpty());
538
EXPECT_EQ(pool.GetSize(), 0u);
539
EXPECT_EQ(pool.GetCount(), 0u);
540
}
541
542
TEST(StringPool, AddSingleString)
543
{
544
StringPool pool;
545
auto offset = pool.AddString("hello");
546
EXPECT_NE(offset, StringPool::InvalidOffset);
547
EXPECT_EQ(pool.GetCount(), 1u);
548
EXPECT_EQ(pool.GetString(offset), "hello");
549
}
550
551
TEST(StringPool, AddEmptyStringReturnsInvalid)
552
{
553
StringPool pool;
554
auto offset = pool.AddString("");
555
EXPECT_EQ(offset, StringPool::InvalidOffset);
556
EXPECT_TRUE(pool.IsEmpty());
557
EXPECT_EQ(pool.GetCount(), 0u);
558
}
559
560
TEST(StringPool, AddEmptyStringViewReturnsInvalid)
561
{
562
StringPool pool;
563
auto offset = pool.AddString(std::string_view{});
564
EXPECT_EQ(offset, StringPool::InvalidOffset);
565
EXPECT_TRUE(pool.IsEmpty());
566
}
567
568
TEST(StringPool, DeduplicatesSameString)
569
{
570
StringPool pool;
571
auto offset1 = pool.AddString("duplicate");
572
const size_t size_after_first = pool.GetSize();
573
const size_t count_after_first = pool.GetCount();
574
575
auto offset2 = pool.AddString("duplicate");
576
EXPECT_EQ(offset1, offset2);
577
EXPECT_EQ(pool.GetSize(), size_after_first);
578
EXPECT_EQ(pool.GetCount(), count_after_first);
579
}
580
581
TEST(StringPool, DeduplicateMultipleAdds)
582
{
583
StringPool pool;
584
auto offset = pool.AddString("test");
585
const size_t size_after = pool.GetSize();
586
587
for (int i = 0; i < 100; i++)
588
{
589
EXPECT_EQ(pool.AddString("test"), offset);
590
}
591
592
EXPECT_EQ(pool.GetSize(), size_after);
593
EXPECT_EQ(pool.GetCount(), 1u);
594
}
595
596
TEST(StringPool, DistinguishesDifferentStrings)
597
{
598
StringPool pool;
599
auto offset1 = pool.AddString("alpha");
600
auto offset2 = pool.AddString("beta");
601
EXPECT_NE(offset1, offset2);
602
EXPECT_EQ(pool.GetCount(), 2u);
603
EXPECT_EQ(pool.GetString(offset1), "alpha");
604
EXPECT_EQ(pool.GetString(offset2), "beta");
605
}
606
607
TEST(StringPool, SizeDoesNotGrowOnDuplicate)
608
{
609
StringPool pool;
610
std::ignore = pool.AddString("aaa");
611
std::ignore = pool.AddString("bbb");
612
std::ignore = pool.AddString("ccc");
613
const size_t size_before = pool.GetSize();
614
const size_t count_before = pool.GetCount();
615
616
std::ignore = pool.AddString("aaa");
617
std::ignore = pool.AddString("bbb");
618
std::ignore = pool.AddString("ccc");
619
EXPECT_EQ(pool.GetSize(), size_before);
620
EXPECT_EQ(pool.GetCount(), count_before);
621
}
622
623
TEST(StringPool, SizeGrowsOnlyForNewStrings)
624
{
625
StringPool pool;
626
std::ignore = pool.AddString("aaa");
627
const size_t size1 = pool.GetSize();
628
629
std::ignore = pool.AddString("aaa"); // duplicate
630
EXPECT_EQ(pool.GetSize(), size1);
631
632
std::ignore = pool.AddString("bbb"); // new
633
EXPECT_GT(pool.GetSize(), size1);
634
const size_t size2 = pool.GetSize();
635
636
std::ignore = pool.AddString("bbb"); // duplicate
637
EXPECT_EQ(pool.GetSize(), size2);
638
}
639
640
TEST(StringPool, GetStringWithOffset)
641
{
642
StringPool pool;
643
auto offset = pool.AddString("world");
644
EXPECT_EQ(pool.GetString(offset), "world");
645
}
646
647
TEST(StringPool, GetStringWithOffsetAndLength)
648
{
649
StringPool pool;
650
auto offset = pool.AddString("hello world");
651
EXPECT_EQ(pool.GetString(offset, 5), "hello");
652
EXPECT_EQ(pool.GetString(offset, 11), "hello world");
653
}
654
655
TEST(StringPool, GetStringOutOfBounds)
656
{
657
StringPool pool;
658
std::ignore = pool.AddString("test");
659
EXPECT_EQ(pool.GetString(9999), std::string_view{});
660
}
661
662
TEST(StringPool, GetStringWithLengthOutOfBounds)
663
{
664
StringPool pool;
665
auto offset = pool.AddString("short");
666
EXPECT_EQ(pool.GetString(offset, 9999), std::string_view{});
667
}
668
669
TEST(StringPool, Clear)
670
{
671
StringPool pool;
672
std::ignore = pool.AddString("one");
673
std::ignore = pool.AddString("two");
674
std::ignore = pool.AddString("three");
675
EXPECT_FALSE(pool.IsEmpty());
676
EXPECT_EQ(pool.GetCount(), 3u);
677
678
pool.Clear();
679
EXPECT_TRUE(pool.IsEmpty());
680
EXPECT_EQ(pool.GetSize(), 0u);
681
EXPECT_EQ(pool.GetCount(), 0u);
682
}
683
684
TEST(StringPool, ReserveDoesNotChangeState)
685
{
686
StringPool pool;
687
pool.Reserve(4096);
688
EXPECT_TRUE(pool.IsEmpty());
689
EXPECT_EQ(pool.GetSize(), 0u);
690
EXPECT_EQ(pool.GetCount(), 0u);
691
}
692
693
TEST(StringPool, CaseSensitive)
694
{
695
StringPool pool;
696
auto lower = pool.AddString("hello");
697
auto upper = pool.AddString("Hello");
698
auto allupper = pool.AddString("HELLO");
699
700
EXPECT_NE(lower, upper);
701
EXPECT_NE(upper, allupper);
702
EXPECT_NE(lower, allupper);
703
EXPECT_EQ(pool.GetCount(), 3u);
704
}
705
706
TEST(StringPool, SubstringNotDeduplicated)
707
{
708
StringPool pool;
709
auto full = pool.AddString("hello world");
710
auto sub = pool.AddString("hello");
711
EXPECT_NE(full, sub);
712
EXPECT_EQ(pool.GetCount(), 2u);
713
}
714
715
TEST(StringPool, UnicodeStrings)
716
{
717
StringPool pool;
718
auto jp = pool.AddString("日本語テスト");
719
auto cn = pool.AddString("中文测试");
720
auto emoji = pool.AddString("🎮🕹️");
721
722
EXPECT_EQ(pool.GetString(jp), "日本語テスト");
723
EXPECT_EQ(pool.GetString(cn), "中文测试");
724
EXPECT_EQ(pool.GetString(emoji), "🎮🕹️");
725
EXPECT_EQ(pool.GetCount(), 3u);
726
}
727
728
TEST(StringPool, UnicodeDeduplicated)
729
{
730
StringPool pool;
731
auto offset1 = pool.AddString("日本語");
732
const size_t size_after = pool.GetSize();
733
734
auto offset2 = pool.AddString("日本語");
735
EXPECT_EQ(offset1, offset2);
736
EXPECT_EQ(pool.GetSize(), size_after);
737
EXPECT_EQ(pool.GetCount(), 1u);
738
}
739
740
TEST(StringPool, ManyUniqueStrings)
741
{
742
StringPool pool;
743
pool.Reserve(2000);
744
745
for (int i = 0; i < 200; i++)
746
{
747
std::string s = "string_" + std::to_string(i);
748
std::ignore = pool.AddString(s);
749
}
750
751
EXPECT_EQ(pool.GetCount(), 200u);
752
753
// Verify all are retrievable and deduplicated.
754
for (int i = 0; i < 200; i++)
755
{
756
std::string s = "string_" + std::to_string(i);
757
auto offset = pool.AddString(s);
758
EXPECT_EQ(pool.GetString(offset), s);
759
}
760
761
EXPECT_EQ(pool.GetCount(), 200u);
762
}
763
764
TEST(StringPool, VeryLongString)
765
{
766
StringPool pool;
767
std::string long_str(10000, 'x');
768
auto offset1 = pool.AddString(long_str);
769
const size_t size_after = pool.GetSize();
770
771
auto offset2 = pool.AddString(long_str);
772
EXPECT_EQ(offset1, offset2);
773
EXPECT_EQ(pool.GetSize(), size_after);
774
EXPECT_EQ(pool.GetString(offset1), long_str);
775
}
776
777
TEST(StringPool, GetStringInvalidOffset)
778
{
779
StringPool pool;
780
const auto retrieved = pool.GetString(StringPool::InvalidOffset);
781
782
EXPECT_TRUE(retrieved.empty());
783
}
784
785
TEST(StringPool, SpecialCharacters)
786
{
787
StringPool pool;
788
const std::string_view special_str = "Hello\nWorld\t!@#$%^&*()";
789
const auto offset = pool.AddString(special_str);
790
791
EXPECT_NE(offset, StringPool::InvalidOffset);
792
EXPECT_EQ(pool.GetString(offset), special_str);
793
}
794
795
TEST(StringPool, GetCountTracksUniqueStrings)
796
{
797
StringPool pool;
798
EXPECT_EQ(pool.GetCount(), 0u);
799
800
std::ignore = pool.AddString("unique1");
801
EXPECT_EQ(pool.GetCount(), 1u);
802
803
std::ignore = pool.AddString("unique2");
804
EXPECT_EQ(pool.GetCount(), 2u);
805
806
std::ignore = pool.AddString("unique1"); // Duplicate
807
EXPECT_EQ(pool.GetCount(), 2u);
808
809
std::ignore = pool.AddString("unique3");
810
EXPECT_EQ(pool.GetCount(), 3u);
811
}
812
813
TEST(StringPool, ReuseAfterClear)
814
{
815
StringPool pool;
816
const std::string_view test_str = "reuse";
817
818
const auto offset1 = pool.AddString(test_str);
819
EXPECT_EQ(offset1, 0u);
820
EXPECT_EQ(pool.GetCount(), 1u);
821
822
pool.Clear();
823
824
const auto offset2 = pool.AddString(test_str);
825
EXPECT_EQ(pool.GetCount(), 1u);
826
827
// After clear, new strings start at offset 0 again
828
EXPECT_EQ(offset2, 0u);
829
EXPECT_EQ(pool.GetString(offset2), test_str);
830
}
831
832