1/* Self tests for array_view for GDB, the GNU debugger. 2 3 Copyright (C) 2017-2023 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 "gdbsupport/selftest.h" 22#include "gdbsupport/array-view.h" 23#include <array> 24#include <vector> 25 26namespace selftests { 27namespace array_view_tests { 28 29/* Triviality checks. */ 30#define CHECK_TRAIT(TRAIT) \ 31 static_assert (std::TRAIT<gdb::array_view<gdb_byte>>::value, "") 32 33#if HAVE_IS_TRIVIALLY_COPYABLE 34 35CHECK_TRAIT (is_trivially_copyable); 36CHECK_TRAIT (is_trivially_move_assignable); 37CHECK_TRAIT (is_trivially_move_constructible); 38CHECK_TRAIT (is_trivially_destructible); 39 40#endif 41 42#undef CHECK_TRAIT 43 44/* Wrapper around std::is_convertible to make the code using it a bit 45 shorter. (With C++14 we'd use a variable template instead.) */ 46 47template<typename From, typename To> 48static constexpr bool 49is_convertible () 50{ 51 return std::is_convertible<From, To>::value; 52} 53 54/* Check for implicit conversion to immutable and mutable views. */ 55 56static constexpr bool 57check_convertible () 58{ 59 using T = gdb_byte; 60 using gdb::array_view; 61 62 return (true 63 /* immutable array_view */ 64 && is_convertible<const T (&) [1], array_view<const T>> () 65 && is_convertible<T (&) [1], array_view<const T>> () 66 && is_convertible<const T, array_view<const T>> () 67 && is_convertible<T, array_view<const T>> () 68 69 /* mutable array_view */ 70 && is_convertible<T (&) [1], array_view<T>> () 71 && !is_convertible<const T (&) [1], array_view<T>> () 72 && is_convertible<T, array_view<T>> () 73 && !is_convertible<const T, array_view<T>> () 74 75 /* While float is implicitly convertible to gdb_byte, we 76 don't want implicit float->array_view<gdb_byte> 77 conversion. */ 78 && !is_convertible<float, array_view<const T>> () 79 && !is_convertible<float, array_view<T>> ()); 80} 81 82static_assert (check_convertible (), ""); 83 84namespace no_slicing 85{ 86struct A { int i; }; 87struct B : A { int j; }; 88struct C : A { int l; }; 89 90/* Check that there's no array->view conversion for arrays of derived types or 91 subclasses. */ 92static constexpr bool 93check () 94{ 95 using gdb::array_view; 96 97 return (true 98 99 /* array->view */ 100 101 && is_convertible <A (&)[1], array_view<A>> () 102 && !is_convertible <B (&)[1], array_view<A>> () 103 && !is_convertible <C (&)[1], array_view<A>> () 104 105 && !is_convertible <A (&)[1], array_view<B>> () 106 && is_convertible <B (&)[1], array_view<B>> () 107 && !is_convertible <C (&)[1], array_view<B>> () 108 109 /* elem->view */ 110 111 && is_convertible <A, array_view<A>> () 112 && !is_convertible <B, array_view<A>> () 113 && !is_convertible <C, array_view<A>> () 114 115 && !is_convertible <A, array_view<B>> () 116 && is_convertible <B, array_view<B>> () 117 && !is_convertible <C, array_view<B>> ()); 118} 119 120/* Check that there's no container->view conversion for containers of derived 121 types or subclasses. */ 122 123template<template<typename ...> class Container> 124static constexpr bool 125check_ctor_from_container () 126{ 127 using gdb::array_view; 128 129 return ( is_convertible <Container<A>, array_view<A>> () 130 && !is_convertible <Container<B>, array_view<A>> () 131 && !is_convertible <Container<C>, array_view<A>> () 132 133 && !is_convertible <Container<A>, array_view<B>> () 134 && is_convertible <Container<B>, array_view<B>> () 135 && !is_convertible <Container<C>, array_view<B>> ()); 136} 137 138} /* namespace no_slicing */ 139 140/* std::array with only one template argument, so we can pass it to 141 check_ctor_from_container. */ 142template<typename T> using StdArray1 = std::array<T, 1>; 143 144static_assert (no_slicing::check (), ""); 145static_assert (no_slicing::check_ctor_from_container<std::vector> (), ""); 146static_assert (no_slicing::check_ctor_from_container<StdArray1> (), ""); 147static_assert (no_slicing::check_ctor_from_container<gdb::array_view> (), ""); 148 149/* Check that array_view implicitly converts from std::vector. */ 150 151static constexpr bool 152check_convertible_from_std_vector () 153{ 154 using gdb::array_view; 155 using T = gdb_byte; 156 157 /* Note there's no such thing as std::vector<const T>. */ 158 159 return (true 160 && is_convertible <std::vector<T>, array_view<T>> () 161 && is_convertible <std::vector<T>, array_view<const T>> ()); 162} 163 164static_assert (check_convertible_from_std_vector (), ""); 165 166/* Check that array_view implicitly converts from std::array. */ 167 168static constexpr bool 169check_convertible_from_std_array () 170{ 171 using gdb::array_view; 172 using T = gdb_byte; 173 174 /* Note: a non-const T view can't refer to a const T array. */ 175 176 return (true 177 && is_convertible <std::array<T, 1>, array_view<T>> () 178 && is_convertible <std::array<T, 1>, array_view<const T>> () 179 && !is_convertible <std::array<const T, 1>, array_view<T>> () 180 && is_convertible <std::array<const T, 1>, array_view<const T>> ()); 181} 182 183static_assert (check_convertible_from_std_array (), ""); 184 185/* Check that VIEW views C (a container like std::vector/std::array) 186 correctly. */ 187 188template<typename View, typename Container> 189static bool 190check_container_view (const View &view, const Container &c) 191{ 192 if (view.empty ()) 193 return false; 194 if (view.size () != c.size ()) 195 return false; 196 if (view.data () != c.data ()) 197 return false; 198 for (size_t i = 0; i < c.size (); i++) 199 { 200 if (&view[i] != &c[i]) 201 return false; 202 if (view[i] != c[i]) 203 return false; 204 } 205 return true; 206} 207 208/* Check that VIEW views E (an object of the type of a view element) 209 correctly. */ 210 211template<typename View, typename Elem> 212static bool 213check_elem_view (const View &view, const Elem &e) 214{ 215 if (view.empty ()) 216 return false; 217 if (view.size () != 1) 218 return false; 219 if (view.data () != &e) 220 return false; 221 if (&view[0] != &e) 222 return false; 223 if (view[0] != e) 224 return false; 225 return true; 226} 227 228/* Check for operator[]. The first overload is taken iff 229 'view<T>()[0] = T()' is a valid expression. */ 230 231template<typename View, 232 typename = decltype (std::declval<View> ()[0] 233 = std::declval<typename View::value_type> ())> 234static bool 235check_op_subscript (const View &view) 236{ 237 return true; 238} 239 240/* This overload is taken iff 'view<T>()[0] = T()' is not a valid 241 expression. */ 242 243static bool 244check_op_subscript (...) 245{ 246 return false; 247} 248 249/* Check construction with pointer + size. This is a template in 250 order to test both gdb_byte and const gdb_byte. */ 251 252template<typename T> 253static void 254check_ptr_size_ctor () 255{ 256 T data[] = {0x11, 0x22, 0x33, 0x44}; 257 258 gdb::array_view<T> view (data + 1, 2); 259 260 SELF_CHECK (!view.empty ()); 261 SELF_CHECK (view.size () == 2); 262 SELF_CHECK (view.data () == &data[1]); 263 SELF_CHECK (view[0] == data[1]); 264 SELF_CHECK (view[1] == data[2]); 265 266 gdb::array_view<const T> cview (data + 1, 2); 267 SELF_CHECK (!cview.empty ()); 268 SELF_CHECK (cview.size () == 2); 269 SELF_CHECK (cview.data () == &data[1]); 270 SELF_CHECK (cview[0] == data[1]); 271 SELF_CHECK (cview[1] == data[2]); 272} 273 274/* Asserts std::is_constructible. */ 275 276template<typename T, typename... Args> 277static constexpr bool 278require_not_constructible () 279{ 280 static_assert (!std::is_constructible<T, Args...>::value, ""); 281 282 /* constexpr functions can't return void in C++11 (N3444). */ 283 return true; 284}; 285 286/* Check the array_view<T>(PTR, SIZE) ctor, when T is a pointer. */ 287 288static void 289check_ptr_size_ctor2 () 290{ 291 struct A {}; 292 A an_a; 293 294 A *array[] = { &an_a }; 295 const A * const carray[] = { &an_a }; 296 297 gdb::array_view<A *> v1 = {array, ARRAY_SIZE (array)}; 298 gdb::array_view<A *> v2 = {array, (char) ARRAY_SIZE (array)}; 299 gdb::array_view<A * const> v3 = {array, ARRAY_SIZE (array)}; 300 gdb::array_view<const A * const> cv1 = {carray, ARRAY_SIZE (carray)}; 301 302 require_not_constructible<gdb::array_view<A *>, decltype (carray), size_t> (); 303 304 SELF_CHECK (v1[0] == array[0]); 305 SELF_CHECK (v2[0] == array[0]); 306 SELF_CHECK (v3[0] == array[0]); 307 308 SELF_CHECK (!v1.empty ()); 309 SELF_CHECK (v1.size () == 1); 310 SELF_CHECK (v1.data () == &array[0]); 311 312 SELF_CHECK (cv1[0] == carray[0]); 313 314 SELF_CHECK (!cv1.empty ()); 315 SELF_CHECK (cv1.size () == 1); 316 SELF_CHECK (cv1.data () == &carray[0]); 317} 318 319/* Check construction with a pair of pointers. This is a template in 320 order to test both gdb_byte and const gdb_byte. */ 321 322template<typename T> 323static void 324check_ptr_ptr_ctor () 325{ 326 T data[] = {0x11, 0x22, 0x33, 0x44}; 327 328 gdb::array_view<T> view (data + 1, data + 3); 329 330 SELF_CHECK (!view.empty ()); 331 SELF_CHECK (view.size () == 2); 332 SELF_CHECK (view.data () == &data[1]); 333 SELF_CHECK (view[0] == data[1]); 334 SELF_CHECK (view[1] == data[2]); 335 336 gdb_byte array[] = {0x11, 0x22, 0x33, 0x44}; 337 const gdb_byte *p1 = array; 338 gdb_byte *p2 = array + ARRAY_SIZE (array); 339 gdb::array_view<const gdb_byte> view2 (p1, p2); 340} 341 342/* Check construction with a pair of pointers of mixed constness. */ 343 344static void 345check_ptr_ptr_mixed_cv () 346{ 347 gdb_byte array[] = {0x11, 0x22, 0x33, 0x44}; 348 const gdb_byte *cp = array; 349 gdb_byte *p = array; 350 gdb::array_view<const gdb_byte> view1 (cp, p); 351 gdb::array_view<const gdb_byte> view2 (p, cp); 352 SELF_CHECK (view1.empty ()); 353 SELF_CHECK (view2.empty ()); 354} 355 356/* Check range-for support (i.e., begin()/end()). This is a template 357 in order to test both gdb_byte and const gdb_byte. */ 358 359template<typename T> 360static void 361check_range_for () 362{ 363 T data[] = {1, 2, 3, 4}; 364 gdb::array_view<T> view (data); 365 366 typename std::decay<T>::type sum = 0; 367 for (auto &elem : view) 368 sum += elem; 369 SELF_CHECK (sum == 1 + 2 + 3 + 4); 370} 371 372/* Entry point. */ 373 374static void 375run_tests () 376{ 377 /* Empty views. */ 378 { 379 constexpr gdb::array_view<gdb_byte> view1; 380 constexpr gdb::array_view<const gdb_byte> view2; 381 382 static_assert (view1.empty (), ""); 383 static_assert (view1.data () == nullptr, ""); 384 static_assert (view1.size () == 0, ""); 385 static_assert (view2.empty (), ""); 386 static_assert (view2.size () == 0, ""); 387 static_assert (view2.data () == nullptr, ""); 388 } 389 390 std::vector<gdb_byte> vec = {0x11, 0x22, 0x33, 0x44 }; 391 std::array<gdb_byte, 4> array = {{0x11, 0x22, 0x33, 0x44}}; 392 393 /* Various tests of views over std::vector. */ 394 { 395 gdb::array_view<gdb_byte> view = vec; 396 SELF_CHECK (check_container_view (view, vec)); 397 gdb::array_view<const gdb_byte> cview = vec; 398 SELF_CHECK (check_container_view (cview, vec)); 399 } 400 401 /* Likewise, over std::array. */ 402 { 403 gdb::array_view<gdb_byte> view = array; 404 SELF_CHECK (check_container_view (view, array)); 405 gdb::array_view<gdb_byte> cview = array; 406 SELF_CHECK (check_container_view (cview, array)); 407 } 408 409 /* op=(std::vector/std::array/elem) */ 410 { 411 gdb::array_view<gdb_byte> view; 412 413 view = vec; 414 SELF_CHECK (check_container_view (view, vec)); 415 view = std::move (vec); 416 SELF_CHECK (check_container_view (view, vec)); 417 418 view = array; 419 SELF_CHECK (check_container_view (view, array)); 420 view = std::move (array); 421 SELF_CHECK (check_container_view (view, array)); 422 423 gdb_byte elem = 0; 424 view = elem; 425 SELF_CHECK (check_elem_view (view, elem)); 426 view = std::move (elem); 427 SELF_CHECK (check_elem_view (view, elem)); 428 } 429 430 /* Test copy/move ctor and mutable->immutable conversion. */ 431 { 432 gdb_byte data[] = {0x11, 0x22, 0x33, 0x44}; 433 gdb::array_view<gdb_byte> view1 = data; 434 gdb::array_view<gdb_byte> view2 = view1; 435 gdb::array_view<gdb_byte> view3 = std::move (view1); 436 gdb::array_view<const gdb_byte> cview1 = data; 437 gdb::array_view<const gdb_byte> cview2 = cview1; 438 gdb::array_view<const gdb_byte> cview3 = std::move (cview1); 439 SELF_CHECK (view1[0] == data[0]); 440 SELF_CHECK (view2[0] == data[0]); 441 SELF_CHECK (view3[0] == data[0]); 442 SELF_CHECK (cview1[0] == data[0]); 443 SELF_CHECK (cview2[0] == data[0]); 444 SELF_CHECK (cview3[0] == data[0]); 445 } 446 447 /* Same, but op=(view). */ 448 { 449 gdb_byte data[] = {0x55, 0x66, 0x77, 0x88}; 450 gdb::array_view<gdb_byte> view1; 451 gdb::array_view<gdb_byte> view2; 452 gdb::array_view<gdb_byte> view3; 453 gdb::array_view<const gdb_byte> cview1; 454 gdb::array_view<const gdb_byte> cview2; 455 gdb::array_view<const gdb_byte> cview3; 456 457 view1 = data; 458 view2 = view1; 459 view3 = std::move (view1); 460 cview1 = data; 461 cview2 = cview1; 462 cview3 = std::move (cview1); 463 SELF_CHECK (view1[0] == data[0]); 464 SELF_CHECK (view2[0] == data[0]); 465 SELF_CHECK (view3[0] == data[0]); 466 SELF_CHECK (cview1[0] == data[0]); 467 SELF_CHECK (cview2[0] == data[0]); 468 SELF_CHECK (cview3[0] == data[0]); 469 } 470 471 /* op[] */ 472 { 473 std::vector<gdb_byte> vec2 = {0x11, 0x22}; 474 gdb::array_view<gdb_byte> view = vec2; 475 gdb::array_view<const gdb_byte> cview = vec2; 476 477 /* Check that op[] on a non-const view of non-const T returns a 478 mutable reference. */ 479 view[0] = 0x33; 480 SELF_CHECK (vec2[0] == 0x33); 481 482 /* OTOH, check that assigning through op[] on a view of const T 483 wouldn't compile. */ 484 SELF_CHECK (!check_op_subscript (cview)); 485 /* For completeness. */ 486 SELF_CHECK (check_op_subscript (view)); 487 } 488 489 check_ptr_size_ctor<const gdb_byte> (); 490 check_ptr_size_ctor<gdb_byte> (); 491 check_ptr_size_ctor2 (); 492 check_ptr_ptr_ctor<const gdb_byte> (); 493 check_ptr_ptr_ctor<gdb_byte> (); 494 check_ptr_ptr_mixed_cv (); 495 496 check_range_for<gdb_byte> (); 497 check_range_for<const gdb_byte> (); 498 499 /* Check that the right ctor overloads are taken when the element is 500 a container. */ 501 { 502 using Vec = std::vector<gdb_byte>; 503 Vec vecs[3]; 504 505 gdb::array_view<Vec> view_array = vecs; 506 SELF_CHECK (view_array.size () == 3); 507 508 Vec elem; 509 gdb::array_view<Vec> view_elem = elem; 510 SELF_CHECK (view_elem.size () == 1); 511 } 512 513 /* gdb::make_array_view, int length. */ 514 { 515 gdb_byte data[] = {0x55, 0x66, 0x77, 0x88}; 516 int len = sizeof (data) / sizeof (data[0]); 517 auto view = gdb::make_array_view (data, len); 518 519 SELF_CHECK (view.data () == data); 520 SELF_CHECK (view.size () == len); 521 522 for (size_t i = 0; i < len; i++) 523 SELF_CHECK (view[i] == data[i]); 524 } 525 526 /* Test slicing. */ 527 { 528 gdb_byte data[] = {0x55, 0x66, 0x77, 0x88, 0x99}; 529 gdb::array_view<gdb_byte> view = data; 530 531 { 532 auto slc = view.slice (1, 3); 533 SELF_CHECK (slc.data () == data + 1); 534 SELF_CHECK (slc.size () == 3); 535 SELF_CHECK (slc[0] == data[1]); 536 SELF_CHECK (slc[0] == view[1]); 537 } 538 539 { 540 auto slc = view.slice (2); 541 SELF_CHECK (slc.data () == data + 2); 542 SELF_CHECK (slc.size () == 3); 543 SELF_CHECK (slc[0] == view[2]); 544 SELF_CHECK (slc[0] == data[2]); 545 } 546 } 547} 548 549template <typename T> 550void 551run_copy_test () 552{ 553 /* Test non-overlapping copy. */ 554 { 555 const std::vector<T> src_v = {1, 2, 3, 4}; 556 std::vector<T> dest_v (4, -1); 557 558 SELF_CHECK (dest_v != src_v); 559 copy (gdb::array_view<const T> (src_v), gdb::array_view<T> (dest_v)); 560 SELF_CHECK (dest_v == src_v); 561 } 562 563 /* Test overlapping copy, where the source is before the destination. */ 564 { 565 std::vector<T> vec = {1, 2, 3, 4, 5, 6, 7, 8}; 566 gdb::array_view<T> v = vec; 567 568 copy (v.slice (1, 4), 569 v.slice (2, 4)); 570 571 std::vector<T> expected = {1, 2, 2, 3, 4, 5, 7, 8}; 572 SELF_CHECK (vec == expected); 573 } 574 575 /* Test overlapping copy, where the source is after the destination. */ 576 { 577 std::vector<T> vec = {1, 2, 3, 4, 5, 6, 7, 8}; 578 gdb::array_view<T> v = vec; 579 580 copy (v.slice (2, 4), 581 v.slice (1, 4)); 582 583 std::vector<T> expected = {1, 3, 4, 5, 6, 6, 7, 8}; 584 SELF_CHECK (vec == expected); 585 } 586 587 /* Test overlapping copy, where the source is the same as the destination. */ 588 { 589 std::vector<T> vec = {1, 2, 3, 4, 5, 6, 7, 8}; 590 gdb::array_view<T> v = vec; 591 592 copy (v.slice (2, 4), 593 v.slice (2, 4)); 594 595 std::vector<T> expected = {1, 2, 3, 4, 5, 6, 7, 8}; 596 SELF_CHECK (vec == expected); 597 } 598} 599 600/* Class with a non-trivial copy assignment operator, used to test the 601 array_view copy function. */ 602struct foo 603{ 604 /* Can be implicitly constructed from an int, such that we can use the same 605 templated test function to test against array_view<int> and 606 array_view<foo>. */ 607 foo (int n) 608 : n (n) 609 {} 610 611 /* Needed to avoid -Wdeprecated-copy-with-user-provided-copy error with 612 Clang. */ 613 foo (const foo &other) = default; 614 615 void operator= (const foo &other) 616 { 617 this->n = other.n; 618 this->n_assign_op_called++; 619 } 620 621 bool operator==(const foo &other) const 622 { 623 return this->n == other.n; 624 } 625 626 int n; 627 628 /* Number of times the assignment operator has been called. */ 629 static int n_assign_op_called; 630}; 631 632int foo::n_assign_op_called = 0; 633 634/* Test the array_view copy free function. */ 635 636static void 637run_copy_tests () 638{ 639 /* Test with a trivial type. */ 640 run_copy_test<int> (); 641 642 /* Test with a non-trivial type. */ 643 foo::n_assign_op_called = 0; 644 run_copy_test<foo> (); 645 646 /* Make sure that for the non-trivial type foo, the assignment operator was 647 called an amount of times that makes sense. */ 648 SELF_CHECK (foo::n_assign_op_called == 12); 649} 650 651} /* namespace array_view_tests */ 652} /* namespace selftests */ 653 654void _initialize_array_view_selftests (); 655void 656_initialize_array_view_selftests () 657{ 658 selftests::register_test ("array_view", 659 selftests::array_view_tests::run_tests); 660 selftests::register_test ("array_view-copy", 661 selftests::array_view_tests::run_copy_tests); 662} 663