1/*
2 * Copyright 2015-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
17#include <folly/futures/Future.h>
18#include <folly/portability/GTest.h>
19
20using namespace folly;
21
22TEST(Reduce, basic) {
23 auto makeFutures = [](int count) {
24 std::vector<Future<int>> fs;
25 for (int i = 1; i <= count; ++i) {
26 fs.emplace_back(makeFuture(i));
27 }
28 return fs;
29 };
30
31 // Empty (Try)
32 {
33 auto fs = makeFutures(0);
34
35 Future<double> f1 =
36 reduce(fs, 1.2, [](double a, Try<int>&& b) { return a + *b + 0.1; });
37 EXPECT_EQ(1.2, std::move(f1).get());
38 }
39
40 // One (Try)
41 {
42 auto fs = makeFutures(1);
43
44 Future<double> f1 =
45 reduce(fs, 0.0, [](double a, Try<int>&& b) { return a + *b + 0.1; });
46 EXPECT_EQ(1.1, std::move(f1).get());
47 }
48
49 // Returning values (Try)
50 {
51 auto fs = makeFutures(3);
52
53 Future<double> f1 =
54 reduce(fs, 0.0, [](double a, Try<int>&& b) { return a + *b + 0.1; });
55 EXPECT_EQ(6.3, std::move(f1).get());
56 }
57
58 // Returning values
59 {
60 auto fs = makeFutures(3);
61
62 Future<double> f1 =
63 reduce(fs, 0.0, [](double a, int&& b) { return a + b + 0.1; });
64 EXPECT_EQ(6.3, std::move(f1).get());
65 }
66
67 // Returning futures (Try)
68 {
69 auto fs = makeFutures(3);
70
71 Future<double> f2 = reduce(fs, 0.0, [](double a, Try<int>&& b) {
72 return makeFuture<double>(a + *b + 0.1);
73 });
74 EXPECT_EQ(6.3, std::move(f2).get());
75 }
76
77 // Returning futures
78 {
79 auto fs = makeFutures(3);
80
81 Future<double> f2 = reduce(fs, 0.0, [](double a, int&& b) {
82 return makeFuture<double>(a + b + 0.1);
83 });
84 EXPECT_EQ(6.3, std::move(f2).get());
85 }
86}
87
88TEST(Reduce, chain) {
89 auto makeFutures = [](int count) {
90 std::vector<Future<int>> fs;
91 for (int i = 1; i <= count; ++i) {
92 fs.emplace_back(makeFuture(i));
93 }
94 return fs;
95 };
96
97 {
98 auto f = collectAll(makeFutures(3)).reduce(0, [](int a, Try<int>&& b) {
99 return a + *b;
100 });
101 EXPECT_EQ(6, std::move(f).get());
102 }
103 {
104 auto f =
105 collect(makeFutures(3)).reduce(0, [](int a, int&& b) { return a + b; });
106 EXPECT_EQ(6, std::move(f).get());
107 }
108}
109
110TEST(Reduce, unorderedReduce) {
111 {
112 std::vector<Future<int>> fs;
113 fs.push_back(makeFuture(1));
114 fs.push_back(makeFuture(2));
115 fs.push_back(makeFuture(3));
116
117 Future<double> f =
118 unorderedReduce(fs.begin(), fs.end(), 0.0, [](double /* a */, int&& b) {
119 return double(b);
120 });
121 EXPECT_EQ(3.0, std::move(f).get());
122 }
123 {
124 Promise<int> p1;
125 Promise<int> p2;
126 Promise<int> p3;
127
128 std::vector<Future<int>> fs;
129 fs.push_back(p1.getFuture());
130 fs.push_back(p2.getFuture());
131 fs.push_back(p3.getFuture());
132
133 Future<double> f =
134 unorderedReduce(fs.begin(), fs.end(), 0.0, [](double /* a */, int&& b) {
135 return double(b);
136 });
137 p3.setValue(3);
138 p2.setValue(2);
139 p1.setValue(1);
140 EXPECT_EQ(1.0, std::move(f).get());
141 }
142}
143
144TEST(Reduce, unorderedReduceException) {
145 Promise<int> p1;
146 Promise<int> p2;
147 Promise<int> p3;
148
149 std::vector<Future<int>> fs;
150 fs.push_back(p1.getFuture());
151 fs.push_back(p2.getFuture());
152 fs.push_back(p3.getFuture());
153
154 Future<double> f =
155 unorderedReduce(fs.begin(), fs.end(), 0.0, [](double /* a */, int&& b) {
156 return b + 0.0;
157 });
158 p3.setValue(3);
159 p2.setException(exception_wrapper(std::runtime_error("blah")));
160 p1.setValue(1);
161 EXPECT_THROW(std::move(f).get(), std::runtime_error);
162}
163
164TEST(Reduce, unorderedReduceFuture) {
165 Promise<int> p1;
166 Promise<int> p2;
167 Promise<int> p3;
168
169 std::vector<Future<int>> fs;
170 fs.push_back(p1.getFuture());
171 fs.push_back(p2.getFuture());
172 fs.push_back(p3.getFuture());
173
174 std::vector<Promise<double>> ps(3);
175
176 Future<int> f =
177 unorderedReduce(fs.begin(), fs.end(), 0.0, [&](double /* a */, int&& b) {
178 return ps[b - 1].getFuture();
179 });
180 p3.setValue(3);
181 p2.setValue(2);
182 p1.setValue(1);
183
184 ps[0].setValue(1.0);
185 ps[1].setValue(2.0);
186 ps[2].setValue(3.0);
187
188 EXPECT_EQ(1.0, std::move(f).get());
189}
190