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