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 | |
23 | template <typename V=VECTOR_T<unsigned>> |
24 | auto 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 | |
32 | TEST_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 | |
43 | TEST_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 | |
53 | TEST_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 | |
82 | TEST_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 | |
110 | TEST_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 | |
138 | TEST_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 | |
166 | TEST_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 | |