Commit | Line | Data |
---|---|---|
7c44b49c PA |
1 | /* Self tests for array_view for GDB, the GNU debugger. |
2 | ||
3 | Copyright (C) 2017 Free Software Foundation, Inc. | |
4 | ||
5 | This file is part of GDB. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 3 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
19 | ||
20 | #include "defs.h" | |
21 | #include "selftest.h" | |
22 | #include "common/array-view.h" | |
23 | ||
24 | namespace selftests { | |
25 | namespace array_view_tests { | |
26 | ||
27 | /* Triviality checks. */ | |
28 | #define CHECK_TRAIT(TRAIT) \ | |
29 | static_assert (std::TRAIT<gdb::array_view<gdb_byte>>::value, "") | |
30 | ||
31 | #if HAVE_IS_TRIVIALLY_COPYABLE | |
32 | ||
33 | CHECK_TRAIT (is_trivially_copyable); | |
34 | CHECK_TRAIT (is_trivially_move_assignable); | |
35 | CHECK_TRAIT (is_trivially_move_constructible); | |
36 | CHECK_TRAIT (is_trivially_destructible); | |
37 | ||
38 | #endif | |
39 | ||
40 | #undef CHECK_TRAIT | |
41 | ||
42 | /* Wrapper around std::is_convertible to make the code using it a bit | |
43 | shorter. (With C++14 we'd use a variable template instead.) */ | |
44 | ||
45 | template<typename From, typename To> | |
46 | static constexpr bool | |
47 | is_convertible () | |
48 | { | |
49 | return std::is_convertible<From, To>::value; | |
50 | } | |
51 | ||
52 | /* Check for implicit conversion to immutable and mutable views. */ | |
53 | ||
54 | static constexpr bool | |
55 | check_convertible () | |
56 | { | |
57 | using T = gdb_byte; | |
58 | using gdb::array_view; | |
59 | ||
60 | return (true | |
61 | /* immutable array_view */ | |
62 | && is_convertible<const T (&) [1], array_view<const T>> () | |
63 | && is_convertible<T (&) [1], array_view<const T>> () | |
64 | && is_convertible<const T, array_view<const T>> () | |
65 | && is_convertible<T, array_view<const T>> () | |
66 | ||
67 | /* mutable array_view */ | |
68 | && is_convertible<T (&) [1], array_view<T>> () | |
69 | && !is_convertible<const T (&) [1], array_view<T>> () | |
70 | && is_convertible<T, array_view<T>> () | |
71 | && !is_convertible<const T, array_view<T>> () | |
72 | ||
73 | /* While float is implicitly convertible to gdb_byte, we | |
74 | don't want implicit float->array_view<gdb_byte> | |
75 | conversion. */ | |
76 | && !is_convertible<float, array_view<const T>> () | |
77 | && !is_convertible<float, array_view<T>> ()); | |
78 | } | |
79 | ||
80 | static_assert (check_convertible (), ""); | |
81 | ||
82 | namespace no_slicing | |
83 | { | |
84 | struct A { int i; }; | |
85 | struct B : A { int j; }; | |
86 | struct C : A { int l; }; | |
87 | ||
88 | /* Check that there's no array->view conversion for arrays of derived | |
89 | types or subclasses. */ | |
90 | static constexpr bool | |
91 | check () | |
92 | { | |
93 | using gdb::array_view; | |
94 | ||
95 | return (true | |
96 | ||
97 | /* array->view */ | |
98 | ||
99 | && is_convertible <A (&)[1], array_view<A>> () | |
100 | && !is_convertible <B (&)[1], array_view<A>> () | |
101 | && !is_convertible <C (&)[1], array_view<A>> () | |
102 | ||
103 | && !is_convertible <A (&)[1], array_view<B>> () | |
104 | && is_convertible <B (&)[1], array_view<B>> () | |
105 | && !is_convertible <C (&)[1], array_view<B>> () | |
106 | ||
107 | /* elem->view */ | |
108 | ||
109 | && is_convertible <A, array_view<A>> () | |
110 | && !is_convertible <B, array_view<A>> () | |
111 | && !is_convertible <C, array_view<A>> () | |
112 | ||
113 | && !is_convertible <A, array_view<B>> () | |
114 | && is_convertible <B, array_view<B>> () | |
115 | && !is_convertible <C, array_view<B>> ()); | |
116 | } | |
117 | ||
118 | } /* namespace no_slicing */ | |
119 | ||
120 | static_assert (no_slicing::check (), ""); | |
121 | ||
122 | /* Check that array_view implicitly converts from std::vector. */ | |
123 | ||
124 | static constexpr bool | |
125 | check_convertible_from_std_vector () | |
126 | { | |
127 | using gdb::array_view; | |
128 | using T = gdb_byte; | |
129 | ||
130 | /* Note there's no such thing as std::vector<const T>. */ | |
131 | ||
132 | return (true | |
133 | && is_convertible <std::vector<T>, array_view<T>> () | |
134 | && is_convertible <std::vector<T>, array_view<const T>> ()); | |
135 | } | |
136 | ||
137 | static_assert (check_convertible_from_std_vector (), ""); | |
138 | ||
139 | /* Check that array_view implicitly converts from std::array. */ | |
140 | ||
141 | static constexpr bool | |
142 | check_convertible_from_std_array () | |
143 | { | |
144 | using gdb::array_view; | |
145 | using T = gdb_byte; | |
146 | ||
147 | /* Note: a non-const T view can't refer to a const T array. */ | |
148 | ||
149 | return (true | |
150 | && is_convertible <std::array<T, 1>, array_view<T>> () | |
151 | && is_convertible <std::array<T, 1>, array_view<const T>> () | |
152 | && !is_convertible <std::array<const T, 1>, array_view<T>> () | |
153 | && is_convertible <std::array<const T, 1>, array_view<const T>> ()); | |
154 | } | |
155 | ||
156 | static_assert (check_convertible_from_std_array (), ""); | |
157 | ||
158 | /* Check that VIEW views C (a container like std::vector/std::array) | |
159 | correctly. */ | |
160 | ||
161 | template<typename View, typename Container> | |
162 | static bool | |
163 | check_container_view (const View &view, const Container &c) | |
164 | { | |
165 | if (view.empty ()) | |
166 | return false; | |
167 | if (view.size () != c.size ()) | |
168 | return false; | |
169 | if (view.data () != c.data ()) | |
170 | return false; | |
171 | for (size_t i = 0; i < c.size (); i++) | |
172 | { | |
173 | if (&view[i] != &c[i]) | |
174 | return false; | |
175 | if (view[i] != c[i]) | |
176 | return false; | |
177 | } | |
178 | return true; | |
179 | } | |
180 | ||
181 | /* Check that VIEW views E (an object of the type of a view element) | |
182 | correctly. */ | |
183 | ||
184 | template<typename View, typename Elem> | |
185 | static bool | |
186 | check_elem_view (const View &view, const Elem &e) | |
187 | { | |
188 | if (view.empty ()) | |
189 | return false; | |
190 | if (view.size () != 1) | |
191 | return false; | |
192 | if (view.data () != &e) | |
193 | return false; | |
194 | if (&view[0] != &e) | |
195 | return false; | |
196 | if (view[0] != e) | |
197 | return false; | |
198 | return true; | |
199 | } | |
200 | ||
201 | /* Check for operator[]. The first overload is taken iff | |
202 | 'view<T>()[0] = T()' is a valid expression. */ | |
203 | ||
204 | template<typename View, | |
205 | typename = decltype (std::declval<View> ()[0] | |
206 | = std::declval<typename View::value_type> ())> | |
207 | static bool | |
208 | check_op_subscript (const View &view) | |
209 | { | |
210 | return true; | |
211 | } | |
212 | ||
213 | /* This overload is taken iff 'view<T>()[0] = T()' is not a valid | |
214 | expression. */ | |
215 | ||
216 | static bool | |
217 | check_op_subscript (...) | |
218 | { | |
219 | return false; | |
220 | } | |
221 | ||
222 | /* Check construction with pointer + size. This is a template in | |
223 | order to test both gdb_byte and const gdb_byte. */ | |
224 | ||
225 | template<typename T> | |
226 | static void | |
227 | check_ptr_size_ctor () | |
228 | { | |
229 | T data[] = {0x11, 0x22, 0x33, 0x44}; | |
230 | ||
231 | gdb::array_view<T> view (data + 1, 2); | |
232 | ||
233 | SELF_CHECK (!view.empty ()); | |
234 | SELF_CHECK (view.size () == 2); | |
235 | SELF_CHECK (view.data () == &data[1]); | |
236 | SELF_CHECK (view[0] == data[1]); | |
237 | SELF_CHECK (view[1] == data[2]); | |
238 | ||
239 | gdb::array_view<const T> cview (data + 1, 2); | |
240 | SELF_CHECK (!cview.empty ()); | |
241 | SELF_CHECK (cview.size () == 2); | |
242 | SELF_CHECK (cview.data () == &data[1]); | |
243 | SELF_CHECK (cview[0] == data[1]); | |
244 | SELF_CHECK (cview[1] == data[2]); | |
245 | } | |
246 | ||
247 | /* Asserts std::is_constructible. */ | |
248 | ||
249 | template<typename T, typename... Args> | |
250 | static constexpr bool | |
251 | require_not_constructible () | |
252 | { | |
253 | static_assert (!std::is_constructible<T, Args...>::value, ""); | |
254 | ||
255 | /* constexpr functions can't return void in C++11 (N3444). */ | |
256 | return true; | |
257 | }; | |
258 | ||
259 | /* Check the array_view<T>(PTR, SIZE) ctor, when T is a pointer. */ | |
260 | ||
261 | void | |
262 | check_ptr_size_ctor2 () | |
263 | { | |
264 | struct A {}; | |
265 | A an_a; | |
266 | ||
267 | A *array[] = { &an_a }; | |
268 | const A * const carray[] = { &an_a }; | |
269 | ||
270 | gdb::array_view<A *> v1 = {array, ARRAY_SIZE (array)}; | |
271 | gdb::array_view<A *> v2 = {array, (char) ARRAY_SIZE (array)}; | |
272 | gdb::array_view<A * const> v3 = {array, ARRAY_SIZE (array)}; | |
273 | gdb::array_view<const A * const> cv1 = {carray, ARRAY_SIZE (carray)}; | |
274 | ||
275 | require_not_constructible<gdb::array_view<A *>, decltype (carray), size_t> (); | |
276 | ||
277 | SELF_CHECK (v1[0] == array[0]); | |
278 | SELF_CHECK (v2[0] == array[0]); | |
279 | SELF_CHECK (v3[0] == array[0]); | |
280 | ||
281 | SELF_CHECK (!v1.empty ()); | |
282 | SELF_CHECK (v1.size () == 1); | |
283 | SELF_CHECK (v1.data () == &array[0]); | |
284 | ||
285 | SELF_CHECK (cv1[0] == carray[0]); | |
286 | ||
287 | SELF_CHECK (!cv1.empty ()); | |
288 | SELF_CHECK (cv1.size () == 1); | |
289 | SELF_CHECK (cv1.data () == &carray[0]); | |
290 | } | |
291 | ||
292 | /* Check construction with a pair of pointers. This is a template in | |
293 | order to test both gdb_byte and const gdb_byte. */ | |
294 | ||
295 | template<typename T> | |
296 | static void | |
297 | check_ptr_ptr_ctor () | |
298 | { | |
299 | T data[] = {0x11, 0x22, 0x33, 0x44}; | |
300 | ||
301 | gdb::array_view<T> view (data + 1, data + 3); | |
302 | ||
303 | SELF_CHECK (!view.empty ()); | |
304 | SELF_CHECK (view.size () == 2); | |
305 | SELF_CHECK (view.data () == &data[1]); | |
306 | SELF_CHECK (view[0] == data[1]); | |
307 | SELF_CHECK (view[1] == data[2]); | |
308 | ||
309 | gdb_byte array[] = {0x11, 0x22, 0x33, 0x44}; | |
310 | const gdb_byte *p1 = array; | |
311 | gdb_byte *p2 = array + ARRAY_SIZE (array); | |
312 | gdb::array_view<const gdb_byte> view2 (p1, p2); | |
313 | } | |
314 | ||
315 | /* Check construction with a pair of pointers of mixed constness. */ | |
316 | ||
317 | static void | |
318 | check_ptr_ptr_mixed_cv () | |
319 | { | |
320 | gdb_byte array[] = {0x11, 0x22, 0x33, 0x44}; | |
321 | const gdb_byte *cp = array; | |
322 | gdb_byte *p = array; | |
323 | gdb::array_view<const gdb_byte> view1 (cp, p); | |
324 | gdb::array_view<const gdb_byte> view2 (p, cp); | |
325 | SELF_CHECK (view1.empty ()); | |
326 | SELF_CHECK (view2.empty ()); | |
327 | } | |
328 | ||
329 | /* Check range-for support (i.e., begin()/end()). This is a template | |
330 | in order to test both gdb_byte and const gdb_byte. */ | |
331 | ||
332 | template<typename T> | |
333 | static void | |
334 | check_range_for () | |
335 | { | |
336 | T data[] = {1, 2, 3, 4}; | |
337 | gdb::array_view<T> view (data); | |
338 | ||
339 | typename std::decay<T>::type sum = 0; | |
340 | for (auto &elem : view) | |
341 | sum += elem; | |
342 | SELF_CHECK (sum == 1 + 2 + 3 + 4); | |
343 | } | |
344 | ||
345 | /* Entry point. */ | |
346 | ||
347 | static void | |
348 | run_tests () | |
349 | { | |
350 | /* Empty views. */ | |
351 | { | |
352 | constexpr gdb::array_view<gdb_byte> view1; | |
353 | constexpr gdb::array_view<const gdb_byte> view2; | |
354 | ||
355 | static_assert (view1.empty (), ""); | |
356 | static_assert (view1.data () == nullptr, ""); | |
357 | static_assert (view1.size () == 0, ""); | |
358 | static_assert (view2.empty (), ""); | |
359 | static_assert (view2.size () == 0, ""); | |
360 | static_assert (view2.data () == nullptr, ""); | |
361 | } | |
362 | ||
363 | std::vector<gdb_byte> vec = {0x11, 0x22, 0x33, 0x44 }; | |
364 | std::array<gdb_byte, 4> array = {{0x11, 0x22, 0x33, 0x44}}; | |
365 | ||
366 | /* Various tests of views over std::vector. */ | |
367 | { | |
368 | gdb::array_view<gdb_byte> view = vec; | |
369 | SELF_CHECK (check_container_view (view, vec)); | |
370 | gdb::array_view<const gdb_byte> cview = vec; | |
371 | SELF_CHECK (check_container_view (cview, vec)); | |
372 | } | |
373 | ||
374 | /* Likewise, over std::array. */ | |
375 | { | |
376 | gdb::array_view<gdb_byte> view = array; | |
377 | SELF_CHECK (check_container_view (view, array)); | |
378 | gdb::array_view<gdb_byte> cview = array; | |
379 | SELF_CHECK (check_container_view (cview, array)); | |
380 | } | |
381 | ||
382 | /* op=(std::vector/std::array/elem) */ | |
383 | { | |
384 | gdb::array_view<gdb_byte> view; | |
385 | ||
386 | view = vec; | |
387 | SELF_CHECK (check_container_view (view, vec)); | |
388 | view = std::move (vec); | |
389 | SELF_CHECK (check_container_view (view, vec)); | |
390 | ||
391 | view = array; | |
392 | SELF_CHECK (check_container_view (view, array)); | |
393 | view = std::move (array); | |
394 | SELF_CHECK (check_container_view (view, array)); | |
395 | ||
396 | gdb_byte elem = 0; | |
397 | view = elem; | |
398 | SELF_CHECK (check_elem_view (view, elem)); | |
399 | view = std::move (elem); | |
400 | SELF_CHECK (check_elem_view (view, elem)); | |
401 | } | |
402 | ||
403 | /* Test copy/move ctor and mutable->immutable conversion. */ | |
404 | { | |
405 | gdb_byte data[] = {0x11, 0x22, 0x33, 0x44}; | |
406 | gdb::array_view<gdb_byte> view1 = data; | |
407 | gdb::array_view<gdb_byte> view2 = view1; | |
408 | gdb::array_view<gdb_byte> view3 = std::move (view1); | |
409 | gdb::array_view<const gdb_byte> cview1 = data; | |
410 | gdb::array_view<const gdb_byte> cview2 = cview1; | |
411 | gdb::array_view<const gdb_byte> cview3 = std::move (cview1); | |
412 | SELF_CHECK (view1[0] == data[0]); | |
413 | SELF_CHECK (view2[0] == data[0]); | |
414 | SELF_CHECK (view3[0] == data[0]); | |
415 | SELF_CHECK (cview1[0] == data[0]); | |
416 | SELF_CHECK (cview2[0] == data[0]); | |
417 | SELF_CHECK (cview3[0] == data[0]); | |
418 | } | |
419 | ||
420 | /* Same, but op=(view). */ | |
421 | { | |
422 | gdb_byte data[] = {0x55, 0x66, 0x77, 0x88}; | |
423 | gdb::array_view<gdb_byte> view1; | |
424 | gdb::array_view<gdb_byte> view2; | |
425 | gdb::array_view<gdb_byte> view3; | |
426 | gdb::array_view<const gdb_byte> cview1; | |
427 | gdb::array_view<const gdb_byte> cview2; | |
428 | gdb::array_view<const gdb_byte> cview3; | |
429 | ||
430 | view1 = data; | |
431 | view2 = view1; | |
432 | view3 = std::move (view1); | |
433 | cview1 = data; | |
434 | cview2 = cview1; | |
435 | cview3 = std::move (cview1); | |
436 | SELF_CHECK (view1[0] == data[0]); | |
437 | SELF_CHECK (view2[0] == data[0]); | |
438 | SELF_CHECK (view3[0] == data[0]); | |
439 | SELF_CHECK (cview1[0] == data[0]); | |
440 | SELF_CHECK (cview2[0] == data[0]); | |
441 | SELF_CHECK (cview3[0] == data[0]); | |
442 | } | |
443 | ||
444 | /* op[] */ | |
445 | { | |
446 | std::vector<gdb_byte> vec = {0x11, 0x22}; | |
447 | gdb::array_view<gdb_byte> view = vec; | |
448 | gdb::array_view<const gdb_byte> cview = vec; | |
449 | ||
450 | /* Check that op[] on a non-const view of non-const T returns a | |
451 | mutable reference. */ | |
452 | view[0] = 0x33; | |
453 | SELF_CHECK (vec[0] == 0x33); | |
454 | ||
455 | /* OTOH, check that assigning through op[] on a view of const T | |
456 | wouldn't compile. */ | |
457 | SELF_CHECK (!check_op_subscript (cview)); | |
458 | /* For completeness. */ | |
459 | SELF_CHECK (check_op_subscript (view)); | |
460 | } | |
461 | ||
462 | check_ptr_size_ctor<const gdb_byte> (); | |
463 | check_ptr_size_ctor<gdb_byte> (); | |
464 | check_ptr_size_ctor2 (); | |
465 | check_ptr_ptr_ctor<const gdb_byte> (); | |
466 | check_ptr_ptr_ctor<gdb_byte> (); | |
467 | check_ptr_ptr_mixed_cv (); | |
468 | ||
469 | check_range_for<gdb_byte> (); | |
470 | check_range_for<const gdb_byte> (); | |
471 | ||
472 | /* Check that the right ctor overloads are taken when the element is | |
473 | a container. */ | |
474 | { | |
475 | using Vec = std::vector<gdb_byte>; | |
476 | Vec vecs[3]; | |
477 | ||
478 | gdb::array_view<Vec> view_array = vecs; | |
479 | SELF_CHECK (view_array.size () == 3); | |
480 | ||
481 | Vec elem; | |
482 | gdb::array_view<Vec> view_elem = elem; | |
483 | SELF_CHECK (view_elem.size () == 1); | |
484 | } | |
485 | } | |
486 | ||
487 | } /* namespace array_view_tests */ | |
488 | } /* namespace selftests */ | |
489 | ||
490 | void | |
491 | _initialize_array_view_selftests () | |
492 | { | |
493 | selftests::register_test (selftests::array_view_tests::run_tests); | |
494 | } |