summaryrefslogtreecommitdiff
path: root/unittests/CoreTests/RangeTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/CoreTests/RangeTest.cpp')
-rw-r--r--unittests/CoreTests/RangeTest.cpp240
1 files changed, 240 insertions, 0 deletions
diff --git a/unittests/CoreTests/RangeTest.cpp b/unittests/CoreTests/RangeTest.cpp
new file mode 100644
index 0000000000000..2b2fcd5f78753
--- /dev/null
+++ b/unittests/CoreTests/RangeTest.cpp
@@ -0,0 +1,240 @@
+//===- lld/unittest/RangeTest.cpp -----------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief range.h unit tests.
+///
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+#include "lld/Core/range.h"
+#include <array>
+#include <assert.h>
+#include <deque>
+#include <forward_list>
+#include <iterator>
+#include <list>
+#include <numeric>
+#include <sstream>
+#include <vector>
+
+template <typename T, typename U> struct AssertTypesSame;
+template <typename T> struct AssertTypesSame<T, T> {};
+#define ASSERT_TYPES_SAME(T, U) AssertTypesSame<T, U>()
+
+struct no_begin {};
+struct member_begin {
+ int *begin();
+};
+struct free_begin {};
+int *begin(free_begin);
+
+template <typename T>
+auto type_of_forward(T &&t) -> decltype(std::forward<T>(t)) {
+ return std::forward<T>(t);
+}
+
+template <typename To> To implicit_cast(To val) { return val; }
+
+void test_traits() {
+ using namespace lld::detail;
+ ASSERT_TYPES_SAME(begin_result<no_begin>::type, undefined);
+ // This causes clang to segfault.
+#if 0
+ ASSERT_TYPES_SAME(
+ begin_result<decltype(type_of_forward(member_begin()))>::type, int *);
+#endif
+ ASSERT_TYPES_SAME(begin_result<free_begin>::type, int *);
+}
+
+TEST(Range, constructors) {
+ std::vector<int> v(5);
+ std::iota(v.begin(), v.end(), 0);
+ lld::range<std::vector<int>::iterator> r = v;
+ EXPECT_EQ(v.begin(), r.begin());
+ EXPECT_EQ(v.end(), r.end());
+
+ int arr[] = { 1, 2, 3, 4, 5 };
+ std::begin(arr);
+ lld::range<int *> r2 = arr;
+ EXPECT_EQ(5, r2.back());
+}
+
+TEST(Range, conversion_to_pointer_range) {
+ std::vector<int> v(5);
+ std::iota(v.begin(), v.end(), 0);
+ lld::range<int *> r = v;
+ EXPECT_EQ(&*v.begin(), r.begin());
+ EXPECT_EQ(2, r[2]);
+}
+
+template <typename Iter> void takes_range(lld::range<Iter> r) {
+ int expected = 0;
+ for (int val : r) {
+ EXPECT_EQ(expected++, val);
+ }
+}
+
+void takes_ptr_range(lld::range<const int *> r) {
+ int expected = 0;
+ for (int val : r) {
+ EXPECT_EQ(expected++, val);
+ }
+}
+
+TEST(Range, passing) {
+ using lld::make_range;
+ using lld::make_ptr_range;
+ std::list<int> l(5);
+ std::iota(l.begin(), l.end(), 0);
+ takes_range(make_range(l));
+ takes_range(make_range(implicit_cast<const std::list<int> &>(l)));
+ std::deque<int> d(5);
+ std::iota(d.begin(), d.end(), 0);
+ takes_range(make_range(d));
+ takes_range(make_range(implicit_cast<const std::deque<int> &>(d)));
+ std::vector<int> v(5);
+ std::iota(v.begin(), v.end(), 0);
+ takes_range(make_range(v));
+ takes_range(make_range(implicit_cast<const std::vector<int> &>(v)));
+ static_assert(
+ std::is_same<decltype(make_ptr_range(v)), lld::range<int *>>::value,
+ "make_ptr_range should return a range of pointers");
+ takes_range(make_ptr_range(v));
+ takes_range(make_ptr_range(implicit_cast<const std::vector<int> &>(v)));
+ int arr[] = { 0, 1, 2, 3, 4 };
+ takes_range(make_range(arr));
+ const int carr[] = { 0, 1, 2, 3, 4 };
+ takes_range(make_range(carr));
+
+ takes_ptr_range(v);
+ takes_ptr_range(implicit_cast<const std::vector<int> &>(v));
+ takes_ptr_range(arr);
+ takes_ptr_range(carr);
+}
+
+TEST(Range, access) {
+ std::array<int, 5> a = { { 1, 2, 3, 4, 5 } };
+ lld::range<decltype(a.begin())> r = a;
+ EXPECT_EQ(4, r[3]);
+ EXPECT_EQ(4, r[-2]);
+}
+
+template <bool b> struct CompileAssert;
+template <> struct CompileAssert<true> {};
+
+#if __has_feature(cxx_constexpr)
+constexpr int arr[] = { 1, 2, 3, 4, 5 };
+TEST(Range, constexpr) {
+ constexpr lld::range<const int *> r(arr, arr + 5);
+ CompileAssert<r.front() == 1>();
+ CompileAssert<r.size() == 5>();
+ CompileAssert<r[4] == 5>();
+}
+#endif
+
+template <typename Container> void test_slice() {
+ Container cont(10);
+ std::iota(cont.begin(), cont.end(), 0);
+ lld::range<decltype(cont.begin())> r = cont;
+
+ // One argument.
+ EXPECT_EQ(10, r.slice(0).size());
+ EXPECT_EQ(8, r.slice(2).size());
+ EXPECT_EQ(2, r.slice(2).front());
+ EXPECT_EQ(1, r.slice(-1).size());
+ EXPECT_EQ(9, r.slice(-1).front());
+
+ // Two positive arguments.
+ EXPECT_TRUE(r.slice(5, 2).empty());
+ EXPECT_EQ(next(cont.begin(), 5), r.slice(5, 2).begin());
+ EXPECT_EQ(1, r.slice(1, 2).size());
+ EXPECT_EQ(1, r.slice(1, 2).front());
+
+ // Two negative arguments.
+ EXPECT_TRUE(r.slice(-2, -5).empty());
+ EXPECT_EQ(next(cont.begin(), 8), r.slice(-2, -5).begin());
+ EXPECT_EQ(1, r.slice(-2, -1).size());
+ EXPECT_EQ(8, r.slice(-2, -1).front());
+
+ // Positive start, negative stop.
+ EXPECT_EQ(1, r.slice(6, -3).size());
+ EXPECT_EQ(6, r.slice(6, -3).front());
+ EXPECT_TRUE(r.slice(6, -5).empty());
+ EXPECT_EQ(next(cont.begin(), 6), r.slice(6, -5).begin());
+
+ // Negative start, positive stop.
+ EXPECT_TRUE(r.slice(-3, 6).empty());
+ EXPECT_EQ(next(cont.begin(), 7), r.slice(-3, 6).begin());
+ EXPECT_EQ(1, r.slice(-5, 6).size());
+ EXPECT_EQ(5, r.slice(-5, 6).front());
+}
+
+TEST(Range, slice) {
+ // -fsanitize=undefined complains about this, but only if optimizations are
+ // enabled.
+#if 0
+ test_slice<std::forward_list<int>>();
+#endif
+ test_slice<std::list<int>>();
+// This doesn't build with libstdc++ 4.7
+#if 0
+ test_slice<std::deque<int>>();
+#endif
+}
+
+// This test is flaky and I've yet to pin down why. Changing between
+// EXPECT_EQ(1, input.front()) and EXPECT_TRUE(input.front() == 1) makes it work
+// with VS 2012 in Debug mode. Clang on Linux seems to fail with -03 and -02 -g
+// -fsanitize=undefined.
+#if 0
+TEST(Range, istream_range) {
+ std::istringstream stream("1 2 3 4 5");
+ // MSVC interprets input as a function declaration if you don't declare start
+ // and instead directly pass std::istream_iterator<int>(stream).
+ auto start = std::istream_iterator<int>(stream);
+ lld::range<std::istream_iterator<int>> input(
+ start, std::istream_iterator<int>());
+ EXPECT_TRUE(input.front() == 1);
+ input.pop_front();
+ EXPECT_TRUE(input.front() == 2);
+ input.pop_front(2);
+ EXPECT_TRUE(input.front() == 4);
+ input.pop_front_upto(7);
+ EXPECT_TRUE(input.empty());
+}
+#endif
+
+//! [algorithm using range]
+template <typename T> void partial_sum(T &container) {
+ using lld::make_range;
+ auto range = make_range(container);
+ typename T::value_type sum = 0;
+ // One would actually use a range-based for loop
+ // in this case, but you get the idea:
+ for (; !range.empty(); range.pop_front()) {
+ sum += range.front();
+ range.front() = sum;
+ }
+}
+
+TEST(Range, user1) {
+ std::vector<int> v(5, 2);
+ partial_sum(v);
+ EXPECT_EQ(8, v[3]);
+}
+//! [algorithm using range]
+
+//! [algorithm using ptr_range]
+void my_write(int fd, lld::range<const char *> buffer) {}
+
+TEST(Range, user2) {
+ std::string s("Hello world");
+ my_write(1, s);
+}