diff options
Diffstat (limited to 'unittests/CoreTests/RangeTest.cpp')
-rw-r--r-- | unittests/CoreTests/RangeTest.cpp | 240 |
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); +} |