2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
10 #include "base/CharacterSet.h"
11 #include "base/TextException.h"
12 #include "compat/cppunit.h"
14 #include "HttpReply.h"
15 #include "MemObject.h"
16 #include "sbuf/Algorithms.h"
17 #include "sbuf/SBuf.h"
18 #include "sbuf/Stream.h"
19 #include "tests/SBufFindTest.h"
20 #include "unitTestMain.h"
25 #include <unordered_map>
28 * test the SBuf functionalities
31 class TestSBuf
: public CPPUNIT_NS::TestFixture
33 CPPUNIT_TEST_SUITE(TestSBuf
);
34 CPPUNIT_TEST(testSBufConstructDestruct
);
35 CPPUNIT_TEST(testSBufConstructDestructAfterMemInit
);
36 CPPUNIT_TEST(testSBufLength
);
37 CPPUNIT_TEST(testEqualityTest
);
38 CPPUNIT_TEST(testStartsWith
);
39 CPPUNIT_TEST(testAppendSBuf
);
40 CPPUNIT_TEST(testAppendCString
);
41 CPPUNIT_TEST(testAppendStdString
);
42 CPPUNIT_TEST(testAppendf
);
43 CPPUNIT_TEST(testSubscriptOp
);
44 CPPUNIT_TEST_EXCEPTION(testSubscriptOpFail
, TextException
);
45 CPPUNIT_TEST(testComparisons
);
46 CPPUNIT_TEST(testConsume
);
47 CPPUNIT_TEST(testRawContent
);
48 CPPUNIT_TEST(testRawSpace
);
49 CPPUNIT_TEST(testChop
);
50 CPPUNIT_TEST(testChomp
);
51 CPPUNIT_TEST(testSubstr
);
52 CPPUNIT_TEST(testFindChar
);
53 CPPUNIT_TEST(testFindSBuf
);
54 CPPUNIT_TEST(testRFindChar
);
55 CPPUNIT_TEST(testRFindSBuf
);
56 CPPUNIT_TEST(testFindFirstOf
);
57 CPPUNIT_TEST(testFindFirstNotOf
);
58 CPPUNIT_TEST(testPrintf
);
59 CPPUNIT_TEST(testCopy
);
60 CPPUNIT_TEST(testStringOps
);
61 CPPUNIT_TEST(testGrow
);
62 CPPUNIT_TEST(testReserve
);
63 CPPUNIT_TEST(testSBufStream
);
64 CPPUNIT_TEST(testAutoFind
);
65 CPPUNIT_TEST(testStdStringOps
);
66 CPPUNIT_TEST(testIterators
);
67 CPPUNIT_TEST(testSBufHash
);
68 CPPUNIT_TEST(testStdAlgorithm
);
69 // CPPUNIT_TEST( testDumpStats ); //fake test, to print alloc stats
70 CPPUNIT_TEST_SUITE_END();
74 void testSBufConstructDestruct();
75 void testSBufConstructDestructAfterMemInit();
76 void testEqualityTest();
77 void testAppendSBuf();
78 void testAppendCString();
79 void testAppendStdString();
82 void testSubscriptOp();
83 void testSubscriptOpFail();
85 void testComparisons();
87 void testRawContent();
93 void testSBufLength();
98 void testSearchFail();
100 void testStringOps();
103 void testStartsWith();
104 void testSBufStream();
105 void testFindFirstOf();
106 void testFindFirstNotOf();
108 void testStdStringOps();
109 void testIterators();
111 void testStdAlgorithm();
113 CPPUNIT_TEST_SUITE_REGISTRATION( TestSBuf
);
115 /* let this test link sanely */
117 eventAdd(const char *, EVH
*, void *, double, int, bool)
120 MemObject::endOffset() const
125 static const char fox
[] = "The quick brown fox jumped over the lazy dog";
126 static const char fox1
[] = "The quick brown fox ";
127 static const char fox2
[] = "jumped over the lazy dog";
129 // TEST: globals variables (default/empty and with contents) are
130 // created outside and before any unit tests and memory subsystem
131 // initialization. Check for correct constructor operation.
133 SBuf
literal("The quick brown fox jumped over the lazy dog");
136 TestSBuf::testSBufConstructDestruct()
138 /* NOTE: Do not initialize memory here because we need
139 * to test correct operation before and after Mem::Init
142 // XXX: partial demo below of how to do constructor unit-test. use scope to ensure each test
143 // is working on local-scope variables constructed fresh for the test, and destructed when
144 // scope exists. use nested scopes to test destructor affects on copied data (MemBlob etc)
146 // TEST: default constructor (implicit destructor non-crash test)
147 // test accessors on empty SBuf.
150 CPPUNIT_ASSERT_EQUAL(0U,s1
.length());
151 CPPUNIT_ASSERT_EQUAL(SBuf(""),s1
);
152 CPPUNIT_ASSERT_EQUAL(empty_sbuf
,s1
);
153 CPPUNIT_ASSERT_EQUAL(0,strcmp("",s1
.c_str()));
156 // TEST: copy-construct NULL string (implicit destructor non-crash test)
159 CPPUNIT_ASSERT_EQUAL(0U,s1
.length());
160 CPPUNIT_ASSERT_EQUAL(SBuf(""),s1
);
161 CPPUNIT_ASSERT_EQUAL(empty_sbuf
,s1
);
162 CPPUNIT_ASSERT_EQUAL(0,strcmp("",s1
.c_str()));
165 // TEST: copy-construct empty string (implicit destructor non-crash test)
168 CPPUNIT_ASSERT_EQUAL(0U,s1
.length());
169 CPPUNIT_ASSERT_EQUAL(SBuf(""),s1
);
170 CPPUNIT_ASSERT_EQUAL(empty_sbuf
,s1
);
171 CPPUNIT_ASSERT_EQUAL(0,strcmp("",s1
.c_str()));
174 // TEST: copy-construct from a SBuf
177 CPPUNIT_ASSERT_EQUAL(0U,s1
.length());
178 CPPUNIT_ASSERT_EQUAL(SBuf(""),s1
);
179 CPPUNIT_ASSERT_EQUAL(empty_sbuf
,s1
);
180 CPPUNIT_ASSERT_EQUAL(0,strcmp("",s1
.c_str()));
183 CPPUNIT_ASSERT_EQUAL(literal
,s5
);
185 CPPUNIT_ASSERT_EQUAL(literal
,s6
);
186 // XXX: other state checks. expected result of calling any state accessor on s4 ?
189 // TEST: check that COW doesn't happen upon copy-construction
191 SBuf
s1(empty_sbuf
), s2(s1
);
192 CPPUNIT_ASSERT_EQUAL(s1
.rawContent(), s2
.rawContent());
193 SBuf
s3(literal
), s4(literal
);
194 CPPUNIT_ASSERT_EQUAL(s3
.rawContent(), s4
.rawContent());
197 // TEST: sub-string copy
199 SBuf s1
=SBuf(fox
+4), s2(fox
);
200 SBuf s3
=s2
.substr(4,s2
.length()); //n is out-of-bounds
201 CPPUNIT_ASSERT_EQUAL(s1
,s3
);
204 CPPUNIT_ASSERT_EQUAL(s4
,s3
);
207 // TEST: go via std::string adapter.
209 std::string
str(fox
);
211 CPPUNIT_ASSERT_EQUAL(literal
,s1
);
216 TestSBuf::testSBufConstructDestructAfterMemInit()
219 testSBufConstructDestruct();
223 TestSBuf::testEqualityTest()
225 SBuf
s1(fox
),s2(fox
);
226 CPPUNIT_ASSERT_EQUAL(s1
,s1
); //self-equality
227 CPPUNIT_ASSERT_EQUAL(s1
,s2
); //same contents
228 s2
.assign("The quick brown fox jumped over the lazy doe");
229 CPPUNIT_ASSERT(!(s1
== s2
)); //same length, different contents
231 CPPUNIT_ASSERT(!(s1
== s2
)); //different length and contents
232 CPPUNIT_ASSERT(s1
!= s2
); //while we're ready, let's test inequality
234 CPPUNIT_ASSERT(!(s1
== s2
)); //null and not-null
235 CPPUNIT_ASSERT(s1
!= s2
); //while we're ready, let's test inequality
237 CPPUNIT_ASSERT_EQUAL(s1
,s2
); //null and null
241 TestSBuf::testAppendSBuf()
243 const SBuf
appendix(fox1
);
244 const char * const rawAppendix
= appendix
.rawContent();
246 // check whether the optimization that prevents copying when append()ing to
247 // default-constructed SBuf actually works
250 CPPUNIT_ASSERT_EQUAL(s0
.rawContent(), appendix
.rawContent());
251 CPPUNIT_ASSERT_EQUAL(s0
, appendix
);
253 // paranoid: check that the above code can actually detect copies
256 CPPUNIT_ASSERT(s1
.rawContent() != appendix
.rawContent());
257 CPPUNIT_ASSERT(s1
!= appendix
);
258 CPPUNIT_ASSERT_EQUAL(rawAppendix
, appendix
.rawContent());
262 TestSBuf::testPrintf()
265 s1
.Printf("%s:%d:%03.3f","fox",10,12345.67);
266 s2
.assign("fox:10:12345.670");
267 CPPUNIT_ASSERT_EQUAL(s1
,s2
);
271 TestSBuf::testAppendCString()
275 CPPUNIT_ASSERT_EQUAL(s1
,literal
);
279 TestSBuf::testAppendStdString()
281 const char *alphabet
="abcdefghijklmnopqrstuvwxyz";
283 SBuf
alpha(alphabet
), s
;
284 s
.append(alphabet
,5).append(alphabet
+5);
285 CPPUNIT_ASSERT_EQUAL(alpha
,s
);
290 s
.append(alphabet
,5).append("\0",1).append(alphabet
+6,SBuf::npos
);
291 control
.append(alphabet
,5).append(1,'\0').append(alphabet
,6,std::string::npos
);
292 SBuf
scontrol(control
); // we need this to test the equality. sigh.
293 CPPUNIT_ASSERT_EQUAL(scontrol
,s
);
296 const char *alphazero
="abcdefghijk\0mnopqrstuvwxyz";
297 SBuf
s(alphazero
,26);
298 std::string
str(alphazero
,26);
299 CPPUNIT_ASSERT_EQUAL(0,memcmp(str
.data(),s
.rawContent(),26));
304 TestSBuf::testAppendf()
307 s1
.appendf("%s:%d:%03.2f",fox
,1234,1234.56);
308 s2
.assign("The quick brown fox jumped over the lazy dog:1234:1234.56");
309 CPPUNIT_ASSERT_EQUAL(s2
,s1
);
313 TestSBuf::testDumpStats()
315 SBuf::GetStats().dump(std::cout
);
316 MemBlob::GetStats().dump(std::cout
);
317 std::cout
<< "sizeof(SBuf): " << sizeof(SBuf
) << std::endl
;
318 std::cout
<< "sizeof(MemBlob): " << sizeof(MemBlob
) << std::endl
;
322 TestSBuf::testSubscriptOp()
325 CPPUNIT_ASSERT_EQUAL(chg
[5],'u');
327 CPPUNIT_ASSERT_EQUAL(literal
[5],'u');
328 CPPUNIT_ASSERT_EQUAL(chg
[5],'e');
331 // note: can't use cppunit's CPPUNIT_TEST_EXCEPTION because TextException asserts, and
332 // so the test can't be properly completed.
334 TestSBuf::testSubscriptOpFail()
337 c
=literal
.at(literal
.length()); //out of bounds by 1
339 std::cout
<< c
<< std::endl
;
342 static int sign(int v
)
352 testComparisonStdFull(const char *left
, const char *right
)
354 if (sign(strcmp(left
, right
)) != sign(SBuf(left
).cmp(SBuf(right
))))
355 std::cerr
<< std::endl
<< " cmp(SBuf) npos " << left
<< " ?= " << right
<< std::endl
;
356 CPPUNIT_ASSERT_EQUAL(sign(strcmp(left
, right
)), sign(SBuf(left
).cmp(SBuf(right
))));
358 if (sign(strcmp(left
, right
)) != sign(SBuf(left
).cmp(right
)))
359 std::cerr
<< std::endl
<< " cmp(char*) npos " << left
<< " ?= " << right
<< std::endl
;
360 CPPUNIT_ASSERT_EQUAL(sign(strcmp(left
, right
)), sign(SBuf(left
).cmp(right
)));
362 if (sign(strcasecmp(left
, right
)) != sign(SBuf(left
).caseCmp(SBuf(right
))))
363 std::cerr
<< std::endl
<< " caseCmp(SBuf) npos " << left
<< " ?= " << right
<< std::endl
;
364 CPPUNIT_ASSERT_EQUAL(sign(strcasecmp(left
, right
)), sign(SBuf(left
).caseCmp(SBuf(right
))));
366 if (sign(strcasecmp(left
, right
)) != sign(SBuf(left
).caseCmp(right
)))
367 std::cerr
<< std::endl
<< " caseCmp(char*) npos " << left
<< " ?= " << right
<< std::endl
;
368 CPPUNIT_ASSERT_EQUAL(sign(strcasecmp(left
, right
)), sign(SBuf(left
).caseCmp(right
)));
372 testComparisonStdN(const char *left
, const char *right
, const size_t n
)
374 if (sign(strncmp(left
, right
, n
)) != sign(SBuf(left
).cmp(SBuf(right
), n
)))
375 std::cerr
<< std::endl
<< " cmp(SBuf) " << n
<< ' ' << left
<< " ?= " << right
<< std::endl
;
376 CPPUNIT_ASSERT_EQUAL(sign(strncmp(left
, right
, n
)), sign(SBuf(left
).cmp(SBuf(right
), n
)));
378 if (sign(strncmp(left
, right
, n
)) != sign(SBuf(left
).cmp(right
, n
)))
379 std::cerr
<< std::endl
<< " cmp(char*) " << n
<< ' ' << SBuf(left
) << " ?= " << right
<< std::endl
;
380 CPPUNIT_ASSERT_EQUAL(sign(strncmp(left
, right
, n
)), sign(SBuf(left
).cmp(right
, n
)));
382 if (sign(strncasecmp(left
, right
, n
)) != sign(SBuf(left
).caseCmp(SBuf(right
), n
)))
383 std::cerr
<< std::endl
<< " caseCmp(SBuf) " << n
<< ' ' << left
<< " ?= " << right
<< std::endl
;
384 CPPUNIT_ASSERT_EQUAL(sign(strncasecmp(left
, right
, n
)), sign(SBuf(left
).caseCmp(SBuf(right
), n
)));
386 if (sign(strncasecmp(left
, right
, n
)) != sign(SBuf(left
).caseCmp(right
, n
)))
387 std::cerr
<< std::endl
<< " caseCmp(char*) " << n
<< ' ' << SBuf(left
) << " ?= " << right
<< std::endl
;
388 CPPUNIT_ASSERT_EQUAL(sign(strncasecmp(left
, right
, n
)), sign(SBuf(left
).caseCmp(right
, n
)));
392 testComparisonStdOneWay(const char *left
, const char *right
)
394 testComparisonStdFull(left
, right
);
395 const size_t maxN
= 2 + min(strlen(left
), strlen(right
));
396 for (size_t n
= 0; n
<= maxN
; ++n
) {
397 testComparisonStdN(left
, right
, n
);
402 testComparisonStd(const char *s1
, const char *s2
)
404 testComparisonStdOneWay(s1
, s2
);
405 testComparisonStdOneWay(s2
, s1
);
409 TestSBuf::testComparisons()
412 SBuf
s1("foo"),s2("foe");
413 CPPUNIT_ASSERT(s1
.cmp(s2
)>0);
414 CPPUNIT_ASSERT(s1
.caseCmp(s2
)>0);
415 CPPUNIT_ASSERT(s2
.cmp(s1
)<0);
416 CPPUNIT_ASSERT_EQUAL(0,s1
.cmp(s2
,2));
417 CPPUNIT_ASSERT_EQUAL(0,s1
.caseCmp(s2
,2));
418 CPPUNIT_ASSERT(s1
> s2
);
419 CPPUNIT_ASSERT(s2
< s1
);
420 CPPUNIT_ASSERT_EQUAL(sign(s1
.cmp(s2
)),sign(strcmp(s1
.c_str(),s2
.c_str())));
424 CPPUNIT_ASSERT(s1
.cmp(s2
)<0);
425 CPPUNIT_ASSERT_EQUAL(sign(s1
.cmp(s2
)),sign(strcmp(s1
.c_str(),s2
.c_str())));
426 CPPUNIT_ASSERT(s1
< s2
);
427 // specifying the max-length and overhanging size
428 CPPUNIT_ASSERT_EQUAL(1,SBuf("foolong").caseCmp(SBuf("foo"), 5));
429 // case-insensive comaprison
432 CPPUNIT_ASSERT_EQUAL(0,s1
.caseCmp(s2
));
433 CPPUNIT_ASSERT_EQUAL(0,s1
.caseCmp(s2
,2));
434 // \0-clenliness test
435 s1
.assign("f\0oo",4);
436 s2
.assign("f\0Oo",4);
437 CPPUNIT_ASSERT(s1
.cmp(s2
) > 0);
438 CPPUNIT_ASSERT_EQUAL(0,s1
.caseCmp(s2
));
439 CPPUNIT_ASSERT_EQUAL(0,s1
.caseCmp(s2
,3));
440 CPPUNIT_ASSERT_EQUAL(0,s1
.caseCmp(s2
,2));
441 CPPUNIT_ASSERT_EQUAL(0,s1
.cmp(s2
,2));
443 testComparisonStd("foo", "fooz");
444 testComparisonStd("foo", "foo");
445 testComparisonStd("foo", "f");
446 testComparisonStd("foo", "bar");
448 testComparisonStd("foo", "FOOZ");
449 testComparisonStd("foo", "FOO");
450 testComparisonStd("foo", "F");
452 testComparisonStdOneWay("", "");
454 // rare case C-string input matching SBuf with N>strlen(s)
456 char *right
= xstrdup("foo34567890123456789012345678");
457 SBuf
left("fooZYXWVUTSRQPONMLKJIHGFEDCBA");
458 // is 3 bytes in length. NEVER more.
462 // pick another spot to truncate at if something goes horribly wrong.
464 left
.setAt(14, '\0');
466 const SBuf::size_type maxN
= 20 + min(left
.length(), static_cast<SBuf::size_type
>(strlen(right
)));
467 for (SBuf::size_type n
= 0; n
<= maxN
; ++n
) {
468 if (sign(strncmp(left
.rawContent(), right
, n
)) != sign(left
.cmp(right
, n
)) )
469 std::cerr
<< std::endl
<< " cmp(char*) " << n
<< ' ' << left
<< " ?= " << right
;
470 CPPUNIT_ASSERT_EQUAL(sign(strncmp(left
.rawContent(), right
, n
)), sign(left
.cmp(right
, n
)));
471 if (sign(strncasecmp(left
.rawContent(), right
, n
)) != sign(left
.caseCmp(right
, n
)))
472 std::cerr
<< std::endl
<< " caseCmp(char*) " << n
<< ' ' << left
<< " ?= " << right
;
473 CPPUNIT_ASSERT_EQUAL(sign(strncasecmp(left
.rawContent(), right
, n
)), sign(left
.caseCmp(right
, n
)));
480 TestSBuf::testConsume()
482 SBuf
s1(literal
),s2
,s3
;
485 CPPUNIT_ASSERT_EQUAL(s2
,s3
);
486 s3
.assign("quick brown fox jumped over the lazy dog");
487 CPPUNIT_ASSERT_EQUAL(s1
,s3
);
489 CPPUNIT_ASSERT_EQUAL(s1
,SBuf());
493 TestSBuf::testRawContent()
499 foo
= s1
.rawContent();
500 CPPUNIT_ASSERT_EQUAL(0,strncmp(fox
,foo
,s1
.length()));
502 CPPUNIT_ASSERT(!strcmp(fox
,foo
));
506 TestSBuf::testRawSpace()
510 char *rb
=s2
.rawAppendStart(strlen(fox2
)+1);
512 s2
.rawAppendFinish(rb
, strlen(fox2
));
513 CPPUNIT_ASSERT_EQUAL(s1
,s2
);
522 CPPUNIT_ASSERT_EQUAL(s1
,s2
);
526 CPPUNIT_ASSERT_EQUAL(s1
,s2
);
527 const char *alphabet
="abcdefghijklmnopqrstuvwxyz";
529 std::string
s(alphabet
); // TODO
534 CPPUNIT_ASSERT_EQUAL(ref
,b
);
538 b
.chop(b
.length()-3);
540 CPPUNIT_ASSERT_EQUAL(ref
,b
);
542 { // chop at beginning
546 CPPUNIT_ASSERT_EQUAL(ref
,b
);
548 { // chop to zero length
552 CPPUNIT_ASSERT_EQUAL(ref
,b
);
554 { // chop beyond end (at npos)
556 b
.chop(SBuf::npos
,4);
558 CPPUNIT_ASSERT_EQUAL(ref
,b
);
562 b
.chop(b
.length()+2,4);
564 CPPUNIT_ASSERT_EQUAL(ref
,b
);
568 b
.chop(0,b
.length());
570 CPPUNIT_ASSERT_EQUAL(ref
,b
);
572 { // overflow chopped area
574 b
.chop(b
.length()-3,b
.length());
576 CPPUNIT_ASSERT_EQUAL(ref
,b
);
581 TestSBuf::testChomp()
583 SBuf
s1("complete string");
586 CPPUNIT_ASSERT_EQUAL(s1
,s2
);
587 s2
.assign(" complete string ,");
589 CPPUNIT_ASSERT_EQUAL(s1
,s2
);
590 s1
.assign(", complete string ,");
593 CPPUNIT_ASSERT_EQUAL(s1
,s2
);
596 // inspired by SBufFindTest; to be expanded.
597 class SBufSubstrAutoTest
600 std::string fullReference
, str
;
602 void performEqualityTest() {
604 CPPUNIT_ASSERT_EQUAL(ref
,sb
);
606 SBufSubstrAutoTest() : fullString(fox
), fullReference(fox
) {
607 for (int offset
=fullString
.length()-1; offset
>= 0; --offset
) {
608 for (int length
=fullString
.length()-1-offset
; length
>= 0; --length
) {
609 sb
=fullString
.substr(offset
,length
);
610 str
=fullReference
.substr(offset
,length
);
611 performEqualityTest();
618 TestSBuf::testSubstr()
620 SBuf
s1(literal
),s2
,s3
;
623 CPPUNIT_ASSERT_EQUAL(s2
,s3
);
625 CPPUNIT_ASSERT_EQUAL(s1
,s2
);
626 SBufSubstrAutoTest sat
; // work done in the constructor
630 TestSBuf::testFindChar()
632 const char *alphabet
="abcdefghijklmnopqrstuvwxyz";
635 SBuf::size_type nposResult
=SBuf::npos
;
638 // needle in haystack
640 CPPUNIT_ASSERT_EQUAL(3U,idx
);
641 CPPUNIT_ASSERT_EQUAL('d',s1
[idx
]);
643 // needle not present in haystack
644 idx
=s1
.find(' '); //fails
645 CPPUNIT_ASSERT_EQUAL(nposResult
,idx
);
649 CPPUNIT_ASSERT_EQUAL(4U,idx
);
651 // char not in searched portion
653 CPPUNIT_ASSERT_EQUAL(nposResult
,idx
);
655 // invalid start position
656 idx
=s1
.find('d',SBuf::npos
);
657 CPPUNIT_ASSERT_EQUAL(nposResult
,idx
);
659 // search outside of haystack
660 idx
=s1
.find('d',s1
.length()+1);
661 CPPUNIT_ASSERT_EQUAL(nposResult
,idx
);
664 // needle in haystack
666 CPPUNIT_ASSERT_EQUAL(3U, idx
);
667 CPPUNIT_ASSERT_EQUAL('d', s1
[idx
]);
669 // needle not present in haystack
670 idx
=s1
.rfind(' '); //fails
671 CPPUNIT_ASSERT_EQUAL(nposResult
,idx
);
675 CPPUNIT_ASSERT_EQUAL(4U,idx
);
677 // char not in searched portion
679 CPPUNIT_ASSERT_EQUAL(nposResult
,idx
);
681 // overlong haystack specification
682 idx
=s1
.rfind('d',s1
.length()+1);
683 CPPUNIT_ASSERT_EQUAL(3U,idx
);
687 TestSBuf::testFindSBuf()
689 const char *alphabet
="abcdefghijklmnopqrstuvwxyz";
690 SBuf
haystack(alphabet
);
692 SBuf::size_type nposResult
=SBuf::npos
;
695 // needle in haystack
696 idx
= haystack
.find(SBuf("def"));
697 CPPUNIT_ASSERT_EQUAL(3U,idx
);
699 idx
= haystack
.find(SBuf("xyz"));
700 CPPUNIT_ASSERT_EQUAL(23U,idx
);
702 // needle not in haystack, no initial char match
703 idx
= haystack
.find(SBuf(" eq"));
704 CPPUNIT_ASSERT_EQUAL(nposResult
, idx
);
706 // needle not in haystack, initial sequence match
707 idx
= haystack
.find(SBuf("deg"));
708 CPPUNIT_ASSERT_EQUAL(nposResult
, idx
);
710 // needle past end of haystack
711 idx
= haystack
.find(SBuf("xyz1"));
712 CPPUNIT_ASSERT_EQUAL(nposResult
, idx
);
714 // search in portion: needle not in searched part
715 idx
= haystack
.find(SBuf("def"),7);
716 CPPUNIT_ASSERT_EQUAL(nposResult
, idx
);
718 // search in portion: overhang
719 idx
= haystack
.find(SBuf("def"),4);
720 CPPUNIT_ASSERT_EQUAL(nposResult
, idx
);
722 // invalid start position
723 idx
= haystack
.find(SBuf("def"),SBuf::npos
);
724 CPPUNIT_ASSERT_EQUAL(nposResult
, idx
);
726 // needle bigger than haystack
727 idx
= SBuf("def").find(haystack
);
728 CPPUNIT_ASSERT_EQUAL(nposResult
, idx
);
730 // search in a double-matching haystack
735 idx
= h2
.find(SBuf("def"));
736 CPPUNIT_ASSERT_EQUAL(3U,idx
);
738 idx
= h2
.find(SBuf("xyzab"));
739 CPPUNIT_ASSERT_EQUAL(23U,idx
);
743 // needle in haystack
744 idx
= haystack
.rfind(SBuf("def"));
745 CPPUNIT_ASSERT_EQUAL(3U,idx
);
747 idx
= haystack
.rfind(SBuf("xyz"));
748 CPPUNIT_ASSERT_EQUAL(23U,idx
);
750 // needle not in haystack, no initial char match
751 idx
= haystack
.rfind(SBuf(" eq"));
752 CPPUNIT_ASSERT_EQUAL(nposResult
, idx
);
754 // needle not in haystack, initial sequence match
755 idx
= haystack
.rfind(SBuf("deg"));
756 CPPUNIT_ASSERT_EQUAL(nposResult
, idx
);
758 // needle past end of haystack
759 idx
= haystack
.rfind(SBuf("xyz1"));
760 CPPUNIT_ASSERT_EQUAL(nposResult
, idx
);
762 // search in portion: needle in searched part
763 idx
= haystack
.rfind(SBuf("def"),7);
764 CPPUNIT_ASSERT_EQUAL(3U, idx
);
766 // search in portion: needle not in searched part
767 idx
= haystack
.rfind(SBuf("mno"),3);
768 CPPUNIT_ASSERT_EQUAL(nposResult
, idx
);
770 // search in portion: overhang
771 idx
= haystack
.rfind(SBuf("def"),4);
772 CPPUNIT_ASSERT_EQUAL(3U, idx
);
774 // npos start position
775 idx
= haystack
.rfind(SBuf("def"),SBuf::npos
);
776 CPPUNIT_ASSERT_EQUAL(3U, idx
);
778 // needle bigger than haystack
779 idx
= SBuf("def").rfind(haystack
);
780 CPPUNIT_ASSERT_EQUAL(nposResult
, idx
);
782 // search in a double-matching haystack
787 idx
= h2
.rfind(SBuf("def"));
788 CPPUNIT_ASSERT_EQUAL(29U,idx
);
790 idx
= h2
.find(SBuf("xyzab"));
791 CPPUNIT_ASSERT_EQUAL(23U,idx
);
796 TestSBuf::testRFindChar()
801 CPPUNIT_ASSERT_EQUAL(40U,idx
);
802 CPPUNIT_ASSERT_EQUAL(' ',s1
[idx
]);
806 TestSBuf::testRFindSBuf()
808 SBuf
haystack(literal
),afox("fox");
809 SBuf
goobar("goobar");
812 // corner case: search for a zero-length SBuf
813 idx
=haystack
.rfind(SBuf(""));
814 CPPUNIT_ASSERT_EQUAL(haystack
.length(),idx
);
816 // corner case: search for a needle longer than the haystack
817 idx
=afox
.rfind(SBuf(" "));
818 CPPUNIT_ASSERT_EQUAL(SBuf::npos
,idx
);
820 idx
=haystack
.rfind(SBuf("fox"));
821 CPPUNIT_ASSERT_EQUAL(16U,idx
);
823 // needle not found, no match for first char
824 idx
=goobar
.rfind(SBuf("foo"));
825 CPPUNIT_ASSERT_EQUAL(SBuf::npos
,idx
);
827 // needle not found, match for first char but no match for SBuf
828 idx
=haystack
.rfind(SBuf("foe"));
829 CPPUNIT_ASSERT_EQUAL(SBuf::npos
,idx
);
831 SBuf
g("g"); //match at the last char
832 idx
=haystack
.rfind(g
);
833 CPPUNIT_ASSERT_EQUAL(43U,idx
);
834 CPPUNIT_ASSERT_EQUAL('g',haystack
[idx
]);
836 idx
=haystack
.rfind(SBuf("The"));
837 CPPUNIT_ASSERT_EQUAL(0U,idx
);
839 haystack
.append("The");
840 idx
=haystack
.rfind(SBuf("The"));
841 CPPUNIT_ASSERT_EQUAL(44U,idx
);
844 haystack
="The quick brown fox";
845 SBuf
needle("foxy lady");
846 idx
=haystack
.rfind(needle
);
847 CPPUNIT_ASSERT_EQUAL(SBuf::npos
,idx
);
851 TestSBuf::testSBufLength()
854 CPPUNIT_ASSERT_EQUAL(strlen(fox
),(size_t)s
.length());
860 char buf
[40]; //shorter than literal()
862 CPPUNIT_ASSERT_EQUAL(s
.length(),s
.copy(buf
,40));
863 CPPUNIT_ASSERT_EQUAL(0,strncmp(s
.rawContent(),buf
,s
.length()));
865 CPPUNIT_ASSERT_EQUAL(40U,s
.copy(buf
,40));
868 CPPUNIT_ASSERT_EQUAL(s2
,s
);
872 TestSBuf::testStringOps()
874 SBuf
sng(ToLower(literal
)),
875 ref("the quick brown fox jumped over the lazy dog");
876 CPPUNIT_ASSERT_EQUAL(ref
,sng
);
878 CPPUNIT_ASSERT_EQUAL(0,sng
.compare(ref
,caseInsensitive
));
879 // max-size comparison
880 CPPUNIT_ASSERT_EQUAL(0,ref
.compare(SBuf("THE"),caseInsensitive
,3));
881 CPPUNIT_ASSERT_EQUAL(1,ref
.compare(SBuf("THE"),caseInsensitive
,6));
882 CPPUNIT_ASSERT_EQUAL(0,SBuf("the").compare(SBuf("THE"),caseInsensitive
,6));
890 const char *ref
=t
.rawContent();
891 t
.reserveCapacity(10240);
892 const char *match
=t
.rawContent();
893 CPPUNIT_ASSERT(match
!=ref
);
895 t
.append(literal
).append(literal
).append(literal
).append(literal
).append(literal
);
896 t
.append(t
).append(t
).append(t
).append(t
).append(t
);
897 CPPUNIT_ASSERT_EQUAL(ref
,match
);
901 TestSBuf::testReserve()
903 SBufReservationRequirements requirements
;
904 // use unusual numbers to ensure we do not hit a lucky boundary situation
905 requirements
.minSpace
= 10;
906 requirements
.idealSpace
= 82;
907 requirements
.maxCapacity
= 259;
908 requirements
.allowShared
= true;
910 // for each possible starting buffer length within the capacity
911 for (SBuf::size_type startLength
= 0; startLength
<= requirements
.maxCapacity
; ++startLength
) {
914 b
.reserveCapacity(startLength
);
915 CPPUNIT_ASSERT_EQUAL(b
.length(), static_cast<unsigned int>(0));
916 CPPUNIT_ASSERT_EQUAL(b
.spaceSize(), startLength
);
918 // check that it never grows outside capacity.
919 // do 5 excess cycles to check that.
920 for (SBuf::size_type filled
= 0; filled
< requirements
.maxCapacity
+5; ++filled
) {
921 CPPUNIT_ASSERT_EQUAL(b
.length(), min(filled
, requirements
.maxCapacity
));
922 auto x
= b
.reserve(requirements
);
923 // the amount of space advertized must not cause users to exceed capacity
924 CPPUNIT_ASSERT(x
<= requirements
.maxCapacity
- filled
);
925 CPPUNIT_ASSERT(b
.spaceSize() <= requirements
.maxCapacity
- filled
);
926 // the total size of buffer must not cause users to exceed capacity
927 CPPUNIT_ASSERT(b
.length() + b
.spaceSize() <= requirements
.maxCapacity
);
933 // the minimal space requirement should overwrite idealSpace preferences
934 requirements
.minSpace
= 10;
935 for (const int delta
: {-1,0,+1}) {
936 requirements
.idealSpace
= requirements
.minSpace
+ delta
;
938 buffer
.reserve(requirements
);
939 CPPUNIT_ASSERT(buffer
.spaceSize() >= requirements
.minSpace
);
942 // TODO: Decide whether to encapsulate the (nearly identical) code of the
943 // gap-related test cases below into a function, obscuring each case logic a
944 // little, but facilitating new test cases (and removing code duplication).
946 { // reserveSpace() uses the trailing space before the front gap
949 // assure there is some trailing space
950 buffer
.reserveSpace(1);
951 CPPUNIT_ASSERT(buffer
.spaceSize() > 0);
953 // create a leading gap and (weak-)check that it was created
954 const auto gap
= 1U; // the smallest gap may be the most challenging
955 CPPUNIT_ASSERT(gap
< buffer
.length());
956 const void *gapEnd
= buffer
.rawContent() + gap
;
958 CPPUNIT_ASSERT_EQUAL(gapEnd
, static_cast<const void*>(buffer
.rawContent()));
960 const auto before
= SBuf::GetStats();
961 const auto beforeSpaceSize
= buffer
.spaceSize();
962 const void * const beforePosition
= buffer
.rawContent();
963 buffer
.reserveSpace(beforeSpaceSize
);
964 const auto after
= SBuf::GetStats();
965 const void * const afterPosition
= buffer
.rawContent();
966 CPPUNIT_ASSERT_EQUAL(before
.cowAvoided
+ 1, after
.cowAvoided
);
967 CPPUNIT_ASSERT_EQUAL(before
.cowShift
, after
.cowShift
);
968 CPPUNIT_ASSERT_EQUAL(before
.cowJustAlloc
, after
.cowJustAlloc
);
969 CPPUNIT_ASSERT_EQUAL(before
.cowAllocCopy
, after
.cowAllocCopy
);
970 CPPUNIT_ASSERT_EQUAL(beforeSpaceSize
, buffer
.spaceSize());
971 CPPUNIT_ASSERT_EQUAL(beforePosition
, afterPosition
);
972 CPPUNIT_ASSERT(strcmp(fox
+ gap
, buffer
.c_str()) == 0);
975 { // reserveSpace() uses the front gap when the trailing space is not enough
978 // assure there is some trailing space to keep the test case challenging
979 buffer
.reserveSpace(1);
980 CPPUNIT_ASSERT(buffer
.spaceSize() > 0);
981 const void * const initialStorage
= buffer
.rawContent();
983 // create a leading gap and (weak-)check that it was created
984 const auto gap
= 1U; // the smallest gap may be the most challenging
985 CPPUNIT_ASSERT(gap
< buffer
.length());
986 const void *gapEnd
= buffer
.rawContent() + gap
;
988 CPPUNIT_ASSERT_EQUAL(gapEnd
, static_cast<const void*>(buffer
.rawContent()));
990 const auto before
= SBuf::GetStats();
991 const auto beforeSpaceSize
= buffer
.spaceSize();
992 buffer
.reserveSpace(beforeSpaceSize
+ gap
); // force (entire) gap use
993 const auto after
= SBuf::GetStats();
994 const void * const afterStorage
= buffer
.rawContent();
995 CPPUNIT_ASSERT_EQUAL(before
.cowAvoided
, after
.cowAvoided
);
996 CPPUNIT_ASSERT_EQUAL(before
.cowShift
+ 1, after
.cowShift
);
997 CPPUNIT_ASSERT_EQUAL(before
.cowJustAlloc
, after
.cowJustAlloc
);
998 CPPUNIT_ASSERT_EQUAL(before
.cowAllocCopy
, after
.cowAllocCopy
);
999 CPPUNIT_ASSERT_EQUAL(initialStorage
, afterStorage
);
1000 CPPUNIT_ASSERT(beforeSpaceSize
+ gap
<= buffer
.spaceSize());
1001 CPPUNIT_ASSERT(strcmp(fox
+ gap
, buffer
.c_str()) == 0);
1004 { // reserveSpace() uses the entire front gap when using the front gap
1007 // assure there is some trailing space to keep the test case challenging
1008 buffer
.reserveSpace(1);
1009 CPPUNIT_ASSERT(buffer
.spaceSize() > 0);
1010 const void * const initialStorage
= buffer
.rawContent();
1012 // create a leading gap and (weak-)check that it was created
1013 const auto gap
= 2U; // the smallest extra gap may be the most challenging
1014 CPPUNIT_ASSERT(gap
< buffer
.length());
1015 const void *gapEnd
= buffer
.rawContent() + gap
;
1016 buffer
.consume(gap
);
1017 CPPUNIT_ASSERT_EQUAL(gapEnd
, static_cast<const void*>(buffer
.rawContent()));
1019 const auto before
= SBuf::GetStats();
1020 const auto beforeSpaceSize
= buffer
.spaceSize();
1021 buffer
.reserveSpace(beforeSpaceSize
+ 1); // force (minimal) gap use
1022 const auto after
= SBuf::GetStats();
1023 const void * const afterStorage
= buffer
.rawContent();
1024 CPPUNIT_ASSERT_EQUAL(before
.cowAvoided
, after
.cowAvoided
);
1025 CPPUNIT_ASSERT_EQUAL(before
.cowShift
+ 1, after
.cowShift
);
1026 CPPUNIT_ASSERT_EQUAL(before
.cowJustAlloc
, after
.cowJustAlloc
);
1027 CPPUNIT_ASSERT_EQUAL(before
.cowAllocCopy
, after
.cowAllocCopy
);
1028 CPPUNIT_ASSERT_EQUAL(initialStorage
, afterStorage
);
1029 CPPUNIT_ASSERT(beforeSpaceSize
+ gap
<= buffer
.spaceSize());
1030 CPPUNIT_ASSERT(strcmp(fox
+ gap
, buffer
.c_str()) == 0);
1035 TestSBuf::testStartsWith()
1037 static SBuf
casebuf("THE QUICK");
1038 CPPUNIT_ASSERT(literal
.startsWith(SBuf(fox1
)));
1039 CPPUNIT_ASSERT(!SBuf("The quick brown").startsWith(SBuf(fox1
))); //too short
1040 CPPUNIT_ASSERT(!literal
.startsWith(SBuf(fox2
))); //different contents
1042 // case-insensitive checks
1043 CPPUNIT_ASSERT(literal
.startsWith(casebuf
,caseInsensitive
));
1044 casebuf
=ToUpper(SBuf(fox1
));
1045 CPPUNIT_ASSERT(literal
.startsWith(casebuf
,caseInsensitive
));
1046 CPPUNIT_ASSERT(literal
.startsWith(SBuf(fox1
),caseInsensitive
));
1047 casebuf
= "tha quick";
1048 CPPUNIT_ASSERT_EQUAL(false,literal
.startsWith(casebuf
,caseInsensitive
));
1052 TestSBuf::testSBufStream()
1054 SBuf
b("const.string, int 10 and a float 10.5");
1056 ss
<< "const.string, int " << 10 << " and a float " << 10.5;
1058 CPPUNIT_ASSERT_EQUAL(b
,o
);
1061 CPPUNIT_ASSERT_EQUAL(SBuf(),o
);
1065 CPPUNIT_ASSERT_EQUAL(ss2
.buf(),literal
);
1066 CPPUNIT_ASSERT_EQUAL(f1
,SBuf(fox1
));
1070 TestSBuf::testFindFirstOf()
1072 SBuf
haystack(literal
);
1073 SBuf::size_type idx
;
1076 idx
=haystack
.findFirstOf(CharacterSet("t1","ADHRWYP"));
1077 CPPUNIT_ASSERT_EQUAL(SBuf::npos
,idx
);
1079 // found at beginning
1080 idx
=haystack
.findFirstOf(CharacterSet("t2","THANDF"));
1081 CPPUNIT_ASSERT_EQUAL(0U,idx
);
1083 //found at end of haystack
1084 idx
=haystack
.findFirstOf(CharacterSet("t3","QWERYVg"));
1085 CPPUNIT_ASSERT_EQUAL(haystack
.length()-1,idx
);
1087 //found in the middle of haystack
1088 idx
=haystack
.findFirstOf(CharacterSet("t4","QWERqYV"));
1089 CPPUNIT_ASSERT_EQUAL(4U,idx
);
1093 TestSBuf::testFindFirstNotOf()
1095 SBuf
haystack(literal
);
1096 SBuf::size_type idx
;
1098 // all chars from the set
1099 idx
=haystack
.findFirstNotOf(CharacterSet("t1",literal
.c_str()));
1100 CPPUNIT_ASSERT_EQUAL(SBuf::npos
,idx
);
1102 // found at beginning
1103 idx
=haystack
.findFirstNotOf(CharacterSet("t2","a"));
1104 CPPUNIT_ASSERT_EQUAL(0U,idx
);
1106 //found at end of haystack
1107 idx
=haystack
.findFirstNotOf(CharacterSet("t3",literal
.substr(0,literal
.length()-1).c_str()));
1108 CPPUNIT_ASSERT_EQUAL(haystack
.length()-1,idx
);
1110 //found in the middle of haystack
1111 idx
=haystack
.findFirstNotOf(CharacterSet("t4","The"));
1112 CPPUNIT_ASSERT_EQUAL(3U,idx
);
1116 TestSBuf::testAutoFind()
1123 TestSBuf::testStdStringOps()
1125 const char *alphabet
="abcdefghijklmnopqrstuvwxyz";
1126 std::string
astr(alphabet
);
1128 CPPUNIT_ASSERT_EQUAL(astr
,sb
.toStdString());
1132 TestSBuf::testIterators()
1134 SBuf
text("foo"), text2("foo");
1135 CPPUNIT_ASSERT(text
.begin() == text
.begin());
1136 CPPUNIT_ASSERT(text
.begin() != text
.end());
1137 CPPUNIT_ASSERT(text
.begin() != text2
.begin());
1139 auto i
= text
.begin();
1140 auto e
= text
.end();
1141 CPPUNIT_ASSERT_EQUAL('f', *i
);
1142 CPPUNIT_ASSERT(i
!= e
);
1144 CPPUNIT_ASSERT_EQUAL('o', *i
);
1145 CPPUNIT_ASSERT(i
!= e
);
1147 CPPUNIT_ASSERT_EQUAL('o', *i
);
1148 CPPUNIT_ASSERT(i
!= e
);
1150 CPPUNIT_ASSERT(i
== e
);
1153 auto i
= text
.rbegin();
1154 auto e
= text
.rend();
1155 CPPUNIT_ASSERT_EQUAL('o', *i
);
1156 CPPUNIT_ASSERT(i
!= e
);
1158 CPPUNIT_ASSERT_EQUAL('o', *i
);
1159 CPPUNIT_ASSERT(i
!= e
);
1161 CPPUNIT_ASSERT_EQUAL('f', *i
);
1162 CPPUNIT_ASSERT(i
!= e
);
1164 CPPUNIT_ASSERT(i
== e
);
1169 TestSBuf::testSBufHash()
1171 // same SBuf must have same hash
1172 auto hasher
=std::hash
<SBuf
>();
1173 CPPUNIT_ASSERT_EQUAL(hasher(literal
),hasher(literal
));
1175 // same content must have same hash
1176 CPPUNIT_ASSERT_EQUAL(hasher(literal
),hasher(SBuf(fox
)));
1177 CPPUNIT_ASSERT_EQUAL(hasher(SBuf(fox
)),hasher(SBuf(fox
)));
1179 //different content should have different hash
1180 CPPUNIT_ASSERT(hasher(SBuf(fox
)) != hasher(SBuf(fox1
)));
1183 std::unordered_map
<SBuf
, int> um
;
1184 um
[SBuf("one")] = 1;
1185 um
[SBuf("two")] = 2;
1187 auto i
= um
.find(SBuf("one"));
1188 CPPUNIT_ASSERT(i
!= um
.end());
1189 CPPUNIT_ASSERT(i
->second
== 1);
1191 i
= um
.find(SBuf("eleventy"));
1192 CPPUNIT_ASSERT(i
== um
.end());
1197 TestSBuf::testStdAlgorithm()
1201 std::copy(src
.begin(), src
.end(), std::back_inserter(dst
));
1202 CPPUNIT_ASSERT_EQUAL(src
, dst
);
1207 std::copy_if(src
.begin(), src
.end(), std::back_inserter(dst
),
1208 [](char c
) { return c
!= 'o'; });
1209 CPPUNIT_ASSERT_EQUAL(SBuf("The quick brwn fx jumped ver the lazy dg"), dst
);
1214 main(int argc
, char *argv
[])
1216 return TestProgram().run(argc
, argv
);