1/*
2 * Copyright 2011-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/container/Foreach.h>
18
19#include <array>
20#include <initializer_list>
21#include <iterator>
22#include <list>
23#include <map>
24#include <string>
25#include <tuple>
26#include <vector>
27
28#include <folly/portability/GTest.h>
29
30using namespace folly;
31using namespace folly::detail;
32
33namespace folly {
34namespace test {
35
36class TestRValueConstruct {
37 public:
38 TestRValueConstruct() = default;
39 TestRValueConstruct(TestRValueConstruct&&) noexcept {
40 this->constructed_from_rvalue = true;
41 }
42 TestRValueConstruct(const TestRValueConstruct&) {
43 this->constructed_from_rvalue = false;
44 }
45 TestRValueConstruct& operator=(const TestRValueConstruct&) = delete;
46 TestRValueConstruct& operator=(TestRValueConstruct&&) = delete;
47
48 bool constructed_from_rvalue{false};
49};
50
51class TestAdlIterable {
52 public:
53 std::vector<int> vec{0, 1, 2, 3};
54};
55
56auto begin(TestAdlIterable& instance) {
57 return instance.vec.begin();
58}
59auto begin(const TestAdlIterable& instance) {
60 return instance.vec.begin();
61}
62auto end(TestAdlIterable& instance) {
63 return instance.vec.end();
64}
65auto end(const TestAdlIterable& instance) {
66 return instance.vec.end();
67}
68
69class TestBothIndexingAndIter {
70 public:
71 class Iterator {
72 public:
73 using difference_type = std::size_t;
74 using value_type = int;
75 using pointer = int*;
76 using reference = int&;
77 using iterator_category = std::random_access_iterator_tag;
78 int& operator*() {
79 return this->val;
80 }
81 Iterator operator+(int) {
82 return *this;
83 }
84 explicit Iterator(int& val_in) : val{val_in} {}
85 int& val;
86 };
87 auto begin() {
88 this->called_begin = true;
89 return Iterator{val};
90 }
91 auto end() {
92 return Iterator{val};
93 }
94 int& operator[](int) {
95 return this->val;
96 }
97
98 int val{0};
99 bool called_begin = false;
100};
101} // namespace test
102} // namespace folly
103
104TEST(Foreach, ForEachFunctionBasic) {
105 auto range = std::make_tuple(1, 2, 3);
106 auto result_range = std::vector<int>{};
107 auto correct_result_range = std::vector<int>{1, 2, 3};
108
109 folly::for_each(range, [&](auto ele) { result_range.push_back(ele); });
110
111 EXPECT_TRUE(std::equal(
112 result_range.begin(), result_range.end(), correct_result_range.begin()));
113}
114
115TEST(Foreach, ForEachFunctionBasicRuntimeOneArg) {
116 auto range = std::vector<int>{1, 2, 3};
117 auto current = 0;
118 folly::for_each(range, [&](auto ele) {
119 if (current == 0) {
120 EXPECT_EQ(ele, 1);
121 } else if (current == 1) {
122 EXPECT_EQ(ele, 2);
123 } else {
124 EXPECT_EQ(ele, 3);
125 }
126 ++current;
127 });
128}
129
130TEST(Foreach, ForEachFunctionBasicRuntimeTwoArg) {
131 auto range = std::vector<int>{1, 2, 3};
132 folly::for_each(range, [](auto ele, auto index) {
133 EXPECT_TRUE(index < 3);
134 if (index == 0) {
135 EXPECT_EQ(ele, 1);
136 } else if (index == 1) {
137 EXPECT_EQ(ele, 2);
138 } else if (index == 2) {
139 EXPECT_EQ(ele, 3);
140 }
141 });
142}
143
144TEST(Foreach, ForEachFunctionBasicRuntimeThreeArg) {
145 auto range = std::list<int>{1, 2, 3};
146 auto result_range = std::list<int>{1, 3};
147 folly::for_each(range, [&](auto ele, auto, auto iter) {
148 if (ele == 2) {
149 range.erase(iter);
150 }
151 });
152 EXPECT_TRUE(std::equal(range.begin(), range.end(), result_range.begin()));
153}
154
155TEST(Foreach, ForEachFunctionBasicTupleOneArg) {
156 auto range = std::make_tuple(1, 2, 3);
157 auto current = 0;
158 folly::for_each(range, [&](auto ele) {
159 if (current == 0) {
160 EXPECT_EQ(ele, 1);
161 } else if (current == 1) {
162 EXPECT_EQ(ele, 2);
163 } else {
164 EXPECT_EQ(ele, 3);
165 }
166 ++current;
167 });
168}
169
170TEST(Foreach, ForEachFunctionBasicTupleTwoArg) {
171 auto range = std::make_tuple(1, 2, 3);
172 folly::for_each(range, [](auto ele, auto index) {
173 EXPECT_TRUE(index < 3);
174 if (index == 0) {
175 EXPECT_EQ(ele, 1);
176 } else if (index == 1) {
177 EXPECT_EQ(ele, 2);
178 } else if (index == 2) {
179 EXPECT_EQ(ele, 3);
180 }
181 });
182}
183
184TEST(Foreach, ForEachFunctionBreakRuntimeOneArg) {
185 auto range = std::vector<int>{1, 2, 3};
186 auto iterations = 0;
187 folly::for_each(range, [&](auto) {
188 ++iterations;
189 if (iterations == 1) {
190 return folly::loop_break;
191 }
192 return folly::loop_continue;
193 });
194 EXPECT_EQ(iterations, 1);
195}
196
197TEST(Foreach, ForEachFunctionBreakRuntimeTwoArg) {
198 auto range = std::vector<int>{1, 2, 3};
199 auto iterations = 0;
200 folly::for_each(range, [&](auto, auto index) {
201 ++iterations;
202 if (index == 1) {
203 return folly::loop_break;
204 }
205 return folly::loop_continue;
206 });
207 EXPECT_EQ(iterations, 2);
208}
209
210TEST(Foreach, ForEachFunctionBreakRuntimeThreeArg) {
211 auto range = std::vector<int>{1, 2, 3};
212 auto iterations = 0;
213 folly::for_each(range, [&](auto, auto index, auto) {
214 ++iterations;
215 if (index == 1) {
216 return folly::loop_break;
217 }
218 return folly::loop_continue;
219 });
220 EXPECT_EQ(iterations, 2);
221}
222
223TEST(Foreach, ForEachFunctionBreakTupleOneArg) {
224 auto range = std::vector<int>{1, 2, 3};
225 auto iterations = 0;
226 folly::for_each(range, [&](auto) {
227 ++iterations;
228 if (iterations == 1) {
229 return folly::loop_break;
230 }
231 return folly::loop_continue;
232 });
233 EXPECT_EQ(iterations, 1);
234}
235
236TEST(Foreach, ForEachFunctionBreakTupleTwoArg) {
237 auto range = std::vector<int>{1, 2, 3};
238 auto iterations = 0;
239 folly::for_each(range, [&](auto, auto index) {
240 ++iterations;
241 if (index == 1) {
242 return folly::loop_break;
243 }
244 return folly::loop_continue;
245 });
246 EXPECT_EQ(iterations, 2);
247}
248
249TEST(Foreach, ForEachFunctionArray) {
250 auto range = std::array<int, 3>{{1, 2, 3}};
251 auto iterations = 0;
252 folly::for_each(range, [&](auto, auto index) {
253 ++iterations;
254 if (index == 1) {
255 return folly::loop_break;
256 }
257 return folly::loop_continue;
258 });
259 EXPECT_EQ(iterations, 2);
260}
261
262TEST(Foreach, ForEachFunctionInitializerListBasic) {
263 folly::for_each(std::initializer_list<int>{1, 2, 3}, [](auto ele) { ++ele; });
264}
265
266TEST(Foreach, ForEachFunctionTestForward) {
267 using folly::test::TestRValueConstruct;
268 auto range_one = std::vector<TestRValueConstruct>{};
269 range_one.resize(3);
270
271 folly::for_each(std::move(range_one), [](auto ele) {
272 EXPECT_FALSE(ele.constructed_from_rvalue);
273 });
274
275 folly::for_each(
276 std::make_tuple(TestRValueConstruct{}, TestRValueConstruct{}),
277 [](auto ele) { EXPECT_TRUE(ele.constructed_from_rvalue); });
278}
279
280TEST(Foreach, ForEachFunctionAdlIterable) {
281 auto range = test::TestAdlIterable{};
282 auto iterations = 0;
283 folly::for_each(range, [&](auto ele, auto index) {
284 ++iterations;
285 EXPECT_EQ(ele, index);
286 });
287 EXPECT_EQ(iterations, 4);
288}
289
290TEST(ForEach, FetchRandomAccessIterator) {
291 auto vec = std::vector<int>{1, 2, 3};
292 auto& second = folly::fetch(vec, 1);
293 EXPECT_EQ(second, 2);
294 second = 3;
295 EXPECT_EQ(second, 3);
296}
297
298TEST(ForEach, FetchIndexing) {
299 auto mp = std::map<int, int>{{1, 2}};
300 auto& ele = folly::fetch(mp, 1);
301 EXPECT_EQ(ele, 2);
302 ele = 3;
303 EXPECT_EQ(ele, 3);
304}
305
306TEST(ForEach, FetchTuple) {
307 auto mp = std::make_tuple(1, 2, 3);
308 auto& ele = folly::fetch(mp, std::integral_constant<int, 1>{});
309 EXPECT_EQ(ele, 2);
310 ele = 3;
311 EXPECT_EQ(ele, 3);
312}
313
314TEST(ForEach, FetchTestPreferIterator) {
315 auto range = test::TestBothIndexingAndIter{};
316 auto& ele = folly::fetch(range, 0);
317 EXPECT_TRUE(range.called_begin);
318 EXPECT_EQ(ele, 0);
319 ele = 2;
320 EXPECT_EQ(folly::fetch(range, 0), 2);
321}
322
323TEST(Foreach, ForEachRvalue) {
324 const char* const hello = "hello";
325 int n = 0;
326 FOR_EACH (it, std::string(hello)) { ++n; }
327 EXPECT_EQ(strlen(hello), n);
328 FOR_EACH_R (it, std::string(hello)) {
329 --n;
330 EXPECT_EQ(hello[n], *it);
331 }
332 EXPECT_EQ(0, n);
333}
334
335TEST(Foreach, ForEachNested) {
336 const std::string hello = "hello";
337 size_t n = 0;
338 FOR_EACH (i, hello) {
339 FOR_EACH (j, hello) { ++n; }
340 }
341 auto len = hello.size();
342 EXPECT_EQ(len * len, n);
343}
344
345TEST(Foreach, ForEachKV) {
346 std::map<std::string, int> testMap;
347 testMap["abc"] = 1;
348 testMap["def"] = 2;
349 std::string keys = "";
350 int values = 0;
351 int numEntries = 0;
352 FOR_EACH_KV (key, value, testMap) {
353 keys += key;
354 values += value;
355 ++numEntries;
356 }
357 EXPECT_EQ("abcdef", keys);
358 EXPECT_EQ(3, values);
359 EXPECT_EQ(2, numEntries);
360}
361
362TEST(Foreach, ForEachKVBreak) {
363 std::map<std::string, int> testMap;
364 testMap["abc"] = 1;
365 testMap["def"] = 2;
366 std::string keys = "";
367 int values = 0;
368 int numEntries = 0;
369 FOR_EACH_KV (key, value, testMap) {
370 keys += key;
371 values += value;
372 ++numEntries;
373 break;
374 }
375 EXPECT_EQ("abc", keys);
376 EXPECT_EQ(1, values);
377 EXPECT_EQ(1, numEntries);
378}
379
380TEST(Foreach, ForEachKvWithMultiMap) {
381 std::multimap<std::string, int> testMap;
382 testMap.insert(std::make_pair("abc", 1));
383 testMap.insert(std::make_pair("abc", 2));
384 testMap.insert(std::make_pair("def", 3));
385 std::string keys = "";
386 int values = 0;
387 int numEntries = 0;
388 FOR_EACH_KV (key, value, testMap) {
389 keys += key;
390 values += value;
391 ++numEntries;
392 }
393 EXPECT_EQ("abcabcdef", keys);
394 EXPECT_EQ(6, values);
395 EXPECT_EQ(3, numEntries);
396}
397
398TEST(Foreach, ForEachEnumerate) {
399 std::vector<int> vv;
400 int sumAA = 0;
401 int sumIter = 0;
402 int numIterations = 0;
403 FOR_EACH_ENUMERATE (aa, iter, vv) {
404 sumAA += aa;
405 sumIter += *iter;
406 ++numIterations;
407 }
408 EXPECT_EQ(sumAA, 0);
409 EXPECT_EQ(sumIter, 0);
410 EXPECT_EQ(numIterations, 0);
411
412 vv.push_back(1);
413 vv.push_back(3);
414 vv.push_back(5);
415 FOR_EACH_ENUMERATE (aa, iter, vv) {
416 sumAA += aa;
417 sumIter += *iter;
418 ++numIterations;
419 }
420 EXPECT_EQ(sumAA, 3); // 0 + 1 + 2
421 EXPECT_EQ(sumIter, 9); // 1 + 3 + 5
422 EXPECT_EQ(numIterations, 3);
423}
424
425TEST(Foreach, ForEachEnumerateBreak) {
426 std::vector<int> vv;
427 int sumAA = 0;
428 int sumIter = 0;
429 int numIterations = 0;
430 vv.push_back(1);
431 vv.push_back(2);
432 vv.push_back(4);
433 vv.push_back(8);
434 FOR_EACH_ENUMERATE (aa, iter, vv) {
435 sumAA += aa;
436 sumIter += *iter;
437 ++numIterations;
438 if (aa == 1) {
439 break;
440 }
441 }
442 EXPECT_EQ(sumAA, 1); // 0 + 1
443 EXPECT_EQ(sumIter, 3); // 1 + 2
444 EXPECT_EQ(numIterations, 2);
445}
446
447TEST(Foreach, ForEachRangeR) {
448 int sum = 0;
449
450 FOR_EACH_RANGE_R (i, 0, 0) { sum += i; }
451 EXPECT_EQ(0, sum);
452
453 FOR_EACH_RANGE_R (i, 0, -1) { sum += i; }
454 EXPECT_EQ(0, sum);
455
456 FOR_EACH_RANGE_R (i, 0, 5) { sum += i; }
457 EXPECT_EQ(10, sum);
458
459 std::list<int> lst = {0, 1, 2, 3, 4};
460 sum = 0;
461 FOR_EACH_RANGE_R (i, lst.begin(), lst.end()) { sum += *i; }
462 EXPECT_EQ(10, sum);
463}
464