]> git.ipfire.org Git - thirdparty/squid.git/blob - src/tests/testSBuf.cc
Unit test for SBuf::reserve()
[thirdparty/squid.git] / src / tests / testSBuf.cc
1 /*
2 * Copyright (C) 1996-2016 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 "sbuf/Algorithms.h"
12 #include "sbuf/SBuf.h"
13 #include "sbuf/Stream.h"
14 #include "tests/SBufFindTest.h"
15 #include "tests/testSBuf.h"
16 #include "unitTestMain.h"
17
18 #include <iostream>
19 #include <stdexcept>
20 #include <unordered_map>
21
22 CPPUNIT_TEST_SUITE_REGISTRATION( testSBuf );
23
24 /* let this test link sanely */
25 #include "event.h"
26 #include "MemObject.h"
27 void
28 eventAdd(const char *name, EVH * func, void *arg, double when, int, bool cbdata)
29 {}
30 int64_t
31 MemObject::endOffset() const
32 { return 0; }
33 /* end of stubs */
34
35 // test string
36 static char fox[]="The quick brown fox jumped over the lazy dog";
37 static char fox1[]="The quick brown fox ";
38 static char fox2[]="jumped over the lazy dog";
39
40 // TEST: globals variables (default/empty and with contents) are
41 // created outside and before any unit tests and memory subsystem
42 // initialization. Check for correct constructor operation.
43 SBuf empty_sbuf;
44 SBuf literal("The quick brown fox jumped over the lazy dog");
45
46 void
47 testSBuf::testSBufConstructDestruct()
48 {
49 /* NOTE: Do not initialize memory here because we need
50 * to test correct operation before and after Mem::Init
51 */
52
53 // XXX: partial demo below of how to do constructor unit-test. use scope to ensure each test
54 // is working on local-scope variables constructed fresh for the test, and destructed when
55 // scope exists. use nested scopes to test destructor affects on copied data (MemBlob etc)
56
57 // TEST: default constructor (implicit destructor non-crash test)
58 // test accessors on empty SBuf.
59 {
60 SBuf s1;
61 CPPUNIT_ASSERT_EQUAL(0U,s1.length());
62 CPPUNIT_ASSERT_EQUAL(SBuf(""),s1);
63 CPPUNIT_ASSERT_EQUAL(empty_sbuf,s1);
64 CPPUNIT_ASSERT_EQUAL(0,strcmp("",s1.c_str()));
65 }
66
67 // TEST: copy-construct NULL string (implicit destructor non-crash test)
68 {
69 SBuf s1(NULL);
70 CPPUNIT_ASSERT_EQUAL(0U,s1.length());
71 CPPUNIT_ASSERT_EQUAL(SBuf(""),s1);
72 CPPUNIT_ASSERT_EQUAL(empty_sbuf,s1);
73 CPPUNIT_ASSERT_EQUAL(0,strcmp("",s1.c_str()));
74 }
75
76 // TEST: copy-construct empty string (implicit destructor non-crash test)
77 {
78 SBuf s1("");
79 CPPUNIT_ASSERT_EQUAL(0U,s1.length());
80 CPPUNIT_ASSERT_EQUAL(SBuf(""),s1);
81 CPPUNIT_ASSERT_EQUAL(empty_sbuf,s1);
82 CPPUNIT_ASSERT_EQUAL(0,strcmp("",s1.c_str()));
83 }
84
85 // TEST: copy-construct from a SBuf
86 {
87 SBuf s1(empty_sbuf);
88 CPPUNIT_ASSERT_EQUAL(0U,s1.length());
89 CPPUNIT_ASSERT_EQUAL(SBuf(""),s1);
90 CPPUNIT_ASSERT_EQUAL(empty_sbuf,s1);
91 CPPUNIT_ASSERT_EQUAL(0,strcmp("",s1.c_str()));
92
93 SBuf s5(literal);
94 CPPUNIT_ASSERT_EQUAL(literal,s5);
95 SBuf s6(fox);
96 CPPUNIT_ASSERT_EQUAL(literal,s6);
97 // XXX: other state checks. expected result of calling any state accessor on s4 ?
98 }
99
100 // TEST: check that COW doesn't happen upon copy-construction
101 {
102 SBuf s1(empty_sbuf), s2(s1);
103 CPPUNIT_ASSERT_EQUAL(s1.rawContent(), s2.rawContent());
104 SBuf s3(literal), s4(literal);
105 CPPUNIT_ASSERT_EQUAL(s3.rawContent(), s4.rawContent());
106 }
107
108 // TEST: sub-string copy
109 {
110 SBuf s1=SBuf(fox+4), s2(fox);
111 SBuf s3=s2.substr(4,s2.length()); //n is out-of-bounds
112 CPPUNIT_ASSERT_EQUAL(s1,s3);
113 SBuf s4=SBuf(fox,4);
114 s3=s2.substr(0,4);
115 CPPUNIT_ASSERT_EQUAL(s4,s3);
116 }
117
118 // TEST: go via std::string adapter.
119 {
120 std::string str(fox);
121 SBuf s1(str);
122 CPPUNIT_ASSERT_EQUAL(literal,s1);
123 }
124 }
125
126 void
127 testSBuf::testSBufConstructDestructAfterMemInit()
128 {
129 Mem::Init();
130 testSBufConstructDestruct();
131 }
132
133 void
134 testSBuf::testEqualityTest()
135 {
136 SBuf s1(fox),s2(fox);
137 CPPUNIT_ASSERT_EQUAL(s1,s1); //self-equality
138 CPPUNIT_ASSERT_EQUAL(s1,s2); //same contents
139 s2.assign("The quick brown fox jumped over the lazy doe");
140 CPPUNIT_ASSERT(!(s1 == s2)); //same length, different contents
141 s2.assign("foo");
142 CPPUNIT_ASSERT(!(s1 == s2)); //different length and contents
143 CPPUNIT_ASSERT(s1 != s2); //while we're ready, let's test inequality
144 s2.clear();
145 CPPUNIT_ASSERT(!(s1 == s2)); //null and not-null
146 CPPUNIT_ASSERT(s1 != s2); //while we're ready, let's test inequality
147 s1.clear();
148 CPPUNIT_ASSERT_EQUAL(s1,s2); //null and null
149 }
150
151 void
152 testSBuf::testAppendSBuf()
153 {
154 SBuf s1(fox1),s2(fox2);
155 s1.append(s2);
156 CPPUNIT_ASSERT_EQUAL(s1,literal);
157 }
158
159 void
160 testSBuf::testPrintf()
161 {
162 SBuf s1,s2;
163 s1.Printf("%s:%d:%03.3f","fox",10,12345.67);
164 s2.assign("fox:10:12345.670");
165 CPPUNIT_ASSERT_EQUAL(s1,s2);
166 }
167
168 void
169 testSBuf::testAppendCString()
170 {
171 SBuf s1(fox1);
172 s1.append(fox2);
173 CPPUNIT_ASSERT_EQUAL(s1,literal);
174 }
175
176 void
177 testSBuf::testAppendStdString()
178 {
179 const char *alphabet="abcdefghijklmnopqrstuvwxyz";
180 {
181 SBuf alpha(alphabet), s;
182 s.append(alphabet,5).append(alphabet+5);
183 CPPUNIT_ASSERT_EQUAL(alpha,s);
184 }
185 {
186 SBuf s;
187 std::string control;
188 s.append(alphabet,5).append("\0",1).append(alphabet+6,SBuf::npos);
189 control.append(alphabet,5).append(1,'\0').append(alphabet,6,std::string::npos);
190 SBuf scontrol(control); // we need this to test the equality. sigh.
191 CPPUNIT_ASSERT_EQUAL(scontrol,s);
192 }
193 {
194 const char *alphazero="abcdefghijk\0mnopqrstuvwxyz";
195 SBuf s(alphazero,26);
196 std::string str(alphazero,26);
197 CPPUNIT_ASSERT_EQUAL(0,memcmp(str.data(),s.rawContent(),26));
198 }
199 }
200
201 void
202 testSBuf::testAppendf()
203 {
204 SBuf s1,s2;
205 s1.appendf("%s:%d:%03.2f",fox,1234,1234.56);
206 s2.assign("The quick brown fox jumped over the lazy dog:1234:1234.56");
207 CPPUNIT_ASSERT_EQUAL(s2,s1);
208 }
209
210 void
211 testSBuf::testDumpStats()
212 {
213 SBuf::GetStats().dump(std::cout);
214 MemBlob::GetStats().dump(std::cout);
215 std::cout << "sizeof(SBuf): " << sizeof(SBuf) << std::endl;
216 std::cout << "sizeof(MemBlob): " << sizeof(MemBlob) << std::endl;
217 }
218
219 void
220 testSBuf::testSubscriptOp()
221 {
222 SBuf chg(literal);
223 CPPUNIT_ASSERT_EQUAL(chg[5],'u');
224 chg.setAt(5,'e');
225 CPPUNIT_ASSERT_EQUAL(literal[5],'u');
226 CPPUNIT_ASSERT_EQUAL(chg[5],'e');
227 }
228
229 // note: can't use cppunit's CPPUNIT_TEST_EXCEPTION because TextException asserts, and
230 // so the test can't be properly completed.
231 void
232 testSBuf::testSubscriptOpFail()
233 {
234 char c;
235 c=literal.at(literal.length()); //out of bounds by 1
236 //notreached
237 std::cout << c << std::endl;
238 }
239
240 static int sign(int v)
241 {
242 if (v < 0)
243 return -1;
244 if (v>0)
245 return 1;
246 return 0;
247 }
248
249 static void
250 testComparisonStdFull(const char *left, const char *right)
251 {
252 if (sign(strcmp(left, right)) != sign(SBuf(left).cmp(SBuf(right))))
253 std::cerr << std::endl << " cmp(SBuf) npos " << left << " ?= " << right << std::endl;
254 CPPUNIT_ASSERT_EQUAL(sign(strcmp(left, right)), sign(SBuf(left).cmp(SBuf(right))));
255
256 if (sign(strcmp(left, right)) != sign(SBuf(left).cmp(right)))
257 std::cerr << std::endl << " cmp(char*) npos " << left << " ?= " << right << std::endl;
258 CPPUNIT_ASSERT_EQUAL(sign(strcmp(left, right)), sign(SBuf(left).cmp(right)));
259
260 if (sign(strcasecmp(left, right)) != sign(SBuf(left).caseCmp(SBuf(right))))
261 std::cerr << std::endl << " caseCmp(SBuf) npos " << left << " ?= " << right << std::endl;
262 CPPUNIT_ASSERT_EQUAL(sign(strcasecmp(left, right)), sign(SBuf(left).caseCmp(SBuf(right))));
263
264 if (sign(strcasecmp(left, right)) != sign(SBuf(left).caseCmp(right)))
265 std::cerr << std::endl << " caseCmp(char*) npos " << left << " ?= " << right << std::endl;
266 CPPUNIT_ASSERT_EQUAL(sign(strcasecmp(left, right)), sign(SBuf(left).caseCmp(right)));
267 }
268
269 static void
270 testComparisonStdN(const char *left, const char *right, const size_t n)
271 {
272 if (sign(strncmp(left, right, n)) != sign(SBuf(left).cmp(SBuf(right), n)))
273 std::cerr << std::endl << " cmp(SBuf) " << n << ' ' << left << " ?= " << right << std::endl;
274 CPPUNIT_ASSERT_EQUAL(sign(strncmp(left, right, n)), sign(SBuf(left).cmp(SBuf(right), n)));
275
276 if (sign(strncmp(left, right, n)) != sign(SBuf(left).cmp(right, n)))
277 std::cerr << std::endl << " cmp(char*) " << n << ' ' << SBuf(left) << " ?= " << right << std::endl;
278 CPPUNIT_ASSERT_EQUAL(sign(strncmp(left, right, n)), sign(SBuf(left).cmp(right, n)));
279
280 if (sign(strncasecmp(left, right, n)) != sign(SBuf(left).caseCmp(SBuf(right), n)))
281 std::cerr << std::endl << " caseCmp(SBuf) " << n << ' ' << left << " ?= " << right << std::endl;
282 CPPUNIT_ASSERT_EQUAL(sign(strncasecmp(left, right, n)), sign(SBuf(left).caseCmp(SBuf(right), n)));
283
284 if (sign(strncasecmp(left, right, n)) != sign(SBuf(left).caseCmp(right, n)))
285 std::cerr << std::endl << " caseCmp(char*) " << n << ' ' << SBuf(left) << " ?= " << right << std::endl;
286 CPPUNIT_ASSERT_EQUAL(sign(strncasecmp(left, right, n)), sign(SBuf(left).caseCmp(right, n)));
287 }
288
289 static void
290 testComparisonStdOneWay(const char *left, const char *right)
291 {
292 testComparisonStdFull(left, right);
293 const size_t maxN = 2 + min(strlen(left), strlen(right));
294 for (size_t n = 0; n <= maxN; ++n) {
295 testComparisonStdN(left, right, n);
296 }
297 }
298
299 static void
300 testComparisonStd(const char *s1, const char *s2)
301 {
302 testComparisonStdOneWay(s1, s2);
303 testComparisonStdOneWay(s2, s1);
304 }
305
306 void
307 testSBuf::testComparisons()
308 {
309 //same length
310 SBuf s1("foo"),s2("foe");
311 CPPUNIT_ASSERT(s1.cmp(s2)>0);
312 CPPUNIT_ASSERT(s1.caseCmp(s2)>0);
313 CPPUNIT_ASSERT(s2.cmp(s1)<0);
314 CPPUNIT_ASSERT_EQUAL(0,s1.cmp(s2,2));
315 CPPUNIT_ASSERT_EQUAL(0,s1.caseCmp(s2,2));
316 CPPUNIT_ASSERT(s1 > s2);
317 CPPUNIT_ASSERT(s2 < s1);
318 CPPUNIT_ASSERT_EQUAL(sign(s1.cmp(s2)),sign(strcmp(s1.c_str(),s2.c_str())));
319 //different lengths
320 s1.assign("foo");
321 s2.assign("foof");
322 CPPUNIT_ASSERT(s1.cmp(s2)<0);
323 CPPUNIT_ASSERT_EQUAL(sign(s1.cmp(s2)),sign(strcmp(s1.c_str(),s2.c_str())));
324 CPPUNIT_ASSERT(s1 < s2);
325 // specifying the max-length and overhanging size
326 CPPUNIT_ASSERT_EQUAL(1,SBuf("foolong").caseCmp(SBuf("foo"), 5));
327 // case-insensive comaprison
328 s1 = "foo";
329 s2 = "fOo";
330 CPPUNIT_ASSERT_EQUAL(0,s1.caseCmp(s2));
331 CPPUNIT_ASSERT_EQUAL(0,s1.caseCmp(s2,2));
332 // \0-clenliness test
333 s1.assign("f\0oo",4);
334 s2.assign("f\0Oo",4);
335 CPPUNIT_ASSERT(s1.cmp(s2) > 0);
336 CPPUNIT_ASSERT_EQUAL(0,s1.caseCmp(s2));
337 CPPUNIT_ASSERT_EQUAL(0,s1.caseCmp(s2,3));
338 CPPUNIT_ASSERT_EQUAL(0,s1.caseCmp(s2,2));
339 CPPUNIT_ASSERT_EQUAL(0,s1.cmp(s2,2));
340
341 testComparisonStd("foo", "fooz");
342 testComparisonStd("foo", "foo");
343 testComparisonStd("foo", "f");
344 testComparisonStd("foo", "bar");
345
346 testComparisonStd("foo", "FOOZ");
347 testComparisonStd("foo", "FOO");
348 testComparisonStd("foo", "F");
349
350 testComparisonStdOneWay("", "");
351
352 // rare case C-string input matching SBuf with N>strlen(s)
353 {
354 char *right = xstrdup("foo34567890123456789012345678");
355 SBuf left("fooZYXWVUTSRQPONMLKJIHGFEDCBA");
356 // is 3 bytes in length. NEVER more.
357 right[3] = '\0';
358 left.setAt(3, '\0');
359
360 // pick another spot to truncate at if something goes horribly wrong.
361 right[14] = '\0';
362 left.setAt(14, '\0');
363
364 const SBuf::size_type maxN = 20 + min(left.length(), static_cast<SBuf::size_type>(strlen(right)));
365 for (SBuf::size_type n = 0; n <= maxN; ++n) {
366 if (sign(strncmp(left.rawContent(), right, n)) != sign(left.cmp(right, n)) )
367 std::cerr << std::endl << " cmp(char*) " << n << ' ' << left << " ?= " << right;
368 CPPUNIT_ASSERT_EQUAL(sign(strncmp(left.rawContent(), right, n)), sign(left.cmp(right, n)));
369 if (sign(strncasecmp(left.rawContent(), right, n)) != sign(left.caseCmp(right, n)))
370 std::cerr << std::endl << " caseCmp(char*) " << n << ' ' << left << " ?= " << right;
371 CPPUNIT_ASSERT_EQUAL(sign(strncasecmp(left.rawContent(), right, n)), sign(left.caseCmp(right, n)));
372 }
373 xfree(right);
374 }
375 }
376
377 void
378 testSBuf::testConsume()
379 {
380 SBuf s1(literal),s2,s3;
381 s2=s1.consume(4);
382 s3.assign("The ");
383 CPPUNIT_ASSERT_EQUAL(s2,s3);
384 s3.assign("quick brown fox jumped over the lazy dog");
385 CPPUNIT_ASSERT_EQUAL(s1,s3);
386 s1.consume(40);
387 CPPUNIT_ASSERT_EQUAL(s1,SBuf());
388 }
389
390 void
391 testSBuf::testRawContent()
392 {
393 SBuf s1(literal);
394 SBuf s2(s1);
395 s2.append("foo");
396 const char *foo;
397 foo = s1.rawContent();
398 CPPUNIT_ASSERT_EQUAL(0,strncmp(fox,foo,s1.length()));
399 foo = s1.c_str();
400 CPPUNIT_ASSERT(!strcmp(fox,foo));
401 }
402
403 void
404 testSBuf::testRawSpace()
405 {
406 SBuf s1(literal);
407 SBuf s2(fox1);
408 SBuf::size_type sz=s2.length();
409 char *rb=s2.rawSpace(strlen(fox2)+1);
410 strcpy(rb,fox2);
411 s2.forceSize(sz+strlen(fox2));
412 CPPUNIT_ASSERT_EQUAL(s1,s2);
413 }
414
415 void
416 testSBuf::testChop()
417 {
418 SBuf s1(literal),s2;
419 s1.chop(4,5);
420 s2.assign("quick");
421 CPPUNIT_ASSERT_EQUAL(s1,s2);
422 s1=literal;
423 s2.clear();
424 s1.chop(5,0);
425 CPPUNIT_ASSERT_EQUAL(s1,s2);
426 const char *alphabet="abcdefghijklmnopqrstuvwxyz";
427 SBuf a(alphabet);
428 std::string s(alphabet); // TODO
429 { //regular chopping
430 SBuf b(a);
431 b.chop(3,3);
432 SBuf ref("def");
433 CPPUNIT_ASSERT_EQUAL(ref,b);
434 }
435 { // chop at end
436 SBuf b(a);
437 b.chop(b.length()-3);
438 SBuf ref("xyz");
439 CPPUNIT_ASSERT_EQUAL(ref,b);
440 }
441 { // chop at beginning
442 SBuf b(a);
443 b.chop(0,3);
444 SBuf ref("abc");
445 CPPUNIT_ASSERT_EQUAL(ref,b);
446 }
447 { // chop to zero length
448 SBuf b(a);
449 b.chop(5,0);
450 SBuf ref("");
451 CPPUNIT_ASSERT_EQUAL(ref,b);
452 }
453 { // chop beyond end (at npos)
454 SBuf b(a);
455 b.chop(SBuf::npos,4);
456 SBuf ref("");
457 CPPUNIT_ASSERT_EQUAL(ref,b);
458 }
459 { // chop beyond end
460 SBuf b(a);
461 b.chop(b.length()+2,4);
462 SBuf ref("");
463 CPPUNIT_ASSERT_EQUAL(ref,b);
464 }
465 { // null-chop
466 SBuf b(a);
467 b.chop(0,b.length());
468 SBuf ref(a);
469 CPPUNIT_ASSERT_EQUAL(ref,b);
470 }
471 { // overflow chopped area
472 SBuf b(a);
473 b.chop(b.length()-3,b.length());
474 SBuf ref("xyz");
475 CPPUNIT_ASSERT_EQUAL(ref,b);
476 }
477 }
478
479 void
480 testSBuf::testChomp()
481 {
482 SBuf s1("complete string");
483 SBuf s2(s1);
484 s2.trim(SBuf(" ,"));
485 CPPUNIT_ASSERT_EQUAL(s1,s2);
486 s2.assign(" complete string ,");
487 s2.trim(SBuf(" ,"));
488 CPPUNIT_ASSERT_EQUAL(s1,s2);
489 s1.assign(", complete string ,");
490 s2=s1;
491 s2.trim(SBuf(" "));
492 CPPUNIT_ASSERT_EQUAL(s1,s2);
493 }
494
495 // inspired by SBufFindTest; to be expanded.
496 class SBufSubstrAutoTest
497 {
498 SBuf fullString, sb;
499 std::string fullReference, str;
500 public:
501 void performEqualityTest() {
502 SBuf ref(str);
503 CPPUNIT_ASSERT_EQUAL(ref,sb);
504 }
505 SBufSubstrAutoTest() : fullString(fox), fullReference(fox) {
506 for (int offset=fullString.length()-1; offset >= 0; --offset ) {
507 for (int length=fullString.length()-1-offset; length >= 0; --length) {
508 sb=fullString.substr(offset,length);
509 str=fullReference.substr(offset,length);
510 performEqualityTest();
511 }
512 }
513 }
514 };
515
516 void
517 testSBuf::testSubstr()
518 {
519 SBuf s1(literal),s2,s3;
520 s2=s1.substr(4,5);
521 s3.assign("quick");
522 CPPUNIT_ASSERT_EQUAL(s2,s3);
523 s1.chop(4,5);
524 CPPUNIT_ASSERT_EQUAL(s1,s2);
525 SBufSubstrAutoTest sat; // work done in the constructor
526 }
527
528 void
529 testSBuf::testFindChar()
530 {
531 const char *alphabet="abcdefghijklmnopqrstuvwxyz";
532 SBuf s1(alphabet);
533 SBuf::size_type idx;
534 SBuf::size_type nposResult=SBuf::npos;
535
536 // FORWARD SEARCH
537 // needle in haystack
538 idx=s1.find('d');
539 CPPUNIT_ASSERT_EQUAL(3U,idx);
540 CPPUNIT_ASSERT_EQUAL('d',s1[idx]);
541
542 // needle not present in haystack
543 idx=s1.find(' '); //fails
544 CPPUNIT_ASSERT_EQUAL(nposResult,idx);
545
546 // search in portion
547 idx=s1.find('e',3U);
548 CPPUNIT_ASSERT_EQUAL(4U,idx);
549
550 // char not in searched portion
551 idx=s1.find('e',5U);
552 CPPUNIT_ASSERT_EQUAL(nposResult,idx);
553
554 // invalid start position
555 idx=s1.find('d',SBuf::npos);
556 CPPUNIT_ASSERT_EQUAL(nposResult,idx);
557
558 // search outside of haystack
559 idx=s1.find('d',s1.length()+1);
560 CPPUNIT_ASSERT_EQUAL(nposResult,idx);
561
562 // REVERSE SEARCH
563 // needle in haystack
564 idx=s1.rfind('d');
565 CPPUNIT_ASSERT_EQUAL(3U, idx);
566 CPPUNIT_ASSERT_EQUAL('d', s1[idx]);
567
568 // needle not present in haystack
569 idx=s1.rfind(' '); //fails
570 CPPUNIT_ASSERT_EQUAL(nposResult,idx);
571
572 // search in portion
573 idx=s1.rfind('e',5);
574 CPPUNIT_ASSERT_EQUAL(4U,idx);
575
576 // char not in searched portion
577 idx=s1.rfind('e',3);
578 CPPUNIT_ASSERT_EQUAL(nposResult,idx);
579
580 // overlong haystack specification
581 idx=s1.rfind('d',s1.length()+1);
582 CPPUNIT_ASSERT_EQUAL(3U,idx);
583 }
584
585 void
586 testSBuf::testFindSBuf()
587 {
588 const char *alphabet="abcdefghijklmnopqrstuvwxyz";
589 SBuf haystack(alphabet);
590 SBuf::size_type idx;
591 SBuf::size_type nposResult=SBuf::npos;
592
593 // FORWARD search
594 // needle in haystack
595 idx = haystack.find(SBuf("def"));
596 CPPUNIT_ASSERT_EQUAL(3U,idx);
597
598 idx = haystack.find(SBuf("xyz"));
599 CPPUNIT_ASSERT_EQUAL(23U,idx);
600
601 // needle not in haystack, no initial char match
602 idx = haystack.find(SBuf(" eq"));
603 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
604
605 // needle not in haystack, initial sequence match
606 idx = haystack.find(SBuf("deg"));
607 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
608
609 // needle past end of haystack
610 idx = haystack.find(SBuf("xyz1"));
611 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
612
613 // search in portion: needle not in searched part
614 idx = haystack.find(SBuf("def"),7);
615 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
616
617 // search in portion: overhang
618 idx = haystack.find(SBuf("def"),4);
619 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
620
621 // invalid start position
622 idx = haystack.find(SBuf("def"),SBuf::npos);
623 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
624
625 // needle bigger than haystack
626 idx = SBuf("def").find(haystack);
627 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
628
629 // search in a double-matching haystack
630 {
631 SBuf h2=haystack;
632 h2.append(haystack);
633
634 idx = h2.find(SBuf("def"));
635 CPPUNIT_ASSERT_EQUAL(3U,idx);
636
637 idx = h2.find(SBuf("xyzab"));
638 CPPUNIT_ASSERT_EQUAL(23U,idx);
639 }
640
641 // REVERSE search
642 // needle in haystack
643 idx = haystack.rfind(SBuf("def"));
644 CPPUNIT_ASSERT_EQUAL(3U,idx);
645
646 idx = haystack.rfind(SBuf("xyz"));
647 CPPUNIT_ASSERT_EQUAL(23U,idx);
648
649 // needle not in haystack, no initial char match
650 idx = haystack.rfind(SBuf(" eq"));
651 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
652
653 // needle not in haystack, initial sequence match
654 idx = haystack.rfind(SBuf("deg"));
655 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
656
657 // needle past end of haystack
658 idx = haystack.rfind(SBuf("xyz1"));
659 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
660
661 // search in portion: needle in searched part
662 idx = haystack.rfind(SBuf("def"),7);
663 CPPUNIT_ASSERT_EQUAL(3U, idx);
664
665 // search in portion: needle not in searched part
666 idx = haystack.rfind(SBuf("mno"),3);
667 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
668
669 // search in portion: overhang
670 idx = haystack.rfind(SBuf("def"),4);
671 CPPUNIT_ASSERT_EQUAL(3U, idx);
672
673 // npos start position
674 idx = haystack.rfind(SBuf("def"),SBuf::npos);
675 CPPUNIT_ASSERT_EQUAL(3U, idx);
676
677 // needle bigger than haystack
678 idx = SBuf("def").rfind(haystack);
679 CPPUNIT_ASSERT_EQUAL(nposResult, idx);
680
681 // search in a double-matching haystack
682 {
683 SBuf h2=haystack;
684 h2.append(haystack);
685
686 idx = h2.rfind(SBuf("def"));
687 CPPUNIT_ASSERT_EQUAL(29U,idx);
688
689 idx = h2.find(SBuf("xyzab"));
690 CPPUNIT_ASSERT_EQUAL(23U,idx);
691 }
692 }
693
694 void
695 testSBuf::testRFindChar()
696 {
697 SBuf s1(literal);
698 SBuf::size_type idx;
699 idx=s1.rfind(' ');
700 CPPUNIT_ASSERT_EQUAL(40U,idx);
701 CPPUNIT_ASSERT_EQUAL(' ',s1[idx]);
702 }
703
704 void
705 testSBuf::testRFindSBuf()
706 {
707 SBuf haystack(literal),afox("fox");
708 SBuf goobar("goobar");
709 SBuf::size_type idx;
710
711 // corner case: search for a zero-length SBuf
712 idx=haystack.rfind(SBuf(""));
713 CPPUNIT_ASSERT_EQUAL(haystack.length(),idx);
714
715 // corner case: search for a needle longer than the haystack
716 idx=afox.rfind(SBuf(" "));
717 CPPUNIT_ASSERT_EQUAL(SBuf::npos,idx);
718
719 idx=haystack.rfind(SBuf("fox"));
720 CPPUNIT_ASSERT_EQUAL(16U,idx);
721
722 // needle not found, no match for first char
723 idx=goobar.rfind(SBuf("foo"));
724 CPPUNIT_ASSERT_EQUAL(SBuf::npos,idx);
725
726 // needle not found, match for first char but no match for SBuf
727 idx=haystack.rfind(SBuf("foe"));
728 CPPUNIT_ASSERT_EQUAL(SBuf::npos,idx);
729
730 SBuf g("g"); //match at the last char
731 idx=haystack.rfind(g);
732 CPPUNIT_ASSERT_EQUAL(43U,idx);
733 CPPUNIT_ASSERT_EQUAL('g',haystack[idx]);
734
735 idx=haystack.rfind(SBuf("The"));
736 CPPUNIT_ASSERT_EQUAL(0U,idx);
737
738 haystack.append("The");
739 idx=haystack.rfind(SBuf("The"));
740 CPPUNIT_ASSERT_EQUAL(44U,idx);
741
742 //partial match
743 haystack="The quick brown fox";
744 SBuf needle("foxy lady");
745 idx=haystack.rfind(needle);
746 CPPUNIT_ASSERT_EQUAL(SBuf::npos,idx);
747 }
748
749 void
750 testSBuf::testSBufLength()
751 {
752 SBuf s(fox);
753 CPPUNIT_ASSERT_EQUAL(strlen(fox),(size_t)s.length());
754 }
755
756 void
757 testSBuf::testScanf()
758 {
759 SBuf s1;
760 char s[128];
761 int i;
762 float f;
763 int rv;
764 s1.assign("string , 123 , 123.50");
765 rv=s1.scanf("%s , %d , %f",s,&i,&f);
766 CPPUNIT_ASSERT_EQUAL(3,rv);
767 CPPUNIT_ASSERT_EQUAL(0,strcmp(s,"string"));
768 CPPUNIT_ASSERT_EQUAL(123,i);
769 CPPUNIT_ASSERT_EQUAL(static_cast<float>(123.5),f);
770 }
771
772 void
773 testSBuf::testCopy()
774 {
775 char buf[40]; //shorter than literal()
776 SBuf s(fox1),s2;
777 CPPUNIT_ASSERT_EQUAL(s.length(),s.copy(buf,40));
778 CPPUNIT_ASSERT_EQUAL(0,strncmp(s.rawContent(),buf,s.length()));
779 s=literal;
780 CPPUNIT_ASSERT_EQUAL(40U,s.copy(buf,40));
781 s2.assign(buf,40);
782 s.chop(0,40);
783 CPPUNIT_ASSERT_EQUAL(s2,s);
784 }
785
786 void
787 testSBuf::testStringOps()
788 {
789 SBuf sng(ToLower(literal)),
790 ref("the quick brown fox jumped over the lazy dog");
791 CPPUNIT_ASSERT_EQUAL(ref,sng);
792 sng=literal;
793 CPPUNIT_ASSERT_EQUAL(0,sng.compare(ref,caseInsensitive));
794 // max-size comparison
795 CPPUNIT_ASSERT_EQUAL(0,ref.compare(SBuf("THE"),caseInsensitive,3));
796 CPPUNIT_ASSERT_EQUAL(1,ref.compare(SBuf("THE"),caseInsensitive,6));
797 CPPUNIT_ASSERT_EQUAL(0,SBuf("the").compare(SBuf("THE"),caseInsensitive,6));
798 }
799
800 void
801 testSBuf::testGrow()
802 {
803 SBuf t;
804 t.assign("foo");
805 const char *ref=t.rawContent();
806 t.reserveCapacity(10240);
807 const char *match=t.rawContent();
808 CPPUNIT_ASSERT(match!=ref);
809 ref=match;
810 t.append(literal).append(literal).append(literal).append(literal).append(literal);
811 t.append(t).append(t).append(t).append(t).append(t);
812 CPPUNIT_ASSERT_EQUAL(ref,match);
813 }
814
815 void
816 testSBuf::testReserve()
817 {
818 SBufReservationRequirements requirements;
819 // use unusual numbers to ensure we dont hit a lucky boundary situation
820 requirements.minSpace = 10;
821 requirements.idealSpace = 82;
822 requirements.maxCapacity = 259;
823 requirements.allowShared = true;
824
825 // for each possible starting buffer length within the capacity
826 for (SBuf::size_type startLength = 0; startLength <= requirements.maxCapacity; ++startLength) {
827 std::cerr << ".";
828 SBuf b;
829 b.reserveCapacity(startLength);
830 CPPUNIT_ASSERT_EQUAL(b.length(), static_cast<unsigned int>(0));
831 CPPUNIT_ASSERT_EQUAL(b.spaceSize(), startLength);
832
833 // check that it never grows outside capacity.
834 // do 5 excess cycles to check that.
835 for (SBuf::size_type filled = 0; filled < requirements.maxCapacity +5; ++filled) {
836 CPPUNIT_ASSERT_EQUAL(b.length(), min(filled, requirements.maxCapacity));
837 CPPUNIT_ASSERT(b.spaceSize() >= 0);
838 auto x = b.reserve(requirements);
839 // the amount of space advertized must not cause users to exceed capacity
840 CPPUNIT_ASSERT(x <= requirements.maxCapacity - filled);
841 CPPUNIT_ASSERT(b.spaceSize() <= requirements.maxCapacity - filled);
842 // the total size of buffer must not cause users to exceed capacity
843 CPPUNIT_ASSERT(b.length() + b.spaceSize() <= requirements.maxCapacity);
844 if (x > 0)
845 b.append('X');
846 }
847 }
848 }
849
850 void
851 testSBuf::testStartsWith()
852 {
853 static SBuf casebuf("THE QUICK");
854 CPPUNIT_ASSERT(literal.startsWith(SBuf(fox1)));
855 CPPUNIT_ASSERT(!SBuf("The quick brown").startsWith(SBuf(fox1))); //too short
856 CPPUNIT_ASSERT(!literal.startsWith(SBuf(fox2))); //different contents
857
858 // case-insensitive checks
859 CPPUNIT_ASSERT(literal.startsWith(casebuf,caseInsensitive));
860 casebuf=ToUpper(SBuf(fox1));
861 CPPUNIT_ASSERT(literal.startsWith(casebuf,caseInsensitive));
862 CPPUNIT_ASSERT(literal.startsWith(SBuf(fox1),caseInsensitive));
863 casebuf = "tha quick";
864 CPPUNIT_ASSERT_EQUAL(false,literal.startsWith(casebuf,caseInsensitive));
865 }
866
867 void
868 testSBuf::testSBufStream()
869 {
870 SBuf b("const.string, int 10 and a float 10.5");
871 SBufStream ss;
872 ss << "const.string, int " << 10 << " and a float " << 10.5;
873 SBuf o=ss.buf();
874 CPPUNIT_ASSERT_EQUAL(b,o);
875 ss.clearBuf();
876 o=ss.buf();
877 CPPUNIT_ASSERT_EQUAL(SBuf(),o);
878 SBuf f1(fox1);
879 SBufStream ss2(f1);
880 ss2 << fox2;
881 CPPUNIT_ASSERT_EQUAL(ss2.buf(),literal);
882 CPPUNIT_ASSERT_EQUAL(f1,SBuf(fox1));
883 }
884
885 void
886 testSBuf::testFindFirstOf()
887 {
888 SBuf haystack(literal);
889 SBuf::size_type idx;
890
891 // not found
892 idx=haystack.findFirstOf(CharacterSet("t1","ADHRWYP"));
893 CPPUNIT_ASSERT_EQUAL(SBuf::npos,idx);
894
895 // found at beginning
896 idx=haystack.findFirstOf(CharacterSet("t2","THANDF"));
897 CPPUNIT_ASSERT_EQUAL(0U,idx);
898
899 //found at end of haystack
900 idx=haystack.findFirstOf(CharacterSet("t3","QWERYVg"));
901 CPPUNIT_ASSERT_EQUAL(haystack.length()-1,idx);
902
903 //found in the middle of haystack
904 idx=haystack.findFirstOf(CharacterSet("t4","QWERqYV"));
905 CPPUNIT_ASSERT_EQUAL(4U,idx);
906 }
907
908 void
909 testSBuf::testFindFirstNotOf()
910 {
911 SBuf haystack(literal);
912 SBuf::size_type idx;
913
914 // all chars from the set
915 idx=haystack.findFirstNotOf(CharacterSet("t1",literal.c_str()));
916 CPPUNIT_ASSERT_EQUAL(SBuf::npos,idx);
917
918 // found at beginning
919 idx=haystack.findFirstNotOf(CharacterSet("t2","a"));
920 CPPUNIT_ASSERT_EQUAL(0U,idx);
921
922 //found at end of haystack
923 idx=haystack.findFirstNotOf(CharacterSet("t3",literal.substr(0,literal.length()-1).c_str()));
924 CPPUNIT_ASSERT_EQUAL(haystack.length()-1,idx);
925
926 //found in the middle of haystack
927 idx=haystack.findFirstNotOf(CharacterSet("t4","The"));
928 CPPUNIT_ASSERT_EQUAL(3U,idx);
929 }
930
931 void
932 testSBuf::testAutoFind()
933 {
934 SBufFindTest test;
935 test.run();
936 }
937
938 void
939 testSBuf::testStdStringOps()
940 {
941 const char *alphabet="abcdefghijklmnopqrstuvwxyz";
942 std::string astr(alphabet);
943 SBuf sb(alphabet);
944 CPPUNIT_ASSERT_EQUAL(astr,sb.toStdString());
945 }
946
947 void
948 testSBuf::testIterators()
949 {
950 SBuf text("foo"), text2("foo");
951 CPPUNIT_ASSERT(text.begin() == text.begin());
952 CPPUNIT_ASSERT(text.begin() != text.end());
953 CPPUNIT_ASSERT(text.begin() != text2.begin());
954 {
955 auto i = text.begin();
956 auto e = text.end();
957 CPPUNIT_ASSERT_EQUAL('f', *i);
958 CPPUNIT_ASSERT(i != e);
959 ++i;
960 CPPUNIT_ASSERT_EQUAL('o', *i);
961 CPPUNIT_ASSERT(i != e);
962 ++i;
963 CPPUNIT_ASSERT_EQUAL('o', *i);
964 CPPUNIT_ASSERT(i != e);
965 ++i;
966 CPPUNIT_ASSERT(i == e);
967 }
968 {
969 auto i = text.rbegin();
970 auto e = text.rend();
971 CPPUNIT_ASSERT_EQUAL('o', *i);
972 CPPUNIT_ASSERT(i != e);
973 ++i;
974 CPPUNIT_ASSERT_EQUAL('o', *i);
975 CPPUNIT_ASSERT(i != e);
976 ++i;
977 CPPUNIT_ASSERT_EQUAL('f', *i);
978 CPPUNIT_ASSERT(i != e);
979 ++i;
980 CPPUNIT_ASSERT(i == e);
981 }
982 }
983
984 void
985 testSBuf::testSBufHash()
986 {
987 // same SBuf must have same hash
988 auto hasher=std::hash<SBuf>();
989 CPPUNIT_ASSERT_EQUAL(hasher(literal),hasher(literal));
990
991 // same content must have same hash
992 CPPUNIT_ASSERT_EQUAL(hasher(literal),hasher(SBuf(fox)));
993 CPPUNIT_ASSERT_EQUAL(hasher(SBuf(fox)),hasher(SBuf(fox)));
994
995 //differen content should have different hash
996 CPPUNIT_ASSERT(hasher(SBuf(fox)) != hasher(SBuf(fox1)));
997
998 {
999 std::unordered_map<SBuf, int> um;
1000 um[SBuf("one")] = 1;
1001 um[SBuf("two")] = 2;
1002
1003 auto i = um.find(SBuf("one"));
1004 CPPUNIT_ASSERT(i != um.end());
1005 CPPUNIT_ASSERT(i->second == 1);
1006
1007 i = um.find(SBuf("eleventy"));
1008 CPPUNIT_ASSERT(i == um.end());
1009 }
1010 }
1011