284a2c0930a52c0bdd27ea901d0b367d083bb01c
[folly.git] / folly / experimental / test / GenTest.cpp
1 /*
2  * Copyright 2012 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include <glog/logging.h>
18 #include <gtest/gtest.h>
19 #include <iostream>
20 #include <set>
21 #include <vector>
22 #include "folly/experimental/Gen.h"
23 #include "folly/experimental/StringGen.h"
24 #include "folly/experimental/FileGen.h"
25 #include "folly/experimental/TestUtil.h"
26 #include "folly/FBVector.h"
27 #include "folly/Format.h"
28 #include "folly/dynamic.h"
29
30 using namespace folly::gen;
31 using namespace folly;
32 using std::ostream;
33 using std::pair;
34 using std::set;
35 using std::unique_ptr;
36 using std::vector;
37 using std::string;
38 using std::tuple;
39 using std::make_tuple;
40 //using std::unordered_map;
41
42 #define EXPECT_SAME(A, B) \
43   static_assert(std::is_same<A, B>::value, "Mismatched: " #A ", " #B)
44 EXPECT_SAME(int&&, typename ArgumentReference<int>::type);
45 EXPECT_SAME(int&, typename ArgumentReference<int&>::type);
46 EXPECT_SAME(const int&, typename ArgumentReference<const int&>::type);
47 EXPECT_SAME(const int&, typename ArgumentReference<const int>::type);
48
49 template<typename T>
50 ostream& operator<<(ostream& os, const set<T>& values) {
51   return os << from(values);
52 }
53
54 template<typename T>
55 ostream& operator<<(ostream& os, const vector<T>& values) {
56   os << "[";
57   for (auto& value : values) {
58     if (&value != &values.front()) {
59       os << " ";
60     }
61     os << value;
62   }
63   return os << "]";
64 }
65
66 auto square = [](int x) { return x * x; };
67 auto add = [](int a, int b) { return a + b; };
68 auto multiply = [](int a, int b) { return a * b; };
69
70 auto product = foldl(1, multiply);
71
72 template<typename A, typename B>
73 ostream& operator<<(ostream& os, const pair<A, B>& pair) {
74   return os << "(" << pair.first << ", " << pair.second << ")";
75 }
76
77 TEST(Gen, Count) {
78   auto gen = seq(1, 10);
79   EXPECT_EQ(10, gen | count);
80   EXPECT_EQ(5, gen | take(5) | count);
81 }
82
83 TEST(Gen, Sum) {
84   auto gen = seq(1, 10);
85   EXPECT_EQ((1 + 10) * 10 / 2, gen | sum);
86   EXPECT_EQ((1 + 5) * 5 / 2, gen | take(5) | sum);
87 }
88
89 TEST(Gen, Foreach) {
90   auto gen = seq(1, 4);
91   int accum = 0;
92   gen | [&](int x) { accum += x; };
93   EXPECT_EQ(10, accum);
94   int accum2 = 0;
95   gen | take(3) | [&](int x) { accum2 += x; };
96   EXPECT_EQ(6, accum2);
97 }
98
99 TEST(Gen, Map) {
100   auto expected = vector<int>{4, 9, 16};
101   auto gen = from({2, 3, 4}) | map(square);
102   EXPECT_EQ((vector<int>{4, 9, 16}), gen | as<vector>());
103   EXPECT_EQ((vector<int>{4, 9}), gen | take(2) | as<vector>());
104 }
105
106 TEST(Gen, Seq) {
107   // cover the fenceposts of the loop unrolling
108   for (int n = 1; n < 100; ++n) {
109     EXPECT_EQ(n, seq(1, n) | count);
110     EXPECT_EQ(n + 1, seq(1) | take(n + 1) | count);
111   }
112 }
113
114 TEST(Gen, Range) {
115   // cover the fenceposts of the loop unrolling
116   for (int n = 1; n < 100; ++n) {
117     EXPECT_EQ(range(0, n) | count, n);
118   }
119 }
120
121 TEST(Gen, FromIterators) {
122   vector<int> source {2, 3, 5, 7, 11};
123   auto gen = from(makeRange(source.begin() + 1, source.end() - 1));
124   EXPECT_EQ(3 * 5 * 7, gen | product);
125 }
126
127 TEST(Gen, FromMap) {
128   auto source = seq(0, 10)
129               | map([](int i) { return std::make_pair(i, i * i); })
130               | as<std::map<int, int>>();
131   auto gen = fromConst(source)
132            | map([&](const std::pair<const int, int>& p) {
133                return p.second - p.first;
134              });
135   EXPECT_EQ(330, gen | sum);
136 }
137
138 TEST(Gen, Filter) {
139   const auto expected = vector<int>{1, 2, 4, 5, 7, 8};
140   auto actual =
141       seq(1, 9)
142     | filter([](int x) { return x % 3; })
143     | as<vector<int>>();
144   EXPECT_EQ(expected, actual);
145 }
146
147 TEST(Gen, Take) {
148   auto expected = vector<int>{1, 4, 9, 16};
149   auto actual =
150       seq(1, 1000)
151     | mapped([](int x) { return x * x; })
152     | take(4)
153     | as<vector<int>>();
154   EXPECT_EQ(expected, actual);
155 }
156
157 TEST(Gen, Skip) {
158   auto gen =
159       seq(1, 1000)
160     | mapped([](int x) { return x * x; })
161     | skip(4)
162     | take(4);
163   EXPECT_EQ((vector<int>{25, 36, 49, 64}), gen | as<vector>());
164 }
165
166 TEST(Gen, Until) {
167   auto gen =
168       seq(1) //infinite
169     | mapped([](int x) { return x * x; })
170     | until([](int x) { return x >= 1000; });
171   EXPECT_EQ(31, gen | count);
172 }
173
174 TEST(Gen, Composed) {
175   // Operator, Operator
176   auto valuesOf =
177       filter([](Optional<int>& o) { return o.hasValue(); })
178     | map([](Optional<int>& o) -> int& { return o.value(); });
179   std::vector<Optional<int>> opts {
180     none, 4, none, 6, none
181   };
182   EXPECT_EQ(4 * 4 + 6 * 6, from(opts) | valuesOf | map(square) | sum);
183   // Operator, Sink
184   auto sumOpt = valuesOf | sum;
185   EXPECT_EQ(10, from(opts) | sumOpt);
186 }
187
188 TEST(Gen, Chain) {
189   std::vector<int> nums {2, 3, 5, 7};
190   std::map<int, int> mappings { { 3, 9}, {5, 25} };
191   auto gen = from(nums) + (from(mappings) | get<1>());
192   EXPECT_EQ(51, gen | sum);
193   EXPECT_EQ(5, gen | take(2) | sum);
194   EXPECT_EQ(26, gen | take(5) | sum);
195 }
196
197 TEST(Gen, Concat) {
198   std::vector<std::vector<int>> nums {{2, 3}, {5, 7}};
199   auto gen = from(nums) | rconcat;
200   EXPECT_EQ(17, gen | sum);
201   EXPECT_EQ(10, gen | take(3) | sum);
202 }
203
204 TEST(Gen, ConcatGen) {
205   auto gen = seq(1, 10)
206            | map([](int i) { return seq(1, i); })
207            | concat;
208   EXPECT_EQ(220, gen | sum);
209   EXPECT_EQ(10, gen | take(6) | sum);
210 }
211
212 TEST(Gen, ConcatAlt) {
213   std::vector<std::vector<int>> nums {{2, 3}, {5, 7}};
214   auto actual = from(nums)
215               | map([](std::vector<int>& v) { return from(v); })
216               | concat
217               | sum;
218   auto expected = 17;
219   EXPECT_EQ(expected, actual);
220 }
221
222 TEST(Gen, Order) {
223   auto expected = vector<int>{0, 3, 5, 6, 7, 8, 9};
224   auto actual =
225       from({8, 6, 7, 5, 3, 0, 9})
226     | order
227     | as<vector>();
228   EXPECT_EQ(expected, actual);
229 }
230
231 TEST(Gen, OrderMoved) {
232   auto expected = vector<int>{0, 9, 25, 36, 49, 64, 81};
233   auto actual =
234       from({8, 6, 7, 5, 3, 0, 9})
235     | move
236     | order
237     | map(square)
238     | as<vector>();
239   EXPECT_EQ(expected, actual);
240 }
241
242 TEST(Gen, OrderTake) {
243   auto expected = vector<int>{9, 8, 7};
244   auto actual =
245       from({8, 6, 7, 5, 3, 0, 9})
246     | orderByDescending(square)
247     | take(3)
248     | as<vector>();
249   EXPECT_EQ(expected, actual);
250 }
251
252 TEST(Gen, MinBy) {
253   EXPECT_EQ(7, seq(1, 10)
254              | minBy([](int i) {
255                  auto d = i - 6.8;
256                  return d * d;
257                }));
258 }
259
260 TEST(Gen, MaxBy) {
261   auto gen = from({"three", "eleven", "four"});
262
263   EXPECT_EQ("eleven", gen | maxBy(&strlen));
264 }
265
266 TEST(Gen, Append) {
267   fbstring expected = "facebook";
268   fbstring actual = "face";
269   from(StringPiece("book")) | appendTo(actual);
270   EXPECT_EQ(expected, actual);
271 }
272
273 TEST(Gen, FromRValue) {
274   {
275     // AFAICT The C++ Standard does not specify what happens to the rvalue
276     // reference of a std::vector when it is used as the 'other' for an rvalue
277     // constructor.  Use fbvector because we're sure its size will be zero in
278     // this case.
279     folly::fbvector<int> v({1,2,3,4});
280     auto q1 = from(v);
281     EXPECT_EQ(v.size(), 4);  // ensure that the lvalue version was called!
282     auto expected = 1 * 2 * 3 * 4;
283     EXPECT_EQ(expected, q1 | product);
284
285     auto q2 = from(std::move(v));
286     EXPECT_EQ(v.size(), 0);  // ensure that rvalue version was called
287     EXPECT_EQ(expected, q2 | product);
288   }
289   {
290     auto expected = 7;
291     auto q = from([] {return vector<int>({3,7,5}); }());
292     EXPECT_EQ(expected, q | max);
293   }
294   {
295     for (auto size: {5, 1024, 16384, 1<<20}) {
296       auto q1 = from(vector<int>(size, 2));
297       auto q2 = from(vector<int>(size, 3));
298       // If the rvalue specialization is broken/gone, then the compiler will
299       // (disgustingly!) just store a *reference* to the temporary object,
300       // which is bad.  Try to catch this by allocating two temporary vectors
301       // of the same size, so that they'll probably use the same underlying
302       // buffer if q1's vector is destructed before q2's vector is constructed.
303       EXPECT_EQ(size * 2 + size * 3, (q1 | sum) + (q2 | sum));
304     }
305   }
306   {
307     auto q = from(set<int>{1,2,3,2,1});
308     EXPECT_EQ(q | sum, 6);
309   }
310 }
311
312 TEST(Gen, OrderBy) {
313   auto expected = vector<int>{5, 6, 4, 7, 3, 8, 2, 9, 1, 10};
314   auto actual =
315       seq(1, 10)
316     | orderBy([](int x) { return (5.1 - x) * (5.1 - x); })
317     | as<vector>();
318   EXPECT_EQ(expected, actual);
319 }
320
321 TEST(Gen, Foldl) {
322   int expected = 2 * 3 * 4 * 5;
323   auto actual =
324       seq(2, 5)
325     | foldl(1, multiply);
326   EXPECT_EQ(expected, actual);
327 }
328
329 TEST(Gen, Reduce) {
330   int expected = 2 + 3 + 4 + 5;
331   auto actual = seq(2, 5) | reduce(add);
332   EXPECT_EQ(expected, actual);
333 }
334
335 TEST(Gen, ReduceBad) {
336   auto gen = seq(1) | take(0);
337   try {
338     EXPECT_TRUE(true);
339     gen | reduce(add);
340     EXPECT_TRUE(false);
341   } catch (...) {
342   }
343 }
344
345 TEST(Gen, Moves) {
346   std::vector<unique_ptr<int>> ptrs;
347   ptrs.emplace_back(new int(1));
348   EXPECT_NE(ptrs.front().get(), nullptr);
349   auto ptrs2 = from(ptrs) | move | as<vector>();
350   EXPECT_EQ(ptrs.front().get(), nullptr);
351   EXPECT_EQ(**ptrs2.data(), 1);
352 }
353
354 TEST(Gen, First) {
355   auto gen =
356       seq(0)
357     | filter([](int x) { return x > 3; });
358   EXPECT_EQ(4, gen | first);
359 }
360
361 TEST(Gen, FromCopy) {
362   vector<int> v {3, 5};
363   auto src = from(v);
364   auto copy = fromCopy(v);
365   EXPECT_EQ(8, src | sum);
366   EXPECT_EQ(8, copy | sum);
367   v[1] = 7;
368   EXPECT_EQ(10, src | sum);
369   EXPECT_EQ(8, copy | sum);
370 }
371
372 TEST(Gen, Get) {
373   std::map<int, int> pairs {
374     {1, 1},
375     {2, 4},
376     {3, 9},
377     {4, 16},
378   };
379   auto pairSrc = from(pairs);
380   auto keys = pairSrc | get<0>();
381   auto values = pairSrc | get<1>();
382   EXPECT_EQ(10, keys | sum);
383   EXPECT_EQ(30, values | sum);
384   EXPECT_EQ(30, keys | map(square) | sum);
385   pairs[5] = 25;
386   EXPECT_EQ(15, keys | sum);
387   EXPECT_EQ(55, values | sum);
388
389   vector<tuple<int, int, int>> tuples {
390     make_tuple(1, 1, 1),
391     make_tuple(2, 4, 8),
392     make_tuple(3, 9, 27),
393   };
394   EXPECT_EQ(36, from(tuples) | get<2>() | sum);
395 }
396
397 TEST(Gen, Any) {
398   EXPECT_TRUE(seq(0) | any);
399   EXPECT_TRUE(seq(0, 1) | any);
400   EXPECT_TRUE(from({1}) | any);
401   EXPECT_FALSE(range(0, 0) | any);
402   EXPECT_FALSE(from({1}) | take(0) | any);
403 }
404
405 TEST(Gen, Yielders) {
406   auto gen = GENERATOR(int) {
407     for (int i = 1; i <= 5; ++i) {
408       yield(i);
409     }
410     yield(7);
411     for (int i = 3; ; ++i) {
412       yield(i * i);
413     }
414   };
415   vector<int> expected {
416     1, 2, 3, 4, 5, 7, 9, 16, 25
417   };
418   EXPECT_EQ(expected, gen | take(9) | as<vector>());
419 }
420
421 TEST(Gen, NestedYield) {
422   auto nums = GENERATOR(int) {
423     for (int i = 1; ; ++i) {
424       yield(i);
425     }
426   };
427   auto gen = GENERATOR(int) {
428     nums | take(10) | yield;
429     seq(1, 5) | [&](int i) {
430       yield(i);
431     };
432   };
433   EXPECT_EQ(70, gen | sum);
434 }
435
436 TEST(Gen, MapYielders) {
437   auto gen = seq(1, 5)
438            | map([](int n) {
439                return GENERATOR(int) {
440                  int i;
441                  for (i = 1; i < n; ++i)
442                    yield(i);
443                  for (; i >= 1; --i)
444                    yield(i);
445                };
446              })
447            | concat;
448   vector<int> expected {
449                 1,
450              1, 2, 1,
451           1, 2, 3, 2, 1,
452        1, 2, 3, 4, 3, 2, 1,
453     1, 2, 3, 4, 5, 4, 3, 2, 1,
454   };
455   EXPECT_EQ(expected, gen | as<vector>());
456 }
457
458 TEST(Gen, VirtualGen) {
459   VirtualGen<int> v(seq(1, 10));
460   EXPECT_EQ(55, v | sum);
461   v = v | map(square);
462   EXPECT_EQ(385, v | sum);
463   v = v | take(5);
464   EXPECT_EQ(55, v | sum);
465   EXPECT_EQ(30, v | take(4) | sum);
466 }
467
468
469 TEST(Gen, CustomType) {
470   struct Foo{
471     int y;
472   };
473   auto gen = from({Foo{2}, Foo{3}})
474            | map([](const Foo& f) { return f.y; });
475   EXPECT_EQ(5, gen | sum);
476 }
477
478 TEST(Gen, NoNeedlessCopies) {
479   auto gen = seq(1, 5)
480            | map([](int x) { return unique_ptr<int>(new int(x)); })
481            | map([](unique_ptr<int> p) { return p; })
482            | map([](unique_ptr<int>&& p) { return std::move(p); })
483            | map([](const unique_ptr<int>& p) { return *p; });
484   EXPECT_EQ(15, gen | sum);
485   EXPECT_EQ(6, gen | take(3) | sum);
486 }
487
488 namespace {
489 class TestIntSeq : public GenImpl<int, TestIntSeq> {
490  public:
491   TestIntSeq() { }
492
493   template <class Body>
494   bool apply(Body&& body) const {
495     for (int i = 1; i < 6; ++i) {
496       if (!body(i)) {
497         return false;
498       }
499     }
500     return true;
501   }
502
503   TestIntSeq(TestIntSeq&&) = default;
504   TestIntSeq& operator=(TestIntSeq&&) = default;
505   TestIntSeq(const TestIntSeq&) = delete;
506   TestIntSeq& operator=(const TestIntSeq&) = delete;
507 };
508 }  // namespace
509
510 TEST(Gen, NoGeneratorCopies) {
511   EXPECT_EQ(15, TestIntSeq() | sum);
512   auto x = TestIntSeq() | take(3);
513   EXPECT_EQ(6, std::move(x) | sum);
514 }
515
516 TEST(Gen, FromArray) {
517   int source[] = {2, 3, 5, 7};
518   auto gen = from(source);
519   EXPECT_EQ(2 * 3 * 5 * 7, gen | product);
520 }
521
522 TEST(Gen, FromStdArray) {
523   std::array<int,4> source {{2, 3, 5, 7}};
524   auto gen = from(source);
525   EXPECT_EQ(2 * 3 * 5 * 7, gen | product);
526 }
527
528 TEST(Gen, StringConcat) {
529   auto gen = seq(1, 10)
530            | map([](int n) { return folly::to<fbstring>(n); })
531            | rconcat;
532   EXPECT_EQ("12345678910", gen | as<fbstring>());
533 }
534
535 struct CopyCounter {
536   static int alive;
537   int copies;
538   int moves;
539
540   CopyCounter() : copies(0), moves(0) {
541     ++alive;
542   }
543
544   CopyCounter(CopyCounter&& source) {
545     *this = std::move(source);
546     ++alive;
547   }
548
549   CopyCounter(const CopyCounter& source) {
550     *this = source;
551     ++alive;
552   }
553
554   ~CopyCounter() {
555     --alive;
556   }
557
558   CopyCounter& operator=(const CopyCounter& source) {
559     this->copies = source.copies + 1;
560     this->moves = source.moves;
561     return *this;
562   }
563
564   CopyCounter& operator=(CopyCounter&& source) {
565     this->copies = source.copies;
566     this->moves = source.moves + 1;
567     return *this;
568   }
569 };
570
571 int CopyCounter::alive = 0;
572
573 TEST(Gen, CopyCount) {
574   vector<CopyCounter> originals;
575   originals.emplace_back();
576   EXPECT_EQ(1, originals.size());
577   EXPECT_EQ(0, originals.back().copies);
578
579   vector<CopyCounter> copies = from(originals) | as<vector>();
580   EXPECT_EQ(1, copies.back().copies);
581   EXPECT_EQ(0, copies.back().moves);
582
583   vector<CopyCounter> moves = from(originals) | move | as<vector>();
584   EXPECT_EQ(0, moves.back().copies);
585   EXPECT_EQ(1, moves.back().moves);
586 }
587
588 // test dynamics with various layers of nested arrays.
589 TEST(Gen, Dynamic) {
590   dynamic array1 = {1, 2};
591   EXPECT_EQ(dynamic(3), from(array1) | sum);
592   dynamic array2 = {{1}, {1, 2}};
593   EXPECT_EQ(dynamic(4), from(array2) | rconcat | sum);
594   dynamic array3 = {{{1}}, {{1}, {1, 2}}};
595   EXPECT_EQ(dynamic(5), from(array3) | rconcat | rconcat | sum);
596 }
597
598 TEST(StringGen, EmptySplit) {
599   auto collect = eachTo<std::string>() | as<vector>();
600   {
601     auto pieces = split("", ',') | collect;
602     EXPECT_EQ(0, pieces.size());
603   }
604
605   // The last delimiter is eaten, just like std::getline
606   {
607     auto pieces = split(",", ',') | collect;
608     EXPECT_EQ(1, pieces.size());
609     EXPECT_EQ("", pieces[0]);
610   }
611
612   {
613     auto pieces = split(",,", ',') | collect;
614     EXPECT_EQ(2, pieces.size());
615     EXPECT_EQ("", pieces[0]);
616     EXPECT_EQ("", pieces[1]);
617   }
618
619   {
620     auto pieces = split(",,", ',') | take(1) | collect;
621     EXPECT_EQ(1, pieces.size());
622     EXPECT_EQ("", pieces[0]);
623   }
624 }
625
626 TEST(StringGen, Split) {
627   auto collect = eachTo<std::string>() | as<vector>();
628   {
629     auto pieces = split("hello,, world, goodbye, meow", ',') | collect;
630     EXPECT_EQ(5, pieces.size());
631     EXPECT_EQ("hello", pieces[0]);
632     EXPECT_EQ("", pieces[1]);
633     EXPECT_EQ(" world", pieces[2]);
634     EXPECT_EQ(" goodbye", pieces[3]);
635     EXPECT_EQ(" meow", pieces[4]);
636   }
637
638   {
639     auto pieces = split("hello,, world, goodbye, meow", ',')
640                 | take(3) | collect;
641     EXPECT_EQ(3, pieces.size());
642     EXPECT_EQ("hello", pieces[0]);
643     EXPECT_EQ("", pieces[1]);
644     EXPECT_EQ(" world", pieces[2]);
645   }
646
647   {
648     auto pieces = split("hello,, world, goodbye, meow", ',')
649                 | take(5) | collect;
650     EXPECT_EQ(5, pieces.size());
651     EXPECT_EQ("hello", pieces[0]);
652     EXPECT_EQ("", pieces[1]);
653     EXPECT_EQ(" world", pieces[2]);
654   }
655 }
656
657 TEST(StringGen, EmptyResplit) {
658   auto collect = eachTo<std::string>() | as<vector>();
659   {
660     auto pieces = from({""}) | resplit(',') | collect;
661     EXPECT_EQ(0, pieces.size());
662   }
663
664   // The last delimiter is eaten, just like std::getline
665   {
666     auto pieces = from({","}) | resplit(',') | collect;
667     EXPECT_EQ(1, pieces.size());
668     EXPECT_EQ("", pieces[0]);
669   }
670
671   {
672     auto pieces = from({",,"}) | resplit(',') | collect;
673     EXPECT_EQ(2, pieces.size());
674     EXPECT_EQ("", pieces[0]);
675     EXPECT_EQ("", pieces[1]);
676   }
677 }
678
679 TEST(StringGen, Resplit) {
680   auto collect = eachTo<std::string>() | as<vector>();
681   {
682     auto pieces = from({"hello,, world, goodbye, meow"}) |
683       resplit(',') | collect;
684     EXPECT_EQ(5, pieces.size());
685     EXPECT_EQ("hello", pieces[0]);
686     EXPECT_EQ("", pieces[1]);
687     EXPECT_EQ(" world", pieces[2]);
688     EXPECT_EQ(" goodbye", pieces[3]);
689     EXPECT_EQ(" meow", pieces[4]);
690   }
691   {
692     auto pieces = from({"hel", "lo,", ", world", ", goodbye, m", "eow"}) |
693       resplit(',') | collect;
694     EXPECT_EQ(5, pieces.size());
695     EXPECT_EQ("hello", pieces[0]);
696     EXPECT_EQ("", pieces[1]);
697     EXPECT_EQ(" world", pieces[2]);
698     EXPECT_EQ(" goodbye", pieces[3]);
699     EXPECT_EQ(" meow", pieces[4]);
700   }
701 }
702
703 TEST(FileGen, ByLine) {
704   auto collect = eachTo<std::string>() | as<vector>();
705   test::TemporaryFile file("ByLine");
706   static const std::string lines(
707       "Hello world\n"
708       "This is the second line\n"
709       "\n"
710       "\n"
711       "a few empty lines above\n"
712       "incomplete last line");
713   EXPECT_EQ(lines.size(), write(file.fd(), lines.data(), lines.size()));
714
715   auto expected = from({lines}) | resplit('\n') | collect;
716   auto found = byLine(file.path().c_str()) | collect;
717
718   EXPECT_TRUE(expected == found);
719 }
720
721 class FileGenBufferedTest : public ::testing::TestWithParam<int> { };
722
723 TEST_P(FileGenBufferedTest, FileWriter) {
724   size_t bufferSize = GetParam();
725   test::TemporaryFile file("FileWriter");
726
727   static const std::string lines(
728       "Hello world\n"
729       "This is the second line\n"
730       "\n"
731       "\n"
732       "a few empty lines above\n");
733
734   auto src = from({lines, lines, lines, lines, lines, lines, lines, lines});
735   auto collect = eachTo<std::string>() | as<vector>();
736   auto expected = src | resplit('\n') | collect;
737
738   src | eachAs<StringPiece>() | toFile(file.fd(), bufferSize);
739   auto found = byLine(file.path().c_str()) | collect;
740
741   EXPECT_TRUE(expected == found);
742 }
743
744 INSTANTIATE_TEST_CASE_P(
745     DifferentBufferSizes,
746     FileGenBufferedTest,
747     ::testing::Values(0, 1, 2, 4, 8, 64, 4096));
748
749 int main(int argc, char *argv[]) {
750   testing::InitGoogleTest(&argc, argv);
751   google::ParseCommandLineFlags(&argc, &argv, true);
752   return RUN_ALL_TESTS();
753 }