]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/tests/SBufFindTest.cc
a63faf5bf256b2f8fa421cd329d8aa2238418d28
2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
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.
10 #include "base/CharacterSet.h"
11 #include "base/Random.h"
12 #include "tests/SBufFindTest.h"
14 #include <cppunit/extensions/HelperMacros.h>
15 #include <cppunit/Message.h>
18 /* TODO: The whole SBufFindTest class is currently implemented as a single
19 CppUnit test case (because we do not want to register and report every one
20 of the thousands of generated test cases). Is there a better way to
21 integrate with CppUnit?
24 SBufFindTest::SBufFindTest():
25 caseLimit(std::numeric_limits
<int>::max()),
26 errorLimit(std::numeric_limits
<int>::max()),
30 thePlacement(placeEof
),
47 for (SBuf::size_type hayLen
= 0U; hayLen
<= maxHayLength
; nextLen(hayLen
, maxHayLength
)) {
48 const SBuf cleanHay
= RandomSBuf(hayLen
);
50 const SBuf::size_type maxNeedleLen
= hayLen
+ 10;
51 for (SBuf::size_type needleLen
= 0U; needleLen
<= maxNeedleLen
; nextLen(needleLen
, maxNeedleLen
)) {
52 theSBufNeedle
= RandomSBuf(needleLen
);
54 for (int i
= 0; i
< placeEof
; i
++) {
55 thePlacement
= Placement(i
);
56 placeNeedle(cleanHay
);
58 const SBuf::size_type maxArg
=
59 max(theSBufHay
.length(), theSBufNeedle
.length()) + 10;
60 for (thePos
= 0; thePos
<= maxArg
; nextLen(thePos
, maxArg
))
63 // the special npos value is not tested as the behavior is
64 // different from std::string (where the behavior is undefined)
65 // It is ad-hoc tested in testSBuf instead
66 //thePos = SBuf::npos;
73 std::cerr
<< "Generated SBuf test cases: " << caseCount
<< std::endl
;
74 std::cerr
<< "\tfailed cases: " << errorCount
<< std::endl
;
75 std::cerr
<< "\treported cases: " << reportCount
<< std::endl
;
76 std::cerr
<< "Asserting because some cases failed..." << std::endl
;
77 CPPUNIT_ASSERT(!SBufFindTest::errorCount
);
81 /// tests SBuf::find(string needle)
83 SBufFindTest::testFindDefs()
85 theFindString
= theBareNeedlePos
= theStringHay
.find(theStringNeedle
);
86 theFindSBuf
= theSBufHay
.find(theSBufNeedle
);
90 /// tests SBuf::rfind(string needle)
92 SBufFindTest::testRFindDefs()
94 theFindString
= theBareNeedlePos
= theStringHay
.rfind(theStringNeedle
);
95 theFindSBuf
= theSBufHay
.rfind(theSBufNeedle
);
96 checkResults("rfind");
99 /// tests SBuf::find(string needle, pos)
101 SBufFindTest::testFind()
103 theFindString
= theStringHay
.find(theStringNeedle
, thePos
);
104 theBareNeedlePos
= theStringHay
.find(theStringNeedle
);
105 theFindSBuf
= theSBufHay
.find(theSBufNeedle
, thePos
);
106 checkResults("find");
109 /// tests SBuf::findFirstOf(string needle, pos)
111 SBufFindTest::testFindFirstOf()
113 theFindString
= theStringHay
.find_first_of(theStringNeedle
, thePos
);
114 theBareNeedlePos
= theStringHay
.find_first_of(theStringNeedle
);
115 theFindSBuf
= theSBufHay
.findFirstOf(CharacterSet("cs",theSBufNeedle
.c_str()), thePos
);
116 checkResults("find_first_of");
119 /// tests SBuf::rfind(string needle, pos)
121 SBufFindTest::testRFind()
123 theFindString
= theStringHay
.rfind(theStringNeedle
, thePos
);
124 theBareNeedlePos
= theStringHay
.rfind(theStringNeedle
);
125 theFindSBuf
= theSBufHay
.rfind(theSBufNeedle
, thePos
);
126 checkResults("rfind");
129 /// tests SBuf::find(char needle)
131 SBufFindTest::testFindCharDefs()
133 const char c
= theStringNeedle
[0];
134 theFindString
= theBareNeedlePos
= theStringHay
.find(c
);
135 theFindSBuf
= theSBufHay
.find(c
);
136 checkResults("find");
139 /// tests SBuf::find(char needle, pos)
141 SBufFindTest::testFindChar()
143 const char c
= theStringNeedle
[0];
144 theFindString
= theStringHay
.find(c
, thePos
);
145 theBareNeedlePos
= theStringHay
.find(c
);
146 theFindSBuf
= theSBufHay
.find(c
, thePos
);
147 checkResults("find");
150 /// tests SBuf::rfind(char needle)
152 SBufFindTest::testRFindCharDefs()
154 const char c
= theStringNeedle
[0];
155 theFindString
= theBareNeedlePos
= theStringHay
.rfind(c
);
156 theFindSBuf
= theSBufHay
.rfind(c
);
157 checkResults("rfind");
160 /// tests SBuf::rfind(char needle, pos)
162 SBufFindTest::testRFindChar()
164 const char c
= theStringNeedle
[0];
165 theFindString
= theStringHay
.rfind(c
, thePos
);
166 theBareNeedlePos
= theStringHay
.rfind(c
);
167 theFindSBuf
= theSBufHay
.rfind(c
, thePos
);
168 checkResults("rfind");
171 /// whether the last SBuf and std::string find() results are the same
173 SBufFindTest::resultsMatch() const
175 // this method is needed because SBuf and std::string use different
176 // size_types (and npos values); comparing the result values directly
177 // would lead to bugs
179 if (theFindString
== std::string::npos
&& theFindSBuf
== SBuf::npos
)
180 return true; // both npos
182 // now safe to cast a non-negative SBuf result
183 return theFindString
== static_cast<std::string::size_type
>(theFindSBuf
);
186 /// called at the end of test case to update state, detect and report failures
188 SBufFindTest::checkResults(const char *method
)
192 handleFailure(method
);
195 /// helper function to convert "printable" Type to std::string
196 template<typename Type
>
198 AnyToString(const Type
&value
)
200 std::stringstream sbuf
;
205 /// helper function to convert std::string position to a human-friendly string
207 PosToString(const std::string::size_type pos
)
209 return pos
== std::string::npos
? std::string("npos") : AnyToString(pos
);
212 /// tests each supported SBuf::*find() method using generated hay, needle, pos
214 SBufFindTest::testAllMethods()
216 theStringHay
= std::string(theSBufHay
.rawContent(), theSBufHay
.length());
217 theStringNeedle
= std::string(theSBufNeedle
.rawContent(), theSBufNeedle
.length());
218 theBareNeedlePos
= std::string::npos
;
219 const std::string reportPos
= PosToString(thePos
);
221 // always test string search
223 theReportQuote
= '"';
224 theReportNeedle
= theStringNeedle
;
230 theReportPos
= reportPos
;
236 // if possible, test char search
237 if (!theStringNeedle
.empty()) {
238 theReportQuote
= '\'';
239 theReportNeedle
= theStringNeedle
[0];
245 theReportPos
= reportPos
;
251 /// helper function to format a length-based key (part of case category string)
253 lengthKey(const std::string
&str
)
255 if (str
.length() == 0)
257 if (str
.length() == 1)
262 /// formats position key (part of the case category string)
264 SBufFindTest::posKey() const
266 // the search position does not matter if needle is not in hay
267 if (theBareNeedlePos
== std::string::npos
)
268 return std::string();
270 if (thePos
== SBuf::npos
)
273 if (thePos
< theBareNeedlePos
)
274 return ",posL"; // to the Left of the needle
276 if (thePos
== theBareNeedlePos
)
277 return ",posB"; // Beginning of the needle
279 if (thePos
< theBareNeedlePos
+ theStringNeedle
.length())
280 return ",posM"; // in the Middle of the needle
282 if (thePos
== theBareNeedlePos
+ theStringNeedle
.length())
283 return ",posE"; // at the End of the needle
285 if (thePos
< theStringHay
.length())
286 return ",posR"; // to the Right of the needle
288 return ",posP"; // past the hay
291 /// formats placement key (part of the case category string)
293 SBufFindTest::placementKey() const
295 // Ignore thePlacement because theBareNeedlePos covers it better: we may
296 // try to place the needle somewhere, but hay limits the actual placement.
298 // the placent does not matter if needle is not in hay
299 if (theBareNeedlePos
== std::string::npos
)
300 return std::string();
302 if (theBareNeedlePos
== 0)
303 return "@B"; // at the beginning of the hay string
304 if (theBareNeedlePos
== theStringHay
.length()-theStringNeedle
.length())
305 return "@E"; // at the end of the hay string
306 return "@M"; // in the "middle" of the hay string
309 /// called when a test case fails; counts and possibly reports the failure
311 SBufFindTest::handleFailure(const char *method
)
313 // line break after "........." printed for previous tests
315 std::cerr
<< std::endl
;
319 if (errorCount
> errorLimit
) {
320 std::cerr
<< "Will stop generating SBuf test cases because the " <<
321 "number of failed ones is over the limit: " << errorCount
<<
322 " (after " << caseCount
<< " test cases)" << std::endl
;
323 CPPUNIT_ASSERT(errorCount
<= errorLimit
);
327 // format test case category; category allows us to hush failure reports
328 // for already seen categories with failed cases (to reduce output noise)
329 std::string category
= "hay" + lengthKey(theStringHay
) +
331 if (theReportQuote
== '"')
332 category
+= "needle" + lengthKey(theStringNeedle
);
335 category
+= placementKey();
336 category
+= posKey();
340 if (failedCats
.find(category
) != failedCats
.end())
341 return; // do not report another similar test case failure
342 failedCats
.insert(category
);
345 std::string reportPos
= theReportPos
;
346 if (!reportPos
.empty())
347 reportPos
= ", " + reportPos
;
349 std::cerr
<< "case" << caseCount
<< ": " <<
350 "SBuf(\"" << theStringHay
<< "\")." << method
<<
351 "(" << theReportQuote
<< theReportNeedle
<< theReportQuote
<<
352 reportPos
<< ") returns " << PosToString(theFindSBuf
) <<
353 " instead of " << PosToString(theFindString
) <<
355 " std::string(\"" << theStringHay
<< "\")." << method
<<
356 "(" << theReportQuote
<< theReportNeedle
<< theReportQuote
<<
357 reportPos
<< ") returns " << PosToString(theFindString
) <<
359 " category: " << category
<< std::endl
;
364 /// generates a random string of the specified length
366 SBufFindTest::RandomSBuf(const int length
)
368 static const char characters
[] =
370 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
371 "abcdefghijklomnpqrstuvwxyz";
373 static std::mt19937
mt(RandomSeed32());
375 // sizeof() counts the terminating zero at the end of characters
376 // and the distribution is an 'inclusive' value range, so -2
377 // TODO: add \0 character (needs reporting adjustments to print it as \0)
378 static std::uniform_int_distribution
<uint8_t> dist(0, sizeof(characters
)-2);
381 buf
.reserveCapacity(length
);
382 for (int i
= 0; i
< length
; ++i
)
383 buf
.append(characters
[dist(mt
)]);
387 /// increments len to quickly cover [0, max] range, slowing down in risky areas
388 /// jumps to max+1 if caseLimit is reached
390 SBufFindTest::nextLen(SBuf::size_type
&len
, const SBuf::size_type max
)
394 if (caseCount
>= caseLimit
)
395 len
= max
+1; // avoid future test cases
397 ++len
; // move slowly at the beginning of the [0,max] range
398 else if (len
>= max
- 10)
399 ++len
; // move slowly at the end of the [0,max] range
401 // move fast in the middle of the [0,max] range
404 // but do not overshoot the interesting area at the end of the range
410 /// Places the needle into the hay using cleanHay as a starting point.
412 SBufFindTest::placeNeedle(const SBuf
&cleanHay
)
414 // For simplicity, we do not overwrite clean hay characters but use them as
415 // needle suffix and/or prefix. Should not matter since hay length varies?
417 // TODO: support two needles per hay (explicitly)
418 // TODO: better handle cases where clean hay already contains needle
419 switch (thePlacement
) {
421 theSBufHay
.assign(theSBufNeedle
).append(cleanHay
);
425 const SBuf firstHalf
= cleanHay
.substr(0, cleanHay
.length()/2);
426 const SBuf secondHalf
= cleanHay
.substr(cleanHay
.length()/2);
427 theSBufHay
.assign(firstHalf
).append(theSBufNeedle
).append(secondHalf
);
432 theSBufHay
.assign(cleanHay
).append(theSBufNeedle
);
436 theSBufHay
.assign(cleanHay
);
440 assert(false); // should not happen