2 * Copyright 2017 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
21 #include <folly/ApplyTuple.h>
22 #include <folly/gen/String.h>
23 #include <folly/portability/GTest.h>
25 using namespace folly::gen;
26 using namespace folly;
27 using std::make_tuple;
32 using std::unique_ptr;
35 TEST(StringGen, EmptySplit) {
36 auto collect = eachTo<std::string>() | as<vector>();
38 auto pieces = split("", ',') | collect;
39 EXPECT_EQ(0, pieces.size());
42 // The last delimiter is eaten, just like std::getline
44 auto pieces = split(",", ',') | collect;
45 EXPECT_EQ(1, pieces.size());
46 EXPECT_EQ("", pieces[0]);
50 auto pieces = split(",,", ',') | collect;
51 EXPECT_EQ(2, pieces.size());
52 EXPECT_EQ("", pieces[0]);
53 EXPECT_EQ("", pieces[1]);
57 auto pieces = split(",,", ',') | take(1) | collect;
58 EXPECT_EQ(1, pieces.size());
59 EXPECT_EQ("", pieces[0]);
63 TEST(StringGen, Split) {
64 auto collect = eachTo<std::string>() | as<vector>();
66 auto pieces = split("hello,, world, goodbye, meow", ',') | collect;
67 EXPECT_EQ(5, pieces.size());
68 EXPECT_EQ("hello", pieces[0]);
69 EXPECT_EQ("", pieces[1]);
70 EXPECT_EQ(" world", pieces[2]);
71 EXPECT_EQ(" goodbye", pieces[3]);
72 EXPECT_EQ(" meow", pieces[4]);
76 auto pieces = split("hello,, world, goodbye, meow", ',')
78 EXPECT_EQ(3, pieces.size());
79 EXPECT_EQ("hello", pieces[0]);
80 EXPECT_EQ("", pieces[1]);
81 EXPECT_EQ(" world", pieces[2]);
85 auto pieces = split("hello,, world, goodbye, meow", ",")
87 EXPECT_EQ(5, pieces.size());
88 EXPECT_EQ("hello", pieces[0]);
89 EXPECT_EQ("", pieces[1]);
90 EXPECT_EQ(" world", pieces[2]);
94 auto pieces = split("hello,, world, goodbye, meow", ", ")
96 EXPECT_EQ(4, pieces.size());
97 EXPECT_EQ("hello,", pieces[0]);
98 EXPECT_EQ("world", pieces[1]);
99 EXPECT_EQ("goodbye", pieces[2]);
100 EXPECT_EQ("meow", pieces[3]);
104 TEST(StringGen, SplitByNewLine) {
105 auto collect = eachTo<std::string>() | as<vector>();
107 auto pieces = lines("hello\n\n world\r\n goodbye\r me\n\row") | collect;
108 EXPECT_EQ(7, pieces.size());
109 EXPECT_EQ("hello", pieces[0]);
110 EXPECT_EQ("", pieces[1]);
111 EXPECT_EQ(" world", pieces[2]);
112 EXPECT_EQ(" goodbye", pieces[3]);
113 EXPECT_EQ(" me", pieces[4]);
114 EXPECT_EQ("", pieces[5]);
115 EXPECT_EQ("ow", pieces[6]);
119 TEST(StringGen, EmptyResplit) {
120 auto collect = eachTo<std::string>() | as<vector>();
122 auto pieces = from({""}) | resplit(',') | collect;
123 EXPECT_EQ(0, pieces.size());
126 // The last delimiter is eaten, just like std::getline
128 auto pieces = from({","}) | resplit(',') | collect;
129 EXPECT_EQ(1, pieces.size());
130 EXPECT_EQ("", pieces[0]);
134 auto pieces = from({",,"}) | resplit(',') | collect;
135 EXPECT_EQ(2, pieces.size());
136 EXPECT_EQ("", pieces[0]);
137 EXPECT_EQ("", pieces[1]);
141 TEST(StringGen, EachToTuple) {
143 auto lines = "2:1.414:yo 3:1.732:hi";
146 | eachToTuple<int, double, std::string>(':')
148 vector<tuple<int, double, std::string>> expected {
149 make_tuple(2, 1.414, "yo"),
150 make_tuple(3, 1.732, "hi"),
152 EXPECT_EQ(expected, actual);
158 | eachToTuple<int>(',')
160 vector<tuple<int>> expected {
164 EXPECT_EQ(expected, actual);
167 // StringPiece target
168 auto lines = "1:cat 2:dog";
171 | eachToTuple<int, StringPiece>(':')
173 vector<tuple<int, StringPiece>> expected {
174 make_tuple(1, "cat"),
175 make_tuple(2, "dog"),
177 EXPECT_EQ(expected, actual);
181 auto lines = "2:tjackson:4 3::5";
184 | eachToTuple<int, fbstring, int>(':')
186 vector<tuple<int, fbstring, int>> expected {
187 make_tuple(2, "tjackson", 4),
188 make_tuple(3, "", 5),
190 EXPECT_EQ(expected, actual);
194 auto lines = "1:2 3:4:5";
195 EXPECT_THROW((split(lines, ' ')
196 | eachToTuple<int, int>(':')
202 auto lines = "1:2:3 4:5";
203 EXPECT_THROW((split(lines, ' ')
204 | eachToTuple<int, int, int>(':')
210 TEST(StringGen, EachToPair) {
213 auto lines = "2:1.414 3:1.732";
216 | eachToPair<int, double>(':')
217 | as<std::map<int, double>>();
218 std::map<int, double> expected {
222 EXPECT_EQ(expected, actual);
226 auto lines = "ab=>cd ef=>gh";
229 | eachToPair<string, string>("=>")
230 | as<std::map<string, string>>();
231 std::map<string, string> expected {
235 EXPECT_EQ(expected, actual);
239 TEST(StringGen, Resplit) {
240 auto collect = eachTo<std::string>() | as<vector>();
242 auto pieces = from({"hello,, world, goodbye, meow"}) |
243 resplit(',') | collect;
244 EXPECT_EQ(5, pieces.size());
245 EXPECT_EQ("hello", pieces[0]);
246 EXPECT_EQ("", pieces[1]);
247 EXPECT_EQ(" world", pieces[2]);
248 EXPECT_EQ(" goodbye", pieces[3]);
249 EXPECT_EQ(" meow", pieces[4]);
252 auto pieces = from({"hel", "lo,", ", world", ", goodbye, m", "eow"}) |
253 resplit(',') | collect;
254 EXPECT_EQ(5, pieces.size());
255 EXPECT_EQ("hello", pieces[0]);
256 EXPECT_EQ("", pieces[1]);
257 EXPECT_EQ(" world", pieces[2]);
258 EXPECT_EQ(" goodbye", pieces[3]);
259 EXPECT_EQ(" meow", pieces[4]);
263 TEST(StringGen, ResplitKeepDelimiter) {
264 auto collect = eachTo<std::string>() | as<vector>();
267 from({"hello,, world, goodbye, meow"}) | resplit(',', true) | collect;
268 ASSERT_EQ(5, pieces.size());
269 EXPECT_EQ("hello,", pieces[0]);
270 EXPECT_EQ(",", pieces[1]);
271 EXPECT_EQ(" world,", pieces[2]);
272 EXPECT_EQ(" goodbye,", pieces[3]);
273 EXPECT_EQ(" meow", pieces[4]);
276 auto pieces = from({"hel", "lo,", ", world", ", goodbye, m", "eow"}) |
277 resplit(',', true) | collect;
278 ASSERT_EQ(5, pieces.size());
279 EXPECT_EQ("hello,", pieces[0]);
280 EXPECT_EQ(",", pieces[1]);
281 EXPECT_EQ(" world,", pieces[2]);
282 EXPECT_EQ(" goodbye,", pieces[3]);
283 EXPECT_EQ(" meow", pieces[4]);
287 void checkResplitMaxLength(vector<string> ins,
290 vector<string> outs) {
291 vector<std::string> pieces;
292 auto splitter = streamSplitter(delim, [&pieces](StringPiece s) {
293 pieces.push_back(string(s.begin(), s.end()));
296 for (const auto& in : ins) {
301 EXPECT_EQ(outs.size(), pieces.size());
302 for (size_t i = 0; i < outs.size(); ++i) {
303 EXPECT_EQ(outs[i], pieces[i]);
306 // Also check the concatenated input against the same output
307 if (ins.size() > 1) {
308 checkResplitMaxLength({folly::join("", ins)}, delim, maxLength, outs);
312 TEST(StringGen, ResplitMaxLength) {
313 checkResplitMaxLength(
314 {"hel", "lo,", ", world", ", goodbye, m", "ew"}, ',', 5,
315 {"hello", ",", ",", " worl", "d,", " good", "bye,", " mew"}
317 // " meow" cannot be "end of stream", since it's maxLength long
318 checkResplitMaxLength(
319 {"hel", "lo,", ", world", ", goodbye, m", "eow"}, ',', 5,
320 {"hello", ",", ",", " worl", "d,", " good", "bye,", " meow", ""}
322 checkResplitMaxLength(
323 {"||", "", "", "", "|a|b", "cdefghijklmn", "|opqrst",
324 "uvwx|y|||", "z", "0123456789", "|", ""}, '|', 2,
325 {"|", "|", "|", "a|", "bc", "de", "fg", "hi", "jk", "lm", "n|", "op", "qr",
326 "st", "uv", "wx", "|", "y|", "|", "|", "z0", "12", "34", "56", "78", "9|",
332 void runUnsplitSuite(F fn) {
334 fn("hello,world,goodbye");
341 TEST(StringGen, Unsplit) {
343 auto basicFn = [](StringPiece s) {
344 EXPECT_EQ(split(s, ',') | unsplit(','), s);
347 auto existingBuffer = [](StringPiece s) {
348 folly::fbstring buffer("asdf");
349 split(s, ',') | unsplit(',', &buffer);
350 auto expected = folly::to<folly::fbstring>(
351 "asdf", s.empty() ? "" : ",", s);
352 EXPECT_EQ(expected, buffer);
355 auto emptyBuffer = [](StringPiece s) {
357 split(s, ',') | unsplit(',', &buffer);
358 EXPECT_EQ(s, buffer);
361 auto stringDelim = [](StringPiece s) {
362 EXPECT_EQ(s, split(s, ',') | unsplit(","));
364 split(s, ',') | unsplit(",", &buffer);
365 EXPECT_EQ(buffer, s);
368 runUnsplitSuite(basicFn);
369 runUnsplitSuite(existingBuffer);
370 runUnsplitSuite(emptyBuffer);
371 runUnsplitSuite(stringDelim);
372 EXPECT_EQ("1, 2, 3", seq(1, 3) | unsplit(", "));
375 TEST(StringGen, Batch) {
376 std::vector<std::string> chunks{
377 "on", "e\nt", "w", "o", "\nthr", "ee\nfo", "ur\n",
379 std::vector<std::string> lines{
380 "one", "two", "three", "four",
382 EXPECT_EQ(4, from(chunks) | resplit('\n') | count);
383 EXPECT_EQ(4, from(chunks) | resplit('\n') | batch(2) | rconcat | count);
384 EXPECT_EQ(4, from(chunks) | resplit('\n') | batch(3) | rconcat | count);
385 EXPECT_EQ(lines, from(chunks) | resplit('\n') | eachTo<std::string>() |
386 batch(3) | rconcat | as<vector>());
389 TEST(StringGen, UncurryTuple) {
390 folly::StringPiece file = "1\t2\t3\n1\t4\t9";
391 auto rows = split(file, '\n') | eachToTuple<int, int, int>('\t');
393 rows | map(uncurry([](int x, int y, int z) { return x * y * z; })) | sum;
394 EXPECT_EQ(42, productSum);
397 TEST(StringGen, UncurryPair) {
398 folly::StringPiece file = "2\t3\n4\t9";
399 auto rows = split(file, '\n') | eachToPair<int, int>('\t');
401 rows | map(uncurry([](int x, int y) { return x * y; })) | sum;
402 EXPECT_EQ(42, productSum);