1 // Copyright 2012 Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 #include "utils/text/operations.ipp"
30
31 #include <iostream>
32 #include <set>
33 #include <string>
34 #include <vector>
35
36 #include <atf-c++.hpp>
37
38 #include "utils/text/exceptions.hpp"
39
40 namespace text = utils::text;
41
42
43 namespace {
44
45
46 /// Tests text::refill() on an input string with a range of widths.
47 ///
48 /// \param expected The expected refilled paragraph.
49 /// \param input The input paragraph to be refilled.
50 /// \param first_width The first width to validate.
51 /// \param last_width The last width to validate (inclusive).
52 static void
refill_test(const char * expected,const char * input,const std::size_t first_width,const std::size_t last_width)53 refill_test(const char* expected, const char* input,
54 const std::size_t first_width, const std::size_t last_width)
55 {
56 for (std::size_t width = first_width; width <= last_width; ++width) {
57 const std::vector< std::string > lines = text::split(expected, '\n');
58 std::cout << "Breaking at width " << width << '\n';
59 ATF_REQUIRE_EQ(expected, text::refill_as_string(input, width));
60 ATF_REQUIRE(lines == text::refill(input, width));
61 }
62 }
63
64
65 } // anonymous namespace
66
67
68 ATF_TEST_CASE_WITHOUT_HEAD(quote__empty);
ATF_TEST_CASE_BODY(quote__empty)69 ATF_TEST_CASE_BODY(quote__empty)
70 {
71 ATF_REQUIRE_EQ("''", text::quote("", '\''));
72 ATF_REQUIRE_EQ("##", text::quote("", '#'));
73 }
74
75
76 ATF_TEST_CASE_WITHOUT_HEAD(quote__no_escaping);
ATF_TEST_CASE_BODY(quote__no_escaping)77 ATF_TEST_CASE_BODY(quote__no_escaping)
78 {
79 ATF_REQUIRE_EQ("'Some text\"'", text::quote("Some text\"", '\''));
80 ATF_REQUIRE_EQ("#Another'string#", text::quote("Another'string", '#'));
81 }
82
83
84 ATF_TEST_CASE_WITHOUT_HEAD(quote__some_escaping);
ATF_TEST_CASE_BODY(quote__some_escaping)85 ATF_TEST_CASE_BODY(quote__some_escaping)
86 {
87 ATF_REQUIRE_EQ("'Some\\'text'", text::quote("Some'text", '\''));
88 ATF_REQUIRE_EQ("#Some\\#text#", text::quote("Some#text", '#'));
89
90 ATF_REQUIRE_EQ("'More than one\\' quote\\''",
91 text::quote("More than one' quote'", '\''));
92 ATF_REQUIRE_EQ("'Multiple quotes \\'\\'\\' together'",
93 text::quote("Multiple quotes ''' together", '\''));
94
95 ATF_REQUIRE_EQ("'\\'escape at the beginning'",
96 text::quote("'escape at the beginning", '\''));
97 ATF_REQUIRE_EQ("'escape at the end\\''",
98 text::quote("escape at the end'", '\''));
99 }
100
101
102 ATF_TEST_CASE_WITHOUT_HEAD(refill__empty);
ATF_TEST_CASE_BODY(refill__empty)103 ATF_TEST_CASE_BODY(refill__empty)
104 {
105 ATF_REQUIRE_EQ(1, text::refill("", 0).size());
106 ATF_REQUIRE(text::refill("", 0)[0].empty());
107 ATF_REQUIRE_EQ("", text::refill_as_string("", 0));
108
109 ATF_REQUIRE_EQ(1, text::refill("", 10).size());
110 ATF_REQUIRE(text::refill("", 10)[0].empty());
111 ATF_REQUIRE_EQ("", text::refill_as_string("", 10));
112 }
113
114
115 ATF_TEST_CASE_WITHOUT_HEAD(refill__no_changes);
ATF_TEST_CASE_BODY(refill__no_changes)116 ATF_TEST_CASE_BODY(refill__no_changes)
117 {
118 std::vector< std::string > exp_lines;
119 exp_lines.push_back("foo bar\nbaz");
120
121 ATF_REQUIRE(exp_lines == text::refill("foo bar\nbaz", 12));
122 ATF_REQUIRE_EQ("foo bar\nbaz", text::refill_as_string("foo bar\nbaz", 12));
123
124 ATF_REQUIRE(exp_lines == text::refill("foo bar\nbaz", 18));
125 ATF_REQUIRE_EQ("foo bar\nbaz", text::refill_as_string("foo bar\nbaz", 80));
126 }
127
128
129 ATF_TEST_CASE_WITHOUT_HEAD(refill__break_one);
ATF_TEST_CASE_BODY(refill__break_one)130 ATF_TEST_CASE_BODY(refill__break_one)
131 {
132 refill_test("only break the\nfirst line", "only break the first line",
133 14, 19);
134 }
135
136
137 ATF_TEST_CASE_WITHOUT_HEAD(refill__break_one__not_first_word);
ATF_TEST_CASE_BODY(refill__break_one__not_first_word)138 ATF_TEST_CASE_BODY(refill__break_one__not_first_word)
139 {
140 refill_test("first-long-word\nother\nwords", "first-long-word other words",
141 6, 10);
142 refill_test("first-long-word\nother words", "first-long-word other words",
143 11, 20);
144 refill_test("first-long-word other\nwords", "first-long-word other words",
145 21, 26);
146 refill_test("first-long-word other words", "first-long-word other words",
147 27, 28);
148 }
149
150
151 ATF_TEST_CASE_WITHOUT_HEAD(refill__break_many);
ATF_TEST_CASE_BODY(refill__break_many)152 ATF_TEST_CASE_BODY(refill__break_many)
153 {
154 refill_test("this is a long\nparagraph to be\nsplit into\npieces",
155 "this is a long paragraph to be split into pieces",
156 15, 15);
157 }
158
159
160 ATF_TEST_CASE_WITHOUT_HEAD(refill__cannot_break);
ATF_TEST_CASE_BODY(refill__cannot_break)161 ATF_TEST_CASE_BODY(refill__cannot_break)
162 {
163 refill_test("this-is-a-long-string", "this-is-a-long-string", 5, 5);
164
165 refill_test("this is\na-string-with-long-words",
166 "this is a-string-with-long-words", 10, 10);
167 }
168
169
170 ATF_TEST_CASE_WITHOUT_HEAD(refill__preserve_whitespace);
ATF_TEST_CASE_BODY(refill__preserve_whitespace)171 ATF_TEST_CASE_BODY(refill__preserve_whitespace)
172 {
173 refill_test("foo bar baz ", "foo bar baz ", 80, 80);
174 refill_test("foo \n bar", "foo bar", 5, 5);
175
176 std::vector< std::string > exp_lines;
177 exp_lines.push_back("foo \n");
178 exp_lines.push_back(" bar");
179 ATF_REQUIRE(exp_lines == text::refill("foo \n bar", 5));
180 ATF_REQUIRE_EQ("foo \n\n bar", text::refill_as_string("foo \n bar", 5));
181 }
182
183
184 ATF_TEST_CASE_WITHOUT_HEAD(join__empty);
ATF_TEST_CASE_BODY(join__empty)185 ATF_TEST_CASE_BODY(join__empty)
186 {
187 std::vector< std::string > lines;
188 ATF_REQUIRE_EQ("", text::join(lines, " "));
189 }
190
191
192 ATF_TEST_CASE_WITHOUT_HEAD(join__one);
ATF_TEST_CASE_BODY(join__one)193 ATF_TEST_CASE_BODY(join__one)
194 {
195 std::vector< std::string > lines;
196 lines.push_back("first line");
197 ATF_REQUIRE_EQ("first line", text::join(lines, "*"));
198 }
199
200
201 ATF_TEST_CASE_WITHOUT_HEAD(join__several);
ATF_TEST_CASE_BODY(join__several)202 ATF_TEST_CASE_BODY(join__several)
203 {
204 std::vector< std::string > lines;
205 lines.push_back("first abc");
206 lines.push_back("second");
207 lines.push_back("and last line");
208 ATF_REQUIRE_EQ("first abc second and last line", text::join(lines, " "));
209 ATF_REQUIRE_EQ("first abc***second***and last line",
210 text::join(lines, "***"));
211 }
212
213
214 ATF_TEST_CASE_WITHOUT_HEAD(join__unordered);
ATF_TEST_CASE_BODY(join__unordered)215 ATF_TEST_CASE_BODY(join__unordered)
216 {
217 std::set< std::string > lines;
218 lines.insert("first");
219 lines.insert("second");
220 const std::string joined = text::join(lines, " ");
221 ATF_REQUIRE(joined == "first second" || joined == "second first");
222 }
223
224
225 ATF_TEST_CASE_WITHOUT_HEAD(split__empty);
ATF_TEST_CASE_BODY(split__empty)226 ATF_TEST_CASE_BODY(split__empty)
227 {
228 std::vector< std::string > words = text::split("", ' ');
229 std::vector< std::string > exp_words;
230 ATF_REQUIRE(exp_words == words);
231 }
232
233
234 ATF_TEST_CASE_WITHOUT_HEAD(split__one);
ATF_TEST_CASE_BODY(split__one)235 ATF_TEST_CASE_BODY(split__one)
236 {
237 std::vector< std::string > words = text::split("foo", ' ');
238 std::vector< std::string > exp_words;
239 exp_words.push_back("foo");
240 ATF_REQUIRE(exp_words == words);
241 }
242
243
244 ATF_TEST_CASE_WITHOUT_HEAD(split__several__simple);
ATF_TEST_CASE_BODY(split__several__simple)245 ATF_TEST_CASE_BODY(split__several__simple)
246 {
247 std::vector< std::string > words = text::split("foo bar baz", ' ');
248 std::vector< std::string > exp_words;
249 exp_words.push_back("foo");
250 exp_words.push_back("bar");
251 exp_words.push_back("baz");
252 ATF_REQUIRE(exp_words == words);
253 }
254
255
256 ATF_TEST_CASE_WITHOUT_HEAD(split__several__delimiters);
ATF_TEST_CASE_BODY(split__several__delimiters)257 ATF_TEST_CASE_BODY(split__several__delimiters)
258 {
259 std::vector< std::string > words = text::split("XfooXXbarXXXbazXX", 'X');
260 std::vector< std::string > exp_words;
261 exp_words.push_back("");
262 exp_words.push_back("foo");
263 exp_words.push_back("");
264 exp_words.push_back("bar");
265 exp_words.push_back("");
266 exp_words.push_back("");
267 exp_words.push_back("baz");
268 exp_words.push_back("");
269 exp_words.push_back("");
270 ATF_REQUIRE(exp_words == words);
271 }
272
273
274 ATF_TEST_CASE_WITHOUT_HEAD(to_type__ok__bool);
ATF_TEST_CASE_BODY(to_type__ok__bool)275 ATF_TEST_CASE_BODY(to_type__ok__bool)
276 {
277 ATF_REQUIRE( text::to_type< bool >("true"));
278 ATF_REQUIRE(!text::to_type< bool >("false"));
279 }
280
281
282 ATF_TEST_CASE_WITHOUT_HEAD(to_type__ok__numerical);
ATF_TEST_CASE_BODY(to_type__ok__numerical)283 ATF_TEST_CASE_BODY(to_type__ok__numerical)
284 {
285 ATF_REQUIRE_EQ(12, text::to_type< int >("12"));
286 ATF_REQUIRE_EQ(18745, text::to_type< int >("18745"));
287 ATF_REQUIRE_EQ(-12345, text::to_type< int >("-12345"));
288
289 ATF_REQUIRE_EQ(12.0, text::to_type< double >("12"));
290 ATF_REQUIRE_EQ(12.5, text::to_type< double >("12.5"));
291 }
292
293
294 ATF_TEST_CASE_WITHOUT_HEAD(to_type__ok__string);
ATF_TEST_CASE_BODY(to_type__ok__string)295 ATF_TEST_CASE_BODY(to_type__ok__string)
296 {
297 // While this seems redundant, having this particular specialization that
298 // does nothing allows callers to delegate work to to_type without worrying
299 // about the particular type being converted.
300 ATF_REQUIRE_EQ("", text::to_type< std::string >(""));
301 ATF_REQUIRE_EQ(" abcd ", text::to_type< std::string >(" abcd "));
302 }
303
304
305 ATF_TEST_CASE_WITHOUT_HEAD(to_type__empty);
ATF_TEST_CASE_BODY(to_type__empty)306 ATF_TEST_CASE_BODY(to_type__empty)
307 {
308 ATF_REQUIRE_THROW(text::value_error, text::to_type< int >(""));
309 }
310
311
312 ATF_TEST_CASE_WITHOUT_HEAD(to_type__invalid__bool);
ATF_TEST_CASE_BODY(to_type__invalid__bool)313 ATF_TEST_CASE_BODY(to_type__invalid__bool)
314 {
315 ATF_REQUIRE_THROW(text::value_error, text::to_type< bool >(""));
316 ATF_REQUIRE_THROW(text::value_error, text::to_type< bool >("true "));
317 ATF_REQUIRE_THROW(text::value_error, text::to_type< bool >("foo"));
318 }
319
320
321 ATF_TEST_CASE_WITHOUT_HEAD(to_type__invalid__numerical);
ATF_TEST_CASE_BODY(to_type__invalid__numerical)322 ATF_TEST_CASE_BODY(to_type__invalid__numerical)
323 {
324 ATF_REQUIRE_THROW(text::value_error, text::to_type< int >(" 3"));
325 ATF_REQUIRE_THROW(text::value_error, text::to_type< int >("3 "));
326 ATF_REQUIRE_THROW(text::value_error, text::to_type< int >("3a"));
327 ATF_REQUIRE_THROW(text::value_error, text::to_type< int >("a3"));
328 }
329
330
ATF_INIT_TEST_CASES(tcs)331 ATF_INIT_TEST_CASES(tcs)
332 {
333 ATF_ADD_TEST_CASE(tcs, quote__empty);
334 ATF_ADD_TEST_CASE(tcs, quote__no_escaping);
335 ATF_ADD_TEST_CASE(tcs, quote__some_escaping);
336
337 ATF_ADD_TEST_CASE(tcs, refill__empty);
338 ATF_ADD_TEST_CASE(tcs, refill__no_changes);
339 ATF_ADD_TEST_CASE(tcs, refill__break_one);
340 ATF_ADD_TEST_CASE(tcs, refill__break_one__not_first_word);
341 ATF_ADD_TEST_CASE(tcs, refill__break_many);
342 ATF_ADD_TEST_CASE(tcs, refill__cannot_break);
343 ATF_ADD_TEST_CASE(tcs, refill__preserve_whitespace);
344
345 ATF_ADD_TEST_CASE(tcs, join__empty);
346 ATF_ADD_TEST_CASE(tcs, join__one);
347 ATF_ADD_TEST_CASE(tcs, join__several);
348 ATF_ADD_TEST_CASE(tcs, join__unordered);
349
350 ATF_ADD_TEST_CASE(tcs, split__empty);
351 ATF_ADD_TEST_CASE(tcs, split__one);
352 ATF_ADD_TEST_CASE(tcs, split__several__simple);
353 ATF_ADD_TEST_CASE(tcs, split__several__delimiters);
354
355 ATF_ADD_TEST_CASE(tcs, to_type__ok__bool);
356 ATF_ADD_TEST_CASE(tcs, to_type__ok__numerical);
357 ATF_ADD_TEST_CASE(tcs, to_type__ok__string);
358 ATF_ADD_TEST_CASE(tcs, to_type__empty);
359 ATF_ADD_TEST_CASE(tcs, to_type__invalid__bool);
360 ATF_ADD_TEST_CASE(tcs, to_type__invalid__numerical);
361 }
362