1//
2// immer: immutable data structures for C++
3// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
4//
5// This software is distributed under the Boost Software License, Version 1.0.
6// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
7//
8
9#include "test/util.hpp"
10#include "test/dada.hpp"
11#include "test/transient_tester.hpp"
12
13#include <catch.hpp>
14
15#ifndef VECTOR_T
16#error "define the vector template to use in VECTOR_T"
17#endif
18
19#ifndef VECTOR_TRANSIENT_T
20#error "define the vector template to use in VECTOR_TRANSIENT_T"
21#endif
22
23template <typename V=VECTOR_T<unsigned>>
24auto make_test_vector(unsigned min, unsigned max)
25{
26 auto v = V{};
27 for (auto i = min; i < max; ++i)
28 v = v.push_back({i});
29 return v;
30}
31
32TEST_CASE("from vector and to vector")
33{
34 constexpr auto n = 100u;
35
36 auto v = make_test_vector(0, n).transient();
37 CHECK_VECTOR_EQUALS(v, boost::irange(0u, n));
38
39 auto p = v.persistent();
40 CHECK_VECTOR_EQUALS(p, boost::irange(0u, n));
41}
42
43TEST_CASE("protect persistence")
44{
45 auto v = VECTOR_T<unsigned>{}.transient();
46 v.push_back(12);
47 auto p = v.persistent();
48 v.set(0, 42);
49 CHECK(p[0] == 12);
50 CHECK(v[0] == 42);
51}
52
53TEST_CASE("push back move")
54{
55 using vector_t = VECTOR_T<unsigned>;
56
57 auto v = vector_t{};
58
59 auto check_move = [&] (vector_t&& x) -> vector_t&& {
60 if (vector_t::memory_policy::use_transient_rvalues)
61 CHECK(&x == &v);
62 else
63 CHECK(&x != &v);
64 return std::move(x);
65 };
66
67 v = check_move(std::move(v).push_back(0));
68 v = check_move(std::move(v).push_back(1));
69 v = check_move(std::move(v).push_back(2));
70 auto addr_before = &v[0];
71 v = check_move(std::move(v).push_back(3));
72 auto addr_after = &v[0];
73
74 if (vector_t::memory_policy::use_transient_rvalues)
75 CHECK(addr_before == addr_after);
76 else
77 CHECK(addr_before != addr_after);
78
79 CHECK_VECTOR_EQUALS(v, boost::irange(0u, 4u));
80}
81
82TEST_CASE("set move")
83{
84 using vector_t = VECTOR_T<unsigned>;
85
86 auto v = vector_t{};
87
88 auto check_move = [&] (vector_t&& x) -> vector_t&& {
89 if (vector_t::memory_policy::use_transient_rvalues)
90 CHECK(&x == &v);
91 else
92 CHECK(&x != &v);
93 return std::move(x);
94 };
95
96 v = v.push_back(0);
97
98 auto addr_before = &v[0];
99 v = check_move(std::move(v).set(0, 1));
100 auto addr_after = &v[0];
101
102 if (vector_t::memory_policy::use_transient_rvalues)
103 CHECK(addr_before == addr_after);
104 else
105 CHECK(addr_before != addr_after);
106
107 CHECK_VECTOR_EQUALS(v, boost::irange(1u, 2u));
108}
109
110TEST_CASE("update move")
111{
112 using vector_t = VECTOR_T<unsigned>;
113
114 auto v = vector_t{};
115
116 auto check_move = [&] (vector_t&& x) -> vector_t&& {
117 if (vector_t::memory_policy::use_transient_rvalues)
118 CHECK(&x == &v);
119 else
120 CHECK(&x != &v);
121 return std::move(x);
122 };
123
124 v = v.push_back(0);
125
126 auto addr_before = &v[0];
127 v = check_move(std::move(v).update(0, [] (auto x) { return x + 1; }));
128 auto addr_after = &v[0];
129
130 if (vector_t::memory_policy::use_transient_rvalues)
131 CHECK(addr_before == addr_after);
132 else
133 CHECK(addr_before != addr_after);
134
135 CHECK_VECTOR_EQUALS(v, boost::irange(1u, 2u));
136}
137
138TEST_CASE("take move")
139{
140 using vector_t = VECTOR_T<unsigned>;
141
142 auto v = vector_t{};
143
144 auto check_move = [&] (vector_t&& x) -> vector_t&& {
145 if (vector_t::memory_policy::use_transient_rvalues)
146 CHECK(&x == &v);
147 else
148 CHECK(&x != &v);
149 return std::move(x);
150 };
151
152 v = v.push_back(0).push_back(1);
153
154 auto addr_before = &v[0];
155 v = check_move(std::move(v).take(1));
156 auto addr_after = &v[0];
157
158 if (vector_t::memory_policy::use_transient_rvalues)
159 CHECK(addr_before == addr_after);
160 else
161 CHECK(addr_before != addr_after);
162
163 CHECK_VECTOR_EQUALS(v, boost::irange(0u, 1u));
164}
165
166TEST_CASE("exception safety")
167{
168 constexpr auto n = 667u;
169
170 using dadaist_vector_t = typename dadaist_wrapper<VECTOR_T<unsigned>>::type;
171
172 SECTION("push back")
173 {
174 auto t = as_transient_tester(dadaist_vector_t{});
175 auto d = dadaism{};
176 for (auto li = 0u, i = 0u; i < n;) {
177 auto s = d.next();
178 try {
179 if (t.transient)
180 t.vt.push_back({i});
181 else
182 t.vp = t.vp.push_back({i});
183 ++i;
184 if (t.step())
185 li = i;
186 } catch (dada_error) {}
187 if (t.transient) {
188 CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, i));
189 CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, li));
190 } else {
191 CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, i));
192 CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, li));
193 }
194 }
195 CHECK(d.happenings > 0);
196 CHECK(t.d.happenings > 0);
197 IMMER_TRACE_E(d.happenings);
198 IMMER_TRACE_E(t.d.happenings);
199 }
200
201 SECTION("update")
202 {
203 using boost::join;
204 using boost::irange;
205
206 auto t = as_transient_tester(make_test_vector<dadaist_vector_t>(0, n));
207 auto d = dadaism{};
208 for (auto li = 0u, i = 0u; i < n;) {
209 auto s = d.next();
210 try {
211 if (t.transient)
212 t.vt.update(i, [] (auto x) { return dada(), x + 1; });
213 else
214 t.vp = t.vp.update(i, [] (auto x) { return dada(), x + 1; });
215 ++i;
216 if (t.step())
217 li = i;
218 } catch (dada_error) {}
219 if (t.transient) {
220 CHECK_VECTOR_EQUALS(t.vt, join(irange(1u, 1u + i), irange(i, n)));
221 CHECK_VECTOR_EQUALS(t.vp, join(irange(1u, 1u + li), irange(li, n)));
222 } else {
223 CHECK_VECTOR_EQUALS(t.vp, join(irange(1u, 1u + i), irange(i, n)));
224 CHECK_VECTOR_EQUALS(t.vt, join(irange(1u, 1u + li), irange(li, n)));
225 }
226 }
227 CHECK(d.happenings > 0);
228 CHECK(t.d.happenings > 0);
229 }
230
231 SECTION("take")
232 {
233 auto t = as_transient_tester(make_test_vector<dadaist_vector_t>(0, n));
234 auto d = dadaism{};
235 auto deltas = magic_rotator();
236 auto delta = 0u;
237 for (auto i = n, li = i;;) {
238 auto s = d.next();
239 auto r = dadaist_vector_t{};
240 try {
241 if (t.transient)
242 t.vt.take(i);
243 else
244 t.vp = t.vp.take(i);
245 if (t.step())
246 li = i;
247 delta = deltas.next();
248 if (i < delta)
249 break;
250 i -= delta;
251 } catch (dada_error) {}
252 if (t.transient) {
253 CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, i + delta));
254 CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, li));
255 } else {
256 CHECK_VECTOR_EQUALS(t.vp, boost::irange(0u, i + delta));
257 CHECK_VECTOR_EQUALS(t.vt, boost::irange(0u, li));
258 }
259 }
260 CHECK(d.happenings > 0);
261 CHECK(t.d.happenings > 0);
262 }
263}
264