// // immer: immutable data structures for C++ // Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente // // This software is distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt // #include "test/dada.hpp" #include "test/transient_tester.hpp" #include "test/util.hpp" #include <catch.hpp> #ifndef VECTOR_T #error "define the vector template to use in VECTOR_T" #endif #ifndef VECTOR_TRANSIENT_T #error "define the vector template to use in VECTOR_TRANSIENT_T" #endif template <typename V = VECTOR_T<unsigned>> auto make_test_vector(unsigned min, unsigned max) { auto v = V{}; for (auto i = min; i < max; ++i) v = v.push_back({i}); return v; } TEST_CASE("from vector and to vector") { constexpr auto n = 100u; auto v = make_test_vector(0, n).transient(); CHECK_VECTOR_EQUALS(v, boost::irange(0u, n)); auto p = v.persistent(); CHECK_VECTOR_EQUALS(p, boost::irange(0u, n)); } TEST_CASE("protect persistence") { auto v = VECTOR_T<unsigned>{}.transient(); v.push_back(12); auto p = v.persistent(); v.set(0, 42); CHECK(p[0] == 12); CHECK(v[0] == 42); } TEST_CASE("push back move") { using vector_t = VECTOR_T<unsigned>; auto v = vector_t{}; auto check_move = [&](vector_t&& x) -> vector_t&& { if (vector_t::memory_policy::use_transient_rvalues) CHECK(&x == &v); else CHECK(&x != &v); return std::move(x); }; v = check_move(std::move(v).push_back(0)); v = check_move(std::move(v).push_back(1)); v = check_move(std::move(v).push_back(2)); auto addr_before = &v[0]; v = check_move(std::move(v).push_back(3)); auto addr_after = &v[0]; if (vector_t::memory_policy::use_transient_rvalues) CHECK(addr_before == addr_after); else CHECK(addr_before != addr_after); CHECK_VECTOR_EQUALS(v, boost::irange(0u, 4u)); } TEST_CASE("set move") { using vector_t = VECTOR_T<unsigned>; auto v = vector_t{}; auto check_move = [&](vector_t&& x) -> vector_t&& { if (vector_t::memory_policy::use_transient_rvalues) CHECK(&x == &v); else CHECK(&x != &v); return std::move(x); }; v = v.push_back(0); auto addr_before = &v[0]; v = check_move(std::move(v).set(0, 1)); auto addr_after = &v[0]; if (vector_t::memory_policy::use_transient_rvalues) CHECK(addr_before == addr_after); else CHECK(addr_before != addr_after); CHECK_VECTOR_EQUALS(v, boost::irange(1u, 2u)); } TEST_CASE("update move") { using vector_t = VECTOR_T<unsigned>; auto v = vector_t{}; auto check_move = [&](vector_t&& x) -> vector_t&& { if (vector_t::memory_policy::use_transient_rvalues) CHECK(&x == &v); else CHECK(&x != &v); return std::move(x); }; v = v.push_back(0); auto addr_before = &v[0]; v = check_move(std::move(v).update(0, [](auto x) { return x + 1; })); auto addr_after = &v[0]; if (vector_t::memory_policy::use_transient_rvalues) CHECK(addr_before == addr_after); else CHECK(addr_before != addr_after); CHECK_VECTOR_EQUALS(v, boost::irange(1u, 2u)); } TEST_CASE("take move") { using vector_t = VECTOR_T<unsigned>; auto v = vector_t{}; auto check_move = [&](vector_t&& x) -> vector_t&& { if (vector_t::memory_policy::use_transient_rvalues) CHECK(&x == &v); else CHECK(&x != &v); return std::move(x); }; v = v.push_back(0).push_back(1); auto addr_before = &v[0]; v = check_move(std::move(v).take(1)); auto addr_after = &v[0]; if (vector_t::memory_policy::use_transient_rvalues) CHECK(addr_before == addr_after); else CHECK(addr_before != addr_after); CHECK_VECTOR_EQUALS(v, boost::irange(0u, 1u)); } TEST_CASE("exception safety") { constexpr auto n = 667u; using dadaist_vector_t = typename dadaist_wrapper<VECTOR_T<unsigned>>::type; SECTION("push back") { auto t = as_transient_tester(dadaist_vector_t{}); auto d = dadaism{}; for (auto li = 0u, i = 0u; i < n;) { auto s = d.next(); try { if (t.transient) t.vt.push_back({i}); else t.vp = t.vp.push_back({i}); ++i; if (t.step()) li = i; } catch (dada_error) {} if (t.transient) { CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, i)); CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, li)); } else { CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, i)); CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, li)); } } CHECK(d.happenings > 0); CHECK(t.d.happenings > 0); IMMER_TRACE_E(d.happenings); IMMER_TRACE_E(t.d.happenings); } SECTION("update") { using boost::irange; using boost::join; auto t = as_transient_tester(make_test_vector<dadaist_vector_t>(0, n)); auto d = dadaism{}; for (auto li = 0u, i = 0u; i < n;) { auto s = d.next(); try { if (t.transient) t.vt.update(i, [](auto x) { return dada(), x + 1; }); else t.vp = t.vp.update(i, [](auto x) { return dada(), x + 1; }); ++i; if (t.step()) li = i; } catch (dada_error) {} if (t.transient) { CHECK_VECTOR_EQUALS(t.vt, join(irange(1u, 1u + i), irange(i, n))); CHECK_VECTOR_EQUALS(t.vp, join(irange(1u, 1u + li), irange(li, n))); } else { CHECK_VECTOR_EQUALS(t.vp, join(irange(1u, 1u + i), irange(i, n))); CHECK_VECTOR_EQUALS(t.vt, join(irange(1u, 1u + li), irange(li, n))); } } CHECK(d.happenings > 0); CHECK(t.d.happenings > 0); } SECTION("take") { auto t = as_transient_tester(make_test_vector<dadaist_vector_t>(0, n)); auto d = dadaism{}; auto deltas = magic_rotator(); auto delta = 0u; for (auto i = n, li = i;;) { auto s = d.next(); auto r = dadaist_vector_t{}; try { if (t.transient) t.vt.take(i); else t.vp = t.vp.take(i); if (t.step()) li = i; delta = deltas.next(); if (i < delta) break; i -= delta; } catch (dada_error) {} if (t.transient) { CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, i + delta)); CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, li)); } else { CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, i + delta)); CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, li)); } } CHECK(d.happenings > 0); CHECK(t.d.happenings > 0); } }