]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/tests/SBufFindTest.cc
2 #include "base/CharacterSet.h"
3 #include "SBufFindTest.h"
4 #include <cppunit/extensions/HelperMacros.h>
5 #include <cppunit/Message.h>
8 /* TODO: The whole SBufFindTest class is currently implemented as a single
9 CppUnit test case (because we do not want to register and report every one
10 of the thousands of generated test cases). Is there a better way to
11 integrate with CppUnit?
14 SBufFindTest::SBufFindTest():
15 caseLimit(std::numeric_limits
<int>::max()),
16 errorLimit(std::numeric_limits
<int>::max()),
21 thePlacement(placeEof
),
41 for (SBuf::size_type hayLen
= 0U; hayLen
<= maxHayLength
; nextLen(hayLen
, maxHayLength
)) {
42 const SBuf cleanHay
= RandomSBuf(hayLen
);
44 const SBuf::size_type maxNeedleLen
= hayLen
+ 10;
45 for (SBuf::size_type needleLen
= 0U; needleLen
<= maxNeedleLen
; nextLen(needleLen
, maxNeedleLen
)) {
46 theSBufNeedle
= RandomSBuf(needleLen
);
48 for (int i
= 0; i
< placeEof
; i
++) {
49 thePlacement
= Placement(i
);
50 placeNeedle(cleanHay
);
52 const SBuf::size_type maxArg
=
53 max(theSBufHay
.length(), theSBufNeedle
.length()) + 10;
54 for (thePos
= 0; thePos
<= maxArg
; nextLen(thePos
, maxArg
))
57 // the special npos value is not tested as the behavior is
58 // different from std::string (where the behavior is undefined)
59 // It is ad-hoc tested in testSBuf instead
60 //thePos = SBuf::npos;
67 std::cerr
<< "Generated SBuf test cases: " << caseCount
<< std::endl
;
68 std::cerr
<< "\tfailed cases: " << errorCount
<< std::endl
;
69 std::cerr
<< "\treported cases: " << reportCount
<< std::endl
;
70 std::cerr
<< "Asserting because some cases failed..." << std::endl
;
71 CPPUNIT_ASSERT(!SBufFindTest::errorCount
);
75 /// tests SBuf::find(string needle)
77 SBufFindTest::testFindDefs()
79 theFindString
= theBareNeedlePos
= theStringHay
.find(theStringNeedle
);
80 theFindSBuf
= theSBufHay
.find(theSBufNeedle
);
84 /// tests SBuf::rfind(string needle)
86 SBufFindTest::testRFindDefs()
88 theFindString
= theBareNeedlePos
= theStringHay
.rfind(theStringNeedle
);
89 theFindSBuf
= theSBufHay
.rfind(theSBufNeedle
);
90 checkResults("rfind");
93 /// tests SBuf::find(string needle, pos)
95 SBufFindTest::testFind()
97 theFindString
= theStringHay
.find(theStringNeedle
, thePos
);
98 theBareNeedlePos
= theStringHay
.find(theStringNeedle
);
99 theFindSBuf
= theSBufHay
.find(theSBufNeedle
, thePos
);
100 checkResults("find");
103 /// tests SBuf::findFirstOf(string needle, pos)
105 SBufFindTest::testFindFirstOf()
107 theFindString
= theStringHay
.find_first_of(theStringNeedle
, thePos
);
108 theBareNeedlePos
= theStringHay
.find_first_of(theStringNeedle
);
109 theFindSBuf
= theSBufHay
.findFirstOf(CharacterSet("cs",theSBufNeedle
.c_str()), thePos
);
110 checkResults("find_first_of");
113 /// tests SBuf::rfind(string needle, pos)
115 SBufFindTest::testRFind()
117 theFindString
= theStringHay
.rfind(theStringNeedle
, thePos
);
118 theBareNeedlePos
= theStringHay
.rfind(theStringNeedle
);
119 theFindSBuf
= theSBufHay
.rfind(theSBufNeedle
, thePos
);
120 checkResults("rfind");
123 /// tests SBuf::find(char needle)
125 SBufFindTest::testFindCharDefs()
127 const char c
= theStringNeedle
[0];
128 theFindString
= theBareNeedlePos
= theStringHay
.find(c
);
129 theFindSBuf
= theSBufHay
.find(c
);
130 checkResults("find");
133 /// tests SBuf::find(char needle, pos)
135 SBufFindTest::testFindChar()
137 const char c
= theStringNeedle
[0];
138 theFindString
= theStringHay
.find(c
, thePos
);
139 theBareNeedlePos
= theStringHay
.find(c
);
140 theFindSBuf
= theSBufHay
.find(c
, thePos
);
141 checkResults("find");
144 /// tests SBuf::rfind(char needle)
146 SBufFindTest::testRFindCharDefs()
148 const char c
= theStringNeedle
[0];
149 theFindString
= theBareNeedlePos
= theStringHay
.rfind(c
);
150 theFindSBuf
= theSBufHay
.rfind(c
);
151 checkResults("rfind");
154 /// tests SBuf::rfind(char needle, pos)
156 SBufFindTest::testRFindChar()
158 const char c
= theStringNeedle
[0];
159 theFindString
= theStringHay
.rfind(c
, thePos
);
160 theBareNeedlePos
= theStringHay
.rfind(c
);
161 theFindSBuf
= theSBufHay
.rfind(c
, thePos
);
162 checkResults("rfind");
165 /// whether the last SBuf and std::string find() results are the same
167 SBufFindTest::resultsMatch() const
169 // this method is needed because SBuf and std::string use different
170 // size_types (and npos values); comparing the result values directly
171 // would lead to bugs
173 if (theFindString
== std::string::npos
&& theFindSBuf
== SBuf::npos
)
174 return true; // both npos
176 // now safe to cast a non-negative SBuf result
177 return theFindString
== static_cast<std::string::size_type
>(theFindSBuf
);
180 /// called at the end of test case to update state, detect and report failures
182 SBufFindTest::checkResults(const char *method
)
186 handleFailure(method
);
189 /// helper function to convert "printable" Type to std::string
190 template<typename Type
>
192 AnyToString(const Type
&value
)
194 std::stringstream sbuf
;
200 /// helper function to convert SBuf position to a human-friendly string
202 PosToString(const SBuf::size_type pos
)
204 return pos
== SBuf::npos
? std::string("npos") : AnyToString(pos
);
208 /// helper function to convert std::string position to a human-friendly string
210 PosToString(const std::string::size_type pos
)
212 return pos
== std::string::npos
? std::string("npos") : AnyToString(pos
);
215 /// tests each supported SBuf::*find() method using generated hay, needle, pos
217 SBufFindTest::testAllMethods()
219 theStringHay
= std::string(theSBufHay
.rawContent(), theSBufHay
.length());
220 theStringNeedle
= std::string(theSBufNeedle
.rawContent(), theSBufNeedle
.length());
221 theBareNeedlePos
= std::string::npos
;
222 const std::string reportPos
= PosToString(thePos
);
224 // always test string search
226 theReportQuote
= '"';
227 theReportNeedle
= theStringNeedle
;
233 theReportPos
= reportPos
;
239 // if possible, test char search
240 if (!theStringNeedle
.empty()) {
241 theReportQuote
= '\'';
242 theReportNeedle
= theStringNeedle
[0];
248 theReportPos
= reportPos
;
254 /// helper function to format a length-based key (part of case category string)
256 lengthKey(const std::string
&str
)
258 if (str
.length() == 0)
260 if (str
.length() == 1)
265 /// formats position key (part of the case category string)
267 SBufFindTest::posKey() const
269 // the search position does not matter if needle is not in hay
270 if (theBareNeedlePos
== std::string::npos
)
271 return std::string();
273 if (thePos
== SBuf::npos
)
276 if (thePos
< theBareNeedlePos
)
277 return ",posL"; // to the Left of the needle
279 if (thePos
== theBareNeedlePos
)
280 return ",posB"; // Beginning of the needle
282 if (thePos
< theBareNeedlePos
+ theStringNeedle
.length())
283 return ",posM"; // in the Middle of the needle
285 if (thePos
== theBareNeedlePos
+ theStringNeedle
.length())
286 return ",posE"; // at the End of the needle
288 if (thePos
< theStringHay
.length())
289 return ",posR"; // to the Right of the needle
291 return ",posP"; // past the hay
294 /// formats placement key (part of the case category string)
296 SBufFindTest::placementKey() const
298 // Ignore thePlacement because theBareNeedlePos covers it better: we may
299 // try to place the needle somewhere, but hay limits the actual placement.
301 // the placent does not matter if needle is not in hay
302 if (theBareNeedlePos
== std::string::npos
)
303 return std::string();
305 if (theBareNeedlePos
== 0)
306 return "@B"; // at the beggining of the hay string
307 if (theBareNeedlePos
== theStringHay
.length()-theStringNeedle
.length())
308 return "@E"; // at the end of the hay string
309 return "@M"; // in the "middle" of the hay string
312 /// called when a test case fails; counts and possibly reports the failure
314 SBufFindTest::handleFailure(const char *method
)
316 // line break after "........." printed for previous tests
318 std::cerr
<< std::endl
;
322 if (errorCount
> errorLimit
) {
323 std::cerr
<< "Will stop generating SBuf test cases because the " <<
324 "number of failed ones is over the limit: " << errorCount
<<
325 " (after " << caseCount
<< " test cases)" << std::endl
;
326 CPPUNIT_ASSERT(errorCount
<= errorLimit
);
330 // format test case category; category allows us to hush failure reports
331 // for already seen categories with failed cases (to reduce output noise)
332 std::string category
= "hay" + lengthKey(theStringHay
) +
334 if (theReportQuote
== '"')
335 category
+= "needle" + lengthKey(theStringNeedle
);
338 category
+= placementKey();
339 category
+= posKey();
343 if (failedCats
.find(category
) != failedCats
.end())
344 return; // do not report another similar test case failure
345 failedCats
.insert(category
);
348 std::string reportPos
= theReportPos
;
349 if (!reportPos
.empty())
350 reportPos
= ", " + reportPos
;
352 std::cerr
<< "case" << caseCount
<< ": " <<
353 "SBuf(\"" << theStringHay
<< "\")." << method
<<
354 "(" << theReportQuote
<< theReportNeedle
<< theReportQuote
<<
355 reportPos
<< ") returns " << PosToString(theFindSBuf
) <<
356 " instead of " << PosToString(theFindString
) <<
358 " std::string(\"" << theStringHay
<< "\")." << method
<<
359 "(" << theReportQuote
<< theReportNeedle
<< theReportQuote
<<
360 reportPos
<< ") returns " << PosToString(theFindString
) <<
362 " category: " << category
<< std::endl
;
367 /// generates a random string of the specified length
369 SBufFindTest::RandomSBuf(const int length
)
371 static const char characters
[] =
373 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
374 "abcdefghijklomnpqrstuvwxyz";
375 // sizeof() counts the terminating zero at the end of characters
376 // TODO: add \0 character (needs reporting adjustments to print it as \0)
377 static const size_t charCount
= sizeof(characters
)-1;
380 for (int i
= 0; i
< length
; ++i
) {
381 const unsigned int pos
= random() % charCount
;
382 assert(pos
< sizeof(characters
));
383 assert(characters
[pos
] > 32);
384 buf
[i
] = characters
[random() % charCount
];
387 return SBuf(buf
, length
);
390 /// increments len to quickly cover [0, max] range, slowing down in risky areas
391 /// jumps to max+1 if caseLimit is reached
393 SBufFindTest::nextLen(SBuf::size_type
&len
, const SBuf::size_type max
)
397 if (caseCount
>= caseLimit
)
398 len
= max
+1; // avoid future test cases
400 ++len
; // move slowly at the beginning of the [0,max] range
401 else if (len
>= max
- 10)
402 ++len
; // move slowly at the end of the [0,max] range
404 // move fast in the middle of the [0,max] range
407 // but do not overshoot the interesting area at the end of the range
413 /// Places the needle into the hay using cleanHay as a starting point.
415 SBufFindTest::placeNeedle(const SBuf
&cleanHay
)
417 // For simplicity, we do not overwrite clean hay characters but use them as
418 // needle suffix and/or prefix. Should not matter since hay length varies?
420 // TODO: support two needles per hay (explicitly)
421 // TODO: better handle cases where clean hay already contains needle
422 switch (thePlacement
) {
424 theSBufHay
.assign(theSBufNeedle
).append(cleanHay
);
428 const SBuf firstHalf
= cleanHay
.substr(0, cleanHay
.length()/2);
429 const SBuf secondHalf
= cleanHay
.substr(cleanHay
.length()/2);
430 theSBufHay
.assign(firstHalf
).append(theSBufNeedle
).append(secondHalf
);
435 theSBufHay
.assign(cleanHay
).append(theSBufNeedle
);
439 theSBufHay
.assign(cleanHay
);
443 assert(false); // should not happen