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/gen/String.h>
22 #include <folly/portability/GTest.h>
24 using namespace folly::gen;
25 using namespace folly;
26 using std::make_tuple;
31 using std::unique_ptr;
34 TEST(StringGen, EmptySplit) {
35 auto collect = eachTo<std::string>() | as<vector>();
37 auto pieces = split("", ',') | collect;
38 EXPECT_EQ(0, pieces.size());
41 // The last delimiter is eaten, just like std::getline
43 auto pieces = split(",", ',') | collect;
44 EXPECT_EQ(1, pieces.size());
45 EXPECT_EQ("", pieces[0]);
49 auto pieces = split(",,", ',') | collect;
50 EXPECT_EQ(2, pieces.size());
51 EXPECT_EQ("", pieces[0]);
52 EXPECT_EQ("", pieces[1]);
56 auto pieces = split(",,", ',') | take(1) | collect;
57 EXPECT_EQ(1, pieces.size());
58 EXPECT_EQ("", pieces[0]);
62 TEST(StringGen, Split) {
63 auto collect = eachTo<std::string>() | as<vector>();
65 auto pieces = split("hello,, world, goodbye, meow", ',') | collect;
66 EXPECT_EQ(5, pieces.size());
67 EXPECT_EQ("hello", pieces[0]);
68 EXPECT_EQ("", pieces[1]);
69 EXPECT_EQ(" world", pieces[2]);
70 EXPECT_EQ(" goodbye", pieces[3]);
71 EXPECT_EQ(" meow", pieces[4]);
75 auto pieces = split("hello,, world, goodbye, meow", ',')
77 EXPECT_EQ(3, pieces.size());
78 EXPECT_EQ("hello", pieces[0]);
79 EXPECT_EQ("", pieces[1]);
80 EXPECT_EQ(" world", pieces[2]);
84 auto pieces = split("hello,, world, goodbye, meow", ",")
86 EXPECT_EQ(5, pieces.size());
87 EXPECT_EQ("hello", pieces[0]);
88 EXPECT_EQ("", pieces[1]);
89 EXPECT_EQ(" world", pieces[2]);
93 auto pieces = split("hello,, world, goodbye, meow", ", ")
95 EXPECT_EQ(4, pieces.size());
96 EXPECT_EQ("hello,", pieces[0]);
97 EXPECT_EQ("world", pieces[1]);
98 EXPECT_EQ("goodbye", pieces[2]);
99 EXPECT_EQ("meow", pieces[3]);
103 TEST(StringGen, SplitByNewLine) {
104 auto collect = eachTo<std::string>() | as<vector>();
106 auto pieces = lines("hello\n\n world\r\n goodbye\r me\n\row") | collect;
107 EXPECT_EQ(7, pieces.size());
108 EXPECT_EQ("hello", pieces[0]);
109 EXPECT_EQ("", pieces[1]);
110 EXPECT_EQ(" world", pieces[2]);
111 EXPECT_EQ(" goodbye", pieces[3]);
112 EXPECT_EQ(" me", pieces[4]);
113 EXPECT_EQ("", pieces[5]);
114 EXPECT_EQ("ow", pieces[6]);
118 TEST(StringGen, EmptyResplit) {
119 auto collect = eachTo<std::string>() | as<vector>();
121 auto pieces = from({""}) | resplit(',') | collect;
122 EXPECT_EQ(0, pieces.size());
125 // The last delimiter is eaten, just like std::getline
127 auto pieces = from({","}) | resplit(',') | collect;
128 EXPECT_EQ(1, pieces.size());
129 EXPECT_EQ("", pieces[0]);
133 auto pieces = from({",,"}) | resplit(',') | collect;
134 EXPECT_EQ(2, pieces.size());
135 EXPECT_EQ("", pieces[0]);
136 EXPECT_EQ("", pieces[1]);
140 TEST(StringGen, EachToTuple) {
142 auto lines = "2:1.414:yo 3:1.732:hi";
145 | eachToTuple<int, double, std::string>(':')
147 vector<tuple<int, double, std::string>> expected {
148 make_tuple(2, 1.414, "yo"),
149 make_tuple(3, 1.732, "hi"),
151 EXPECT_EQ(expected, actual);
157 | eachToTuple<int>(',')
159 vector<tuple<int>> expected {
163 EXPECT_EQ(expected, actual);
166 // StringPiece target
167 auto lines = "1:cat 2:dog";
170 | eachToTuple<int, StringPiece>(':')
172 vector<tuple<int, StringPiece>> expected {
173 make_tuple(1, "cat"),
174 make_tuple(2, "dog"),
176 EXPECT_EQ(expected, actual);
180 auto lines = "2:tjackson:4 3::5";
183 | eachToTuple<int, fbstring, int>(':')
185 vector<tuple<int, fbstring, int>> expected {
186 make_tuple(2, "tjackson", 4),
187 make_tuple(3, "", 5),
189 EXPECT_EQ(expected, actual);
193 auto lines = "1:2 3:4:5";
194 EXPECT_THROW((split(lines, ' ')
195 | eachToTuple<int, int>(':')
201 auto lines = "1:2:3 4:5";
202 EXPECT_THROW((split(lines, ' ')
203 | eachToTuple<int, int, int>(':')
209 TEST(StringGen, EachToPair) {
212 auto lines = "2:1.414 3:1.732";
215 | eachToPair<int, double>(':')
216 | as<std::map<int, double>>();
217 std::map<int, double> expected {
221 EXPECT_EQ(expected, actual);
225 auto lines = "ab=>cd ef=>gh";
228 | eachToPair<string, string>("=>")
229 | as<std::map<string, string>>();
230 std::map<string, string> expected {
234 EXPECT_EQ(expected, actual);
238 TEST(StringGen, Resplit) {
239 auto collect = eachTo<std::string>() | as<vector>();
241 auto pieces = from({"hello,, world, goodbye, meow"}) |
242 resplit(',') | collect;
243 EXPECT_EQ(5, pieces.size());
244 EXPECT_EQ("hello", pieces[0]);
245 EXPECT_EQ("", pieces[1]);
246 EXPECT_EQ(" world", pieces[2]);
247 EXPECT_EQ(" goodbye", pieces[3]);
248 EXPECT_EQ(" meow", pieces[4]);
251 auto pieces = from({"hel", "lo,", ", world", ", goodbye, m", "eow"}) |
252 resplit(',') | collect;
253 EXPECT_EQ(5, pieces.size());
254 EXPECT_EQ("hello", pieces[0]);
255 EXPECT_EQ("", pieces[1]);
256 EXPECT_EQ(" world", pieces[2]);
257 EXPECT_EQ(" goodbye", pieces[3]);
258 EXPECT_EQ(" meow", pieces[4]);
262 void checkResplitMaxLength(vector<string> ins,
265 vector<string> outs) {
266 vector<std::string> pieces;
267 auto splitter = streamSplitter(delim, [&pieces](StringPiece s) {
268 pieces.push_back(string(s.begin(), s.end()));
271 for (const auto& in : ins) {
276 EXPECT_EQ(outs.size(), pieces.size());
277 for (size_t i = 0; i < outs.size(); ++i) {
278 EXPECT_EQ(outs[i], pieces[i]);
281 // Also check the concatenated input against the same output
282 if (ins.size() > 1) {
283 checkResplitMaxLength({folly::join("", ins)}, delim, maxLength, outs);
287 TEST(StringGen, ResplitMaxLength) {
288 checkResplitMaxLength(
289 {"hel", "lo,", ", world", ", goodbye, m", "ew"}, ',', 5,
290 {"hello", ",", ",", " worl", "d,", " good", "bye,", " mew"}
292 // " meow" cannot be "end of stream", since it's maxLength long
293 checkResplitMaxLength(
294 {"hel", "lo,", ", world", ", goodbye, m", "eow"}, ',', 5,
295 {"hello", ",", ",", " worl", "d,", " good", "bye,", " meow", ""}
297 checkResplitMaxLength(
298 {"||", "", "", "", "|a|b", "cdefghijklmn", "|opqrst",
299 "uvwx|y|||", "z", "0123456789", "|", ""}, '|', 2,
300 {"|", "|", "|", "a|", "bc", "de", "fg", "hi", "jk", "lm", "n|", "op", "qr",
301 "st", "uv", "wx", "|", "y|", "|", "|", "z0", "12", "34", "56", "78", "9|",
307 void runUnsplitSuite(F fn) {
309 fn("hello,world,goodbye");
316 TEST(StringGen, Unsplit) {
318 auto basicFn = [](StringPiece s) {
319 EXPECT_EQ(split(s, ',') | unsplit(','), s);
322 auto existingBuffer = [](StringPiece s) {
323 folly::fbstring buffer("asdf");
324 split(s, ',') | unsplit(',', &buffer);
325 auto expected = folly::to<folly::fbstring>(
326 "asdf", s.empty() ? "" : ",", s);
327 EXPECT_EQ(expected, buffer);
330 auto emptyBuffer = [](StringPiece s) {
332 split(s, ',') | unsplit(',', &buffer);
333 EXPECT_EQ(s, buffer);
336 auto stringDelim = [](StringPiece s) {
337 EXPECT_EQ(s, split(s, ',') | unsplit(","));
339 split(s, ',') | unsplit(",", &buffer);
340 EXPECT_EQ(buffer, s);
343 runUnsplitSuite(basicFn);
344 runUnsplitSuite(existingBuffer);
345 runUnsplitSuite(emptyBuffer);
346 runUnsplitSuite(stringDelim);
347 EXPECT_EQ("1, 2, 3", seq(1, 3) | unsplit(", "));
350 TEST(StringGen, Batch) {
351 std::vector<std::string> chunks{
352 "on", "e\nt", "w", "o", "\nthr", "ee\nfo", "ur\n",
354 std::vector<std::string> lines{
355 "one", "two", "three", "four",
357 EXPECT_EQ(4, from(chunks) | resplit('\n') | count);
358 EXPECT_EQ(4, from(chunks) | resplit('\n') | batch(2) | rconcat | count);
359 EXPECT_EQ(4, from(chunks) | resplit('\n') | batch(3) | rconcat | count);
360 EXPECT_EQ(lines, from(chunks) | resplit('\n') | eachTo<std::string>() |
361 batch(3) | rconcat | as<vector>());