| //===- 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); |
| } |