| /* Self tests for array_view for GDB, the GNU debugger. |
| |
| Copyright (C) 2017-2021 Free Software Foundation, Inc. |
| |
| This file is part of GDB. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| #include "defs.h" |
| #include "gdbsupport/selftest.h" |
| #include "gdbsupport/array-view.h" |
| #include <array> |
| |
| namespace selftests { |
| namespace array_view_tests { |
| |
| /* Triviality checks. */ |
| #define CHECK_TRAIT(TRAIT) \ |
| static_assert (std::TRAIT<gdb::array_view<gdb_byte>>::value, "") |
| |
| #if HAVE_IS_TRIVIALLY_COPYABLE |
| |
| CHECK_TRAIT (is_trivially_copyable); |
| CHECK_TRAIT (is_trivially_move_assignable); |
| CHECK_TRAIT (is_trivially_move_constructible); |
| CHECK_TRAIT (is_trivially_destructible); |
| |
| #endif |
| |
| #undef CHECK_TRAIT |
| |
| /* Wrapper around std::is_convertible to make the code using it a bit |
| shorter. (With C++14 we'd use a variable template instead.) */ |
| |
| template<typename From, typename To> |
| static constexpr bool |
| is_convertible () |
| { |
| return std::is_convertible<From, To>::value; |
| } |
| |
| /* Check for implicit conversion to immutable and mutable views. */ |
| |
| static constexpr bool |
| check_convertible () |
| { |
| using T = gdb_byte; |
| using gdb::array_view; |
| |
| return (true |
| /* immutable array_view */ |
| && is_convertible<const T (&) [1], array_view<const T>> () |
| && is_convertible<T (&) [1], array_view<const T>> () |
| && is_convertible<const T, array_view<const T>> () |
| && is_convertible<T, array_view<const T>> () |
| |
| /* mutable array_view */ |
| && is_convertible<T (&) [1], array_view<T>> () |
| && !is_convertible<const T (&) [1], array_view<T>> () |
| && is_convertible<T, array_view<T>> () |
| && !is_convertible<const T, array_view<T>> () |
| |
| /* While float is implicitly convertible to gdb_byte, we |
| don't want implicit float->array_view<gdb_byte> |
| conversion. */ |
| && !is_convertible<float, array_view<const T>> () |
| && !is_convertible<float, array_view<T>> ()); |
| } |
| |
| static_assert (check_convertible (), ""); |
| |
| namespace no_slicing |
| { |
| struct A { int i; }; |
| struct B : A { int j; }; |
| struct C : A { int l; }; |
| |
| /* Check that there's no array->view conversion for arrays of derived |
| types or subclasses. */ |
| static constexpr bool |
| check () |
| { |
| using gdb::array_view; |
| |
| return (true |
| |
| /* array->view */ |
| |
| && is_convertible <A (&)[1], array_view<A>> () |
| && !is_convertible <B (&)[1], array_view<A>> () |
| && !is_convertible <C (&)[1], array_view<A>> () |
| |
| && !is_convertible <A (&)[1], array_view<B>> () |
| && is_convertible <B (&)[1], array_view<B>> () |
| && !is_convertible <C (&)[1], array_view<B>> () |
| |
| /* elem->view */ |
| |
| && is_convertible <A, array_view<A>> () |
| && !is_convertible <B, array_view<A>> () |
| && !is_convertible <C, array_view<A>> () |
| |
| && !is_convertible <A, array_view<B>> () |
| && is_convertible <B, array_view<B>> () |
| && !is_convertible <C, array_view<B>> ()); |
| } |
| |
| } /* namespace no_slicing */ |
| |
| static_assert (no_slicing::check (), ""); |
| |
| /* Check that array_view implicitly converts from std::vector. */ |
| |
| static constexpr bool |
| check_convertible_from_std_vector () |
| { |
| using gdb::array_view; |
| using T = gdb_byte; |
| |
| /* Note there's no such thing as std::vector<const T>. */ |
| |
| return (true |
| && is_convertible <std::vector<T>, array_view<T>> () |
| && is_convertible <std::vector<T>, array_view<const T>> ()); |
| } |
| |
| static_assert (check_convertible_from_std_vector (), ""); |
| |
| /* Check that array_view implicitly converts from std::array. */ |
| |
| static constexpr bool |
| check_convertible_from_std_array () |
| { |
| using gdb::array_view; |
| using T = gdb_byte; |
| |
| /* Note: a non-const T view can't refer to a const T array. */ |
| |
| return (true |
| && is_convertible <std::array<T, 1>, array_view<T>> () |
| && is_convertible <std::array<T, 1>, array_view<const T>> () |
| && !is_convertible <std::array<const T, 1>, array_view<T>> () |
| && is_convertible <std::array<const T, 1>, array_view<const T>> ()); |
| } |
| |
| static_assert (check_convertible_from_std_array (), ""); |
| |
| /* Check that VIEW views C (a container like std::vector/std::array) |
| correctly. */ |
| |
| template<typename View, typename Container> |
| static bool |
| check_container_view (const View &view, const Container &c) |
| { |
| if (view.empty ()) |
| return false; |
| if (view.size () != c.size ()) |
| return false; |
| if (view.data () != c.data ()) |
| return false; |
| for (size_t i = 0; i < c.size (); i++) |
| { |
| if (&view[i] != &c[i]) |
| return false; |
| if (view[i] != c[i]) |
| return false; |
| } |
| return true; |
| } |
| |
| /* Check that VIEW views E (an object of the type of a view element) |
| correctly. */ |
| |
| template<typename View, typename Elem> |
| static bool |
| check_elem_view (const View &view, const Elem &e) |
| { |
| if (view.empty ()) |
| return false; |
| if (view.size () != 1) |
| return false; |
| if (view.data () != &e) |
| return false; |
| if (&view[0] != &e) |
| return false; |
| if (view[0] != e) |
| return false; |
| return true; |
| } |
| |
| /* Check for operator[]. The first overload is taken iff |
| 'view<T>()[0] = T()' is a valid expression. */ |
| |
| template<typename View, |
| typename = decltype (std::declval<View> ()[0] |
| = std::declval<typename View::value_type> ())> |
| static bool |
| check_op_subscript (const View &view) |
| { |
| return true; |
| } |
| |
| /* This overload is taken iff 'view<T>()[0] = T()' is not a valid |
| expression. */ |
| |
| static bool |
| check_op_subscript (...) |
| { |
| return false; |
| } |
| |
| /* Check construction with pointer + size. This is a template in |
| order to test both gdb_byte and const gdb_byte. */ |
| |
| template<typename T> |
| static void |
| check_ptr_size_ctor () |
| { |
| T data[] = {0x11, 0x22, 0x33, 0x44}; |
| |
| gdb::array_view<T> view (data + 1, 2); |
| |
| SELF_CHECK (!view.empty ()); |
| SELF_CHECK (view.size () == 2); |
| SELF_CHECK (view.data () == &data[1]); |
| SELF_CHECK (view[0] == data[1]); |
| SELF_CHECK (view[1] == data[2]); |
| |
| gdb::array_view<const T> cview (data + 1, 2); |
| SELF_CHECK (!cview.empty ()); |
| SELF_CHECK (cview.size () == 2); |
| SELF_CHECK (cview.data () == &data[1]); |
| SELF_CHECK (cview[0] == data[1]); |
| SELF_CHECK (cview[1] == data[2]); |
| } |
| |
| /* Asserts std::is_constructible. */ |
| |
| template<typename T, typename... Args> |
| static constexpr bool |
| require_not_constructible () |
| { |
| static_assert (!std::is_constructible<T, Args...>::value, ""); |
| |
| /* constexpr functions can't return void in C++11 (N3444). */ |
| return true; |
| }; |
| |
| /* Check the array_view<T>(PTR, SIZE) ctor, when T is a pointer. */ |
| |
| static void |
| check_ptr_size_ctor2 () |
| { |
| struct A {}; |
| A an_a; |
| |
| A *array[] = { &an_a }; |
| const A * const carray[] = { &an_a }; |
| |
| gdb::array_view<A *> v1 = {array, ARRAY_SIZE (array)}; |
| gdb::array_view<A *> v2 = {array, (char) ARRAY_SIZE (array)}; |
| gdb::array_view<A * const> v3 = {array, ARRAY_SIZE (array)}; |
| gdb::array_view<const A * const> cv1 = {carray, ARRAY_SIZE (carray)}; |
| |
| require_not_constructible<gdb::array_view<A *>, decltype (carray), size_t> (); |
| |
| SELF_CHECK (v1[0] == array[0]); |
| SELF_CHECK (v2[0] == array[0]); |
| SELF_CHECK (v3[0] == array[0]); |
| |
| SELF_CHECK (!v1.empty ()); |
| SELF_CHECK (v1.size () == 1); |
| SELF_CHECK (v1.data () == &array[0]); |
| |
| SELF_CHECK (cv1[0] == carray[0]); |
| |
| SELF_CHECK (!cv1.empty ()); |
| SELF_CHECK (cv1.size () == 1); |
| SELF_CHECK (cv1.data () == &carray[0]); |
| } |
| |
| /* Check construction with a pair of pointers. This is a template in |
| order to test both gdb_byte and const gdb_byte. */ |
| |
| template<typename T> |
| static void |
| check_ptr_ptr_ctor () |
| { |
| T data[] = {0x11, 0x22, 0x33, 0x44}; |
| |
| gdb::array_view<T> view (data + 1, data + 3); |
| |
| SELF_CHECK (!view.empty ()); |
| SELF_CHECK (view.size () == 2); |
| SELF_CHECK (view.data () == &data[1]); |
| SELF_CHECK (view[0] == data[1]); |
| SELF_CHECK (view[1] == data[2]); |
| |
| gdb_byte array[] = {0x11, 0x22, 0x33, 0x44}; |
| const gdb_byte *p1 = array; |
| gdb_byte *p2 = array + ARRAY_SIZE (array); |
| gdb::array_view<const gdb_byte> view2 (p1, p2); |
| } |
| |
| /* Check construction with a pair of pointers of mixed constness. */ |
| |
| static void |
| check_ptr_ptr_mixed_cv () |
| { |
| gdb_byte array[] = {0x11, 0x22, 0x33, 0x44}; |
| const gdb_byte *cp = array; |
| gdb_byte *p = array; |
| gdb::array_view<const gdb_byte> view1 (cp, p); |
| gdb::array_view<const gdb_byte> view2 (p, cp); |
| SELF_CHECK (view1.empty ()); |
| SELF_CHECK (view2.empty ()); |
| } |
| |
| /* Check range-for support (i.e., begin()/end()). This is a template |
| in order to test both gdb_byte and const gdb_byte. */ |
| |
| template<typename T> |
| static void |
| check_range_for () |
| { |
| T data[] = {1, 2, 3, 4}; |
| gdb::array_view<T> view (data); |
| |
| typename std::decay<T>::type sum = 0; |
| for (auto &elem : view) |
| sum += elem; |
| SELF_CHECK (sum == 1 + 2 + 3 + 4); |
| } |
| |
| /* Entry point. */ |
| |
| static void |
| run_tests () |
| { |
| /* Empty views. */ |
| { |
| constexpr gdb::array_view<gdb_byte> view1; |
| constexpr gdb::array_view<const gdb_byte> view2; |
| |
| static_assert (view1.empty (), ""); |
| static_assert (view1.data () == nullptr, ""); |
| static_assert (view1.size () == 0, ""); |
| static_assert (view2.empty (), ""); |
| static_assert (view2.size () == 0, ""); |
| static_assert (view2.data () == nullptr, ""); |
| } |
| |
| std::vector<gdb_byte> vec = {0x11, 0x22, 0x33, 0x44 }; |
| std::array<gdb_byte, 4> array = {{0x11, 0x22, 0x33, 0x44}}; |
| |
| /* Various tests of views over std::vector. */ |
| { |
| gdb::array_view<gdb_byte> view = vec; |
| SELF_CHECK (check_container_view (view, vec)); |
| gdb::array_view<const gdb_byte> cview = vec; |
| SELF_CHECK (check_container_view (cview, vec)); |
| } |
| |
| /* Likewise, over std::array. */ |
| { |
| gdb::array_view<gdb_byte> view = array; |
| SELF_CHECK (check_container_view (view, array)); |
| gdb::array_view<gdb_byte> cview = array; |
| SELF_CHECK (check_container_view (cview, array)); |
| } |
| |
| /* op=(std::vector/std::array/elem) */ |
| { |
| gdb::array_view<gdb_byte> view; |
| |
| view = vec; |
| SELF_CHECK (check_container_view (view, vec)); |
| view = std::move (vec); |
| SELF_CHECK (check_container_view (view, vec)); |
| |
| view = array; |
| SELF_CHECK (check_container_view (view, array)); |
| view = std::move (array); |
| SELF_CHECK (check_container_view (view, array)); |
| |
| gdb_byte elem = 0; |
| view = elem; |
| SELF_CHECK (check_elem_view (view, elem)); |
| view = std::move (elem); |
| SELF_CHECK (check_elem_view (view, elem)); |
| } |
| |
| /* Test copy/move ctor and mutable->immutable conversion. */ |
| { |
| gdb_byte data[] = {0x11, 0x22, 0x33, 0x44}; |
| gdb::array_view<gdb_byte> view1 = data; |
| gdb::array_view<gdb_byte> view2 = view1; |
| gdb::array_view<gdb_byte> view3 = std::move (view1); |
| gdb::array_view<const gdb_byte> cview1 = data; |
| gdb::array_view<const gdb_byte> cview2 = cview1; |
| gdb::array_view<const gdb_byte> cview3 = std::move (cview1); |
| SELF_CHECK (view1[0] == data[0]); |
| SELF_CHECK (view2[0] == data[0]); |
| SELF_CHECK (view3[0] == data[0]); |
| SELF_CHECK (cview1[0] == data[0]); |
| SELF_CHECK (cview2[0] == data[0]); |
| SELF_CHECK (cview3[0] == data[0]); |
| } |
| |
| /* Same, but op=(view). */ |
| { |
| gdb_byte data[] = {0x55, 0x66, 0x77, 0x88}; |
| gdb::array_view<gdb_byte> view1; |
| gdb::array_view<gdb_byte> view2; |
| gdb::array_view<gdb_byte> view3; |
| gdb::array_view<const gdb_byte> cview1; |
| gdb::array_view<const gdb_byte> cview2; |
| gdb::array_view<const gdb_byte> cview3; |
| |
| view1 = data; |
| view2 = view1; |
| view3 = std::move (view1); |
| cview1 = data; |
| cview2 = cview1; |
| cview3 = std::move (cview1); |
| SELF_CHECK (view1[0] == data[0]); |
| SELF_CHECK (view2[0] == data[0]); |
| SELF_CHECK (view3[0] == data[0]); |
| SELF_CHECK (cview1[0] == data[0]); |
| SELF_CHECK (cview2[0] == data[0]); |
| SELF_CHECK (cview3[0] == data[0]); |
| } |
| |
| /* op[] */ |
| { |
| std::vector<gdb_byte> vec2 = {0x11, 0x22}; |
| gdb::array_view<gdb_byte> view = vec2; |
| gdb::array_view<const gdb_byte> cview = vec2; |
| |
| /* Check that op[] on a non-const view of non-const T returns a |
| mutable reference. */ |
| view[0] = 0x33; |
| SELF_CHECK (vec2[0] == 0x33); |
| |
| /* OTOH, check that assigning through op[] on a view of const T |
| wouldn't compile. */ |
| SELF_CHECK (!check_op_subscript (cview)); |
| /* For completeness. */ |
| SELF_CHECK (check_op_subscript (view)); |
| } |
| |
| check_ptr_size_ctor<const gdb_byte> (); |
| check_ptr_size_ctor<gdb_byte> (); |
| check_ptr_size_ctor2 (); |
| check_ptr_ptr_ctor<const gdb_byte> (); |
| check_ptr_ptr_ctor<gdb_byte> (); |
| check_ptr_ptr_mixed_cv (); |
| |
| check_range_for<gdb_byte> (); |
| check_range_for<const gdb_byte> (); |
| |
| /* Check that the right ctor overloads are taken when the element is |
| a container. */ |
| { |
| using Vec = std::vector<gdb_byte>; |
| Vec vecs[3]; |
| |
| gdb::array_view<Vec> view_array = vecs; |
| SELF_CHECK (view_array.size () == 3); |
| |
| Vec elem; |
| gdb::array_view<Vec> view_elem = elem; |
| SELF_CHECK (view_elem.size () == 1); |
| } |
| |
| /* gdb::make_array_view, int length. */ |
| { |
| gdb_byte data[] = {0x55, 0x66, 0x77, 0x88}; |
| int len = sizeof (data) / sizeof (data[0]); |
| auto view = gdb::make_array_view (data, len); |
| |
| SELF_CHECK (view.data () == data); |
| SELF_CHECK (view.size () == len); |
| |
| for (size_t i = 0; i < len; i++) |
| SELF_CHECK (view[i] == data[i]); |
| } |
| |
| /* Test slicing. */ |
| { |
| gdb_byte data[] = {0x55, 0x66, 0x77, 0x88, 0x99}; |
| gdb::array_view<gdb_byte> view = data; |
| |
| { |
| auto slc = view.slice (1, 3); |
| SELF_CHECK (slc.data () == data + 1); |
| SELF_CHECK (slc.size () == 3); |
| SELF_CHECK (slc[0] == data[1]); |
| SELF_CHECK (slc[0] == view[1]); |
| } |
| |
| { |
| auto slc = view.slice (2); |
| SELF_CHECK (slc.data () == data + 2); |
| SELF_CHECK (slc.size () == 3); |
| SELF_CHECK (slc[0] == view[2]); |
| SELF_CHECK (slc[0] == data[2]); |
| } |
| } |
| } |
| |
| } /* namespace array_view_tests */ |
| } /* namespace selftests */ |
| |
| void _initialize_array_view_selftests (); |
| void |
| _initialize_array_view_selftests () |
| { |
| selftests::register_test ("array_view", |
| selftests::array_view_tests::run_tests); |
| } |