+++ /dev/null
-/*
- * Copyright 2014 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <glog/logging.h>
-#include <gtest/gtest.h>
-#include <iostream>
-#include <random>
-#include <set>
-#include <vector>
-
-#include "folly/FBString.h"
-#include "folly/FBVector.h"
-#include "folly/Format.h"
-#include "folly/MapUtil.h"
-#include "folly/Memory.h"
-#include "folly/dynamic.h"
-#include "folly/experimental/CombineGen.h"
-#include "folly/experimental/FileGen.h"
-#include "folly/experimental/Gen.h"
-#include "folly/experimental/StringGen.h"
-#include "folly/experimental/TestUtil.h"
-
-using namespace folly::gen;
-using namespace folly;
-using std::make_tuple;
-using std::ostream;
-using std::pair;
-using std::set;
-using std::string;
-using std::tuple;
-using std::unique_ptr;
-using std::vector;
-
-#define EXPECT_SAME(A, B) \
- static_assert(std::is_same<A, B>::value, "Mismatched: " #A ", " #B)
-EXPECT_SAME(int&&, typename ArgumentReference<int>::type);
-EXPECT_SAME(int&, typename ArgumentReference<int&>::type);
-EXPECT_SAME(const int&, typename ArgumentReference<const int&>::type);
-EXPECT_SAME(const int&, typename ArgumentReference<const int>::type);
-
-template<typename T>
-ostream& operator<<(ostream& os, const set<T>& values) {
- return os << from(values);
-}
-
-template<typename T>
-ostream& operator<<(ostream& os, const vector<T>& values) {
- os << "[";
- for (auto& value : values) {
- if (&value != &values.front()) {
- os << " ";
- }
- os << value;
- }
- return os << "]";
-}
-
-auto square = [](int x) { return x * x; };
-auto add = [](int a, int b) { return a + b; };
-auto multiply = [](int a, int b) { return a * b; };
-
-auto product = foldl(1, multiply);
-
-template<typename A, typename B>
-ostream& operator<<(ostream& os, const pair<A, B>& pair) {
- return os << "(" << pair.first << ", " << pair.second << ")";
-}
-
-TEST(Gen, Count) {
- auto gen = seq(1, 10);
- EXPECT_EQ(10, gen | count);
- EXPECT_EQ(5, gen | take(5) | count);
-}
-
-TEST(Gen, Sum) {
- auto gen = seq(1, 10);
- EXPECT_EQ((1 + 10) * 10 / 2, gen | sum);
- EXPECT_EQ((1 + 5) * 5 / 2, gen | take(5) | sum);
-}
-
-TEST(Gen, Foreach) {
- auto gen = seq(1, 4);
- int accum = 0;
- gen | [&](int x) { accum += x; };
- EXPECT_EQ(10, accum);
- int accum2 = 0;
- gen | take(3) | [&](int x) { accum2 += x; };
- EXPECT_EQ(6, accum2);
-}
-
-TEST(Gen, Map) {
- auto expected = vector<int>{4, 9, 16};
- auto gen = from({2, 3, 4}) | map(square);
- EXPECT_EQ((vector<int>{4, 9, 16}), gen | as<vector>());
- EXPECT_EQ((vector<int>{4, 9}), gen | take(2) | as<vector>());
-}
-
-TEST(Gen, Member) {
- struct Counter {
- Counter(int start = 0)
- : c(start)
- {}
-
- int count() const { return c; }
- int incr() { return ++c; }
-
- int& ref() { return c; }
- const int& ref() const { return c; }
- private:
- int c;
- };
- auto counters = seq(1, 10) | eachAs<Counter>() | as<vector>();
- EXPECT_EQ(10 * (1 + 10) / 2,
- from(counters)
- | member(&Counter::count)
- | sum);
- EXPECT_EQ(10 * (2 + 11) / 2,
- from(counters)
- | member(&Counter::incr)
- | sum);
- EXPECT_EQ(10 * (2 + 11) / 2,
- from(counters)
- | member(&Counter::count)
- | sum);
-
- // type-verifications
- auto m = empty<Counter&>();
- auto c = empty<const Counter&>();
- m | member(&Counter::incr) | assert_type<int&&>();
- m | member(&Counter::count) | assert_type<int&&>();
- m | member(&Counter::count) | assert_type<int&&>();
- m | member<Const>(&Counter::ref) | assert_type<const int&>();
- m | member<Mutable>(&Counter::ref) | assert_type<int&>();
- c | member<Const>(&Counter::ref) | assert_type<const int&>();
-}
-
-TEST(Gen, Field) {
- struct X {
- X() : a(2), b(3), c(4), d(b) {}
-
- const int a;
- int b;
- mutable int c;
- int& d; // can't access this with a field pointer.
- };
-
- std::vector<X> xs(1);
- EXPECT_EQ(2, from(xs)
- | field(&X::a)
- | first);
- EXPECT_EQ(3, from(xs)
- | field(&X::b)
- | first);
- EXPECT_EQ(4, from(xs)
- | field(&X::c)
- | first);
- // type-verification
- empty<X&>() | field(&X::a) | assert_type<const int&>();
- empty<X&>() | field(&X::b) | assert_type<int&>();
- empty<X&>() | field(&X::c) | assert_type<int&>();
- empty<X&&>() | field(&X::a) | assert_type<const int&&>();
- empty<X&&>() | field(&X::b) | assert_type<int&&>();
- empty<X&&>() | field(&X::c) | assert_type<int&&>();
- // references don't imply ownership so they're not moved
- empty<const X&>() | field(&X::a) | assert_type<const int&>();
- empty<const X&>() | field(&X::b) | assert_type<const int&>();
- // 'mutable' has no effect on field pointers, by C++ spec
- empty<const X&>() | field(&X::c) | assert_type<const int&>();
-
- // can't form pointer-to-reference field: empty<X&>() | field(&X::d)
-}
-
-TEST(Gen, Seq) {
- // cover the fenceposts of the loop unrolling
- for (int n = 1; n < 100; ++n) {
- EXPECT_EQ(n, seq(1, n) | count);
- EXPECT_EQ(n + 1, seq(1) | take(n + 1) | count);
- }
-}
-
-TEST(Gen, Range) {
- // cover the fenceposts of the loop unrolling
- for (int n = 1; n < 100; ++n) {
- EXPECT_EQ(range(0, n) | count, n);
- }
-}
-
-TEST(Gen, FromIterators) {
- vector<int> source {2, 3, 5, 7, 11};
- auto gen = from(makeRange(source.begin() + 1, source.end() - 1));
- EXPECT_EQ(3 * 5 * 7, gen | product);
-}
-
-TEST(Gen, FromMap) {
- auto source = seq(0, 10)
- | map([](int i) { return std::make_pair(i, i * i); })
- | as<std::map<int, int>>();
- auto gen = fromConst(source)
- | map([&](const std::pair<const int, int>& p) {
- return p.second - p.first;
- });
- EXPECT_EQ(330, gen | sum);
-}
-
-TEST(Gen, Filter) {
- const auto expected = vector<int>{1, 2, 4, 5, 7, 8};
- auto actual =
- seq(1, 9)
- | filter([](int x) { return x % 3; })
- | as<vector<int>>();
- EXPECT_EQ(expected, actual);
-}
-
-TEST(Gen, Contains) {
- {
- auto gen =
- seq(1, 9)
- | map(square);
- EXPECT_TRUE(gen | contains(49));
- EXPECT_FALSE(gen | contains(50));
- }
- {
- auto gen =
- seq(1) // infinite, to prove laziness
- | map(square)
- | eachTo<std::string>();
-
- // std::string gen, const char* needle
- EXPECT_TRUE(gen | take(9999) | contains("49"));
- }
-}
-
-TEST(Gen, Take) {
- {
- auto expected = vector<int>{1, 4, 9, 16};
- auto actual =
- seq(1, 1000)
- | mapped([](int x) { return x * x; })
- | take(4)
- | as<vector<int>>();
- EXPECT_EQ(expected, actual);
- }
- {
- auto expected = vector<int>{ 0, 1, 4, 5, 8 };
- auto actual
- = ((seq(0) | take(2)) +
- (seq(4) | take(2)) +
- (seq(8) | take(2)))
- | take(5)
- | as<vector>();
- EXPECT_EQ(expected, actual);
- }
- {
- auto expected = vector<int>{ 0, 1, 4, 5, 8 };
- auto actual
- = seq(0)
- | mapped([](int i) {
- return seq(i * 4) | take(2);
- })
- | concat
- | take(5)
- | as<vector>();
- EXPECT_EQ(expected, actual);
- }
-}
-
-TEST(Gen, Sample) {
- std::mt19937 rnd(42);
-
- auto sampler =
- seq(1, 100)
- | sample(50, rnd);
- std::unordered_map<int,int> hits;
- const int kNumIters = 80;
- for (int i = 0; i < kNumIters; i++) {
- auto vec = sampler | as<vector<int>>();
- EXPECT_EQ(vec.size(), 50);
- auto uniq = fromConst(vec) | as<set<int>>();
- EXPECT_EQ(uniq.size(), vec.size()); // sampling without replacement
- for (auto v: vec) {
- ++hits[v];
- }
- }
-
- // In 80 separate samples of our range, we should have seen every value
- // at least once and no value all 80 times. (The odds of either of those
- // events is 1/2^80).
- EXPECT_EQ(hits.size(), 100);
- for (auto hit: hits) {
- EXPECT_GT(hit.second, 0);
- EXPECT_LT(hit.second, kNumIters);
- }
-
- auto small =
- seq(1, 5)
- | sample(10);
- EXPECT_EQ((small | sum), 15);
- EXPECT_EQ((small | take(3) | count), 3);
-}
-
-TEST(Gen, Skip) {
- auto gen =
- seq(1, 1000)
- | mapped([](int x) { return x * x; })
- | skip(4)
- | take(4);
- EXPECT_EQ((vector<int>{25, 36, 49, 64}), gen | as<vector>());
-}
-
-TEST(Gen, Until) {
- {
- auto expected = vector<int>{1, 4, 9, 16};
- auto actual
- = seq(1, 1000)
- | mapped([](int x) { return x * x; })
- | until([](int x) { return x > 20; })
- | as<vector<int>>();
- EXPECT_EQ(expected, actual);
- }
- {
- auto expected = vector<int>{ 0, 1, 4, 5, 8 };
- auto actual
- = ((seq(0) | until([](int i) { return i > 1; })) +
- (seq(4) | until([](int i) { return i > 5; })) +
- (seq(8) | until([](int i) { return i > 9; })))
- | until([](int i) { return i > 8; })
- | as<vector<int>>();
- EXPECT_EQ(expected, actual);
- }
- /*
- {
- auto expected = vector<int>{ 0, 1, 5, 6, 10 };
- auto actual
- = seq(0)
- | mapped([](int i) {
- return seq(i * 5) | until([=](int j) { return j > i * 5 + 1; });
- })
- | concat
- | until([](int i) { return i > 10; })
- | as<vector<int>>();
- EXPECT_EQ(expected, actual);
- }
- */
-}
-
-auto even = [](int i) -> bool { return i % 2 == 0; };
-auto odd = [](int i) -> bool { return i % 2 == 1; };
-
-TEST(CombineGen, Interleave) {
- { // large (infinite) base, small container
- auto base = seq(1) | filter(odd);
- auto toInterleave = seq(1, 6) | filter(even);
- auto interleaved = base | interleave(toInterleave | as<vector>());
- EXPECT_EQ(interleaved | as<vector>(), vector<int>({1, 2, 3, 4, 5, 6}));
- }
- { // small base, large container
- auto base = seq(1) | filter(odd) | take(3);
- auto toInterleave = seq(1) | filter(even) | take(50);
- auto interleaved = base | interleave(toInterleave | as<vector>());
- EXPECT_EQ(interleaved | as<vector>(),
- vector<int>({1, 2, 3, 4, 5, 6}));
- }
-}
-
-TEST(CombineGen, Zip) {
- auto base0 = seq(1);
- // We rely on std::move(fbvector) emptying the source vector
- auto zippee = fbvector<string>{"one", "two", "three"};
- {
- auto combined = base0
- | zip(zippee)
- | as<vector>();
- ASSERT_EQ(combined.size(), 3);
- EXPECT_EQ(std::get<0>(combined[0]), 1);
- EXPECT_EQ(std::get<1>(combined[0]), "one");
- EXPECT_EQ(std::get<0>(combined[1]), 2);
- EXPECT_EQ(std::get<1>(combined[1]), "two");
- EXPECT_EQ(std::get<0>(combined[2]), 3);
- EXPECT_EQ(std::get<1>(combined[2]), "three");
- ASSERT_FALSE(zippee.empty());
- EXPECT_FALSE(zippee.front().empty()); // shouldn't have been move'd
- }
-
- { // same as top, but using std::move.
- auto combined = base0
- | zip(std::move(zippee))
- | as<vector>();
- ASSERT_EQ(combined.size(), 3);
- EXPECT_EQ(std::get<0>(combined[0]), 1);
- EXPECT_TRUE(zippee.empty());
- }
-
- { // same as top, but base is truncated
- auto baseFinite = seq(1) | take(1);
- auto combined = baseFinite
- | zip(vector<string>{"one", "two", "three"})
- | as<vector>();
- ASSERT_EQ(combined.size(), 1);
- EXPECT_EQ(std::get<0>(combined[0]), 1);
- EXPECT_EQ(std::get<1>(combined[0]), "one");
- }
-}
-
-TEST(CombineGen, TupleFlatten) {
- vector<tuple<int,string>> intStringTupleVec{
- tuple<int,string>{1, "1"},
- tuple<int,string>{2, "2"},
- tuple<int,string>{3, "3"},
- };
-
- vector<tuple<char>> charTupleVec{
- tuple<char>{'A'},
- tuple<char>{'B'},
- tuple<char>{'C'},
- tuple<char>{'D'},
- };
-
- vector<double> doubleVec{
- 1.0,
- 4.0,
- 9.0,
- 16.0,
- 25.0,
- };
-
- auto zipped1 = from(intStringTupleVec)
- | zip(charTupleVec)
- | assert_type<tuple<tuple<int, string>, tuple<char>>>()
- | as<vector>();
- EXPECT_EQ(std::get<0>(zipped1[0]), std::make_tuple(1, "1"));
- EXPECT_EQ(std::get<1>(zipped1[0]), std::make_tuple('A'));
-
- auto zipped2 = from(zipped1)
- | tuple_flatten
- | assert_type<tuple<int, string, char>&&>()
- | as<vector>();
- ASSERT_EQ(zipped2.size(), 3);
- EXPECT_EQ(zipped2[0], std::make_tuple(1, "1", 'A'));
-
- auto zipped3 = from(charTupleVec)
- | zip(intStringTupleVec)
- | tuple_flatten
- | assert_type<tuple<char, int, string>&&>()
- | as<vector>();
- ASSERT_EQ(zipped3.size(), 3);
- EXPECT_EQ(zipped3[0], std::make_tuple('A', 1, "1"));
-
- auto zipped4 = from(intStringTupleVec)
- | zip(doubleVec)
- | tuple_flatten
- | assert_type<tuple<int, string, double>&&>()
- | as<vector>();
- ASSERT_EQ(zipped4.size(), 3);
- EXPECT_EQ(zipped4[0], std::make_tuple(1, "1", 1.0));
-
- auto zipped5 = from(doubleVec)
- | zip(doubleVec)
- | assert_type<tuple<double, double>>()
- | tuple_flatten // essentially a no-op
- | assert_type<tuple<double, double>&&>()
- | as<vector>();
- ASSERT_EQ(zipped5.size(), 5);
- EXPECT_EQ(zipped5[0], std::make_tuple(1.0, 1.0));
-
- auto zipped6 = from(intStringTupleVec)
- | zip(charTupleVec)
- | tuple_flatten
- | zip(doubleVec)
- | tuple_flatten
- | assert_type<tuple<int, string, char, double>&&>()
- | as<vector>();
- ASSERT_EQ(zipped6.size(), 3);
- EXPECT_EQ(zipped6[0], std::make_tuple(1, "1", 'A', 1.0));
-}
-
-TEST(Gen, Composed) {
- // Operator, Operator
- auto valuesOf =
- filter([](Optional<int>& o) { return o.hasValue(); })
- | map([](Optional<int>& o) -> int& { return o.value(); });
- std::vector<Optional<int>> opts {
- none, 4, none, 6, none
- };
- EXPECT_EQ(4 * 4 + 6 * 6, from(opts) | valuesOf | map(square) | sum);
- // Operator, Sink
- auto sumOpt = valuesOf | sum;
- EXPECT_EQ(10, from(opts) | sumOpt);
-}
-
-TEST(Gen, Chain) {
- std::vector<int> nums {2, 3, 5, 7};
- std::map<int, int> mappings { { 3, 9}, {5, 25} };
- auto gen = from(nums) + (from(mappings) | get<1>());
- EXPECT_EQ(51, gen | sum);
- EXPECT_EQ(5, gen | take(2) | sum);
- EXPECT_EQ(26, gen | take(5) | sum);
-}
-
-TEST(Gen, Concat) {
- std::vector<std::vector<int>> nums {{2, 3}, {5, 7}};
- auto gen = from(nums) | rconcat;
- EXPECT_EQ(17, gen | sum);
- EXPECT_EQ(10, gen | take(3) | sum);
-}
-
-TEST(Gen, ConcatGen) {
- auto gen = seq(1, 10)
- | map([](int i) { return seq(1, i); })
- | concat;
- EXPECT_EQ(220, gen | sum);
- EXPECT_EQ(10, gen | take(6) | sum);
-}
-
-TEST(Gen, ConcatAlt) {
- std::vector<std::vector<int>> nums {{2, 3}, {5, 7}};
- auto actual = from(nums)
- | map([](std::vector<int>& v) { return from(v); })
- | concat
- | sum;
- auto expected = 17;
- EXPECT_EQ(expected, actual);
-}
-
-TEST(Gen, Order) {
- auto expected = vector<int>{0, 3, 5, 6, 7, 8, 9};
- auto actual =
- from({8, 6, 7, 5, 3, 0, 9})
- | order
- | as<vector>();
- EXPECT_EQ(expected, actual);
-}
-
-TEST(Gen, OrderMoved) {
- auto expected = vector<int>{0, 9, 25, 36, 49, 64, 81};
- auto actual =
- from({8, 6, 7, 5, 3, 0, 9})
- | move
- | order
- | map(square)
- | as<vector>();
- EXPECT_EQ(expected, actual);
-}
-
-TEST(Gen, OrderTake) {
- auto expected = vector<int>{9, 8, 7};
- auto actual =
- from({8, 6, 7, 5, 3, 0, 9})
- | orderByDescending(square)
- | take(3)
- | as<vector>();
- EXPECT_EQ(expected, actual);
-}
-
-TEST(Gen, Distinct) {
- auto expected = vector<int>{3, 1, 2};
- auto actual =
- from({3, 1, 3, 2, 1, 2, 3})
- | distinct
- | as<vector>();
- EXPECT_EQ(expected, actual);
-}
-
-TEST(Gen, DistinctBy) { // 0 1 4 9 6 5 6 9 4 1 0
- auto expected = vector<int>{0, 1, 2, 3, 4, 5};
- auto actual =
- seq(0, 100)
- | distinctBy([](int i) { return i * i % 10; })
- | as<vector>();
- EXPECT_EQ(expected, actual);
-}
-
-TEST(Gen, DistinctMove) { // 0 1 4 9 6 5 6 9 4 1 0
- auto expected = vector<int>{0, 1, 2, 3, 4, 5};
- auto actual =
- seq(0, 100)
- | mapped([](int i) { return std::unique_ptr<int>(new int(i)); })
- // see comment below about selector parameters for Distinct
- | distinctBy([](const std::unique_ptr<int>& pi) { return *pi * *pi % 10; })
- | mapped([](std::unique_ptr<int> pi) { return *pi; })
- | as<vector>();
-
- // NOTE(tjackson): the following line intentionally doesn't work:
- // | distinctBy([](std::unique_ptr<int> pi) { return *pi * *pi % 10; })
- // This is because distinctBy because the selector intentionally requires a
- // const reference. If it required a move-reference, the value might get
- // gutted by the selector before said value could be passed to downstream
- // operators.
- EXPECT_EQ(expected, actual);
-}
-
-TEST(Gen, MinBy) {
- EXPECT_EQ(7, seq(1, 10)
- | minBy([](int i) -> double {
- double d = i - 6.8;
- return d * d;
- }));
-}
-
-TEST(Gen, MaxBy) {
- auto gen = from({"three", "eleven", "four"});
-
- EXPECT_EQ("eleven", gen | maxBy(&strlen));
-}
-
-TEST(Gen, Append) {
- fbstring expected = "facebook";
- fbstring actual = "face";
- from(StringPiece("book")) | appendTo(actual);
- EXPECT_EQ(expected, actual);
-}
-
-TEST(Gen, FromRValue) {
- {
- // AFAICT The C++ Standard does not specify what happens to the rvalue
- // reference of a std::vector when it is used as the 'other' for an rvalue
- // constructor. Use fbvector because we're sure its size will be zero in
- // this case.
- folly::fbvector<int> v({1,2,3,4});
- auto q1 = from(v);
- EXPECT_EQ(v.size(), 4); // ensure that the lvalue version was called!
- auto expected = 1 * 2 * 3 * 4;
- EXPECT_EQ(expected, q1 | product);
-
- auto q2 = from(std::move(v));
- EXPECT_EQ(v.size(), 0); // ensure that rvalue version was called
- EXPECT_EQ(expected, q2 | product);
- }
- {
- auto expected = 7;
- auto q = from([] {return vector<int>({3,7,5}); }());
- EXPECT_EQ(expected, q | max);
- }
- {
- for (auto size: {5, 1024, 16384, 1<<20}) {
- auto q1 = from(vector<int>(size, 2));
- auto q2 = from(vector<int>(size, 3));
- // If the rvalue specialization is broken/gone, then the compiler will
- // (disgustingly!) just store a *reference* to the temporary object,
- // which is bad. Try to catch this by allocating two temporary vectors
- // of the same size, so that they'll probably use the same underlying
- // buffer if q1's vector is destructed before q2's vector is constructed.
- EXPECT_EQ(size * 2 + size * 3, (q1 | sum) + (q2 | sum));
- }
- }
- {
- auto q = from(set<int>{1,2,3,2,1});
- EXPECT_EQ(q | sum, 6);
- }
-}
-
-TEST(Gen, OrderBy) {
- auto expected = vector<int>{5, 6, 4, 7, 3, 8, 2, 9, 1, 10};
- auto actual =
- seq(1, 10)
- | orderBy([](int x) { return (5.1 - x) * (5.1 - x); })
- | as<vector>();
- EXPECT_EQ(expected, actual);
-}
-
-TEST(Gen, Foldl) {
- int expected = 2 * 3 * 4 * 5;
- auto actual =
- seq(2, 5)
- | foldl(1, multiply);
- EXPECT_EQ(expected, actual);
-}
-
-TEST(Gen, Reduce) {
- int expected = 2 + 3 + 4 + 5;
- auto actual = seq(2, 5) | reduce(add);
- EXPECT_EQ(expected, actual);
-}
-
-TEST(Gen, ReduceBad) {
- auto gen = seq(1) | take(0);
- try {
- EXPECT_TRUE(true);
- gen | reduce(add);
- EXPECT_TRUE(false);
- } catch (...) {
- }
-}
-
-TEST(Gen, Moves) {
- std::vector<unique_ptr<int>> ptrs;
- ptrs.emplace_back(new int(1));
- EXPECT_NE(ptrs.front().get(), nullptr);
- auto ptrs2 = from(ptrs) | move | as<vector>();
- EXPECT_EQ(ptrs.front().get(), nullptr);
- EXPECT_EQ(**ptrs2.data(), 1);
-}
-
-TEST(Gen, First) {
- auto gen =
- seq(0)
- | filter([](int x) { return x > 3; });
- EXPECT_EQ(4, gen | first);
-}
-
-TEST(Gen, FromCopy) {
- vector<int> v {3, 5};
- auto src = from(v);
- auto copy = fromCopy(v);
- EXPECT_EQ(8, src | sum);
- EXPECT_EQ(8, copy | sum);
- v[1] = 7;
- EXPECT_EQ(10, src | sum);
- EXPECT_EQ(8, copy | sum);
-}
-
-TEST(Gen, Get) {
- std::map<int, int> pairs {
- {1, 1},
- {2, 4},
- {3, 9},
- {4, 16},
- };
- auto pairSrc = from(pairs);
- auto keys = pairSrc | get<0>();
- auto values = pairSrc | get<1>();
- EXPECT_EQ(10, keys | sum);
- EXPECT_EQ(30, values | sum);
- EXPECT_EQ(30, keys | map(square) | sum);
- pairs[5] = 25;
- EXPECT_EQ(15, keys | sum);
- EXPECT_EQ(55, values | sum);
-
- vector<tuple<int, int, int>> tuples {
- make_tuple(1, 1, 1),
- make_tuple(2, 4, 8),
- make_tuple(3, 9, 27),
- };
- EXPECT_EQ(36, from(tuples) | get<2>() | sum);
-}
-
-TEST(Gen, Any) {
- EXPECT_TRUE(seq(0) | any);
- EXPECT_TRUE(seq(0, 1) | any);
- EXPECT_TRUE(seq(0, 10) | any([](int i) { return i == 7; }));
- EXPECT_FALSE(seq(0, 10) | any([](int i) { return i == 11; }));
-
- EXPECT_TRUE(from({1}) | any);
- EXPECT_FALSE(range(0, 0) | any);
- EXPECT_FALSE(from({1}) | take(0) | any);
-}
-
-TEST(Gen, All) {
- EXPECT_TRUE(seq(0, 10) | all([](int i) { return i < 11; }));
- EXPECT_FALSE(seq(0, 10) | all([](int i) { return i < 5; }));
- EXPECT_FALSE(seq(0) | take(9999) | all([](int i) { return i < 10; }));
-
- // empty lists satisfies all
- EXPECT_TRUE(seq(0) | take(0) | all([](int i) { return i < 50; }));
- EXPECT_TRUE(seq(0) | take(0) | all([](int i) { return i > 50; }));
-}
-
-TEST(Gen, Yielders) {
- auto gen = GENERATOR(int) {
- for (int i = 1; i <= 5; ++i) {
- yield(i);
- }
- yield(7);
- for (int i = 3; ; ++i) {
- yield(i * i);
- }
- };
- vector<int> expected {
- 1, 2, 3, 4, 5, 7, 9, 16, 25
- };
- EXPECT_EQ(expected, gen | take(9) | as<vector>());
-}
-
-TEST(Gen, NestedYield) {
- auto nums = GENERATOR(int) {
- for (int i = 1; ; ++i) {
- yield(i);
- }
- };
- auto gen = GENERATOR(int) {
- nums | take(10) | yield;
- seq(1, 5) | [&](int i) {
- yield(i);
- };
- };
- EXPECT_EQ(70, gen | sum);
-}
-
-TEST(Gen, MapYielders) {
- auto gen = seq(1, 5)
- | map([](int n) {
- return GENERATOR(int) {
- int i;
- for (i = 1; i < n; ++i)
- yield(i);
- for (; i >= 1; --i)
- yield(i);
- };
- })
- | concat;
- vector<int> expected {
- 1,
- 1, 2, 1,
- 1, 2, 3, 2, 1,
- 1, 2, 3, 4, 3, 2, 1,
- 1, 2, 3, 4, 5, 4, 3, 2, 1,
- };
- EXPECT_EQ(expected, gen | as<vector>());
-}
-
-TEST(Gen, VirtualGen) {
- VirtualGen<int> v(seq(1, 10));
- EXPECT_EQ(55, v | sum);
- v = v | map(square);
- EXPECT_EQ(385, v | sum);
- v = v | take(5);
- EXPECT_EQ(55, v | sum);
- EXPECT_EQ(30, v | take(4) | sum);
-}
-
-
-TEST(Gen, CustomType) {
- struct Foo{
- int y;
- };
- auto gen = from({Foo{2}, Foo{3}})
- | map([](const Foo& f) { return f.y; });
- EXPECT_EQ(5, gen | sum);
-}
-
-TEST(Gen, NoNeedlessCopies) {
- auto gen = seq(1, 5)
- | map([](int x) { return unique_ptr<int>(new int(x)); })
- | map([](unique_ptr<int> p) { return p; })
- | map([](unique_ptr<int>&& p) { return std::move(p); })
- | map([](const unique_ptr<int>& p) { return *p; });
- EXPECT_EQ(15, gen | sum);
- EXPECT_EQ(6, gen | take(3) | sum);
-}
-
-namespace {
-
-class TestIntSeq : public GenImpl<int, TestIntSeq> {
- public:
- TestIntSeq() { }
-
- template <class Body>
- bool apply(Body&& body) const {
- for (int i = 1; i < 6; ++i) {
- if (!body(i)) {
- return false;
- }
- }
- return true;
- }
-
- TestIntSeq(TestIntSeq&&) = default;
- TestIntSeq& operator=(TestIntSeq&&) = default;
- TestIntSeq(const TestIntSeq&) = delete;
- TestIntSeq& operator=(const TestIntSeq&) = delete;
-};
-
-} // namespace
-
-TEST(Gen, NoGeneratorCopies) {
- EXPECT_EQ(15, TestIntSeq() | sum);
- auto x = TestIntSeq() | take(3);
- EXPECT_EQ(6, std::move(x) | sum);
-}
-
-TEST(Gen, FromArray) {
- int source[] = {2, 3, 5, 7};
- auto gen = from(source);
- EXPECT_EQ(2 * 3 * 5 * 7, gen | product);
-}
-
-TEST(Gen, FromStdArray) {
- std::array<int,4> source {{2, 3, 5, 7}};
- auto gen = from(source);
- EXPECT_EQ(2 * 3 * 5 * 7, gen | product);
-}
-
-TEST(Gen, StringConcat) {
- auto gen = seq(1, 10)
- | map([](int n) { return folly::to<fbstring>(n); })
- | rconcat;
- EXPECT_EQ("12345678910", gen | as<fbstring>());
-}
-
-struct CopyCounter {
- static int alive;
- int copies;
- int moves;
-
- CopyCounter() : copies(0), moves(0) {
- ++alive;
- }
-
- CopyCounter(CopyCounter&& source) {
- *this = std::move(source);
- ++alive;
- }
-
- CopyCounter(const CopyCounter& source) {
- *this = source;
- ++alive;
- }
-
- ~CopyCounter() {
- --alive;
- }
-
- CopyCounter& operator=(const CopyCounter& source) {
- this->copies = source.copies + 1;
- this->moves = source.moves;
- return *this;
- }
-
- CopyCounter& operator=(CopyCounter&& source) {
- this->copies = source.copies;
- this->moves = source.moves + 1;
- return *this;
- }
-};
-
-int CopyCounter::alive = 0;
-
-TEST(Gen, CopyCount) {
- vector<CopyCounter> originals;
- originals.emplace_back();
- EXPECT_EQ(1, originals.size());
- EXPECT_EQ(0, originals.back().copies);
-
- vector<CopyCounter> copies = from(originals) | as<vector>();
- EXPECT_EQ(1, copies.back().copies);
- EXPECT_EQ(0, copies.back().moves);
-
- vector<CopyCounter> moves = from(originals) | move | as<vector>();
- EXPECT_EQ(0, moves.back().copies);
- EXPECT_EQ(1, moves.back().moves);
-}
-
-// test dynamics with various layers of nested arrays.
-TEST(Gen, Dynamic) {
- dynamic array1 = {1, 2};
- EXPECT_EQ(dynamic(3), from(array1) | sum);
- dynamic array2 = {{1}, {1, 2}};
- EXPECT_EQ(dynamic(4), from(array2) | rconcat | sum);
- dynamic array3 = {{{1}}, {{1}, {1, 2}}};
- EXPECT_EQ(dynamic(5), from(array3) | rconcat | rconcat | sum);
-}
-
-TEST(Gen, DynamicObject) {
- const dynamic obj = dynamic::object(1, 2)(3, 4);
- EXPECT_EQ(dynamic(4), from(obj.keys()) | sum);
- EXPECT_EQ(dynamic(6), from(obj.values()) | sum);
- EXPECT_EQ(dynamic(4), from(obj.items()) | get<0>() | sum);
- EXPECT_EQ(dynamic(6), from(obj.items()) | get<1>() | sum);
-}
-
-TEST(Gen, Collect) {
- auto s = from({7, 6, 5, 4, 3}) | as<set<int>>();
- EXPECT_EQ(s.size(), 5);
-}
-
-TEST(StringGen, EmptySplit) {
- auto collect = eachTo<std::string>() | as<vector>();
- {
- auto pieces = split("", ',') | collect;
- EXPECT_EQ(0, pieces.size());
- }
-
- // The last delimiter is eaten, just like std::getline
- {
- auto pieces = split(",", ',') | collect;
- EXPECT_EQ(1, pieces.size());
- EXPECT_EQ("", pieces[0]);
- }
-
- {
- auto pieces = split(",,", ',') | collect;
- EXPECT_EQ(2, pieces.size());
- EXPECT_EQ("", pieces[0]);
- EXPECT_EQ("", pieces[1]);
- }
-
- {
- auto pieces = split(",,", ',') | take(1) | collect;
- EXPECT_EQ(1, pieces.size());
- EXPECT_EQ("", pieces[0]);
- }
-}
-
-TEST(Gen, Cycle) {
- {
- auto s = from({1, 2});
- EXPECT_EQ((vector<int> { 1, 2, 1, 2, 1 }),
- s | cycle | take(5) | as<vector>());
- }
- {
- auto s = from({1, 2});
- EXPECT_EQ((vector<int> { 1, 2, 1, 2 }),
- s | cycle(2) | as<vector>());
- }
- {
- auto s = from({1, 2, 3});
- EXPECT_EQ((vector<int> { 1, 2, 1, 2, 1 }),
- s | take(2) | cycle | take(5) | as<vector>());
- }
- {
- auto s = empty<int>();
- EXPECT_EQ((vector<int> { }),
- s | cycle | take(4) | as<vector>());
- }
- {
- int count = 3;
- int* pcount = &count;
- auto countdown = GENERATOR(int) {
- ASSERT_GE(*pcount, 0)
- << "Cycle should have stopped when it didnt' get values!";
- for (int i = 1; i <= *pcount; ++i) {
- yield(i);
- }
- --*pcount;
- };
- auto s = countdown;
- EXPECT_EQ((vector<int> { 1, 2, 3, 1, 2, 1}),
- s | cycle | as<vector>());
- }
-}
-
-TEST(Gen, Dereference) {
- {
- const int x = 4, y = 2;
- auto s = from<const int*>({&x, nullptr, &y});
- EXPECT_EQ(6, s | dereference | sum);
- }
- {
- vector<int> a { 1, 2 };
- vector<int> b { 3, 4 };
- vector<vector<int>*> pv { &a, nullptr, &b };
- from(pv)
- | dereference
- | [&](vector<int>& v) {
- v.push_back(5);
- };
- EXPECT_EQ(3, a.size());
- EXPECT_EQ(3, b.size());
- EXPECT_EQ(5, a.back());
- EXPECT_EQ(5, b.back());
- }
- {
- vector<std::map<int, int>> maps {
- {
- { 2, 31 },
- { 3, 41 },
- },
- {
- { 3, 52 },
- { 4, 62 },
- },
- {
- { 4, 73 },
- { 5, 83 },
- },
- };
- EXPECT_EQ(
- 93,
- from(maps)
- | map([](std::map<int, int>& m) {
- return get_ptr(m, 3);
- })
- | dereference
- | sum);
- }
- {
- vector<unique_ptr<int>> ups;
- ups.emplace_back(new int(3));
- ups.emplace_back();
- ups.emplace_back(new int(7));
- EXPECT_EQ(10, from(ups) | dereference | sum);
- EXPECT_EQ(10, from(ups) | move | dereference | sum);
- }
-}
-
-TEST(StringGen, Split) {
- auto collect = eachTo<std::string>() | as<vector>();
- {
- auto pieces = split("hello,, world, goodbye, meow", ',') | collect;
- EXPECT_EQ(5, pieces.size());
- EXPECT_EQ("hello", pieces[0]);
- EXPECT_EQ("", pieces[1]);
- EXPECT_EQ(" world", pieces[2]);
- EXPECT_EQ(" goodbye", pieces[3]);
- EXPECT_EQ(" meow", pieces[4]);
- }
-
- {
- auto pieces = split("hello,, world, goodbye, meow", ',')
- | take(3) | collect;
- EXPECT_EQ(3, pieces.size());
- EXPECT_EQ("hello", pieces[0]);
- EXPECT_EQ("", pieces[1]);
- EXPECT_EQ(" world", pieces[2]);
- }
-
- {
- auto pieces = split("hello,, world, goodbye, meow", ',')
- | take(5) | collect;
- EXPECT_EQ(5, pieces.size());
- EXPECT_EQ("hello", pieces[0]);
- EXPECT_EQ("", pieces[1]);
- EXPECT_EQ(" world", pieces[2]);
- }
-}
-
-TEST(StringGen, EmptyResplit) {
- auto collect = eachTo<std::string>() | as<vector>();
- {
- auto pieces = from({""}) | resplit(',') | collect;
- EXPECT_EQ(0, pieces.size());
- }
-
- // The last delimiter is eaten, just like std::getline
- {
- auto pieces = from({","}) | resplit(',') | collect;
- EXPECT_EQ(1, pieces.size());
- EXPECT_EQ("", pieces[0]);
- }
-
- {
- auto pieces = from({",,"}) | resplit(',') | collect;
- EXPECT_EQ(2, pieces.size());
- EXPECT_EQ("", pieces[0]);
- EXPECT_EQ("", pieces[1]);
- }
-}
-
-TEST(StringGen, EachToTuple) {
- {
- auto lines = "2:1.414:yo 3:1.732:hi";
- auto actual
- = split(lines, ' ')
- | eachToTuple<int, double, std::string>(':')
- | as<vector>();
- vector<tuple<int, double, std::string>> expected {
- make_tuple(2, 1.414, "yo"),
- make_tuple(3, 1.732, "hi"),
- };
- EXPECT_EQ(expected, actual);
- }
- {
- auto lines = "2 3";
- auto actual
- = split(lines, ' ')
- | eachToTuple<int>(',')
- | as<vector>();
- vector<tuple<int>> expected {
- make_tuple(2),
- make_tuple(3),
- };
- EXPECT_EQ(expected, actual);
- }
- {
- // StringPiece target
- auto lines = "1:cat 2:dog";
- auto actual
- = split(lines, ' ')
- | eachToTuple<int, StringPiece>(':')
- | as<vector>();
- vector<tuple<int, StringPiece>> expected {
- make_tuple(1, "cat"),
- make_tuple(2, "dog"),
- };
- EXPECT_EQ(expected, actual);
- }
- {
- // Empty field
- auto lines = "2:tjackson:4 3::5";
- auto actual
- = split(lines, ' ')
- | eachToTuple<int, fbstring, int>(':')
- | as<vector>();
- vector<tuple<int, fbstring, int>> expected {
- make_tuple(2, "tjackson", 4),
- make_tuple(3, "", 5),
- };
- EXPECT_EQ(expected, actual);
- }
- {
- // Excess fields
- auto lines = "1:2 3:4:5";
- EXPECT_THROW((split(lines, ' ')
- | eachToTuple<int, int>(':')
- | as<vector>()),
- std::runtime_error);
- }
- {
- // Missing fields
- auto lines = "1:2:3 4:5";
- EXPECT_THROW((split(lines, ' ')
- | eachToTuple<int, int, int>(':')
- | as<vector>()),
- std::runtime_error);
- }
-}
-
-TEST(StringGen, EachToPair) {
- {
- // char delimiters
- auto lines = "2:1.414 3:1.732";
- auto actual
- = split(lines, ' ')
- | eachToPair<int, double>(':')
- | as<std::map<int, double>>();
- std::map<int, double> expected {
- { 3, 1.732 },
- { 2, 1.414 },
- };
- EXPECT_EQ(expected, actual);
- }
- {
- // string delimiters
- auto lines = "ab=>cd ef=>gh";
- auto actual
- = split(lines, ' ')
- | eachToPair<string, string>("=>")
- | as<std::map<string, string>>();
- std::map<string, string> expected {
- { "ab", "cd" },
- { "ef", "gh" },
- };
- EXPECT_EQ(expected, actual);
- }
-}
-
-TEST(StringGen, Resplit) {
- auto collect = eachTo<std::string>() | as<vector>();
- {
- auto pieces = from({"hello,, world, goodbye, meow"}) |
- resplit(',') | collect;
- EXPECT_EQ(5, pieces.size());
- EXPECT_EQ("hello", pieces[0]);
- EXPECT_EQ("", pieces[1]);
- EXPECT_EQ(" world", pieces[2]);
- EXPECT_EQ(" goodbye", pieces[3]);
- EXPECT_EQ(" meow", pieces[4]);
- }
- {
- auto pieces = from({"hel", "lo,", ", world", ", goodbye, m", "eow"}) |
- resplit(',') | collect;
- EXPECT_EQ(5, pieces.size());
- EXPECT_EQ("hello", pieces[0]);
- EXPECT_EQ("", pieces[1]);
- EXPECT_EQ(" world", pieces[2]);
- EXPECT_EQ(" goodbye", pieces[3]);
- EXPECT_EQ(" meow", pieces[4]);
- }
-}
-
-template<typename F>
-void runUnsplitSuite(F fn) {
- fn("hello, world");
- fn("hello,world,goodbye");
- fn(" ");
- fn("");
- fn(", ");
- fn(", a, b,c");
-}
-
-TEST(StringGen, Unsplit) {
-
- auto basicFn = [](const StringPiece& s) {
- EXPECT_EQ(split(s, ',') | unsplit(','), s);
- };
-
- auto existingBuffer = [](const StringPiece& s) {
- folly::fbstring buffer("asdf");
- split(s, ',') | unsplit(',', &buffer);
- auto expected = folly::to<folly::fbstring>(
- "asdf", s.empty() ? "" : ",", s);
- EXPECT_EQ(expected, buffer);
- };
-
- auto emptyBuffer = [](const StringPiece& s) {
- std::string buffer;
- split(s, ',') | unsplit(',', &buffer);
- EXPECT_EQ(s, buffer);
- };
-
- auto stringDelim = [](const StringPiece& s) {
- EXPECT_EQ(s, split(s, ',') | unsplit(","));
- std::string buffer;
- split(s, ',') | unsplit(",", &buffer);
- EXPECT_EQ(buffer, s);
- };
-
- runUnsplitSuite(basicFn);
- runUnsplitSuite(existingBuffer);
- runUnsplitSuite(emptyBuffer);
- runUnsplitSuite(stringDelim);
- EXPECT_EQ("1, 2, 3", seq(1, 3) | unsplit(", "));
-}
-
-TEST(FileGen, ByLine) {
- auto collect = eachTo<std::string>() | as<vector>();
- test::TemporaryFile file("ByLine");
- static const std::string lines(
- "Hello world\n"
- "This is the second line\n"
- "\n"
- "\n"
- "a few empty lines above\n"
- "incomplete last line");
- EXPECT_EQ(lines.size(), write(file.fd(), lines.data(), lines.size()));
-
- auto expected = from({lines}) | resplit('\n') | collect;
- auto found = byLine(file.path().c_str()) | collect;
-
- EXPECT_TRUE(expected == found);
-}
-
-class FileGenBufferedTest : public ::testing::TestWithParam<int> { };
-
-TEST_P(FileGenBufferedTest, FileWriter) {
- size_t bufferSize = GetParam();
- test::TemporaryFile file("FileWriter");
-
- static const std::string lines(
- "Hello world\n"
- "This is the second line\n"
- "\n"
- "\n"
- "a few empty lines above\n");
-
- auto src = from({lines, lines, lines, lines, lines, lines, lines, lines});
- auto collect = eachTo<std::string>() | as<vector>();
- auto expected = src | resplit('\n') | collect;
-
- src | eachAs<StringPiece>() | toFile(File(file.fd()), bufferSize);
- auto found = byLine(file.path().c_str()) | collect;
-
- EXPECT_TRUE(expected == found);
-}
-
-INSTANTIATE_TEST_CASE_P(
- DifferentBufferSizes,
- FileGenBufferedTest,
- ::testing::Values(0, 1, 2, 4, 8, 64, 4096));
-
-TEST(Gen, Guard) {
- using std::runtime_error;
- EXPECT_THROW(from({"1", "a", "3"})
- | eachTo<int>()
- | sum,
- runtime_error);
- EXPECT_EQ(4,
- from({"1", "a", "3"})
- | guard<runtime_error>([](runtime_error&, const char*) {
- return true; // continue
- })
- | eachTo<int>()
- | sum);
- EXPECT_EQ(1,
- from({"1", "a", "3"})
- | guard<runtime_error>([](runtime_error&, const char*) {
- return false; // break
- })
- | eachTo<int>()
- | sum);
- EXPECT_THROW(from({"1", "a", "3"})
- | guard<runtime_error>([](runtime_error&, const char* v) {
- if (v[0] == 'a') {
- throw;
- }
- return true;
- })
- | eachTo<int>()
- | sum,
- runtime_error);
-}
-
-TEST(Gen, Batch) {
- EXPECT_EQ((vector<vector<int>> { {1} }),
- seq(1, 1) | batch(5) | as<vector>());
- EXPECT_EQ((vector<vector<int>> { {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11} }),
- seq(1, 11) | batch(3) | as<vector>());
- EXPECT_THROW(seq(1, 1) | batch(0) | as<vector>(),
- std::invalid_argument);
-}
-
-TEST(Gen, BatchMove) {
- auto expected = vector<vector<int>>{ {0, 1}, {2, 3}, {4} };
- auto actual =
- seq(0, 4)
- | mapped([](int i) { return std::unique_ptr<int>(new int(i)); })
- | batch(2)
- | mapped([](std::vector<std::unique_ptr<int>>& pVector) {
- std::vector<int> iVector;
- for (const auto& p : pVector) {
- iVector.push_back(*p);
- };
- return iVector;
- })
- | as<vector>();
- EXPECT_EQ(expected, actual);
-}
-
-int main(int argc, char *argv[]) {
- testing::InitGoogleTest(&argc, argv);
- google::ParseCommandLineFlags(&argc, &argv, true);
- return RUN_ALL_TESTS();
-}