1/*
2 * Copyright 2014-present 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#include <string>
17#include <tuple>
18#include <vector>
19
20#include <folly/FBVector.h>
21#include <folly/Range.h>
22#include <folly/gen/Base.h>
23#include <folly/gen/Combine.h>
24#include <folly/portability/GTest.h>
25
26using namespace folly::gen;
27using namespace folly;
28using std::string;
29using std::tuple;
30using std::vector;
31
32const folly::gen::detail::Map<folly::gen::detail::MergeTuples> gTupleFlatten{};
33
34auto even = [](int i) -> bool { return i % 2 == 0; };
35auto odd = [](int i) -> bool { return i % 2 == 1; };
36
37TEST(CombineGen, Interleave) {
38 { // large (infinite) base, small container
39 auto base = seq(1) | filter(odd);
40 auto toInterleave = seq(1, 6) | filter(even);
41 auto interleaved = base | interleave(toInterleave | as<vector>());
42 EXPECT_EQ(interleaved | as<vector>(), vector<int>({1, 2, 3, 4, 5, 6}));
43 }
44 { // small base, large container
45 auto base = seq(1) | filter(odd) | take(3);
46 auto toInterleave = seq(1) | filter(even) | take(50);
47 auto interleaved = base | interleave(toInterleave | as<vector>());
48 EXPECT_EQ(interleaved | as<vector>(), vector<int>({1, 2, 3, 4, 5, 6}));
49 }
50}
51
52TEST(CombineGen, Zip) {
53 auto base0 = seq(1);
54 // We rely on std::move(fbvector) emptying the source vector
55 auto zippee = fbvector<string>{"one", "two", "three"};
56 {
57 auto combined = base0 | zip(zippee) | as<vector>();
58 ASSERT_EQ(combined.size(), 3);
59 EXPECT_EQ(std::get<0>(combined[0]), 1);
60 EXPECT_EQ(std::get<1>(combined[0]), "one");
61 EXPECT_EQ(std::get<0>(combined[1]), 2);
62 EXPECT_EQ(std::get<1>(combined[1]), "two");
63 EXPECT_EQ(std::get<0>(combined[2]), 3);
64 EXPECT_EQ(std::get<1>(combined[2]), "three");
65 ASSERT_FALSE(zippee.empty());
66 EXPECT_FALSE(zippee.front().empty()); // shouldn't have been move'd
67 }
68
69 { // same as top, but using std::move.
70 auto combined = base0 | zip(std::move(zippee)) | as<vector>();
71 ASSERT_EQ(combined.size(), 3);
72 EXPECT_EQ(std::get<0>(combined[0]), 1);
73 EXPECT_TRUE(zippee.empty());
74 }
75
76 { // same as top, but base is truncated
77 auto baseFinite = seq(1) | take(1);
78 auto combined =
79 baseFinite | zip(vector<string>{"one", "two", "three"}) | as<vector>();
80 ASSERT_EQ(combined.size(), 1);
81 EXPECT_EQ(std::get<0>(combined[0]), 1);
82 EXPECT_EQ(std::get<1>(combined[0]), "one");
83 }
84}
85
86TEST(CombineGen, TupleFlatten) {
87 vector<tuple<int, string>> intStringTupleVec{
88 tuple<int, string>{1, "1"},
89 tuple<int, string>{2, "2"},
90 tuple<int, string>{3, "3"},
91 };
92
93 vector<tuple<char>> charTupleVec{
94 tuple<char>{'A'},
95 tuple<char>{'B'},
96 tuple<char>{'C'},
97 tuple<char>{'D'},
98 };
99
100 vector<double> doubleVec{
101 1.0,
102 4.0,
103 9.0,
104 16.0,
105 25.0,
106 };
107
108 // clang-format off
109 auto zipped1 = from(intStringTupleVec)
110 | zip(charTupleVec)
111 | assert_type<tuple<tuple<int, string>, tuple<char>>>()
112 | as<vector>();
113 // clang-format on
114 EXPECT_EQ(std::get<0>(zipped1[0]), std::make_tuple(1, "1"));
115 EXPECT_EQ(std::get<1>(zipped1[0]), std::make_tuple('A'));
116
117 // clang-format off
118 auto zipped2 = from(zipped1)
119 | gTupleFlatten
120 | assert_type<tuple<int, string, char>&&>()
121 | as<vector>();
122 // clang-format on
123 ASSERT_EQ(zipped2.size(), 3);
124 EXPECT_EQ(zipped2[0], std::make_tuple(1, "1", 'A'));
125
126 // clang-format off
127 auto zipped3 = from(charTupleVec)
128 | zip(intStringTupleVec)
129 | gTupleFlatten
130 | assert_type<tuple<char, int, string>&&>()
131 | as<vector>();
132 // clang-format on
133 ASSERT_EQ(zipped3.size(), 3);
134 EXPECT_EQ(zipped3[0], std::make_tuple('A', 1, "1"));
135
136 // clang-format off
137 auto zipped4 = from(intStringTupleVec)
138 | zip(doubleVec)
139 | gTupleFlatten
140 | assert_type<tuple<int, string, double>&&>()
141 | as<vector>();
142 // clang-format on
143 ASSERT_EQ(zipped4.size(), 3);
144 EXPECT_EQ(zipped4[0], std::make_tuple(1, "1", 1.0));
145
146 // clang-format off
147 auto zipped5 = from(doubleVec)
148 | zip(doubleVec)
149 | assert_type<tuple<double, double>>()
150 | gTupleFlatten // essentially a no-op
151 | assert_type<tuple<double, double>&&>()
152 | as<vector>();
153 // clang-format on
154 ASSERT_EQ(zipped5.size(), 5);
155 EXPECT_EQ(zipped5[0], std::make_tuple(1.0, 1.0));
156
157 // clang-format off
158 auto zipped6 = from(intStringTupleVec)
159 | zip(charTupleVec)
160 | gTupleFlatten
161 | zip(doubleVec)
162 | gTupleFlatten
163 | assert_type<tuple<int, string, char, double>&&>()
164 | as<vector>();
165 // clang-format on
166 ASSERT_EQ(zipped6.size(), 3);
167 EXPECT_EQ(zipped6[0], std::make_tuple(1, "1", 'A', 1.0));
168}
169
170int main(int argc, char* argv[]) {
171 testing::InitGoogleTest(&argc, argv);
172 gflags::ParseCommandLineFlags(&argc, &argv, true);
173 return RUN_ALL_TESTS();
174}
175