]> git.ipfire.org Git - thirdparty/squid.git/blob - src/tests/testSBuf.cc
Maintenance: automate header guards 2/3 (#1655)
[thirdparty/squid.git] / src / tests / testSBuf.cc
1 /*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9 #include "squid.h"
10 #include "base/CharacterSet.h"
11 #include "base/TextException.h"
12 #include "compat/cppunit.h"
13 #include "event.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"
21
22 #include <algorithm>
23 #include <iostream>
24 #include <stdexcept>
25 #include <unordered_map>
26
27 /*
28 * test the SBuf functionalities
29 */
30
31 class TestSBuf : public CPPUNIT_NS::TestFixture
32 {
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();
71
72 protected:
73 void commonInit();
74 void testSBufConstructDestruct();
75 void testSBufConstructDestructAfterMemInit();
76 void testEqualityTest();
77 void testAppendSBuf();
78 void testAppendCString();
79 void testAppendStdString();
80 void testAppendf();
81 void testPrintf();
82 void testSubscriptOp();
83 void testSubscriptOpFail();
84 void testDumpStats();
85 void testComparisons();
86 void testConsume();
87 void testRawContent();
88 void testRawSpace();
89 void testChop();
90 void testChomp();
91 void testSubstr();
92 void testTailCopy();
93 void testSBufLength();
94 void testFindChar();
95 void testFindSBuf();
96 void testRFindChar();
97 void testRFindSBuf();
98 void testSearchFail();
99 void testCopy();
100 void testStringOps();
101 void testGrow();
102 void testReserve();
103 void testStartsWith();
104 void testSBufStream();
105 void testFindFirstOf();
106 void testFindFirstNotOf();
107 void testAutoFind();
108 void testStdStringOps();
109 void testIterators();
110 void testSBufHash();
111 void testStdAlgorithm();
112 };
113 CPPUNIT_TEST_SUITE_REGISTRATION( TestSBuf );
114
115 /* let this test link sanely */
116 void
117 eventAdd(const char *, EVH *, void *, double, int, bool)
118 {}
119 int64_t
120 MemObject::endOffset() const
121 { return 0; }
122 /* end of stubs */
123
124 // test string
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";
128
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.
132 SBuf empty_sbuf;
133 SBuf literal("The quick brown fox jumped over the lazy dog");
134
135 void
136 TestSBuf::testSBufConstructDestruct()
137 {
138 /* NOTE: Do not initialize memory here because we need
139 * to test correct operation before and after Mem::Init
140 */
141
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)
145
146 // TEST: default constructor (implicit destructor non-crash test)
147 // test accessors on empty SBuf.
148 {
149 SBuf s1;
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()));
154 }
155
156 // TEST: copy-construct NULL string (implicit destructor non-crash test)
157 {
158 SBuf s1(nullptr);
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()));
163 }
164
165 // TEST: copy-construct empty string (implicit destructor non-crash test)
166 {
167 SBuf s1("");
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()));
172 }
173
174 // TEST: copy-construct from a SBuf
175 {
176 SBuf s1(empty_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()));
181
182 SBuf s5(literal);
183 CPPUNIT_ASSERT_EQUAL(literal,s5);
184 SBuf s6(fox);
185 CPPUNIT_ASSERT_EQUAL(literal,s6);
186 // XXX: other state checks. expected result of calling any state accessor on s4 ?
187 }
188
189 // TEST: check that COW doesn't happen upon copy-construction
190 {
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());
195 }
196
197 // TEST: sub-string copy
198 {
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);
202 SBuf s4=SBuf(fox,4);
203 s3=s2.substr(0,4);
204 CPPUNIT_ASSERT_EQUAL(s4,s3);
205 }
206
207 // TEST: go via std::string adapter.
208 {
209 std::string str(fox);
210 SBuf s1(str);
211 CPPUNIT_ASSERT_EQUAL(literal,s1);
212 }
213 }
214
215 void
216 TestSBuf::testSBufConstructDestructAfterMemInit()
217 {
218 Mem::Init();
219 testSBufConstructDestruct();
220 }
221
222 void
223 TestSBuf::testEqualityTest()
224 {
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
230 s2.assign("foo");
231 CPPUNIT_ASSERT(!(s1 == s2)); //different length and contents
232 CPPUNIT_ASSERT(s1 != s2); //while we're ready, let's test inequality
233 s2.clear();
234 CPPUNIT_ASSERT(!(s1 == s2)); //null and not-null
235 CPPUNIT_ASSERT(s1 != s2); //while we're ready, let's test inequality
236 s1.clear();
237 CPPUNIT_ASSERT_EQUAL(s1,s2); //null and null
238 }
239
240 void
241 TestSBuf::testAppendSBuf()
242 {
243 const SBuf appendix(fox1);
244 const char * const rawAppendix = appendix.rawContent();
245
246 // check whether the optimization that prevents copying when append()ing to
247 // default-constructed SBuf actually works
248 SBuf s0;
249 s0.append(appendix);
250 CPPUNIT_ASSERT_EQUAL(s0.rawContent(), appendix.rawContent());
251 CPPUNIT_ASSERT_EQUAL(s0, appendix);
252
253 // paranoid: check that the above code can actually detect copies
254 SBuf s1(fox1);
255 s1.append(appendix);
256 CPPUNIT_ASSERT(s1.rawContent() != appendix.rawContent());
257 CPPUNIT_ASSERT(s1 != appendix);
258 CPPUNIT_ASSERT_EQUAL(rawAppendix, appendix.rawContent());
259 }
260
261 void
262 TestSBuf::testPrintf()
263 {
264 SBuf s1,s2;
265 s1.Printf("%s:%d:%03.3f","fox",10,12345.67);
266 s2.assign("fox:10:12345.670");
267 CPPUNIT_ASSERT_EQUAL(s1,s2);
268 }
269
270 void
271 TestSBuf::testAppendCString()
272 {
273 SBuf s1(fox1);
274 s1.append(fox2);
275 CPPUNIT_ASSERT_EQUAL(s1,literal);
276 }
277
278 void
279 TestSBuf::testAppendStdString()
280 {
281 const char *alphabet="abcdefghijklmnopqrstuvwxyz";
282 {
283 SBuf alpha(alphabet), s;
284 s.append(alphabet,5).append(alphabet+5);
285 CPPUNIT_ASSERT_EQUAL(alpha,s);
286 }
287 {
288 SBuf s;
289 std::string control;
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);
294 }
295 {
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));
300 }
301 }
302
303 void
304 TestSBuf::testAppendf()
305 {
306 SBuf s1,s2;
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);
310 }
311
312 void
313 TestSBuf::testDumpStats()
314 {
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;
319 }
320
321 void
322 TestSBuf::testSubscriptOp()
323 {
324 SBuf chg(literal);
325 CPPUNIT_ASSERT_EQUAL(chg[5],'u');
326 chg.setAt(5,'e');
327 CPPUNIT_ASSERT_EQUAL(literal[5],'u');
328 CPPUNIT_ASSERT_EQUAL(chg[5],'e');
329 }
330
331 // note: can't use cppunit's CPPUNIT_TEST_EXCEPTION because TextException asserts, and
332 // so the test can't be properly completed.
333 void
334 TestSBuf::testSubscriptOpFail()
335 {
336 char c;
337 c=literal.at(literal.length()); //out of bounds by 1
338 //notreached
339 std::cout << c << std::endl;
340 }
341
342 static int sign(int v)
343 {
344 if (v < 0)
345 return -1;
346 if (v>0)
347 return 1;
348 return 0;
349 }
350
351 static void
352 testComparisonStdFull(const char *left, const char *right)
353 {
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))));
357
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)));
361
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))));
365
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)));
369 }
370
371 static void
372 testComparisonStdN(const char *left, const char *right, const size_t n)
373 {
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)));
377
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)));
381
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)));
385
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)));
389 }
390
391 static void
392 testComparisonStdOneWay(const char *left, const char *right)
393 {
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);
398 }
399 }
400
401 static void
402 testComparisonStd(const char *s1, const char *s2)
403 {
404 testComparisonStdOneWay(s1, s2);
405 testComparisonStdOneWay(s2, s1);
406 }
407
408 void
409 TestSBuf::testComparisons()
410 {
411 //same length
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())));
421 //different lengths
422 s1.assign("foo");
423 s2.assign("foof");
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
430 s1 = "foo";
431 s2 = "fOo";
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));
442
443 testComparisonStd("foo", "fooz");
444 testComparisonStd("foo", "foo");
445 testComparisonStd("foo", "f");
446 testComparisonStd("foo", "bar");
447
448 testComparisonStd("foo", "FOOZ");
449 testComparisonStd("foo", "FOO");
450 testComparisonStd("foo", "F");
451
452 testComparisonStdOneWay("", "");
453
454 // rare case C-string input matching SBuf with N>strlen(s)
455 {
456 char *right = xstrdup("foo34567890123456789012345678");
457 SBuf left("fooZYXWVUTSRQPONMLKJIHGFEDCBA");
458 // is 3 bytes in length. NEVER more.
459 right[3] = '\0';
460 left.setAt(3, '\0');
461
462 // pick another spot to truncate at if something goes horribly wrong.
463 right[14] = '\0';
464 left.setAt(14, '\0');
465
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)));
474 }
475 xfree(right);
476 }
477 }
478
479 void
480 TestSBuf::testConsume()
481 {
482 SBuf s1(literal),s2,s3;
483 s2=s1.consume(4);
484 s3.assign("The ");
485 CPPUNIT_ASSERT_EQUAL(s2,s3);
486 s3.assign("quick brown fox jumped over the lazy dog");
487 CPPUNIT_ASSERT_EQUAL(s1,s3);
488 s1.consume(40);
489 CPPUNIT_ASSERT_EQUAL(s1,SBuf());
490 }
491
492 void
493 TestSBuf::testRawContent()
494 {
495 SBuf s1(literal);
496 SBuf s2(s1);
497 s2.append("foo");
498 const char *foo;
499 foo = s1.rawContent();
500 CPPUNIT_ASSERT_EQUAL(0,strncmp(fox,foo,s1.length()));
501 foo = s1.c_str();
502 CPPUNIT_ASSERT(!strcmp(fox,foo));
503 }
504
505 void
506 TestSBuf::testRawSpace()
507 {
508 SBuf s1(literal);
509 SBuf s2(fox1);
510 char *rb=s2.rawAppendStart(strlen(fox2)+1);
511 strcpy(rb,fox2);
512 s2.rawAppendFinish(rb, strlen(fox2));
513 CPPUNIT_ASSERT_EQUAL(s1,s2);
514 }
515
516 void
517 TestSBuf::testChop()
518 {
519 SBuf s1(literal),s2;
520 s1.chop(4,5);
521 s2.assign("quick");
522 CPPUNIT_ASSERT_EQUAL(s1,s2);
523 s1=literal;
524 s2.clear();
525 s1.chop(5,0);
526 CPPUNIT_ASSERT_EQUAL(s1,s2);
527 const char *alphabet="abcdefghijklmnopqrstuvwxyz";
528 SBuf a(alphabet);
529 std::string s(alphabet); // TODO
530 { //regular chopping
531 SBuf b(a);
532 b.chop(3,3);
533 SBuf ref("def");
534 CPPUNIT_ASSERT_EQUAL(ref,b);
535 }
536 { // chop at end
537 SBuf b(a);
538 b.chop(b.length()-3);
539 SBuf ref("xyz");
540 CPPUNIT_ASSERT_EQUAL(ref,b);
541 }
542 { // chop at beginning
543 SBuf b(a);
544 b.chop(0,3);
545 SBuf ref("abc");
546 CPPUNIT_ASSERT_EQUAL(ref,b);
547 }
548 { // chop to zero length
549 SBuf b(a);
550 b.chop(5,0);
551 SBuf ref("");
552 CPPUNIT_ASSERT_EQUAL(ref,b);
553 }
554 { // chop beyond end (at npos)
555 SBuf b(a);
556 b.chop(SBuf::npos,4);
557 SBuf ref("");
558 CPPUNIT_ASSERT_EQUAL(ref,b);
559 }
560 { // chop beyond end
561 SBuf b(a);
562 b.chop(b.length()+2,4);
563 SBuf ref("");
564 CPPUNIT_ASSERT_EQUAL(ref,b);
565 }
566 { // null-chop
567 SBuf b(a);
568 b.chop(0,b.length());
569 SBuf ref(a);
570 CPPUNIT_ASSERT_EQUAL(ref,b);
571 }
572 { // overflow chopped area
573 SBuf b(a);
574 b.chop(b.length()-3,b.length());
575 SBuf ref("xyz");
576 CPPUNIT_ASSERT_EQUAL(ref,b);
577 }
578 }
579
580 void
581 TestSBuf::testChomp()
582 {
583 SBuf s1("complete string");
584 SBuf s2(s1);
585 s2.trim(SBuf(" ,"));
586 CPPUNIT_ASSERT_EQUAL(s1,s2);
587 s2.assign(" complete string ,");
588 s2.trim(SBuf(" ,"));
589 CPPUNIT_ASSERT_EQUAL(s1,s2);
590 s1.assign(", complete string ,");
591 s2=s1;
592 s2.trim(SBuf(" "));
593 CPPUNIT_ASSERT_EQUAL(s1,s2);
594 }
595
596 // inspired by SBufFindTest; to be expanded.
597 class SBufSubstrAutoTest
598 {
599 SBuf fullString, sb;
600 std::string fullReference, str;
601 public:
602 void performEqualityTest() {
603 SBuf ref(str);
604 CPPUNIT_ASSERT_EQUAL(ref,sb);
605 }
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();
612 }
613 }
614 }
615 };
616
617 void
618 TestSBuf::testSubstr()
619 {
620 SBuf s1(literal),s2,s3;
621 s2=s1.substr(4,5);
622 s3.assign("quick");
623 CPPUNIT_ASSERT_EQUAL(s2,s3);
624 s1.chop(4,5);
625 CPPUNIT_ASSERT_EQUAL(s1,s2);
626 SBufSubstrAutoTest sat; // work done in the constructor
627 }
628
629 void
630 TestSBuf::testFindChar()
631 {
632 const char *alphabet="abcdefghijklmnopqrstuvwxyz";
633 SBuf s1(alphabet);
634 SBuf::size_type idx;
635 SBuf::size_type nposResult=SBuf::npos;
636
637 // FORWARD SEARCH
638 // needle in haystack
639 idx=s1.find('d');
640 CPPUNIT_ASSERT_EQUAL(3U,idx);
641 CPPUNIT_ASSERT_EQUAL('d',s1[idx]);
642
643 // needle not present in haystack
644 idx=s1.find(' '); //fails
645 CPPUNIT_ASSERT_EQUAL(nposResult,idx);
646
647 // search in portion
648 idx=s1.find('e',3U);
649 CPPUNIT_ASSERT_EQUAL(4U,idx);
650
651 // char not in searched portion
652 idx=s1.find('e',5U);
653 CPPUNIT_ASSERT_EQUAL(nposResult,idx);
654
655 // invalid start position
656 idx=s1.find('d',SBuf::npos);
657 CPPUNIT_ASSERT_EQUAL(nposResult,idx);
658
659 // search outside of haystack
660 idx=s1.find('d',s1.length()+1);
661 CPPUNIT_ASSERT_EQUAL(nposResult,idx);
662
663 // REVERSE SEARCH
664 // needle in haystack
665 idx=s1.rfind('d');
666 CPPUNIT_ASSERT_EQUAL(3U, idx);
667 CPPUNIT_ASSERT_EQUAL('d', s1[idx]);
668
669 // needle not present in haystack
670 idx=s1.rfind(' '); //fails
671 CPPUNIT_ASSERT_EQUAL(nposResult,idx);
672
673 // search in portion
674 idx=s1.rfind('e',5);
675 CPPUNIT_ASSERT_EQUAL(4U,idx);
676
677 // char not in searched portion
678 idx=s1.rfind('e',3);
679 CPPUNIT_ASSERT_EQUAL(nposResult,idx);
680
681 // overlong haystack specification
682 idx=s1.rfind('d',s1.length()+1);
683 CPPUNIT_ASSERT_EQUAL(3U,idx);
684 }
685
686 void
687 TestSBuf::testFindSBuf()
688 {
689 const char *alphabet="abcdefghijklmnopqrstuvwxyz";
690 SBuf haystack(alphabet);
691 SBuf::size_type idx;
692 SBuf::size_type nposResult=SBuf::npos;
693
694 // FORWARD search
695 // needle in haystack
696 idx = haystack.find(SBuf("def"));
697 CPPUNIT_ASSERT_EQUAL(3U,idx);
698
699 idx = haystack.find(SBuf("xyz"));
700 CPPUNIT_ASSERT_EQUAL(23U,idx);
701
702 // needle not in haystack, no initial char match
703 idx = haystack.find(SBuf(" eq"));
704 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
705
706 // needle not in haystack, initial sequence match
707 idx = haystack.find(SBuf("deg"));
708 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
709
710 // needle past end of haystack
711 idx = haystack.find(SBuf("xyz1"));
712 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
713
714 // search in portion: needle not in searched part
715 idx = haystack.find(SBuf("def"),7);
716 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
717
718 // search in portion: overhang
719 idx = haystack.find(SBuf("def"),4);
720 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
721
722 // invalid start position
723 idx = haystack.find(SBuf("def"),SBuf::npos);
724 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
725
726 // needle bigger than haystack
727 idx = SBuf("def").find(haystack);
728 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
729
730 // search in a double-matching haystack
731 {
732 SBuf h2=haystack;
733 h2.append(haystack);
734
735 idx = h2.find(SBuf("def"));
736 CPPUNIT_ASSERT_EQUAL(3U,idx);
737
738 idx = h2.find(SBuf("xyzab"));
739 CPPUNIT_ASSERT_EQUAL(23U,idx);
740 }
741
742 // REVERSE search
743 // needle in haystack
744 idx = haystack.rfind(SBuf("def"));
745 CPPUNIT_ASSERT_EQUAL(3U,idx);
746
747 idx = haystack.rfind(SBuf("xyz"));
748 CPPUNIT_ASSERT_EQUAL(23U,idx);
749
750 // needle not in haystack, no initial char match
751 idx = haystack.rfind(SBuf(" eq"));
752 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
753
754 // needle not in haystack, initial sequence match
755 idx = haystack.rfind(SBuf("deg"));
756 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
757
758 // needle past end of haystack
759 idx = haystack.rfind(SBuf("xyz1"));
760 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
761
762 // search in portion: needle in searched part
763 idx = haystack.rfind(SBuf("def"),7);
764 CPPUNIT_ASSERT_EQUAL(3U, idx);
765
766 // search in portion: needle not in searched part
767 idx = haystack.rfind(SBuf("mno"),3);
768 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
769
770 // search in portion: overhang
771 idx = haystack.rfind(SBuf("def"),4);
772 CPPUNIT_ASSERT_EQUAL(3U, idx);
773
774 // npos start position
775 idx = haystack.rfind(SBuf("def"),SBuf::npos);
776 CPPUNIT_ASSERT_EQUAL(3U, idx);
777
778 // needle bigger than haystack
779 idx = SBuf("def").rfind(haystack);
780 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
781
782 // search in a double-matching haystack
783 {
784 SBuf h2=haystack;
785 h2.append(haystack);
786
787 idx = h2.rfind(SBuf("def"));
788 CPPUNIT_ASSERT_EQUAL(29U,idx);
789
790 idx = h2.find(SBuf("xyzab"));
791 CPPUNIT_ASSERT_EQUAL(23U,idx);
792 }
793 }
794
795 void
796 TestSBuf::testRFindChar()
797 {
798 SBuf s1(literal);
799 SBuf::size_type idx;
800 idx=s1.rfind(' ');
801 CPPUNIT_ASSERT_EQUAL(40U,idx);
802 CPPUNIT_ASSERT_EQUAL(' ',s1[idx]);
803 }
804
805 void
806 TestSBuf::testRFindSBuf()
807 {
808 SBuf haystack(literal),afox("fox");
809 SBuf goobar("goobar");
810 SBuf::size_type idx;
811
812 // corner case: search for a zero-length SBuf
813 idx=haystack.rfind(SBuf(""));
814 CPPUNIT_ASSERT_EQUAL(haystack.length(),idx);
815
816 // corner case: search for a needle longer than the haystack
817 idx=afox.rfind(SBuf(" "));
818 CPPUNIT_ASSERT_EQUAL(SBuf::npos,idx);
819
820 idx=haystack.rfind(SBuf("fox"));
821 CPPUNIT_ASSERT_EQUAL(16U,idx);
822
823 // needle not found, no match for first char
824 idx=goobar.rfind(SBuf("foo"));
825 CPPUNIT_ASSERT_EQUAL(SBuf::npos,idx);
826
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);
830
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]);
835
836 idx=haystack.rfind(SBuf("The"));
837 CPPUNIT_ASSERT_EQUAL(0U,idx);
838
839 haystack.append("The");
840 idx=haystack.rfind(SBuf("The"));
841 CPPUNIT_ASSERT_EQUAL(44U,idx);
842
843 //partial match
844 haystack="The quick brown fox";
845 SBuf needle("foxy lady");
846 idx=haystack.rfind(needle);
847 CPPUNIT_ASSERT_EQUAL(SBuf::npos,idx);
848 }
849
850 void
851 TestSBuf::testSBufLength()
852 {
853 SBuf s(fox);
854 CPPUNIT_ASSERT_EQUAL(strlen(fox),(size_t)s.length());
855 }
856
857 void
858 TestSBuf::testCopy()
859 {
860 char buf[40]; //shorter than literal()
861 SBuf s(fox1),s2;
862 CPPUNIT_ASSERT_EQUAL(s.length(),s.copy(buf,40));
863 CPPUNIT_ASSERT_EQUAL(0,strncmp(s.rawContent(),buf,s.length()));
864 s=literal;
865 CPPUNIT_ASSERT_EQUAL(40U,s.copy(buf,40));
866 s2.assign(buf,40);
867 s.chop(0,40);
868 CPPUNIT_ASSERT_EQUAL(s2,s);
869 }
870
871 void
872 TestSBuf::testStringOps()
873 {
874 SBuf sng(ToLower(literal)),
875 ref("the quick brown fox jumped over the lazy dog");
876 CPPUNIT_ASSERT_EQUAL(ref,sng);
877 sng=literal;
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));
883 }
884
885 void
886 TestSBuf::testGrow()
887 {
888 SBuf t;
889 t.assign("foo");
890 const char *ref=t.rawContent();
891 t.reserveCapacity(10240);
892 const char *match=t.rawContent();
893 CPPUNIT_ASSERT(match!=ref);
894 ref=match;
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);
898 }
899
900 void
901 TestSBuf::testReserve()
902 {
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;
909
910 // for each possible starting buffer length within the capacity
911 for (SBuf::size_type startLength = 0; startLength <= requirements.maxCapacity; ++startLength) {
912 std::cerr << ".";
913 SBuf b;
914 b.reserveCapacity(startLength);
915 CPPUNIT_ASSERT_EQUAL(b.length(), static_cast<unsigned int>(0));
916 CPPUNIT_ASSERT_EQUAL(b.spaceSize(), startLength);
917
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);
928 if (x > 0)
929 b.append('X');
930 }
931 }
932
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;
937 SBuf buffer;
938 buffer.reserve(requirements);
939 CPPUNIT_ASSERT(buffer.spaceSize() >= requirements.minSpace);
940 }
941
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).
945
946 { // reserveSpace() uses the trailing space before the front gap
947 SBuf buffer(fox);
948
949 // assure there is some trailing space
950 buffer.reserveSpace(1);
951 CPPUNIT_ASSERT(buffer.spaceSize() > 0);
952
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;
957 buffer.consume(gap);
958 CPPUNIT_ASSERT_EQUAL(gapEnd, static_cast<const void*>(buffer.rawContent()));
959
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);
973 }
974
975 { // reserveSpace() uses the front gap when the trailing space is not enough
976 SBuf buffer(fox);
977
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();
982
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;
987 buffer.consume(gap);
988 CPPUNIT_ASSERT_EQUAL(gapEnd, static_cast<const void*>(buffer.rawContent()));
989
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);
1002 }
1003
1004 { // reserveSpace() uses the entire front gap when using the front gap
1005 SBuf buffer(fox);
1006
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();
1011
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()));
1018
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);
1031 }
1032 }
1033
1034 void
1035 TestSBuf::testStartsWith()
1036 {
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
1041
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));
1049 }
1050
1051 void
1052 TestSBuf::testSBufStream()
1053 {
1054 SBuf b("const.string, int 10 and a float 10.5");
1055 SBufStream ss;
1056 ss << "const.string, int " << 10 << " and a float " << 10.5;
1057 SBuf o=ss.buf();
1058 CPPUNIT_ASSERT_EQUAL(b,o);
1059 ss.clearBuf();
1060 o=ss.buf();
1061 CPPUNIT_ASSERT_EQUAL(SBuf(),o);
1062 SBuf f1(fox1);
1063 SBufStream ss2(f1);
1064 ss2 << fox2;
1065 CPPUNIT_ASSERT_EQUAL(ss2.buf(),literal);
1066 CPPUNIT_ASSERT_EQUAL(f1,SBuf(fox1));
1067 }
1068
1069 void
1070 TestSBuf::testFindFirstOf()
1071 {
1072 SBuf haystack(literal);
1073 SBuf::size_type idx;
1074
1075 // not found
1076 idx=haystack.findFirstOf(CharacterSet("t1","ADHRWYP"));
1077 CPPUNIT_ASSERT_EQUAL(SBuf::npos,idx);
1078
1079 // found at beginning
1080 idx=haystack.findFirstOf(CharacterSet("t2","THANDF"));
1081 CPPUNIT_ASSERT_EQUAL(0U,idx);
1082
1083 //found at end of haystack
1084 idx=haystack.findFirstOf(CharacterSet("t3","QWERYVg"));
1085 CPPUNIT_ASSERT_EQUAL(haystack.length()-1,idx);
1086
1087 //found in the middle of haystack
1088 idx=haystack.findFirstOf(CharacterSet("t4","QWERqYV"));
1089 CPPUNIT_ASSERT_EQUAL(4U,idx);
1090 }
1091
1092 void
1093 TestSBuf::testFindFirstNotOf()
1094 {
1095 SBuf haystack(literal);
1096 SBuf::size_type idx;
1097
1098 // all chars from the set
1099 idx=haystack.findFirstNotOf(CharacterSet("t1",literal.c_str()));
1100 CPPUNIT_ASSERT_EQUAL(SBuf::npos,idx);
1101
1102 // found at beginning
1103 idx=haystack.findFirstNotOf(CharacterSet("t2","a"));
1104 CPPUNIT_ASSERT_EQUAL(0U,idx);
1105
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);
1109
1110 //found in the middle of haystack
1111 idx=haystack.findFirstNotOf(CharacterSet("t4","The"));
1112 CPPUNIT_ASSERT_EQUAL(3U,idx);
1113 }
1114
1115 void
1116 TestSBuf::testAutoFind()
1117 {
1118 SBufFindTest test;
1119 test.run();
1120 }
1121
1122 void
1123 TestSBuf::testStdStringOps()
1124 {
1125 const char *alphabet="abcdefghijklmnopqrstuvwxyz";
1126 std::string astr(alphabet);
1127 SBuf sb(alphabet);
1128 CPPUNIT_ASSERT_EQUAL(astr,sb.toStdString());
1129 }
1130
1131 void
1132 TestSBuf::testIterators()
1133 {
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());
1138 {
1139 auto i = text.begin();
1140 auto e = text.end();
1141 CPPUNIT_ASSERT_EQUAL('f', *i);
1142 CPPUNIT_ASSERT(i != e);
1143 ++i;
1144 CPPUNIT_ASSERT_EQUAL('o', *i);
1145 CPPUNIT_ASSERT(i != e);
1146 ++i;
1147 CPPUNIT_ASSERT_EQUAL('o', *i);
1148 CPPUNIT_ASSERT(i != e);
1149 ++i;
1150 CPPUNIT_ASSERT(i == e);
1151 }
1152 {
1153 auto i = text.rbegin();
1154 auto e = text.rend();
1155 CPPUNIT_ASSERT_EQUAL('o', *i);
1156 CPPUNIT_ASSERT(i != e);
1157 ++i;
1158 CPPUNIT_ASSERT_EQUAL('o', *i);
1159 CPPUNIT_ASSERT(i != e);
1160 ++i;
1161 CPPUNIT_ASSERT_EQUAL('f', *i);
1162 CPPUNIT_ASSERT(i != e);
1163 ++i;
1164 CPPUNIT_ASSERT(i == e);
1165 }
1166 }
1167
1168 void
1169 TestSBuf::testSBufHash()
1170 {
1171 // same SBuf must have same hash
1172 auto hasher=std::hash<SBuf>();
1173 CPPUNIT_ASSERT_EQUAL(hasher(literal),hasher(literal));
1174
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)));
1178
1179 //different content should have different hash
1180 CPPUNIT_ASSERT(hasher(SBuf(fox)) != hasher(SBuf(fox1)));
1181
1182 {
1183 std::unordered_map<SBuf, int> um;
1184 um[SBuf("one")] = 1;
1185 um[SBuf("two")] = 2;
1186
1187 auto i = um.find(SBuf("one"));
1188 CPPUNIT_ASSERT(i != um.end());
1189 CPPUNIT_ASSERT(i->second == 1);
1190
1191 i = um.find(SBuf("eleventy"));
1192 CPPUNIT_ASSERT(i == um.end());
1193 }
1194 }
1195
1196 void
1197 TestSBuf::testStdAlgorithm()
1198 {
1199 {
1200 SBuf src(fox), dst;
1201 std::copy(src.begin(), src.end(), std::back_inserter(dst));
1202 CPPUNIT_ASSERT_EQUAL(src, dst);
1203 }
1204
1205 {
1206 SBuf src(fox), 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);
1210 }
1211 }
1212
1213 int
1214 main(int argc, char *argv[])
1215 {
1216 return TestProgram().run(argc, argv);
1217 }
1218