#include "range.hpp" #include #include #include #include "catch.hpp" #include "helpers.hpp" using Vec = const std::vector; using iter::range; TEST_CASE("range: works with only stop", "[range]") { auto r = range(5); Vec v(std::begin(r), std::end(r)); Vec vc{0, 1, 2, 3, 4}; REQUIRE(v == vc); } TEST_CASE("range: works with start and stop", "[range]") { auto r = range(1, 5); Vec v(std::begin(r), std::end(r)); Vec vc{1, 2, 3, 4}; REQUIRE(v == vc); } TEST_CASE("range: works with positive step > 1", "[range]") { auto r = range(1, 10, 3); Vec v(std::begin(r), std::end(r)); Vec vc{1, 4, 7}; REQUIRE(v == vc); } TEST_CASE("range(0) is empty", "[range]") { auto r = iter::range(0); REQUIRE(std::begin(r) == std::end(r)); } TEST_CASE("range: postfix++", "[range]") { auto r = iter::range(3); auto it = std::begin(r); it++; REQUIRE(*it == 1); } TEST_CASE("start > stop produces empty range", "[range]") { auto r = range(5, 0); Vec v(std::begin(r), std::end(r)); REQUIRE(v.empty()); } TEST_CASE("start < stop and step < 0 produces empty range", "[range]") { auto r = range(0, 5, -1); Vec v(std::begin(r), std::end(r)); REQUIRE(v.empty()); } TEST_CASE("range: with only a negative stop is empty", "[range]") { auto r = range(-3); Vec v(std::begin(r), std::end(r)); REQUIRE(v.empty()); } TEST_CASE("range: works with negative step", "[range]") { auto r = range(5, -5, -3); Vec v(std::begin(r), std::end(r)); Vec vc{5, 2, -1, -4}; REQUIRE(v == vc); } TEST_CASE("range: stops short when step doesn't divide stop-start", "[range]") { auto r = range(0, 5, 2); Vec v(std::begin(r), std::end(r)); Vec vc{0, 2, 4}; REQUIRE(v == vc); } TEST_CASE("range: stops short when step > stop-start", "[range]") { auto r = range(0, 10, 20); Vec v(std::begin(r), std::end(r)); REQUIRE(v.size() == 1); } TEST_CASE("range: step size of 0 gives an empty range", "[range]") { auto r = range(0, 10, 0); REQUIRE(std::begin(r) == std::end(r)); auto r2 = range(0, -10, 0); REQUIRE(std::begin(r2) == std::end(r2)); } TEST_CASE("range: can create constexpr ranges", "[range]") { constexpr auto r = range(10); (void)r; constexpr auto r2 = range(4, 10); (void)r2; constexpr auto r3 = range(4, 10, 2); (void)r3; constexpr auto it = r2.begin(); // std::begin isn't constexpr constexpr auto i = *it; static_assert(i == 4, "range's begin has the wrong value"); constexpr auto rf = range(10.0); constexpr auto itf = rf.begin(); constexpr auto f = *itf; static_assert(f == 0.0, "range's begin has tho wrong value (float)"); } TEST_CASE("range: const iterators compare to non-const iterators", "[range]") { auto r = range(0); const auto& cr = r; (void)(std::begin(r) == std::end(cr)); } TEST_CASE("range: works with a variable start, stop, and step", "[range]") { constexpr int a = 10; constexpr int b = 100; constexpr int c = 50; SECTION("Going up works") { auto r = range(a, a + 2); Vec v(std::begin(r), std::end(r)); Vec vc{a, a + 1}; REQUIRE(v == vc); } SECTION("Going down works") { auto r = range(a + 2, a, -1); Vec v(std::begin(r), std::end(r)); Vec vc{a + 2, a + 1}; REQUIRE(v == vc); } SECTION("Going down with -2 stop works") { auto r = range(a + 4, a, -2); Vec v(std::begin(r), std::end(r)); Vec vc{a + 4, a + 2}; REQUIRE(v == vc); } SECTION("Using three variable") { auto r = range(a, b, c); Vec v(std::begin(r), std::end(r)); REQUIRE(std::find(std::begin(v), std::end(v), a) != std::end(v)); REQUIRE(std::find(std::begin(v), std::end(v), b) == std::end(v)); REQUIRE(v.size() == 2); } SECTION("Using three with a unary negate on step") { auto r = range(b, a, -c); Vec v(std::begin(r), std::end(r)); REQUIRE(std::find(std::begin(v), std::end(v), b) != std::end(v)); REQUIRE(std::find(std::begin(v), std::end(v), a) == std::end(v)); REQUIRE(v.size() == 2); } SECTION("Using all three negated") { auto r = range(-a, -b, -c); Vec v(std::begin(r), std::end(r)); REQUIRE(std::find(std::begin(v), std::end(v), -a) != std::end(v)); REQUIRE(std::find(std::begin(v), std::end(v), -b) == std::end(v)); REQUIRE(v.size() == 2); } } TEST_CASE("range: forward iterator checks", "[range]") { auto r = range(10); REQUIRE(std::end(r) == std::end(r)); auto it1 = std::begin(r); auto it2 = std::begin(r); REQUIRE_FALSE(it1 != it2); REQUIRE(it1 == it2); ++it1; REQUIRE(it1 != it2); ++it2; REQUIRE(it1 == it2); auto it3 = it1++; REQUIRE(it3 == it2); auto it4 = ++it3; REQUIRE(it4 == it3); auto it5 = std::begin(r); const auto& v = *it5; ++it5; REQUIRE(v != *it5); } TEST_CASE("range: forward iterator with double, checks", "[range]") { auto r = range(10.0); REQUIRE(std::end(r) == std::end(r)); auto it1 = std::begin(r); auto it2 = std::begin(r); REQUIRE_FALSE(it1 != it2); REQUIRE(it1 == it2); ++it1; REQUIRE(it1 != it2); ++it2; REQUIRE(it1 == it2); auto it3 = it1++; REQUIRE(it3 == it2); auto it4 = ++it3; REQUIRE(it4 == it3); } using FVec = const std::vector; TEST_CASE("range: using doubles", "[range]") { auto r = range(5.0); FVec fv(std::begin(r), std::end(r)); FVec fvc = {0.0, 1.0, 2.0, 3.0, 4.0}; REQUIRE(fv == fvc); } TEST_CASE("range: using doubles with start and stop", "[range]") { auto r = range(5.0, 10.0); FVec fv(std::begin(r), std::end(r)); FVec fvc = {5.0, 6.0, 7.0, 8.0, 9.0}; REQUIRE(fv == fvc); } TEST_CASE("range: using doubles with start, stop and step", "[range]") { auto r = range(1.0, 4.0, 0.5); FVec fv(std::begin(r), std::end(r)); FVec fvc = {1.0, 1.5, 2.0, 2.5, 3.0, 3.5}; REQUIRE(fv == fvc); } TEST_CASE("range: using doubles with negative", "[range]") { auto r = range(0.5, -2.0, -0.5); FVec fv(std::begin(r), std::end(r)); FVec fvc = {0.5, 0.0, -0.5, -1.0, -1.5}; REQUIRE(fv == fvc); } TEST_CASE("range: using doubles with uneven step", "[range]") { auto r = range(0.0, 1.75, 0.5); FVec fv(std::begin(r), std::end(r)); FVec fvc = {0.0, 0.5, 1.0, 1.5}; REQUIRE(fv == fvc); } TEST_CASE("range: using doubles detects empty ranges", "[range]") { auto r1 = range(0.0, -1.0); REQUIRE(std::begin(r1) == std::end(r1)); auto r2 = range(0.0, 1.0, -1.0); REQUIRE(std::begin(r2) == std::end(r2)); } TEST_CASE("range: iterator meets forward iterator requirements", "[range]") { auto r = range(5); auto r2 = range(5.0); REQUIRE(itertest::IsForwardIterator::value); REQUIRE(itertest::IsForwardIterator::value); }