#include #include #include #include #include #include #include #include "helpers.hpp" #include "catch.hpp" using iter::sorted; using Vec = const std::vector; TEST_CASE("sorted: iterates through a vector in sorted order", "[sorted]") { Vec ns = {4, 0, 5, 1, 6, 7, 9, 3, 2, 8}; auto s = sorted(ns); Vec v(std::begin(s), std::end(s)); Vec vc = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; REQUIRE(v == vc); } TEST_CASE("sorted: can modify elements through sorted", "[sorted]") { std::vector ns(3, 9); for (auto&& n : sorted(ns)) { n = -1; } Vec vc(3, -1); REQUIRE(ns == vc); } TEST_CASE("sorted: can iterate over unordered container", "[sorted]") { std::unordered_set ns = {1, 3, 2, 0, 4}; auto s = sorted(ns); Vec v(std::begin(s), std::end(s)); Vec vc = {0, 1, 2, 3, 4}; REQUIRE(v == vc); } TEST_CASE("sorted: empty when iterable is empty", "[sorted]") { Vec ns{}; auto s = sorted(ns); REQUIRE(std::begin(s) == std::end(s)); } namespace { bool int_greater_than(int lhs, int rhs) { return lhs > rhs; } struct IntGreaterThan { bool operator()(int lhs, int rhs) const { return lhs > rhs; } }; } TEST_CASE("sorted: works with different functor types", "[sorted]") { Vec ns = {4, 1, 3, 2, 0}; std::vector v; SECTION("with function pointer") { auto s = sorted(ns, int_greater_than); v.insert(v.begin(), std::begin(s), std::end(s)); } SECTION("with callable object") { auto s = sorted(ns, IntGreaterThan{}); v.insert(v.begin(), std::begin(s), std::end(s)); } SECTION("with lambda") { auto s = sorted(ns, [](int lhs, int rhs) { return lhs > rhs; }); v.insert(v.begin(), std::begin(s), std::end(s)); } Vec vc = {4, 3, 2, 1, 0}; REQUIRE(v == vc); } namespace { template class BasicIterableWithConstDeref { private: T* data; std::size_t size; bool was_moved_from_ = false; public: BasicIterableWithConstDeref(std::initializer_list il) : data{new T[il.size()]}, size{il.size()} { // would like to use enumerate, can't because it's for unit // testing enumerate std::size_t i = 0; for (auto&& e : il) { data[i] = e; ++i; } } BasicIterableWithConstDeref& operator=( BasicIterableWithConstDeref&&) = delete; BasicIterableWithConstDeref& operator=( const BasicIterableWithConstDeref&) = delete; BasicIterableWithConstDeref(const BasicIterableWithConstDeref&) = delete; BasicIterableWithConstDeref(BasicIterableWithConstDeref&& other) : data{other.data}, size{other.size} { other.data = nullptr; other.was_moved_from_ = true; } bool was_moved_from() const { return this->was_moved_from_; } ~BasicIterableWithConstDeref() { delete[] this->data; } class Iterator { private: T* p; public: Iterator(T* b) : p{b} {} bool operator!=(const Iterator& other) const { return this->p != other.p; } Iterator& operator++() { ++this->p; return *this; } T& operator*() { return *this->p; } const T& operator*() const { return *this->p; } }; Iterator begin() { return {this->data}; } Iterator end() { return {this->data + this->size}; } }; } TEST_CASE("sorted: moves rvalues and binds to lvalues", "[sorted]") { BasicIterableWithConstDeref bi{1, 2}; sorted(bi); REQUIRE_FALSE(bi.was_moved_from()); sorted(std::move(bi)); REQUIRE(bi.was_moved_from()); } TEST_CASE("sorted: doesn't move or copy elements of iterable", "[sorted]") { using itertest::SolidInt; constexpr SolidInt arr[] = {{6}, {7}, {8}}; for (auto &&i : sorted(arr, [](const SolidInt &lhs, const SolidInt &rhs) { return lhs.getint() < rhs.getint(); })) { (void)i; } } template using ImpT = decltype(sorted(std::declval())); TEST_CASE("sorted: has correct ctor and assign ops", "[sorted]") { REQUIRE(itertest::IsMoveConstructibleOnly>::value); REQUIRE(itertest::IsMoveConstructibleOnly>::value); }