diff options
Diffstat (limited to 'gdb/unittests/array-view-selftests.c')
-rw-r--r-- | gdb/unittests/array-view-selftests.c | 494 |
1 files changed, 494 insertions, 0 deletions
diff --git a/gdb/unittests/array-view-selftests.c b/gdb/unittests/array-view-selftests.c new file mode 100644 index 0000000..c4c95bc --- /dev/null +++ b/gdb/unittests/array-view-selftests.c @@ -0,0 +1,494 @@ +/* Self tests for array_view for GDB, the GNU debugger. + + Copyright (C) 2017 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 "selftest.h" +#include "common/array-view.h" + +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. */ + +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> vec = {0x11, 0x22}; + gdb::array_view<gdb_byte> view = vec; + gdb::array_view<const gdb_byte> cview = vec; + + /* Check that op[] on a non-const view of non-const T returns a + mutable reference. */ + view[0] = 0x33; + SELF_CHECK (vec[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); + } +} + +} /* namespace array_view_tests */ +} /* namespace selftests */ + +void +_initialize_array_view_selftests () +{ + selftests::register_test (selftests::array_view_tests::run_tests); +} |