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