From 5317914406060becef31b5dc1a8bce8bcea932a9 Mon Sep 17 00:00:00 2001 From: Benjamin Kosnik Date: Thu, 17 Dec 2009 09:37:16 +0000 Subject: [PATCH] PR libstdc++/21772 part 2 2009-12-16 Benjamin Kosnik PR libstdc++/21772 part 2 * doc/xml/manual/test.xml: Add documentation about testing details. * testsuite/util/exception/safety.h: New. Functor objects for testing C++0x container classes. * testsuite/util/testsuite_container_traits.h: Add traits. * testsuite/23_containers/list/requirements/exception/ basic.cc: New. generation_prohibited.cc: New. propagation_consistent.cc: New. From-SVN: r155306 --- libstdc++-v3/ChangeLog | 13 + libstdc++-v3/doc/xml/manual/test.xml | 340 +++++- .../list/requirements/exception/basic.cc | 40 + .../exception/generation_prohibited.cc | 34 + .../exception/propagation_consistent.cc | 34 + .../testsuite/util/exception/safety.h | 1066 +++++++++++++++++ .../util/testsuite_container_traits.h | 131 +- 7 files changed, 1565 insertions(+), 93 deletions(-) create mode 100644 libstdc++-v3/testsuite/23_containers/list/requirements/exception/basic.cc create mode 100644 libstdc++-v3/testsuite/23_containers/list/requirements/exception/generation_prohibited.cc create mode 100644 libstdc++-v3/testsuite/23_containers/list/requirements/exception/propagation_consistent.cc create mode 100644 libstdc++-v3/testsuite/util/exception/safety.h diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 21a6978dba61..bf7f50ec99da 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,16 @@ +2009-12-16 Benjamin Kosnik + + PR libstdc++/21772 part 2 + * doc/xml/manual/test.xml: Add documentation about testing details. + * testsuite/util/exception/safety.h: New. Functor objects for + testing C++0x container classes. + * testsuite/util/testsuite_container_traits.h: Add traits. + + * testsuite/23_containers/list/requirements/exception/ + basic.cc: New. + generation_prohibited.cc: New. + propagation_consistent.cc: New. + 2009-12-15 Benjamin Kosnik PR libstdc++/21772 part 1 diff --git a/libstdc++-v3/doc/xml/manual/test.xml b/libstdc++-v3/doc/xml/manual/test.xml index 887f40356863..3da168ec45bf 100644 --- a/libstdc++-v3/doc/xml/manual/test.xml +++ b/libstdc++-v3/doc/xml/manual/test.xml @@ -1,6 +1,6 @@ - + @@ -48,7 +48,7 @@ regressions, ABI, and performance. that are packaged in a separate testing library. - + All test cases for functionality required by the runtime components of the C++ standard (ISO 14882) are files within the following directories. @@ -65,6 +65,9 @@ regressions, ABI, and performance. 25_algorithms 26_numerics 27_io +28_regex +29_atomics +30_threads @@ -77,9 +80,8 @@ backward Tests for backwards compatibility and deprecated features. demangle Tests for __cxa_demangle, the IA 64 C++ ABI demangler ext Tests for extensions. performance Tests for performance analysis, and performance regressions. -thread Tests for threads. - + Some directories don't have test files, but instead contain auxiliary information: @@ -104,7 +106,7 @@ util Files for libtestc++, utilities and testing routines. 21_strings/find.cc - + However, that practice soon became a liability as the test cases became huge and unwieldy, and testing new or extended @@ -123,11 +125,11 @@ util Files for libtestc++, utilities and testing routines. 21_strings/basic_string/find/wchar_t/1.cc 21_strings/basic_string/find/wchar_t/2.cc 21_strings/basic_string/find/wchar_t/3.cc - + All new tests should be written with the policy of one test - case, one file in mind. + case, one file in mind. @@ -140,7 +142,7 @@ util Files for libtestc++, utilities and testing routines. used within the testsuite to designate particular kinds of tests. - + @@ -151,10 +153,10 @@ util Files for libtestc++, utilities and testing routines. to finish or pass. At the moment, the interactive tests are not run by default. Instead, they are run by hand, like: - + g++ 27_io/objects/char/3_xin.cc cat 27_io/objects/char/3_xin.in | a.out - + @@ -232,7 +234,7 @@ cat 27_io/objects/char/3_xin.in | a.out Basic - + You can check the status of the build without installing it using the dejagnu harness, much like the rest of the gcc @@ -254,7 +256,7 @@ cat 27_io/objects/char/3_xin.in | a.out the exact command line passed to the compiler, the compiler output, and the executable output (if any). - + Archives of test results for various versions and platforms are available on the GCC website in the - + Variations @@ -322,7 +324,7 @@ make check-target-libstdc++-v3 RUNTESTFLAGS="--target_board=calmrisc32-sid" make check-target-libstdc++-v3 RUNTESTFLAGS="--target_board=arm-sim" - + Also, here is an example of how to run the libstdc++ testsuite for a multilibed build directory with different ABI settings: @@ -330,7 +332,7 @@ make check-target-libstdc++-v3 RUNTESTFLAGS="--target_board=arm-sim" make check-target-libstdc++-v3 RUNTESTFLAGS='--target_board \"unix{-mabi=32,,-mabi=64}\"' - + You can run the tests with a compiler and library that have already been installed. Make sure that the compiler (e.g., @@ -354,7 +356,7 @@ runtest --tool libstdc++ --srcdir=/path/to/gcc/libstdc++-v3/testsuite testsuites in parallel from the same directory. - + In addition, there are some testing options that are mostly of interest to library maintainers and system integrators. As such, these tests may not work on all cpu and host combinations, and @@ -378,7 +380,7 @@ runtest --tool libstdc++ --srcdir=/path/to/gcc/libstdc++-v3/testsuite testsuite_files - + This is a list of all the test cases that will be run. Each test case is on a separate line, given with an absolute path from the libsrcdir/testsuite directory. @@ -389,7 +391,7 @@ runtest --tool libstdc++ --srcdir=/path/to/gcc/libstdc++-v3/testsuite testsuite_files_interactive - + This is a list of all the interactive test cases, using the same format as the file list above. These tests are not run by default. @@ -400,7 +402,7 @@ runtest --tool libstdc++ --srcdir=/path/to/gcc/libstdc++-v3/testsuite testsuite_files_performance - + This is a list of all the performance test cases, using the same format as the file list above. These tests are not run by default. @@ -411,7 +413,7 @@ runtest --tool libstdc++ --srcdir=/path/to/gcc/libstdc++-v3/testsuite testsuite_thread - + This file indicates that the host system can run tests which involved multiple threads. @@ -421,7 +423,7 @@ runtest --tool libstdc++ --srcdir=/path/to/gcc/libstdc++-v3/testsuite testsuite_wchar_t - + This file indicates that the host system can run the wchar_t tests, and corresponds to the macro definition _GLIBCXX_USE_WCHAR_T in the file c++config.h. @@ -432,11 +434,11 @@ runtest --tool libstdc++ --srcdir=/path/to/gcc/libstdc++-v3/testsuite make check-abi - + The library ABI can be tested. This involves testing the shared library against an ABI-defining previous version of symbol - exports. + exports. @@ -507,7 +509,7 @@ runtest --tool libstdc++ --srcdir=/path/to/gcc/libstdc++-v3/testsuite The first step in making a new test case is to choose the correct directory and file name, given the organization as previously - described. + described. @@ -638,7 +640,7 @@ up in the normal.exp file. Dejagnu Harness Details - + Underlying details of testing for conformance and regressions are abstracted via the GNU Dejagnu package. This is similar to the rest of GCC. @@ -684,7 +686,7 @@ only default variables. Utilities - + The testsuite directory also contains some files that implement @@ -709,11 +711,11 @@ only default variables. testsuite_abi_check.cc - Creates the executable abi_check. - Used to check correctness of symbol versioning, visibility of - exported symbols, and compatibility on symbols in the shared - library, for hosts that support this feature. More information - can be found in the ABI documentation here + Creates the executable abi_check. + Used to check correctness of symbol versioning, visibility of + exported symbols, and compatibility on symbols in the shared + library, for hosts that support this feature. More information + can be found in the ABI documentation here @@ -722,11 +724,11 @@ only default variables. testsuite_allocator.cc - Contains specialized allocators that keep track of construction - and destruction. Also, support for overriding global new and - delete operators, including verification that new and delete - are called during execution, and that allocation over max_size - fails. + Contains specialized allocators that keep track of construction + and destruction. Also, support for overriding global new and + delete operators, including verification that new and delete + are called during execution, and that allocation over max_size + fails. @@ -734,9 +736,9 @@ only default variables. testsuite_character.h - Contains std::char_traits and - std::codecvt specializations for a user-defined - POD. + Contains std::char_traits and + std::codecvt specializations for a user-defined + POD. @@ -748,20 +750,20 @@ only default variables. A large number of utilities, including: - VERIFY - set_memory_limits - verify_demangle - run_tests_wrapped_locale - run_tests_wrapped_env - try_named_locale - try_mkfifo - func_callback - counter - copy_tracker - copy_constructor - assignment_operator - destructor - + VERIFY + set_memory_limits + verify_demangle + run_tests_wrapped_locale + run_tests_wrapped_env + try_named_locale + try_mkfifo + func_callback + counter + copy_tracker + copy_constructor + assignment_operator + destructor + pod_char, pod_int and associated char_traits specializations @@ -792,13 +794,239 @@ only default variables. reporting functions including: - time_counter - resource_counter - report_performance + time_counter + resource_counter + report_performance + + +Special Topics + + + + Qualifying Exception Safety Guarantees + <indexterm> + <primary>Test</primary> + <secondary>Exception Safety</secondary> + </indexterm> + + + +Overview + + + Testing is composed of running a particular test sequence, + and looking at what happens to the surrounding code when + exceptions are thrown. Each test is composed of measuring + initial state, executing a particular sequence of code under + some instrumented conditions, measuring a final state, and + then examining the differences between the two states. + + + + Test sequences are composed of constructed code sequences + that exercise a particular function or member function, and + either confirm no exceptions were generated, or confirm the + consistency/coherency of the test subject in the event of a + thrown exception. + + + + Random code paths can be constructed using the the basic test + sequences and instrumentation as above, only combined in a + random or pseudo-random way. + + + To compute the code paths that throw, test instruments + are used that throw on allocation events + (__gnu_cxx::throw_allocator_random + and __gnu_cxx::throw_allocator_limit) + and copy, assignment, comparison, increment, swap, and + various operators + (__gnu_cxx::throw_type_random + and __gnu_cxx::throw_type_limit). Looping + through a given test sequence and conditionally throwing in + all instrumented places. Then, when the test sequence + completes without an exception being thrown, assume all + potential error paths have been exercised in a sequential + manner. + + + + + + + Existing tests + + + + + + Ad Hoc + + + For example, + testsuite/23_containers/list/modifiers/3.cc. + + + + + + Policy Based Data Structures + + + For example, take the test + functor rand_reg_test in + in testsuite/ext/pb_ds/regression/tree_no_data_map_rand.cc. This uses container_rand_regression_test in +testsuite/util/regression/rand/assoc/container_rand_regression_test.h. + + + + + Which has several tests for container member functions, +Includes control and test container objects. Configuration includes +random seed, iterations, number of distinct values, and the +probability that and exception will be thrown. Assumes instantiating +container uses an extension +allocator, __gnu_cxx::throw_allocator_random, +as the allocator type. + + + + + + C++0x Container Requirements. + + + + Coverage is currently limited to testing container + requirements for exception safety, + although __gnu_cxx::throw_type meets + the additional type requirements for testing numeric data + structures and instantiating algorithms. + + + + Of particular interest is extending testing to algorithms and + then to parallel algorithms. Also io, and locales. + + + + + + + + +C++0x Requirements Test Sequence Descriptions + + + + + + Basic + + + + Basic consistency on exception propagation tests. For + each container, an object of that container is constructed, + a specific member function is exercised in + a try block, and then any thrown + exceptions lead to error checking in the appropriate + catch block. The container's use of + resources is compared to the container's use prior to the + test block. Resource monitoring is limited to allocations + made through the container's allocator_type, + which should be sufficient for container data + structures. Included in these tests are member functions + are iterator and const_iterator + operations, pop_front, pop_back, push_front, push_back, insert, erase, swap, clear, + and rehash. The container in question is + instantiated with two instrumented template arguments, + with __gnu_cxx::throw_allocator_limit + as the allocator type, and + with __gnu_cxx::throw_type_limit as + the value type. This allows the test to loop through + conditional throw points. + + + + The general form is demonstrated in + testsuite/23_containers/list/requirements/exception/basic.cc + . The instantiating test object is __gnu_test::basic_safety and is detailed in testsuite/util/exception/safety.h. + + + + + + + Generation Prohibited + + + + Exception generation tests. For each container, an object of + that container is constructed and all member functions + required to not throw exceptions are exercised. Included in + these tests are member functions + are iterator and const_iterator operations, erase, pop_front, pop_back, swap, + and clear. The container in question is + instantiated with two instrumented template arguments, + with __gnu_cxx::throw_allocator_random + as the allocator type, and + with __gnu_cxx::throw_type_random as + the value type. This test does not loop, an instead is sudden + death: first error fails. + + + The general form is demonstrated in + testsuite/23_containers/list/requirements/exception/generation_prohibited.cc + . The instantiating test object is __gnu_test::generation_prohibited and is detailed in testsuite/util/exception/safety.h. + + + + + + + Propagation Consistent + + + + Container rollback on exception propagation tests. For + each container, an object of that container is constructed, + a specific member function that requires rollback to a previous + known good state is exercised in + a try block, and then any thrown + exceptions lead to error checking in the appropriate + catch block. The container is compared to + the container's last known good state using such parameters + as size, contents, and iterator references. Included in these + tests are member functions + are push_front, push_back, insert, + and rehash. The container in question is + instantiated with two instrumented template arguments, + with __gnu_cxx::throw_allocator_limit + as the allocator type, and + with __gnu_cxx::throw_type_limit as + the value type. This allows the test to loop through + conditional throw points. + + + + The general form demonstrated in + testsuite/23_containers/list/requirements/exception/propagation_coherent.cc + . The instantiating test object is __gnu_test::propagation_coherent and is detailed in testsuite/util/exception/safety.h. + + + + + + + + + + diff --git a/libstdc++-v3/testsuite/23_containers/list/requirements/exception/basic.cc b/libstdc++-v3/testsuite/23_containers/list/requirements/exception/basic.cc new file mode 100644 index 000000000000..33232f965f67 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/list/requirements/exception/basic.cc @@ -0,0 +1,40 @@ +// { dg-options "-std=gnu++0x" } +// { dg-require-cstdint "" } + +// 2009-11-30 Benjamin Kosnik + +// Copyright (C) 2009 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library 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, or (at your option) +// any later version. + +// This library 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 library; see the file COPYING3. If not see +// . + +#include +#include + +void +value() +{ + typedef __gnu_cxx::throw_value_limit value_type; + typedef __gnu_cxx::throw_allocator_limit allocator_type; + typedef std::list test_type; + __gnu_test::basic_safety test; +} + +// Container requirement testing, exceptional behavior +int main() +{ + value(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/list/requirements/exception/generation_prohibited.cc b/libstdc++-v3/testsuite/23_containers/list/requirements/exception/generation_prohibited.cc new file mode 100644 index 000000000000..f3d16028e2a3 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/list/requirements/exception/generation_prohibited.cc @@ -0,0 +1,34 @@ +// { dg-options "-std=gnu++0x" } +// { dg-require-cstdint "" } + +// 2009-09-09 Benjamin Kosnik + +// Copyright (C) 2009 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library 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, or (at your option) +// any later version. + +// This library 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 library; see the file COPYING3. If not see +// . + +#include +#include + +// Container requirement testing, exceptional behavior +int main() +{ + typedef __gnu_cxx::throw_value_random value_type; + typedef __gnu_cxx::throw_allocator_random allocator_type; + typedef std::list test_type; + __gnu_test::generation_prohibited test; + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/list/requirements/exception/propagation_consistent.cc b/libstdc++-v3/testsuite/23_containers/list/requirements/exception/propagation_consistent.cc new file mode 100644 index 000000000000..02d8d0b254f1 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/list/requirements/exception/propagation_consistent.cc @@ -0,0 +1,34 @@ +// { dg-options "-std=gnu++0x" } +// { dg-require-cstdint "" } + +// 2009-09-09 Benjamin Kosnik + +// Copyright (C) 2009 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library 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, or (at your option) +// any later version. + +// This library 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 library; see the file COPYING3. If not see +// . + +#include +#include + +// Container requirement testing, exceptional behavior +int main() +{ + typedef __gnu_cxx::throw_value_limit value_type; + typedef __gnu_cxx::throw_allocator_limit allocator_type; + typedef std::list test_type; + __gnu_test::propagation_consistent test; + return 0; +} diff --git a/libstdc++-v3/testsuite/util/exception/safety.h b/libstdc++-v3/testsuite/util/exception/safety.h new file mode 100644 index 000000000000..ce9dad4f7ba6 --- /dev/null +++ b/libstdc++-v3/testsuite/util/exception/safety.h @@ -0,0 +1,1066 @@ +// -*- C++ -*- + +// Copyright (C) 2009 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library 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, or (at your option) any later +// version. + +// This library 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 library; see the file COPYING3. If not see +// . + +#ifndef _GLIBCXX_EXCEPTION_SAFETY_H +#define _GLIBCXX_EXCEPTION_SAFETY_H + +#include +#include + +// Container requirement testing. +namespace __gnu_test +{ + // Base class for exception testing, contains utilities. + struct setup_base + { + typedef std::size_t size_type; + typedef std::uniform_int_distribution distribution_type; + typedef std::mt19937 engine_type; + + // Return randomly generated integer on range [0, __max_size]. + static size_type + generate(size_type __max_size) + { + // Make the generator static... + const engine_type engine; + const distribution_type distribution; + static auto generator = std::bind(distribution, engine, + std::placeholders::_1); + + // ... but set the range for this particular invocation here. + const typename distribution_type::param_type p(0, __max_size); + size_type random = generator(p); + if (random < distribution.min() || random > distribution.max()) + { + std::string __s("setup_base::generate"); + __s += "\n"; + __s += "random number generated is: "; + char buf[40]; + __builtin_sprintf(buf, "%lu", random); + __s += buf; + __s += " on range ["; + __builtin_sprintf(buf, "%lu", distribution.min()); + __s += buf; + __s += ", "; + __builtin_sprintf(buf, "%lu", distribution.max()); + __s += buf; + __s += "]\n"; + std::__throw_out_of_range(__s.c_str()); + } + return random; + } + + // Given an instantiating type, return a unique value. + template + struct generate_unique + { + typedef _Tp value_type; + + operator value_type() + { + static value_type __ret; + ++__ret; + return __ret; + } + }; + + // Partial specialization for pair. + template + struct generate_unique> + { + typedef _Tp1 first_type; + typedef _Tp2 second_type; + typedef std::pair pair_type; + + operator pair_type() + { + static first_type _S_1; + static second_type _S_2; + ++_S_1; + ++_S_2; + return pair_type(_S_1, _S_2); + } + }; + + // Partial specialization for throw_value + template + struct generate_unique<__gnu_cxx::throw_value_base<_Cond>> + { + typedef __gnu_cxx::throw_value_base<_Cond> value_type; + + operator value_type() + { + static size_t _S_i(0); + return value_type(_S_i++); + } + }; + + + // Construct container of size n directly. _Tp == container type. + template + struct make_container_base + { + _Tp _M_container; + + make_container_base() = default; + make_container_base(const size_type n): _M_container(n) { } + + operator _Tp&() { return _M_container; } + }; + + // Construct container of size n, via multiple insertions. For + // associated and unordered types, unique value_type elements are + // necessary. + template::is_mapped::value> + struct make_insert_container_base + : public make_container_base<_Tp> + { + using make_container_base<_Tp>::_M_container; + typedef typename _Tp::value_type value_type; + + make_insert_container_base(const size_type n) + { + for (size_type i = 0; i < n; ++i) + { + value_type v = generate_unique(); + _M_container.insert(v); + } + assert(_M_container.size() == n); + } + }; + + template + struct make_insert_container_base<_Tp, false> + : public make_container_base<_Tp> + { + using make_container_base<_Tp>::_M_container; + typedef typename _Tp::value_type value_type; + + make_insert_container_base(const size_type n) + { + for (size_type i = 0; i < n; ++i) + { + value_type v = generate_unique(); + _M_container.insert(_M_container.end(), v); + } + assert(_M_container.size() == n); + } + }; + + template::has_size_type_constructor::value> + struct make_container_n; + + // Specialization for non-associative types that have a constructor with + // a size argument. + template + struct make_container_n<_Tp, true> + : public make_container_base<_Tp> + { + make_container_n(const size_type n) : make_container_base<_Tp>(n) { } + }; + + template + struct make_container_n<_Tp, false> + : public make_insert_container_base<_Tp> + { + make_container_n(const size_type n) + : make_insert_container_base<_Tp>(n) { } + }; + + + // Randomly size and populate a given container reference. + // NB: Responsibility for turning off exceptions lies with caller. + template::is_allocator_aware::value> + struct populate + { + typedef _Tp container_type; + typedef typename container_type::allocator_type allocator_type; + typedef typename container_type::value_type value_type; + + populate(_Tp& __container) + { + const allocator_type a = __container.get_allocator(); + + // Size test container. + const size_type max_elements = 100; + size_type n = generate(max_elements); + + // Construct new container. + make_container_n made(n); + container_type& tmp = made; + std::swap(tmp, __container); + } + }; + + // Partial specialization, empty. + template + struct populate<_Tp, false> + { + populate(_Tp&) { } + }; + + // Compare two containers for equivalence. + // Right now, that means size. + // Returns true if equal, throws if not. + template + static bool + compare(const _Tp& __control, const _Tp& __test) + { + // Make sure test container is in a consistent state, as + // compared to the control container. + // NB: Should be equivalent to __test != __control, but + // computed without equivalence operators + const size_type szt = std::distance(__test.begin(), __test.end()); + const size_type szc = std::distance(__control.begin(), + __control.end()); + bool __equal_size = szt == szc; + + // Should test iterator validity before and after exception. + bool __equal_it = std::equal(__test.begin(), __test.end(), + __control.begin()); + + if (!__equal_size || !__equal_it) + throw std::logic_error("setup_base::compare containers not equal"); + + return true; + } + }; + + + // Containing structure holding functors. + struct functor_base : public setup_base + { + // Abstract the erase function. + template + struct erase_base + { + typedef typename _Tp::iterator iterator; + + iterator (_Tp::* _F_erase_point)(iterator); + iterator (_Tp::* _F_erase_range)(iterator, iterator); + + erase_base() + : _F_erase_point(&_Tp::erase), _F_erase_range(&_Tp::erase) { } + }; + + // Specialization, as forward_list has erase_after. + template + struct erase_base> + { + typedef std::forward_list<_Tp1, _Tp2> container_type; + typedef typename container_type::iterator iterator; + typedef typename container_type::const_iterator const_iterator; + + void (container_type::* _F_erase_point)(const_iterator); + void (container_type::* _F_erase_range)(const_iterator, const_iterator); + + erase_base() + : _F_erase_point(&container_type::erase_after), + _F_erase_range(&container_type::erase_after) { } + }; + + template::has_erase::value> + struct erase_point : public erase_base<_Tp> + { + using erase_base<_Tp>::_F_erase_point; + + void + operator()(_Tp& __container) + { + try + { + // NB: Should be equivalent to size() member function, but + // computed with begin() and end(). + const size_type sz = std::distance(__container.begin(), + __container.end()); + + // NB: Lowest common denominator: use forward iterator operations. + auto i = __container.begin(); + std::advance(i, generate(sz)); + + // Makes it easier to think of this as __container.erase(i) + (__container.*_F_erase_point)(i); + } + catch(const __gnu_cxx::forced_error&) + { throw; } + } + }; + + // Specialization, empty. + template + struct erase_point<_Tp, false> + { + void + operator()(_Tp&) { } + }; + + + template::has_erase::value> + struct erase_range : public erase_base<_Tp> + { + using erase_base<_Tp>::_F_erase_range; + + void + operator()(_Tp& __container) + { + try + { + const size_type sz = std::distance(__container.begin(), + __container.end()); + size_type s1 = generate(sz); + size_type s2 = generate(sz); + auto i1 = __container.begin(); + auto i2 = __container.begin(); + std::advance(i1, std::min(s1, s2)); + std::advance(i2, std::max(s1, s2)); + + // Makes it easier to think of this as __container.erase(i1, i2). + (__container.*_F_erase_range)(i1, i2); + } + catch(const __gnu_cxx::forced_error&) + { throw; } + } + }; + + // Specialization, empty. + template + struct erase_range<_Tp, false> + { + void + operator()(_Tp&) { } + }; + + + template::has_push_pop::value> + struct pop_front + { + void + operator()(_Tp& __container) + { + try + { + __container.pop_front(); + } + catch(const __gnu_cxx::forced_error&) + { throw; } + } + }; + + // Specialization, empty. + template + struct pop_front<_Tp, false> + { + void + operator()(_Tp&) { } + }; + + + template::has_push_pop::value + && traits<_Tp>::is_reversible::value> + struct pop_back + { + void + operator()(_Tp& __container) + { + try + { + __container.pop_back(); + } + catch(const __gnu_cxx::forced_error&) + { throw; } + } + }; + + // Specialization, empty. + template + struct pop_back<_Tp, false> + { + void + operator()(_Tp&) { } + }; + + + template::has_push_pop::value> + struct push_front + { + typedef _Tp container_type; + typedef typename container_type::value_type value_type; + + void + operator()(_Tp& __test) + { + try + { + const value_type cv = generate_unique(); + __test.push_front(cv); + } + catch(const __gnu_cxx::forced_error&) + { throw; } + } + + // Assumes containers start out equivalent. + void + operator()(_Tp& __control, _Tp& __test) + { + try + { + const value_type cv = generate_unique(); + __test.push_front(cv); + } + catch(const __gnu_cxx::forced_error&) + { throw; } + } + }; + + // Specialization, empty. + template + struct push_front<_Tp, false> + { + void + operator()(_Tp&) { } + + void + operator()(_Tp&, _Tp&) { } + }; + + + template::has_push_pop::value + && traits<_Tp>::is_reversible::value> + struct push_back + { + typedef _Tp container_type; + typedef typename container_type::value_type value_type; + + void + operator()(_Tp& __test) + { + try + { + const value_type cv = generate_unique(); + __test.push_back(cv); + } + catch(const __gnu_cxx::forced_error&) + { throw; } + } + + // Assumes containers start out equivalent. + void + operator()(_Tp& __control, _Tp& __test) + { + try + { + const value_type cv = generate_unique(); + __test.push_back(cv); + } + catch(const __gnu_cxx::forced_error&) + { throw; } + } + }; + + // Specialization, empty. + template + struct push_back<_Tp, false> + { + void + operator()(_Tp&) { } + + void + operator()(_Tp&, _Tp&) { } + }; + + + // Abstract the insert function into two parts: + // 1, insert_base_functions == holds function pointer + // 2, insert_base == links function pointer to class insert method + template + struct insert_base + { + typedef typename _Tp::iterator iterator; + typedef typename _Tp::value_type value_type; + + iterator (_Tp::* _F_insert_point)(iterator, const value_type&); + + insert_base() : _F_insert_point(&_Tp::insert) { } + }; + + // Specialization, as string insertion has a different signature. + template + struct insert_base> + { + typedef std::basic_string<_Tp1, _Tp2, _Tp3> container_type; + typedef typename container_type::iterator iterator; + typedef typename container_type::value_type value_type; + + iterator (container_type::* _F_insert_point)(iterator, value_type); + + insert_base() : _F_insert_point(&container_type::insert) { } + }; + + template + struct insert_base<__gnu_cxx::__versa_string<_Tp1, _Tp2, _Tp3>> + { + typedef __gnu_cxx::__versa_string<_Tp1, _Tp2, _Tp3> container_type; + typedef typename container_type::iterator iterator; + typedef typename container_type::value_type value_type; + + iterator (container_type::* _F_insert_point)(iterator, value_type); + + insert_base() : _F_insert_point(&container_type::insert) { } + }; + + // Specialization, as forward_list insertion has a different signature. + template + struct insert_base> + { + typedef std::forward_list<_Tp1, _Tp2> container_type; + typedef typename container_type::iterator iterator; + typedef typename container_type::const_iterator const_iterator; + typedef typename container_type::value_type value_type; + + iterator (container_type::* _F_insert_point)(const_iterator, + const value_type&); + + insert_base() : _F_insert_point(&container_type::insert_after) { } + }; + + template::has_insert::value> + struct insert_point : public insert_base<_Tp> + { + typedef _Tp container_type; + typedef typename container_type::value_type value_type; + using insert_base<_Tp>::_F_insert_point; + + void + operator()(_Tp& __test) + { + try + { + const value_type cv = generate_unique(); + const size_type sz = std::distance(__test.begin(), __test.end()); + size_type s = generate(sz); + auto i = __test.begin(); + std::advance(i, s); + (__test.*_F_insert_point)(i, cv); + } + catch(const __gnu_cxx::forced_error&) + { throw; } + } + + // Assumes containers start out equivalent. + void + operator()(_Tp& __control, _Tp& __test) + { + try + { + const value_type cv = generate_unique(); + const size_type sz = std::distance(__test.begin(), __test.end()); + size_type s = generate(sz); + auto i = __test.begin(); + std::advance(i, s); + (__test.*_F_insert_point)(i, cv); + } + catch(const __gnu_cxx::forced_error&) + { throw; } + } + }; + + // Specialization, empty. + template + struct insert_point<_Tp, false> + { + void + operator()(_Tp&) { } + + void + operator()(_Tp&, _Tp&) { } + }; + + + template::is_associative::value + || traits<_Tp>::is_unordered::value> + struct clear + { + void + operator()(_Tp& __container) + { + try + { + __container.clear(); + } + catch(const __gnu_cxx::forced_error&) + { throw; } + } + }; + + // Specialization, empty. + template + struct clear<_Tp, false> + { + void + operator()(_Tp&) { } + }; + + + template::is_unordered::value> + struct rehash + { + void + operator()(_Tp& __test) + { + try + { + size_type s = generate(__test.bucket_count()); + __test.rehash(s); + } + catch(const __gnu_cxx::forced_error&) + { throw; } + } + + void + operator()(_Tp& __control, _Tp& __test) + { + try + { + size_type s = generate(__test.bucket_count()); + __test.rehash(s); + } + catch(const __gnu_cxx::forced_error&) + { + // Also check hash status. + bool fail(false); + if (__control.load_factor() != __test.load_factor()) + fail = true; + if (__control.max_load_factor() != __test.max_load_factor()) + fail = true; + if (__control.bucket_count() != __test.bucket_count()) + fail = true; + if (__control.max_bucket_count() != __test.max_bucket_count()) + fail = true; + + if (fail) + { + char buf[40]; + std::string __s("setup_base::rehash " + "containers not equal"); + __s += "\n"; + __s += "\n"; + __s += "\t\t\tcontrol : test"; + __s += "\n"; + __s += "load_factor\t\t"; + __builtin_sprintf(buf, "%lu", __control.load_factor()); + __s += buf; + __s += " : "; + __builtin_sprintf(buf, "%lu", __test.load_factor()); + __s += buf; + __s += "\n"; + + __s += "max_load_factor\t\t"; + __builtin_sprintf(buf, "%lu", __control.max_load_factor()); + __s += buf; + __s += " : "; + __builtin_sprintf(buf, "%lu", __test.max_load_factor()); + __s += buf; + __s += "\n"; + + __s += "bucket_count\t\t"; + __builtin_sprintf(buf, "%lu", __control.bucket_count()); + __s += buf; + __s += " : "; + __builtin_sprintf(buf, "%lu", __test.bucket_count()); + __s += buf; + __s += "\n"; + + __s += "max_bucket_count\t"; + __builtin_sprintf(buf, "%lu", __control.max_bucket_count()); + __s += buf; + __s += " : "; + __builtin_sprintf(buf, "%lu", __test.max_bucket_count()); + __s += buf; + __s += "\n"; + + std::__throw_logic_error(__s.c_str()); + } + } + } + }; + + // Specialization, empty. + template + struct rehash<_Tp, false> + { + void + operator()(_Tp&) { } + + void + operator()(_Tp&, _Tp&) { } + }; + + + template + struct swap + { + _Tp _M_other; + + void + operator()(_Tp& __container) + { + try + { + __container.swap(_M_other); + } + catch(const __gnu_cxx::forced_error&) + { throw; } + } + }; + + + template + struct iterator_operations + { + typedef _Tp container_type; + typedef typename container_type::iterator iterator; + + void + operator()(_Tp& __container) + { + try + { + // Any will do. + iterator i = __container.begin(); + iterator __attribute__((unused)) icopy(i); + iterator __attribute__((unused)) iassign = i; + } + catch(const __gnu_cxx::forced_error&) + { throw; } + } + }; + + + template + struct const_iterator_operations + { + typedef _Tp container_type; + typedef typename container_type::const_iterator const_iterator; + + void + operator()(_Tp& __container) + { + try + { + // Any will do. + const_iterator i = __container.begin(); + const_iterator __attribute__((unused)) icopy(i); + const_iterator __attribute__((unused)) iassign = i; + } + catch(const __gnu_cxx::forced_error&) + { throw; } + } + }; + }; + + // Base class for exception tests. + template + struct test_base: public functor_base + { + typedef _Tp container_type; + + typedef functor_base base_type; + typedef populate populate; + typedef make_container_n make_container_n; + + typedef clear clear; + typedef erase_point erase_point; + typedef erase_range erase_range; + typedef insert_point insert_point; + typedef pop_front pop_front; + typedef pop_back pop_back; + typedef push_front push_front; + typedef push_back push_back; + typedef rehash rehash; + typedef swap swap; + typedef iterator_operations iterator_ops; + typedef const_iterator_operations const_iterator_ops; + + using base_type::compare; + + // Functor objects. + clear _M_clear; + erase_point _M_erasep; + erase_range _M_eraser; + insert_point _M_insertp; + pop_front _M_popf; + pop_back _M_popb; + push_front _M_pushf; + push_back _M_pushb; + rehash _M_rehash; + swap _M_swap; + + iterator_ops _M_iops; + const_iterator_ops _M_ciops; + }; + + + // Run through all member functions for basic exception safety + // guarantee: no resource leaks when exceptions are thrown. + // + // Types of resources checked: memory. + // + // For each member function, use throw_value and throw_allocator as + // value_type and allocator_type to force potential exception safety + // errors. + // + // NB: Assumes + // _Tp::value_type is __gnu_cxx::throw_value_* + // _Tp::allocator_type is __gnu_cxx::throw_allocator_* + // And that the _Cond template parameter for them both is + // __gnu_cxx::limit_condition. + template + struct basic_safety : public test_base<_Tp> + { + typedef _Tp container_type; + typedef test_base base_type; + typedef typename base_type::populate populate; + typedef std::function function_type; + typedef __gnu_cxx::limit_condition condition_type; + + using base_type::generate; + + container_type _M_container; + std::vector _M_functions; + + basic_safety() { run(); } + + void + run() + { + // Setup. + condition_type::never_adjustor off; + + // Construct containers. + populate p1(_M_container); + populate p2(base_type::_M_swap._M_other); + + // Construct list of member functions to exercise. + _M_functions.push_back(function_type(base_type::_M_iops)); + _M_functions.push_back(function_type(base_type::_M_ciops)); + + _M_functions.push_back(function_type(base_type::_M_erasep)); + _M_functions.push_back(function_type(base_type::_M_eraser)); + _M_functions.push_back(function_type(base_type::_M_insertp)); + _M_functions.push_back(function_type(base_type::_M_popf)); + _M_functions.push_back(function_type(base_type::_M_popb)); + _M_functions.push_back(function_type(base_type::_M_pushf)); + _M_functions.push_back(function_type(base_type::_M_pushb)); + _M_functions.push_back(function_type(base_type::_M_rehash)); + _M_functions.push_back(function_type(base_type::_M_swap)); + + // Last. + _M_functions.push_back(function_type(base_type::_M_clear)); + + // Run tests. + auto i = _M_functions.begin(); + for (auto i = _M_functions.begin(); i != _M_functions.end(); ++i) + { + function_type& f = *i; + run_steps_to_limit(f); + } + } + + template + void + run_steps_to_limit(const _Funct& __f) + { + size_t i(1); + bool exit(false); + auto a = _M_container.get_allocator(); + + do + { + // Use the current step as an allocator label. + a.set_label(i); + + try + { + condition_type::limit_adjustor limit(i); + __f(_M_container); + + // If we get here, done. + exit = true; + } + catch(const __gnu_cxx::forced_error&) + { + // Check this step for allocations. + // NB: Will throw std::logic_error if allocations. + a.check_allocated(i); + + // Check memory allocated with operator new. + + ++i; + } + } + while (!exit); + + // Log count info. + std::cout << __f.target_type().name() << std::endl; + std::cout << "end count " << i << std::endl; + } + }; + + + // Run through all member functions with a no throw requirement, sudden death. + // all: member functions erase, pop_back, pop_front, swap + // iterator copy ctor, assignment operator + // unordered and associative: clear + // NB: Assumes _Tp::allocator_type is __gnu_cxx::throw_allocator_random. + template + struct generation_prohibited : public test_base<_Tp> + { + typedef _Tp container_type; + typedef test_base base_type; + typedef typename base_type::populate populate; + typedef __gnu_cxx::random_condition condition_type; + + container_type _M_container; + + generation_prohibited() { run(); } + + void + run() + { + // Furthermore, assumes that the test functor will throw + // forced_exception via throw_allocator, that all errors are + // propagated and in error. Sudden death! + + // Setup. + { + condition_type::never_adjustor off; + populate p1(_M_container); + populate p2(base_type::_M_swap._M_other); + } + + // Run tests. + { + condition_type::always_adjustor on; + + _M_erasep(_M_container); + _M_eraser(_M_container); + + _M_popf(_M_container); + _M_popb(_M_container); + + _M_iops(_M_container); + _M_ciops(_M_container); + + _M_swap(_M_container); + + // Last. + _M_clear(_M_container); + } + } + }; + + + // Test strong exception guarantee. + // Run through all member functions with a roll-back, consistent + // coherent requirement. + // all: member functions insert of a single element, push_back, push_front + // unordered: rehash + template + struct propagation_consistent : public test_base<_Tp> + { + typedef _Tp container_type; + typedef test_base base_type; + typedef typename base_type::populate populate; + typedef std::function function_type; + typedef __gnu_cxx::limit_condition condition_type; + + using base_type::compare; + + container_type _M_container_test; + container_type _M_container_control; + std::vector _M_functions; + + propagation_consistent() { run(); } + + void + sync() + { _M_container_test = _M_container_control; } + + // Run test. + void + run() + { + // Setup. + condition_type::never_adjustor off; + + // Construct containers. + populate p(_M_container_control); + sync(); + + // Construct list of member functions to exercise. + _M_functions.push_back(function_type(base_type::_M_pushf)); + _M_functions.push_back(function_type(base_type::_M_pushb)); + _M_functions.push_back(function_type(base_type::_M_insertp)); + _M_functions.push_back(function_type(base_type::_M_rehash)); + + // Run tests. + auto i = _M_functions.begin(); + for (auto i = _M_functions.begin(); i != _M_functions.end(); ++i) + { + function_type& f = *i; + run_steps_to_limit(f); + } + } + + template + void + run_steps_to_limit(const _Funct& __f) + { + size_t i(1); + bool exit(false); + + do + { + sync(); + + try + { + condition_type::limit_adjustor limit(i); + __f(_M_container_test); + + // If we get here, done. + exit = true; + } + catch(const __gnu_cxx::forced_error&) + { + compare(_M_container_control, _M_container_test); + ++i; + } + } + while (!exit); + + // Log count info. + std::cout << __f.target_type().name() << std::endl; + std::cout << "end count " << i << std::endl; + } + }; + +} // namespace __gnu_test + +#endif diff --git a/libstdc++-v3/testsuite/util/testsuite_container_traits.h b/libstdc++-v3/testsuite/util/testsuite_container_traits.h index 7c4b1a27097d..85d04c5728ca 100644 --- a/libstdc++-v3/testsuite/util/testsuite_container_traits.h +++ b/libstdc++-v3/testsuite/util/testsuite_container_traits.h @@ -24,8 +24,9 @@ #include namespace __gnu_test -{ +{ // Container traits. + // Base class with default false values for all traits. struct traits_base { // Type, nested type, and typedef related traits. @@ -37,6 +38,11 @@ namespace __gnu_test typedef std::false_type is_associative; typedef std::false_type is_unordered; typedef std::false_type is_mapped; + + typedef std::false_type has_erase; + typedef std::false_type has_insert; + typedef std::false_type has_push_pop; + typedef std::false_type has_size_type_constructor; }; // Primary template does nothing. Specialize on each type under @@ -46,40 +52,55 @@ namespace __gnu_test // Specialize for each container. template - struct traits > : public traits_base + struct traits> : public traits_base { typedef std::true_type is_container; typedef std::true_type is_reversible; }; - template - struct traits > : public traits_base + template + struct traits> : public traits_base { typedef std::true_type is_container; typedef std::true_type is_reversible; typedef std::true_type is_allocator_aware; typedef std::true_type is_pointer_aware; + + typedef std::true_type has_erase; + typedef std::true_type has_insert; + typedef std::true_type has_push_pop; + typedef std::true_type has_size_type_constructor; }; - template - struct traits > : public traits_base + template + struct traits> : public traits_base { typedef std::true_type is_container; typedef std::true_type is_allocator_aware; typedef std::true_type is_pointer_aware; + + typedef std::true_type has_erase; + typedef std::true_type has_insert; + typedef std::true_type has_push_pop; + typedef std::true_type has_size_type_constructor; }; - template - struct traits > : public traits_base + template + struct traits> : public traits_base { typedef std::true_type is_container; typedef std::true_type is_reversible; typedef std::true_type is_allocator_aware; typedef std::true_type is_pointer_aware; + + typedef std::true_type has_erase; + typedef std::true_type has_insert; + typedef std::true_type has_push_pop; + typedef std::true_type has_size_type_constructor; }; - template - struct traits > : public traits_base + template + struct traits> : public traits_base { typedef std::true_type is_container; typedef std::true_type is_reversible; @@ -87,10 +108,12 @@ namespace __gnu_test typedef std::true_type is_pointer_aware; typedef std::true_type is_associative; typedef std::true_type is_mapped; + + typedef std::true_type has_insert; }; - template - struct traits > : public traits_base + template + struct traits> : public traits_base { typedef std::true_type is_container; typedef std::true_type is_reversible; @@ -100,108 +123,142 @@ namespace __gnu_test typedef std::true_type is_mapped; }; - template - struct traits > : public traits_base + template + struct traits> : public traits_base { typedef std::true_type is_container; typedef std::true_type is_reversible; typedef std::true_type is_allocator_aware; typedef std::true_type is_pointer_aware; typedef std::true_type is_associative; + + typedef std::true_type has_insert; }; - template - struct traits > : public traits_base + template + struct traits> : public traits_base { typedef std::true_type is_adaptor; }; - template - struct traits > : public traits_base + template + struct traits> : public traits_base { typedef std::true_type is_adaptor; }; - template - struct traits > : public traits_base + template + struct traits> : public traits_base { typedef std::true_type is_container; typedef std::true_type is_reversible; typedef std::true_type is_allocator_aware; typedef std::true_type is_pointer_aware; typedef std::true_type is_associative; + + typedef std::true_type has_insert; }; - template - struct traits > : public traits_base + template + struct traits > : public traits_base { typedef std::true_type is_adaptor; }; - template - struct traits > : public traits_base + template + struct traits> + : public traits_base { typedef std::true_type is_container; typedef std::true_type is_allocator_aware; typedef std::true_type is_pointer_aware; typedef std::true_type is_unordered; typedef std::true_type is_mapped; + + typedef std::true_type has_size_type_constructor; + typedef std::true_type has_insert; }; - template - struct traits > : public traits_base + template + struct traits> + : public traits_base { typedef std::true_type is_container; typedef std::true_type is_allocator_aware; typedef std::true_type is_pointer_aware; typedef std::true_type is_unordered; typedef std::true_type is_mapped; + + typedef std::true_type has_size_type_constructor; }; - template - struct traits > : public traits_base + template + struct traits> + : public traits_base { typedef std::true_type is_container; typedef std::true_type is_allocator_aware; typedef std::true_type is_pointer_aware; typedef std::true_type is_unordered; + + typedef std::true_type has_insert; }; - template - struct traits > : public traits_base + template + struct traits> + : public traits_base { typedef std::true_type is_container; typedef std::true_type is_allocator_aware; typedef std::true_type is_pointer_aware; typedef std::true_type is_unordered; + + typedef std::true_type has_size_type_constructor; + typedef std::true_type has_insert; }; - template - struct traits > : public traits_base + template + struct traits> : public traits_base { typedef std::true_type is_container; typedef std::true_type is_reversible; typedef std::true_type is_allocator_aware; typedef std::true_type is_pointer_aware; + + typedef std::true_type has_erase; + typedef std::true_type has_insert; + typedef std::true_type has_size_type_constructor; }; - template - struct traits > : public traits_base + template + struct traits> : public traits_base { typedef std::true_type is_container; typedef std::true_type is_reversible; typedef std::true_type is_allocator_aware; typedef std::true_type is_pointer_aware; + + typedef std::true_type has_erase; + typedef std::true_type has_insert; }; - template - struct traits<__gnu_cxx::__versa_string<_Tp> > : public traits_base + template class _Tp4> + struct traits<__gnu_cxx::__versa_string<_Tp1, _Tp2, _Tp3, _Tp4>> + : public traits_base { typedef std::true_type is_container; typedef std::true_type is_reversible; typedef std::true_type is_allocator_aware; typedef std::true_type is_pointer_aware; + + typedef std::true_type has_erase; + + // XXX no vstring::insert + // typedef std::true_type has_insert; }; } // namespace __gnu_test -#endif +#endif -- 2.39.5