/*
- * Copyright 2015 Facebook, Inc.
+ * Copyright 2014-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
#include <glog/logging.h>
-#include <gtest/gtest.h>
+
#include <iosfwd>
+#include <memory>
#include <random>
#include <set>
#include <vector>
#include <folly/FBVector.h>
#include <folly/MapUtil.h>
#include <folly/Memory.h>
+#include <folly/String.h>
#include <folly/dynamic.h>
-#include <folly/gen/Base.h>
#include <folly/experimental/TestUtil.h>
+#include <folly/gen/Base.h>
+#include <folly/portability/GTest.h>
using namespace folly::gen;
using namespace folly;
EXPECT_SAME(const int&, typename ArgumentReference<const int&>::type);
EXPECT_SAME(const int&, typename ArgumentReference<const int>::type);
-template<typename T>
+template <typename T>
ostream& operator<<(ostream& os, const set<T>& values) {
return os << from(values);
}
-template<typename T>
+template <typename T>
ostream& operator<<(ostream& os, const vector<T>& values) {
os << "[";
for (auto& value : values) {
auto product = foldl(1, multiply);
-template<typename A, typename B>
+template <typename A, typename B>
ostream& operator<<(ostream& os, const pair<A, B>& pair) {
return os << "(" << pair.first << ", " << pair.second << ")";
}
std::vector<X> xs(1);
EXPECT_EQ(2, from(xs)
| field(&X::a)
- | first);
+ | sum);
EXPECT_EQ(3, from(xs)
| field(&X::b)
- | first);
+ | sum);
EXPECT_EQ(4, from(xs)
| field(&X::c)
- | first);
+ | sum);
EXPECT_EQ(2, seq(&xs[0], &xs[0])
| field(&X::a)
- | first);
+ | sum);
// type-verification
empty<X&>() | field(&X::a) | assert_type<const int&>();
empty<X*>() | field(&X::a) | assert_type<const int&>();
}
}
+TEST(Gen, FilterSink) {
+ auto actual
+ = seq(1, 2)
+ | map([](int x) { return vector<int>{x}; })
+ | filter([](vector<int> v) { return !v.empty(); })
+ | as<vector>();
+ EXPECT_FALSE(from(actual) | rconcat | isEmpty);
+}
+
TEST(Gen, Contains) {
{
auto gen =
| as<vector>();
EXPECT_EQ(expected, actual);
}
+ {
+ int64_t limit = 5;
+ take(limit - 5);
+ EXPECT_THROW(take(limit - 6), std::invalid_argument);
+ }
}
| as<vector<int>>();
EXPECT_EQ(expected, actual);
}
- */
+ */
+}
+
+TEST(Gen, Visit) {
+ auto increment = [](int& i) { ++i; };
+ auto clone = map([](int i) { return i; });
+ { // apply()
+ auto expected = 10;
+ auto actual = seq(0) | clone | visit(increment) | take(4) | sum;
+ EXPECT_EQ(expected, actual);
+ }
+ { // foreach()
+ auto expected = 10;
+ auto actual = seq(0, 3) | clone | visit(increment) | sum;
+ EXPECT_EQ(expected, actual);
+ }
+ { // tee-like
+ std::vector<int> x2, x4;
+ std::vector<int> expected2{0, 1, 4, 9};
+ std::vector<int> expected4{0, 1, 16, 81};
+
+ auto tee = [](std::vector<int>& container) {
+ return visit([&](int value) { container.push_back(value); });
+ };
+ EXPECT_EQ(
+ 98, seq(0, 3) | map(square) | tee(x2) | map(square) | tee(x4) | sum);
+ EXPECT_EQ(expected2, x2);
+ EXPECT_EQ(expected4, x4);
+ }
}
TEST(Gen, Composed) {
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)); })
+ auto actual = seq(0, 100) |
+ mapped([](int i) { return std::make_unique<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>();
+ | 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; })
EXPECT_EQ(expected, actual);
}
+TEST(Gen, DistinctInfinite) {
+ // distinct should be able to handle an infinite sequence, provided that, of
+ // of cource, is it eventually made finite before returning the result.
+ auto expected = seq(0) | take(5) | as<vector>(); // 0 1 2 3 4
+
+ auto actual =
+ seq(0) // 0 1 2 3 4 5 6 7 ...
+ | mapped([](int i) { return i / 2; }) // 0 0 1 1 2 2 3 3 ...
+ | distinct // 0 1 2 3 4 5 6 7 ...
+ | take(5) // 0 1 2 3 4
+ | as<vector>();
+
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, DistinctByInfinite) {
+ // Similarly to the DistinctInfinite test case, distinct by should be able to
+ // handle infinite sequences. Note that depending on how many values we take()
+ // at the end, the sequence may infinite loop. This is fine becasue we cannot
+ // solve the halting problem.
+ auto expected = vector<int>{1, 2};
+ auto actual =
+ seq(1) // 1 2 3 4 5 6 7 8 ...
+ | distinctBy([](int i) { return i % 2; }) // 1 2 (but might by infinite)
+ | take(2) // 1 2
+ | as<vector>();
+ // Note that if we had take(3), this would infinite loop
+
+ 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;
- }));
+ })
+ | unwrap);
}
TEST(Gen, MaxBy) {
auto gen = from({"three", "eleven", "four"});
- EXPECT_EQ("eleven", gen | maxBy(&strlen));
+ EXPECT_EQ("eleven", gen | maxBy(&strlen) | unwrap);
+}
+
+TEST(Gen, Min) {
+ auto odds = seq(2,10) | filter([](int i){ return i % 2; });
+
+ EXPECT_EQ(3, odds | min);
+}
+
+TEST(Gen, Max) {
+ auto odds = seq(2,10) | filter([](int i){ return i % 2; });
+
+ EXPECT_EQ(9, odds | max);
}
TEST(Gen, Append) {
// 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.
- fbvector<int> v({1,2,3,4});
+ 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;
}
{
auto expected = 7;
- auto q = from([] {return vector<int>({3,7,5}); }());
+ auto q = from([] { return vector<int>({3, 7, 5}); }());
EXPECT_EQ(expected, q | max);
}
{
- for (auto size: {5, 1024, 16384, 1<<20}) {
+ 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
TEST(Gen, Reduce) {
int expected = 2 + 3 + 4 + 5;
auto actual = seq(2, 5) | reduce(add);
- EXPECT_EQ(expected, actual);
+ EXPECT_EQ(expected, actual | unwrap);
}
TEST(Gen, ReduceBad) {
auto gen = seq(1) | take(0);
- try {
- EXPECT_TRUE(true);
- gen | reduce(add);
- EXPECT_TRUE(false);
- } catch (...) {
- }
+ auto actual = gen | reduce(add);
+ EXPECT_FALSE(actual); // Empty sequences are okay, they just yeild 'none'
}
TEST(Gen, Moves) {
}
TEST(Gen, First) {
- auto gen =
- seq(0)
- | filter([](int x) { return x > 3; });
- EXPECT_EQ(4, gen | first);
+ auto gen = seq(0) | filter([](int x) { return x > 3; });
+ EXPECT_EQ(4, gen | first | unwrap);
}
TEST(Gen, FromCopy) {
EXPECT_EQ(36, from(tuples) | get<2>() | sum);
}
+TEST(Gen, notEmpty) {
+ EXPECT_TRUE(seq(0, 1) | notEmpty);
+ EXPECT_TRUE(just(1) | notEmpty);
+ EXPECT_FALSE(gen::range(0, 0) | notEmpty);
+ EXPECT_FALSE(from({1}) | take(0) | notEmpty);
+}
+
+TEST(Gen, isEmpty) {
+ EXPECT_FALSE(seq(0, 1) | isEmpty);
+ EXPECT_FALSE(just(1) | isEmpty);
+ EXPECT_TRUE(gen::range(0, 0) | isEmpty);
+ EXPECT_TRUE(from({1}) | take(0) | isEmpty);
+}
+
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(gen::range(0, 0) | any);
- EXPECT_FALSE(from({1}) | take(0) | any);
}
TEST(Gen, All) {
| map([](int n) {
return GENERATOR(int) {
int i;
- for (i = 1; i < n; ++i)
+ for (i = 1; i < n; ++i) {
yield(i);
- for (; i >= 1; --i)
+ }
+ for (; i >= 1; --i) {
yield(i);
+ }
};
})
| concat;
}
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; });
+ auto gen = seq(1, 5) | map([](int x) { return std::make_unique<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);
}
TestIntSeq& operator=(const TestIntSeq&) = delete;
};
-} // namespace
+} // namespace
TEST(Gen, NoGeneratorCopies) {
EXPECT_EQ(15, TestIntSeq() | sum);
// test dynamics with various layers of nested arrays.
TEST(Gen, Dynamic) {
- dynamic array1 = {1, 2};
+ dynamic array1 = dynamic::array(1, 2);
EXPECT_EQ(dynamic(3), from(array1) | sum);
- dynamic array2 = {{1}, {1, 2}};
+ dynamic array2 = folly::dynamic::array(
+ folly::dynamic::array(1), folly::dynamic::array(1, 2));
EXPECT_EQ(dynamic(4), from(array2) | rconcat | sum);
- dynamic array3 = {{{1}}, {{1}, {1, 2}}};
+ dynamic array3 = folly::dynamic::array(
+ folly::dynamic::array(folly::dynamic::array(1)),
+ folly::dynamic::array(
+ folly::dynamic::array(1), folly::dynamic::array(1, 2)));
EXPECT_EQ(dynamic(5), from(array3) | rconcat | rconcat | sum);
}
};
auto s = countdown;
EXPECT_EQ((vector<int> { 1, 2, 3, 1, 2, 1}),
- s | cycle | as<vector>());
+ s | cycle | take(7) | as<vector>());
+ // take necessary as cycle returns an infinite generator
}
}
}
}
+namespace {
+struct DereferenceWrapper {
+ string data;
+ string& operator*() & {
+ return data;
+ }
+ string&& operator*() && {
+ return std::move(data);
+ }
+ explicit operator bool() {
+ return true;
+ }
+};
+bool operator==(const DereferenceWrapper& a, const DereferenceWrapper& b) {
+ return a.data == b.data;
+}
+void PrintTo(const DereferenceWrapper& a, std::ostream* o) {
+ *o << "Wrapper{\"" << cEscape<string>(a.data) << "\"}";
+}
+} // namespace
+
+TEST(Gen, DereferenceWithLValueRef) {
+ auto original = vector<DereferenceWrapper>{{"foo"}, {"bar"}};
+ auto copy = original;
+ auto expected = vector<string>{"foo", "bar"};
+ auto actual = from(original) | dereference | as<vector>();
+ EXPECT_EQ(expected, actual);
+ EXPECT_EQ(copy, original);
+}
+
+TEST(Gen, DereferenceWithRValueRef) {
+ auto original = vector<DereferenceWrapper>{{"foo"}, {"bar"}};
+ auto empty = vector<DereferenceWrapper>{{}, {}};
+ auto expected = vector<string>{"foo", "bar"};
+ auto actual = from(original) | move | dereference | as<vector>();
+ EXPECT_EQ(expected, actual);
+ EXPECT_EQ(empty, original);
+}
+
TEST(Gen, Indirect) {
vector<int> vs{1};
- EXPECT_EQ(&vs[0], from(vs) | indirect | first);
+ EXPECT_EQ(&vs[0], from(vs) | indirect | first | unwrap);
}
TEST(Gen, Guard) {
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>();
+ auto actual = seq(0, 4) |
+ mapped([](int i) { return std::make_unique<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);
}
+TEST(Gen, Window) {
+ auto expected = seq(0, 10) | as<std::vector>();
+ for (size_t windowSize = 1; windowSize <= 20; ++windowSize) {
+ // no early stop
+ auto actual = seq(0, 10) |
+ mapped([](int i) { return std::make_unique<int>(i); }) | window(4) |
+ dereference | as<std::vector>();
+ EXPECT_EQ(expected, actual) << windowSize;
+ }
+ for (size_t windowSize = 1; windowSize <= 20; ++windowSize) {
+ // pre-window take
+ auto actual = seq(0) |
+ mapped([](int i) { return std::make_unique<int>(i); }) | take(11) |
+ window(4) | dereference | as<std::vector>();
+ EXPECT_EQ(expected, actual) << windowSize;
+ }
+ for (size_t windowSize = 1; windowSize <= 20; ++windowSize) {
+ // post-window take
+ auto actual = seq(0) |
+ mapped([](int i) { return std::make_unique<int>(i); }) | window(4) |
+ take(11) | dereference | as<std::vector>();
+ EXPECT_EQ(expected, actual) << windowSize;
+ }
+}
+
TEST(Gen, Just) {
{
int x = 3;
auto j = just(x);
- EXPECT_EQ(&x, j | indirect | first);
+ EXPECT_EQ(&x, j | indirect | first | unwrap);
x = 4;
- EXPECT_EQ(4, j | first);
+ EXPECT_EQ(4, j | sum);
}
{
int x = 3;
const int& cx = x;
auto j = just(cx);
- EXPECT_EQ(&x, j | indirect | first);
+ EXPECT_EQ(&x, j | indirect | first | unwrap);
x = 5;
- EXPECT_EQ(5, j | first);
+ EXPECT_EQ(5, j | sum);
}
{
int x = 3;
auto j = just(std::move(x));
- EXPECT_NE(&x, j | indirect | first);
+ EXPECT_NE(&x, j | indirect | first | unwrap);
x = 5;
- EXPECT_EQ(3, j | first);
+ EXPECT_EQ(3, j | sum);
}
}
EXPECT_EQ(3, gb | count);
vector<string> mode{"zero", "four", "five", "nine"};
- EXPECT_EQ(
- mode,
- gb | maxBy([](const Group<size_t, string>& g) { return g.size(); })
- | as<vector>());
+ EXPECT_EQ(mode,
+ gb | maxBy([](const Group<size_t, string>& g) { return g.size(); })
+ | unwrap
+ | as<vector>());
vector<string> largest{"three", "seven", "eight"};
- EXPECT_EQ(
- largest,
- gb | maxBy([](const Group<size_t, string>& g) { return g.key(); })
- | as<vector>());
+ EXPECT_EQ(largest,
+ gb | maxBy([](const Group<size_t, string>& g) { return g.key(); })
+ | unwrap
+ | as<vector>());
+}
+
+TEST(Gen, Unwrap) {
+ Optional<int> o(4);
+ Optional<int> e;
+ EXPECT_EQ(4, o | unwrap);
+ EXPECT_THROW(e | unwrap, OptionalEmptyException);
+
+ auto oup = folly::make_optional(std::make_unique<int>(5));
+ // optional has a value, and that value is non-null
+ EXPECT_TRUE(bool(oup | unwrap));
+ EXPECT_EQ(5, *(oup | unwrap));
+ EXPECT_TRUE(oup.hasValue()); // still has a pointer (null or not)
+ EXPECT_TRUE(bool(oup.value())); // that value isn't null
+
+ auto moved1 = std::move(oup) | unwrapOr(std::make_unique<int>(6));
+ // oup still has a value, but now it's now nullptr since the pointer was moved
+ // into moved1
+ EXPECT_TRUE(oup.hasValue());
+ EXPECT_FALSE(oup.value());
+ EXPECT_TRUE(bool(moved1));
+ EXPECT_EQ(5, *moved1);
+
+ auto moved2 = std::move(oup) | unwrapOr(std::make_unique<int>(7));
+ // oup's still-valid nullptr value wins here, the pointer to 7 doesn't apply
+ EXPECT_FALSE(moved2);
+
+ oup.clear();
+ auto moved3 = std::move(oup) | unwrapOr(std::make_unique<int>(8));
+ // oup is empty now, so the unwrapOr comes into play.
+ EXPECT_TRUE(bool(moved3));
+ EXPECT_EQ(8, *moved3);
+
+ {
+ // mixed types, with common type matching optional
+ Optional<double> full(3.3);
+ decltype(full) empty;
+ auto fallback = unwrapOr(4);
+ EXPECT_EQ(3.3, full | fallback);
+ EXPECT_EQ(3.3, std::move(full) | fallback);
+ EXPECT_EQ(3.3, full | std::move(fallback));
+ EXPECT_EQ(3.3, std::move(full) | std::move(fallback));
+ EXPECT_EQ(4.0, empty | fallback);
+ EXPECT_EQ(4.0, std::move(empty) | fallback);
+ EXPECT_EQ(4.0, empty | std::move(fallback));
+ EXPECT_EQ(4.0, std::move(empty) | std::move(fallback));
+ }
+
+ {
+ // mixed types, with common type matching fallback
+ Optional<int> full(3);
+ decltype(full) empty;
+ auto fallback = unwrapOr(5.0); // type: double
+ // if we chose 'int' as the common type, we'd see truncation here
+ EXPECT_EQ(1.5, (full | fallback) / 2);
+ EXPECT_EQ(1.5, (std::move(full) | fallback) / 2);
+ EXPECT_EQ(1.5, (full | std::move(fallback)) / 2);
+ EXPECT_EQ(1.5, (std::move(full) | std::move(fallback)) / 2);
+ EXPECT_EQ(2.5, (empty | fallback) / 2);
+ EXPECT_EQ(2.5, (std::move(empty) | fallback) / 2);
+ EXPECT_EQ(2.5, (empty | std::move(fallback)) / 2);
+ EXPECT_EQ(2.5, (std::move(empty) | std::move(fallback)) / 2);
+ }
+
+ {
+ auto opt = folly::make_optional(std::make_shared<int>(8));
+ auto fallback = unwrapOr(std::make_unique<int>(9));
+ // fallback must be std::move'd to be used
+ EXPECT_EQ(8, *(opt | std::move(fallback)));
+ EXPECT_TRUE(bool(opt.value())); // shared_ptr copied out, not moved
+ EXPECT_TRUE(bool(opt)); // value still present
+ EXPECT_TRUE(bool(fallback.value())); // fallback value not needed
+
+ EXPECT_EQ(8, *(std::move(opt) | std::move(fallback)));
+ EXPECT_FALSE(opt.value()); // shared_ptr moved out
+ EXPECT_TRUE(bool(opt)); // gutted value still present
+ EXPECT_TRUE(bool(fallback.value())); // fallback value not needed
+
+ opt.clear();
+
+ EXPECT_FALSE(opt); // opt is empty now
+ EXPECT_EQ(9, *(std::move(opt) | std::move(fallback)));
+ EXPECT_FALSE(fallback.value()); // fallback moved out!
+ }
+
+ {
+ // test with nullptr
+ vector<int> v{1, 2};
+ EXPECT_EQ(&v[1], from(v) | indirect | max | unwrap);
+ v.clear();
+ EXPECT_FALSE(from(v) | indirect | max | unwrapOr(nullptr));
+ }
+
+ {
+ // mixed type determined by fallback
+ Optional<std::nullptr_t> empty;
+ int x = 3;
+ EXPECT_EQ(&x, empty | unwrapOr(&x));
+ }
}
int main(int argc, char *argv[]) {