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