Include `<array>` for test
[folly.git] / folly / test / ForeachTest.cpp
index 742db1c9dcdb77d7d759add9e4dab5f6e6ce3e85..ded3b7210fc3e5788248d5fb8fd843ed2bccb83c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2017 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 <folly/Foreach.h>
 
-#include <folly/Benchmark.h>
-#include <gtest/gtest.h>
+#include <array>
+#include <initializer_list>
+#include <iterator>
+#include <list>
 #include <map>
 #include <string>
+#include <tuple>
 #include <vector>
-#include <list>
+
+#include <folly/portability/GTest.h>
 
 using namespace folly;
 using namespace folly::detail;
 
+namespace folly {
+namespace test {
+
+class TestRValueConstruct {
+ public:
+  TestRValueConstruct() = default;
+  TestRValueConstruct(TestRValueConstruct&&) noexcept {
+    this->constructed_from_rvalue = true;
+  }
+  TestRValueConstruct(const TestRValueConstruct&) {
+    this->constructed_from_rvalue = false;
+  }
+  TestRValueConstruct& operator=(const TestRValueConstruct&) = delete;
+  TestRValueConstruct& operator=(TestRValueConstruct&&) = delete;
+
+  bool constructed_from_rvalue{false};
+};
+
+class TestAdlIterable {
+ public:
+  std::vector<int> vec{0, 1, 2, 3};
+};
+
+auto begin(TestAdlIterable& instance) {
+  return instance.vec.begin();
+}
+auto begin(const TestAdlIterable& instance) {
+  return instance.vec.begin();
+}
+auto end(TestAdlIterable& instance) {
+  return instance.vec.end();
+}
+auto end(const TestAdlIterable& instance) {
+  return instance.vec.end();
+}
+
+class TestBothIndexingAndIter {
+ public:
+  class Iterator {
+   public:
+    using difference_type = std::size_t;
+    using value_type = int;
+    using pointer = int*;
+    using reference = int&;
+    using iterator_category = std::random_access_iterator_tag;
+    int& operator*() {
+      return this->val;
+    }
+    Iterator operator+(int) {
+      return *this;
+    }
+    explicit Iterator(int& val_in) : val{val_in} {}
+    int& val;
+  };
+  auto begin() {
+    this->called_begin = true;
+    return Iterator{val};
+  }
+  auto end() {
+    return Iterator{val};
+  }
+  int& operator[](int) {
+    return this->val;
+  }
+
+  int val{0};
+  bool called_begin = false;
+};
+} // namespace test
+} // namespace folly
+
+TEST(Foreach, ForEachFunctionBasic) {
+  auto range = std::make_tuple(1, 2, 3);
+  auto result_range = std::vector<int>{};
+  auto correct_result_range = std::vector<int>{1, 2, 3};
+
+  folly::for_each(range, [&](auto ele) { result_range.push_back(ele); });
+
+  EXPECT_TRUE(std::equal(
+      result_range.begin(), result_range.end(), correct_result_range.begin()));
+}
+
+TEST(Foreach, ForEachFunctionBasicRuntimeOneArg) {
+  auto range = std::vector<int>{1, 2, 3};
+  auto current = 0;
+  folly::for_each(range, [&](auto ele) {
+    if (current == 0) {
+      EXPECT_EQ(ele, 1);
+    } else if (current == 1) {
+      EXPECT_EQ(ele, 2);
+    } else {
+      EXPECT_EQ(ele, 3);
+    }
+    ++current;
+  });
+}
+
+TEST(Foreach, ForEachFunctionBasicRuntimeTwoArg) {
+  auto range = std::vector<int>{1, 2, 3};
+  folly::for_each(range, [](auto ele, auto index) {
+    EXPECT_TRUE(index < 3);
+    if (index == 0) {
+      EXPECT_EQ(ele, 1);
+    } else if (index == 1) {
+      EXPECT_EQ(ele, 2);
+    } else if (index == 2) {
+      EXPECT_EQ(ele, 3);
+    }
+  });
+}
+
+TEST(Foreach, ForEachFunctionBasicRuntimeThreeArg) {
+  auto range = std::list<int>{1, 2, 3};
+  auto result_range = std::list<int>{1, 3};
+  folly::for_each(range, [&](auto ele, auto, auto iter) {
+    if (ele == 2) {
+      range.erase(iter);
+    }
+  });
+  EXPECT_TRUE(std::equal(range.begin(), range.end(), result_range.begin()));
+}
+
+TEST(Foreach, ForEachFunctionBasicTupleOneArg) {
+  auto range = std::make_tuple(1, 2, 3);
+  auto current = 0;
+  folly::for_each(range, [&](auto ele) {
+    if (current == 0) {
+      EXPECT_EQ(ele, 1);
+    } else if (current == 1) {
+      EXPECT_EQ(ele, 2);
+    } else {
+      EXPECT_EQ(ele, 3);
+    }
+    ++current;
+  });
+}
+
+TEST(Foreach, ForEachFunctionBasicTupleTwoArg) {
+  auto range = std::make_tuple(1, 2, 3);
+  folly::for_each(range, [](auto ele, auto index) {
+    EXPECT_TRUE(index < 3);
+    if (index == 0) {
+      EXPECT_EQ(ele, 1);
+    } else if (index == 1) {
+      EXPECT_EQ(ele, 2);
+    } else if (index == 2) {
+      EXPECT_EQ(ele, 3);
+    }
+  });
+}
+
+TEST(Foreach, ForEachFunctionBreakRuntimeOneArg) {
+  auto range = std::vector<int>{1, 2, 3};
+  auto iterations = 0;
+  folly::for_each(range, [&](auto) {
+    ++iterations;
+    if (iterations == 1) {
+      return folly::loop_break;
+    }
+    return folly::loop_continue;
+  });
+  EXPECT_EQ(iterations, 1);
+}
+
+TEST(Foreach, ForEachFunctionBreakRuntimeTwoArg) {
+  auto range = std::vector<int>{1, 2, 3};
+  auto iterations = 0;
+  folly::for_each(range, [&](auto, auto index) {
+    ++iterations;
+    if (index == 1) {
+      return folly::loop_break;
+    }
+    return folly::loop_continue;
+  });
+  EXPECT_EQ(iterations, 2);
+}
+
+TEST(Foreach, ForEachFunctionBreakRuntimeThreeArg) {
+  auto range = std::vector<int>{1, 2, 3};
+  auto iterations = 0;
+  folly::for_each(range, [&](auto, auto index, auto) {
+    ++iterations;
+    if (index == 1) {
+      return folly::loop_break;
+    }
+    return folly::loop_continue;
+  });
+  EXPECT_EQ(iterations, 2);
+}
+
+TEST(Foreach, ForEachFunctionBreakTupleOneArg) {
+  auto range = std::vector<int>{1, 2, 3};
+  auto iterations = 0;
+  folly::for_each(range, [&](auto) {
+    ++iterations;
+    if (iterations == 1) {
+      return folly::loop_break;
+    }
+    return folly::loop_continue;
+  });
+  EXPECT_EQ(iterations, 1);
+}
+
+TEST(Foreach, ForEachFunctionBreakTupleTwoArg) {
+  auto range = std::vector<int>{1, 2, 3};
+  auto iterations = 0;
+  folly::for_each(range, [&](auto, auto index) {
+    ++iterations;
+    if (index == 1) {
+      return folly::loop_break;
+    }
+    return folly::loop_continue;
+  });
+  EXPECT_EQ(iterations, 2);
+}
+
+TEST(Foreach, ForEachFunctionArray) {
+  auto range = std::array<int, 3>{{1, 2, 3}};
+  auto iterations = 0;
+  folly::for_each(range, [&](auto, auto index) {
+    ++iterations;
+    if (index == 1) {
+      return folly::loop_break;
+    }
+    return folly::loop_continue;
+  });
+  EXPECT_EQ(iterations, 2);
+}
+
+TEST(Foreach, ForEachFunctionInitializerListBasic) {
+  folly::for_each(std::initializer_list<int>{1, 2, 3}, [](auto ele) { ++ele; });
+}
+
+TEST(Foreach, ForEachFunctionTestForward) {
+  using folly::test::TestRValueConstruct;
+  auto range_one = std::vector<TestRValueConstruct>{};
+  range_one.resize(3);
+
+  folly::for_each(std::move(range_one), [](auto ele) {
+    EXPECT_FALSE(ele.constructed_from_rvalue);
+  });
+
+  folly::for_each(
+      std::make_tuple(TestRValueConstruct{}, TestRValueConstruct{}),
+      [](auto ele) { EXPECT_TRUE(ele.constructed_from_rvalue); });
+}
+
+TEST(Foreach, ForEachFunctionAdlIterable) {
+  auto range = test::TestAdlIterable{};
+  auto iterations = 0;
+  folly::for_each(range, [&](auto ele, auto index) {
+    ++iterations;
+    EXPECT_EQ(ele, index);
+  });
+  EXPECT_EQ(iterations, 4);
+}
+
+TEST(ForEach, FetchRandomAccessIterator) {
+  auto vec = std::vector<int>{1, 2, 3};
+  auto& second = folly::fetch(vec, 1);
+  EXPECT_EQ(second, 2);
+  second = 3;
+  EXPECT_EQ(second, 3);
+}
+
+TEST(ForEach, FetchIndexing) {
+  auto mp = std::map<int, int>{{1, 2}};
+  auto& ele = folly::fetch(mp, 1);
+  EXPECT_EQ(ele, 2);
+  ele = 3;
+  EXPECT_EQ(ele, 3);
+}
+
+TEST(ForEach, FetchTuple) {
+  auto mp = std::make_tuple(1, 2, 3);
+  auto& ele = folly::fetch(mp, std::integral_constant<int, 1>{});
+  EXPECT_EQ(ele, 2);
+  ele = 3;
+  EXPECT_EQ(ele, 3);
+}
+
+TEST(ForEach, FetchTestPreferIterator) {
+  auto range = test::TestBothIndexingAndIter{};
+  auto& ele = folly::fetch(range, 0);
+  EXPECT_TRUE(range.called_begin);
+  EXPECT_EQ(ele, 0);
+  ele = 2;
+  EXPECT_EQ(folly::fetch(range, 0), 2);
+}
+
 TEST(Foreach, ForEachRvalue) {
   const char* const hello = "hello";
   int n = 0;
@@ -40,6 +334,18 @@ TEST(Foreach, ForEachRvalue) {
   EXPECT_EQ(0, n);
 }
 
+TEST(Foreach, ForEachNested) {
+  const std::string hello = "hello";
+  size_t n = 0;
+  FOR_EACH(i, hello) {
+    FOR_EACH(j, hello) {
+      ++n;
+    }
+  }
+  auto len = hello.size();
+  EXPECT_EQ(len * len, n);
+}
+
 TEST(Foreach, ForEachKV) {
   std::map<std::string, int> testMap;
   testMap["abc"] = 1;
@@ -165,119 +471,3 @@ TEST(Foreach, ForEachRangeR) {
   }
   EXPECT_EQ(10, sum);
 }
-
-// Benchmarks:
-// 1. Benchmark iterating through the man with FOR_EACH, and also assign
-//    iter->first and iter->second to local vars inside the FOR_EACH loop.
-// 2. Benchmark iterating through the man with FOR_EACH, but use iter->first and
-//    iter->second as is, without assigning to local variables.
-// 3. Use FOR_EACH_KV loop to iterate through the map.
-
-std::map<int, std::string> bmMap;  // For use in benchmarks below.
-
-void setupBenchmark(size_t iters) {
-  bmMap.clear();
-  for (size_t i = 0; i < iters; ++i) {
-    bmMap[i] = "teststring";
-  }
-}
-
-BENCHMARK(ForEachKVNoMacroAssign, iters) {
-  int sumKeys = 0;
-  std::string sumValues;
-
-  BENCHMARK_SUSPEND {
-    setupBenchmark(iters);
-  }
-
-  FOR_EACH (iter, bmMap) {
-    const int k = iter->first;
-    const std::string v = iter->second;
-    sumKeys += k;
-    sumValues += v;
-  }
-}
-
-BENCHMARK(ForEachKVNoMacroNoAssign, iters) {
-  int sumKeys = 0;
-  std::string sumValues;
-
-  BENCHMARK_SUSPEND {
-    setupBenchmark(iters);
-  }
-
-  FOR_EACH (iter, bmMap) {
-    sumKeys += iter->first;
-    sumValues += iter->second;
-  }
-}
-
-BENCHMARK(ManualLoopNoAssign, iters) {
-  int sumKeys = 0;
-  std::string sumValues;
-
-  BENCHMARK_SUSPEND {
-    setupBenchmark(iters);
-  }
-
-  for (auto iter = bmMap.begin(); iter != bmMap.end(); ++iter) {
-    sumKeys += iter->first;
-    sumValues += iter->second;
-  }
-}
-
-BENCHMARK(ForEachKVMacro, iters) {
-  int sumKeys = 0;
-  std::string sumValues;
-
-  BENCHMARK_SUSPEND {
-    setupBenchmark(iters);
-  }
-
-  FOR_EACH_KV (k, v, bmMap) {
-    sumKeys += k;
-    sumValues += v;
-  }
-}
-
-BENCHMARK(ForEachManual, iters) {
-  int sum = 1;
-  for (size_t i = 1; i < iters; ++i) {
-    sum *= i;
-  }
-  doNotOptimizeAway(sum);
-}
-
-BENCHMARK(ForEachRange, iters) {
-  int sum = 1;
-  FOR_EACH_RANGE (i, 1, iters) {
-    sum *= i;
-  }
-  doNotOptimizeAway(sum);
-}
-
-BENCHMARK(ForEachDescendingManual, iters) {
-  int sum = 1;
-  for (size_t i = iters; i-- > 1; ) {
-    sum *= i;
-  }
-  doNotOptimizeAway(sum);
-}
-
-BENCHMARK(ForEachRangeR, iters) {
-  int sum = 1;
-  FOR_EACH_RANGE_R (i, 1U, iters) {
-    sum *= i;
-  }
-  doNotOptimizeAway(sum);
-}
-
-int main(int argc, char** argv) {
-  testing::InitGoogleTest(&argc, argv);
-  auto r = RUN_ALL_TESTS();
-  if (r) {
-    return r;
-  }
-  runBenchmarks();
-  return 0;
-}