1 // Copyright (C) 2011-2020 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <exceptions/exceptions.h>
10 #include <util/strutil.h>
11 #include <util/encode/hex.h>
12 
13 #include <gtest/gtest.h>
14 
15 #include <stdint.h>
16 #include <string>
17 
18 using namespace isc;
19 using namespace isc::util;
20 using namespace isc::util::str;
21 using namespace std;
22 
23 namespace {
24 
25 // Check for slash replacement
26 
TEST(StringUtilTest,Slash)27 TEST(StringUtilTest, Slash) {
28 
29     string instring = "";
30     isc::util::str::normalizeSlash(instring);
31     EXPECT_EQ("", instring);
32 
33     instring = "C:\\A\\B\\C.D";
34     isc::util::str::normalizeSlash(instring);
35     EXPECT_EQ("C:/A/B/C.D", instring);
36 
37     instring = "// \\ //";
38     isc::util::str::normalizeSlash(instring);
39     EXPECT_EQ("// / //", instring);
40 }
41 
42 // Check that leading and trailing space trimming works
43 
TEST(StringUtilTest,Trim)44 TEST(StringUtilTest, Trim) {
45 
46     // Empty and full string.
47     EXPECT_EQ("", isc::util::str::trim(""));
48     EXPECT_EQ("abcxyz", isc::util::str::trim("abcxyz"));
49 
50     // Trim right-most blanks
51     EXPECT_EQ("ABC", isc::util::str::trim("ABC   "));
52     EXPECT_EQ("ABC", isc::util::str::trim("ABC\t\t  \n\t"));
53 
54     // Left-most blank trimming
55     EXPECT_EQ("XYZ", isc::util::str::trim("  XYZ"));
56     EXPECT_EQ("XYZ", isc::util::str::trim("\t\t  \tXYZ"));
57 
58     // Right and left, with embedded spaces
59     EXPECT_EQ("MN \t OP", isc::util::str::trim("\t\tMN \t OP \t"));
60 }
61 
62 // Check tokenization.  Note that ASSERT_EQ is used to check the size of the
63 // returned vector; if not as expected, the following references may be invalid
64 // so should not be used.
65 
TEST(StringUtilTest,Tokens)66 TEST(StringUtilTest, Tokens) {
67     vector<string>  result;
68 
69     // Default delimiters
70 
71     // Degenerate cases
72     result = isc::util::str::tokens("");          // Empty string
73     EXPECT_EQ(0, result.size());
74 
75     result = isc::util::str::tokens(" \n ");      // String is all delimiters
76     EXPECT_EQ(0, result.size());
77 
78     result = isc::util::str::tokens("abc");       // String has no delimiters
79     ASSERT_EQ(1, result.size());
80     EXPECT_EQ(string("abc"), result[0]);
81 
82     // String containing leading and/or trailing delimiters, no embedded ones.
83     result = isc::util::str::tokens("\txyz");     // One leading delimiter
84     ASSERT_EQ(1, result.size());
85     EXPECT_EQ(string("xyz"), result[0]);
86 
87     result = isc::util::str::tokens("\t \nxyz");  // Multiple leading delimiters
88     ASSERT_EQ(1, result.size());
89     EXPECT_EQ(string("xyz"), result[0]);
90 
91     result = isc::util::str::tokens("xyz\n");     // One trailing delimiter
92     ASSERT_EQ(1, result.size());
93     EXPECT_EQ(string("xyz"), result[0]);
94 
95     result = isc::util::str::tokens("xyz  \t");   // Multiple trailing
96     ASSERT_EQ(1, result.size());
97     EXPECT_EQ(string("xyz"), result[0]);
98 
99     result = isc::util::str::tokens("\t xyz \n"); // Leading and trailing
100     ASSERT_EQ(1, result.size());
101     EXPECT_EQ(string("xyz"), result[0]);
102 
103     // Embedded delimiters
104     result = isc::util::str::tokens("abc\ndef");  // 2 tokens, one separator
105     ASSERT_EQ(2, result.size());
106     EXPECT_EQ(string("abc"), result[0]);
107     EXPECT_EQ(string("def"), result[1]);
108 
109     result = isc::util::str::tokens("abc\t\t\ndef");  // 2 tokens, 3 separators
110     ASSERT_EQ(2, result.size());
111     EXPECT_EQ(string("abc"), result[0]);
112     EXPECT_EQ(string("def"), result[1]);
113 
114     result = isc::util::str::tokens("abc\n  \tdef\t\tghi");
115     ASSERT_EQ(3, result.size());                // Multiple tokens, many delims
116     EXPECT_EQ(string("abc"), result[0]);
117     EXPECT_EQ(string("def"), result[1]);
118     EXPECT_EQ(string("ghi"), result[2]);
119 
120     // Embedded and non-embedded delimiters
121 
122     result = isc::util::str::tokens("\t\t  \nabc\n  \tdef\t\tghi   \n\n");
123     ASSERT_EQ(3, result.size());                // Multiple tokens, many delims
124     EXPECT_EQ(string("abc"), result[0]);
125     EXPECT_EQ(string("def"), result[1]);
126     EXPECT_EQ(string("ghi"), result[2]);
127 
128     // Non-default delimiter
129     result = isc::util::str::tokens("alpha/beta/ /gamma//delta/epsilon/", "/");
130     ASSERT_EQ(6, result.size());
131     EXPECT_EQ(string("alpha"), result[0]);
132     EXPECT_EQ(string("beta"), result[1]);
133     EXPECT_EQ(string(" "), result[2]);
134     EXPECT_EQ(string("gamma"), result[3]);
135     EXPECT_EQ(string("delta"), result[4]);
136     EXPECT_EQ(string("epsilon"), result[5]);
137 
138     // Non-default delimiters (plural)
139     result = isc::util::str::tokens("+*--alpha*beta+ -gamma**delta+epsilon-+**",
140         "*+-");
141     ASSERT_EQ(6, result.size());
142     EXPECT_EQ(string("alpha"), result[0]);
143     EXPECT_EQ(string("beta"), result[1]);
144     EXPECT_EQ(string(" "), result[2]);
145     EXPECT_EQ(string("gamma"), result[3]);
146     EXPECT_EQ(string("delta"), result[4]);
147     EXPECT_EQ(string("epsilon"), result[5]);
148 
149     // Escaped delimiter
150     result = isc::util::str::tokens("foo\\,bar", ",", true);
151     EXPECT_EQ(1, result.size());
152     EXPECT_EQ(string("foo,bar"), result[0]);
153 
154     // Escaped escape
155     result = isc::util::str::tokens("foo\\\\,bar", ",", true);
156     ASSERT_EQ(2, result.size());
157     EXPECT_EQ(string("foo\\"), result[0]);
158     EXPECT_EQ(string("bar"), result[1]);
159 
160     // Double escapes
161     result = isc::util::str::tokens("foo\\\\\\\\,\\bar", ",", true);
162     ASSERT_EQ(2, result.size());
163     EXPECT_EQ(string("foo\\\\"), result[0]);
164     EXPECT_EQ(string("\\bar"), result[1]);
165 
166     // Escaped standard character
167     result = isc::util::str::tokens("fo\\o,bar", ",", true);
168     ASSERT_EQ(2, result.size());
169     EXPECT_EQ(string("fo\\o"), result[0]);
170     EXPECT_EQ(string("bar"), result[1]);
171 
172     // Escape at the end
173     result = isc::util::str::tokens("foo,bar\\", ",", true);
174     ASSERT_EQ(2, result.size());
175     EXPECT_EQ(string("foo"), result[0]);
176     EXPECT_EQ(string("bar\\"), result[1]);
177 
178     // Escape opening a token
179     result = isc::util::str::tokens("foo,\\,,bar", ",", true);
180     ASSERT_EQ(3, result.size());
181     EXPECT_EQ(string("foo"), result[0]);
182     EXPECT_EQ(string(","), result[1]);
183     EXPECT_EQ(string("bar"), result[2]);
184 }
185 
186 // Changing case
187 
TEST(StringUtilTest,ChangeCase)188 TEST(StringUtilTest, ChangeCase) {
189     string mixed("abcDEFghiJKLmno123[]{=+--+]}");
190     string upper("ABCDEFGHIJKLMNO123[]{=+--+]}");
191     string lower("abcdefghijklmno123[]{=+--+]}");
192 
193     string test = mixed;
194     isc::util::str::lowercase(test);
195     EXPECT_EQ(lower, test);
196 
197     test = mixed;
198     isc::util::str::uppercase(test);
199     EXPECT_EQ(upper, test);
200 }
201 
202 // Formatting
203 
TEST(StringUtilTest,Formatting)204 TEST(StringUtilTest, Formatting) {
205 
206     vector<string> args;
207     args.push_back("arg1");
208     args.push_back("arg2");
209     args.push_back("arg3");
210 
211     string format1 = "This is a string with no tokens";
212     EXPECT_EQ(format1, isc::util::str::format(format1, args));
213 
214     string format2 = "";    // Empty string
215     EXPECT_EQ(format2, isc::util::str::format(format2, args));
216 
217     string format3 = "   ";    // Empty string
218     EXPECT_EQ(format3, isc::util::str::format(format3, args));
219 
220     string format4 = "String with %d non-string tokens %lf";
221     EXPECT_EQ(format4, isc::util::str::format(format4, args));
222 
223     string format5 = "String with %s correct %s number of tokens %s";
224     string result5 = "String with arg1 correct arg2 number of tokens arg3";
225     EXPECT_EQ(result5, isc::util::str::format(format5, args));
226 
227     string format6 = "String with %s too %s few tokens";
228     string result6 = "String with arg1 too arg2 few tokens";
229     EXPECT_EQ(result6, isc::util::str::format(format6, args));
230 
231     string format7 = "String with %s too %s many %s tokens %s !";
232     string result7 = "String with arg1 too arg2 many arg3 tokens %s !";
233     EXPECT_EQ(result7, isc::util::str::format(format7, args));
234 
235     string format8 = "String with embedded%s%s%stokens";
236     string result8 = "String with embeddedarg1arg2arg3tokens";
237     EXPECT_EQ(result8, isc::util::str::format(format8, args));
238 
239     // Handle an empty vector
240     args.clear();
241     string format9 = "%s %s";
242     EXPECT_EQ(format9, isc::util::str::format(format9, args));
243 }
244 
TEST(StringUtilTest,getToken)245 TEST(StringUtilTest, getToken) {
246     string s("a b c");
247     istringstream ss(s);
248     EXPECT_EQ("a", isc::util::str::getToken(ss));
249     EXPECT_EQ("b", isc::util::str::getToken(ss));
250     EXPECT_EQ("c", isc::util::str::getToken(ss));
251     EXPECT_THROW(isc::util::str::getToken(ss), isc::util::str::StringTokenError);
252 }
253 
tokenToNumCall_32_16(const string & token)254 int32_t tokenToNumCall_32_16(const string& token) {
255     return isc::util::str::tokenToNum<int32_t, 16>(token);
256 }
257 
tokenToNumCall_16_8(const string & token)258 int16_t tokenToNumCall_16_8(const string& token) {
259     return isc::util::str::tokenToNum<int16_t, 8>(token);
260 }
261 
TEST(StringUtilTest,tokenToNum)262 TEST(StringUtilTest, tokenToNum) {
263     uint32_t num32 = tokenToNumCall_32_16("0");
264     EXPECT_EQ(0, num32);
265     num32 = tokenToNumCall_32_16("123");
266     EXPECT_EQ(123, num32);
267     num32 = tokenToNumCall_32_16("65535");
268     EXPECT_EQ(65535, num32);
269 
270     EXPECT_THROW(tokenToNumCall_32_16(""),
271                  isc::util::str::StringTokenError);
272     EXPECT_THROW(tokenToNumCall_32_16("a"),
273                  isc::util::str::StringTokenError);
274     EXPECT_THROW(tokenToNumCall_32_16("-1"),
275                  isc::util::str::StringTokenError);
276     EXPECT_THROW(tokenToNumCall_32_16("65536"),
277                  isc::util::str::StringTokenError);
278     EXPECT_THROW(tokenToNumCall_32_16("1234567890"),
279                  isc::util::str::StringTokenError);
280     EXPECT_THROW(tokenToNumCall_32_16("-1234567890"),
281                  isc::util::str::StringTokenError);
282 
283     uint16_t num16 = tokenToNumCall_16_8("123");
284     EXPECT_EQ(123, num16);
285     num16 = tokenToNumCall_16_8("0");
286     EXPECT_EQ(0, num16);
287     num16 = tokenToNumCall_16_8("255");
288     EXPECT_EQ(255, num16);
289 
290     EXPECT_THROW(tokenToNumCall_16_8(""),
291                  isc::util::str::StringTokenError);
292     EXPECT_THROW(tokenToNumCall_16_8("a"),
293                  isc::util::str::StringTokenError);
294     EXPECT_THROW(tokenToNumCall_16_8("-1"),
295                  isc::util::str::StringTokenError);
296     EXPECT_THROW(tokenToNumCall_16_8("256"),
297                  isc::util::str::StringTokenError);
298     EXPECT_THROW(tokenToNumCall_16_8("1234567890"),
299                  isc::util::str::StringTokenError);
300     EXPECT_THROW(tokenToNumCall_16_8("-1234567890"),
301                  isc::util::str::StringTokenError);
302 
303 }
304 
305 /// @brief Convenience function which calls quotedStringToBinary
306 /// and converts returned vector back to string.
307 ///
308 /// @param s Input string.
309 /// @return String holding a copy of a vector returned by the
310 /// quotedStringToBinary.
testQuoted(const std::string & s)311 std::string testQuoted(const std::string& s) {
312     std::vector<uint8_t> vec = str::quotedStringToBinary(s);
313     std::string s2(vec.begin(), vec.end());
314     return (s2);
315 }
316 
TEST(StringUtilTest,quotedStringToBinary)317 TEST(StringUtilTest, quotedStringToBinary) {
318     // No opening or closing quote should result in empty string.
319     EXPECT_TRUE(str::quotedStringToBinary("'").empty());
320     EXPECT_TRUE(str::quotedStringToBinary("").empty());
321     EXPECT_TRUE(str::quotedStringToBinary("  ").empty());
322     EXPECT_TRUE(str::quotedStringToBinary("'circuit id").empty());
323     EXPECT_TRUE(str::quotedStringToBinary("circuit id'").empty());
324 
325     // If there is only opening and closing quote, an empty
326     // vector should be returned.
327     EXPECT_TRUE(str::quotedStringToBinary("''").empty());
328 
329     // Both opening and ending quote is present.
330     EXPECT_EQ("circuit id", testQuoted("'circuit id'"));
331     EXPECT_EQ("remote id", testQuoted("  ' remote id'"));
332     EXPECT_EQ("duid", testQuoted("  ' duid'"));
333     EXPECT_EQ("duid", testQuoted("'duid    '  "));
334     EXPECT_EQ("remote'id", testQuoted("  ' remote'id  '"));
335     EXPECT_EQ("remote id'", testQuoted("'remote id''"));
336     EXPECT_EQ("'remote id", testQuoted("''remote id'"));
337 
338     // Multiple quotes.
339     EXPECT_EQ("'", testQuoted("'''"));
340     EXPECT_EQ("''", testQuoted("''''"));
341 }
342 
343 /// @brief Test that hex string with colons can be decoded.
344 ///
345 /// @param input Input string to be decoded.
346 /// @param reference A string without colons representing the
347 /// decoded data.
testColonSeparated(const std::string & input,const std::string & reference)348 void testColonSeparated(const std::string& input,
349                         const std::string& reference) {
350     // Create a reference vector.
351     std::vector<uint8_t> reference_vector;
352     ASSERT_NO_THROW(encode::decodeHex(reference, reference_vector));
353 
354     // Fill the output vector with some garbage to make sure that
355     // the data is erased when a string is decoded successfully.
356     std::vector<uint8_t> decoded(1, 10);
357     ASSERT_NO_THROW(decodeColonSeparatedHexString(input, decoded));
358 
359     // Get the string representation of the decoded data for logging
360     // purposes.
361     std::string encoded;
362     ASSERT_NO_THROW(encoded = encode::encodeHex(decoded));
363 
364     // Check if the decoded data matches the reference.
365     EXPECT_TRUE(decoded == reference_vector)
366         << "decoded data don't match the reference, input='"
367         << input << "', reference='" << reference << "'"
368         ", decoded='" << encoded << "'";
369 }
370 
TEST(StringUtilTest,decodeColonSeparatedHexString)371 TEST(StringUtilTest, decodeColonSeparatedHexString) {
372     // Test valid strings.
373     testColonSeparated("A1:02:C3:d4:e5:F6", "A102C3D4E5F6");
374     testColonSeparated("A:02:3:d:E5:F6", "0A02030DE5F6");
375     testColonSeparated("A:B:C:D", "0A0B0C0D");
376     testColonSeparated("1", "01");
377     testColonSeparated("1e", "1E");
378     testColonSeparated("", "");
379 
380     // Test invalid strings.
381     std::vector<uint8_t> decoded;
382     // Whitespaces.
383     EXPECT_THROW(decodeColonSeparatedHexString("   ", decoded),
384                  isc::BadValue);
385     // Whitespace before digits.
386     EXPECT_THROW(decodeColonSeparatedHexString(" A1", decoded),
387                  isc::BadValue);
388     // Two consecutive colons.
389     EXPECT_THROW(decodeColonSeparatedHexString("A::01", decoded),
390                  isc::BadValue);
391     // Three consecutive colons.
392     EXPECT_THROW(decodeColonSeparatedHexString("A:::01", decoded),
393                  isc::BadValue);
394     // Whitespace within a string.
395     EXPECT_THROW(decodeColonSeparatedHexString("A :01", decoded),
396                  isc::BadValue);
397     // Terminating colon.
398     EXPECT_THROW(decodeColonSeparatedHexString("0A:01:", decoded),
399                  isc::BadValue);
400     // Opening colon.
401     EXPECT_THROW(decodeColonSeparatedHexString(":0A:01", decoded),
402                  isc::BadValue);
403     // Three digits before the colon.
404     EXPECT_THROW(decodeColonSeparatedHexString("0A1:B1", decoded),
405                  isc::BadValue);
406 }
407 
testFormatted(const std::string & input,const std::string & reference)408 void testFormatted(const std::string& input,
409                    const std::string& reference) {
410     // Create a reference vector.
411     std::vector<uint8_t> reference_vector;
412     ASSERT_NO_THROW(encode::decodeHex(reference, reference_vector));
413 
414     // Fill the output vector with some garbage to make sure that
415     // the data is erased when a string is decoded successfully.
416     std::vector<uint8_t> decoded(1, 10);
417     ASSERT_NO_THROW(decodeFormattedHexString(input, decoded));
418 
419     // Get the string representation of the decoded data for logging
420     // purposes.
421     std::string encoded;
422     ASSERT_NO_THROW(encoded = encode::encodeHex(decoded));
423 
424     // Check if the decoded data matches the reference.
425     EXPECT_TRUE(decoded == reference_vector)
426         << "decoded data don't match the reference, input='"
427         << input << "', reference='" << reference << "'"
428         ", decoded='" << encoded << "'";
429 }
430 
TEST(StringUtilTest,decodeFormattedHexString)431 TEST(StringUtilTest, decodeFormattedHexString) {
432     // Colon separated.
433     testFormatted("1:A7:B5:4:23", "01A7B50423");
434     // Space separated.
435     testFormatted("1 A7 B5 4 23", "01A7B50423");
436     // No colons, even number of digits.
437     testFormatted("17a534", "17A534");
438     // Odd number of digits.
439     testFormatted("A3A6f78", "0A3A6F78");
440     // '0x' prefix.
441     testFormatted("0xA3A6f78", "0A3A6F78");
442     // '0x' prefix with a special value of 0.
443     testFormatted("0x0", "00");
444     // Empty string.
445     testFormatted("", "");
446 
447     std::vector<uint8_t> decoded;
448     // Dangling colon.
449     EXPECT_THROW(decodeFormattedHexString("0a:", decoded),
450                  isc::BadValue);
451     // Dangling space.
452     EXPECT_THROW(decodeFormattedHexString("0a ", decoded),
453                  isc::BadValue);
454     // '0x' prefix and spaces.
455     EXPECT_THROW(decodeFormattedHexString("x01 02", decoded),
456                  isc::BadValue);
457     // '0x' prefix and colons.
458     EXPECT_THROW(decodeFormattedHexString("0x01:02", decoded),
459                  isc::BadValue);
460     // colon and spaces mixed
461     EXPECT_THROW(decodeFormattedHexString("01:02 03", decoded),
462                  isc::BadValue);
463     // Missing colon.
464     EXPECT_THROW(decodeFormattedHexString("01:0203", decoded),
465                  isc::BadValue);
466     // Missing space.
467     EXPECT_THROW(decodeFormattedHexString("01 0203", decoded),
468                  isc::BadValue);
469     // Invalid prefix.
470     EXPECT_THROW(decodeFormattedHexString("x0102", decoded),
471                  isc::BadValue);
472     // Invalid prefix again.
473     EXPECT_THROW(decodeFormattedHexString("1x0102", decoded),
474                  isc::BadValue);
475 }
476 
477 /// @brief Function used to test StringSantitizer
478 /// @param original - string to sanitize
479 /// @param char_set - regular expression string describing invalid
480 /// characters
481 /// @param char_replacement - character(s) which replace invalid
482 /// characters
483 /// @param expected - expected sanitized string
sanitizeStringTest(const std::string & original,const std::string & char_set,const std::string & char_replacement,const std::string & expected)484 void sanitizeStringTest(
485     const std::string& original,
486     const std::string& char_set,
487     const std::string& char_replacement,
488     const std::string& expected) {
489 
490     StringSanitizerPtr ss;
491     std::string sanitized;
492 
493     try {
494         ss.reset(new StringSanitizer(char_set, char_replacement));
495     } catch (const std::exception& ex) {
496         ADD_FAILURE() << "Could not construct sanitizer:" << ex.what();
497         return;
498     }
499 
500     try {
501         sanitized = ss->scrub(original);
502     } catch (const std::exception& ex) {
503         ADD_FAILURE() << "Could not scrub string:" << ex.what();
504         return;
505     }
506 
507     EXPECT_EQ(sanitized, expected);
508 }
509 
510 // Verifies StringSantizer class
TEST(StringUtilTest,stringSanitizer)511 TEST(StringUtilTest, stringSanitizer) {
512     // Bad regular expression should throw.
513     StringSanitizerPtr ss;
514     ASSERT_THROW(ss.reset(new StringSanitizer("[bogus-regex","")), BadValue);
515 
516     std::string good_data(StringSanitizer::MAX_DATA_SIZE, '0');
517     std::string bad_data(StringSanitizer::MAX_DATA_SIZE + 1, '0');
518 
519     ASSERT_NO_THROW(ss.reset(new StringSanitizer(good_data, good_data)));
520 
521     ASSERT_THROW(ss.reset(new StringSanitizer(bad_data, "")), BadValue);
522     ASSERT_THROW(ss.reset(new StringSanitizer("", bad_data)), BadValue);
523 
524     // List of invalid chars should work: (b,c,2 are invalid)
525     sanitizeStringTest("abc.123", "[b-c2]", "*",
526                        "a**.1*3");
527     // Inverted list of valid chars should work: (b,c,2 are valid)
528     sanitizeStringTest("abc.123", "[^b-c2]", "*",
529                        "*bc**2*");
530 
531     // A string of all valid chars should return an identical string.
532     sanitizeStringTest("-_A--B__Cabc34567_-", "[^A-Ca-c3-7_-]", "x",
533                        "-_A--B__Cabc34567_-");
534 
535     // Replacing with a character should work.
536     sanitizeStringTest("A[b]c\12JoE3-_x!B$Y#e", "[^A-Za-z0-9_]", "*",
537                        "A*b*c*JoE3*_x*B*Y*e");
538 
539     // Removing (i.e.replacing with an "empty" string) should work.
540     sanitizeStringTest("A[b]c\12JoE3-_x!B$Y#e", "[^A-Za-z0-9_]", "",
541                        "AbcJoE3_xBYe");
542 
543     // More than one non-matching in a row should work.
544     sanitizeStringTest("%%A%%B%%C%%", "[^A-Za-z0-9_]", "x",
545                        "xxAxxBxxCxx");
546 
547     // Removing more than one non-matching in a row should work.
548     sanitizeStringTest("%%A%%B%%C%%", "[^A-Za-z0-9_]", "",
549                        "ABC");
550 
551     // Replacing with a string should work.
552     sanitizeStringTest("%%A%%B%%C%%", "[^A-Za-z0-9_]", "xyz",
553                        "xyzxyzAxyzxyzBxyzxyzCxyzxyz");
554 
555     // Dots as valid chars work.
556     sanitizeStringTest("abc.123", "[^A-Za-z0-9_.]", "*",
557                        "abc.123");
558 
559     std::string withNulls("\000ab\000c.12\0003",10);
560     sanitizeStringTest(withNulls, "[^A-Za-z0-9_.]", "*",
561                        "*ab*c.12*3");
562 }
563 
564 // Verifies templated buffer iterator seekTrimmed() function
TEST(StringUtilTest,seekTrimmed)565 TEST(StringUtilTest, seekTrimmed) {
566 
567     // Empty buffer should be fine.
568     std::vector<uint8_t> buffer;
569     auto begin = buffer.end();
570     auto end = buffer.end();
571     ASSERT_NO_THROW(end = seekTrimmed(begin, end, 0));
572     EXPECT_EQ(0, std::distance(begin, end));
573 
574     // Buffer of only trim values, should be fine.
575     buffer = { 1, 1 };
576     begin = buffer.begin();
577     end = buffer.end();
578     ASSERT_NO_THROW(end = seekTrimmed(begin, end, 1));
579     EXPECT_EQ(0, std::distance(begin, end));
580 
581     // One trailing null should trim off.
582     buffer = {'o', 'n', 'e', 0 };
583     begin = buffer.begin();
584     end = buffer.end();
585     ASSERT_NO_THROW(end = seekTrimmed(begin, end, 0));
586     EXPECT_EQ(3, std::distance(begin, end));
587 
588     // More than one trailing null should trim off.
589     buffer = { 't', 'h', 'r', 'e', 'e', 0, 0, 0 };
590     begin = buffer.begin();
591     end = buffer.end();
592     ASSERT_NO_THROW(end = seekTrimmed(begin, end, 0));
593     EXPECT_EQ(5, std::distance(begin, end));
594 
595     // Embedded null should be left in place.
596     buffer = { 'e', 'm', 0, 'b', 'e', 'd' };
597     begin = buffer.begin();
598     end = buffer.end();
599     ASSERT_NO_THROW(end = seekTrimmed(begin, end, 0));
600     EXPECT_EQ(6, std::distance(begin, end));
601 
602     // Leading null should be left in place.
603     buffer = { 0, 'l', 'e', 'a', 'd', 'i', 'n', 'g' };
604     begin = buffer.begin();
605     end = buffer.end();
606     ASSERT_NO_THROW(end = seekTrimmed(begin, end, 0));
607     EXPECT_EQ(8, std::distance(begin, end));
608 }
609 
610 // Verifies isPrintable predicate on strings.
TEST(StringUtilTest,stringIsPrintable)611 TEST(StringUtilTest, stringIsPrintable) {
612     string content;
613 
614     // Empty is printable.
615     EXPECT_TRUE(isPrintable(content));
616 
617     // Check Abcd.
618     content = "Abcd";
619     EXPECT_TRUE(isPrintable(content));
620 
621     // Add a control character (not printable).
622     content += "\a";
623     EXPECT_FALSE(isPrintable(content));
624 }
625 
626 // Verifies isPrintable predicate on byte vectors.
TEST(StringUtilTest,vectorIsPrintable)627 TEST(StringUtilTest, vectorIsPrintable) {
628     vector<uint8_t> content;
629 
630     // Empty is printable.
631     EXPECT_TRUE(isPrintable(content));
632 
633     // Check Abcd.
634     content = { 0x41, 0x62, 0x63, 0x64 };
635     EXPECT_TRUE(isPrintable(content));
636 
637     // Add a control character (not printable).
638     content.push_back('\a');
639     EXPECT_FALSE(isPrintable(content));
640 }
641 
642 } // end of anonymous namespace
643