public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r11-7831] libstdc++: Allow seeding random engines in testsuite
@ 2021-03-25 18:22 Jonathan Wakely
  0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2021-03-25 18:22 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:c7fc73ee459045edabb99816658f14f32d23bf92

commit r11-7831-gc7fc73ee459045edabb99816658f14f32d23bf92
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Mar 25 13:51:08 2021 +0000

    libstdc++: Allow seeding random engines in testsuite
    
    The testsuite utilities that use random numbers use a
    default-constructed mersenne_twister_engine, meaning the values are
    reproducable. This adds support for seeding them, controlledby an
    environment variable. Defining GLIBCXX_SEED_TEST_RNG=val in the
    environment will cause the engines to be seeded with atoi(val) if that
    is non-zero, or with a value read from std::random_device otherwise.
    
    Running with different seeds revealed some bugs in the tests, where a
    randomly selected iterator was past-the-end (which can't be erased), or
    where the randomly populated container was empty, and then we tried to
    remove elements from it unconditionally.
    
    libstdc++-v3/ChangeLog:
    
            * testsuite/util/exception/safety.h (setup_base::generate):
            Support seeding random engine.
            (erase_point, erase_range): Adjust range of random numbers to
            ensure dereferenceable iterators are used where required.
            (generation_prohibited::run): Do not try to erase from empty
            containers.
            * testsuite/util/testsuite_containergen.h (test_containers):
            Support seeding random engine.

Diff:
---
 libstdc++-v3/testsuite/util/exception/safety.h     | 89 ++++++++++++++--------
 .../testsuite/util/testsuite_containergen.h        | 14 ++++
 2 files changed, 73 insertions(+), 30 deletions(-)

diff --git a/libstdc++-v3/testsuite/util/exception/safety.h b/libstdc++-v3/testsuite/util/exception/safety.h
index 6c91e740e0d..2e5d8acae00 100644
--- a/libstdc++-v3/testsuite/util/exception/safety.h
+++ b/libstdc++-v3/testsuite/util/exception/safety.h
@@ -22,6 +22,8 @@
 
 #include <testsuite_container_traits.h>
 #include <ext/throw_allocator.h>
+#include <cstdlib> // getenv, atoi
+#include <cstdio>  // printf, fflush
 
 // Container requirement testing.
 namespace __gnu_test
@@ -33,27 +35,34 @@ namespace __gnu_test
     typedef std::uniform_int_distribution<size_type> 	distribution_type;
     typedef std::mt19937 				engine_type;
 
+    static engine_type
+    get_engine()
+    {
+      engine_type engine;
+      if (const char* v = std::getenv("GLIBCXX_SEED_TEST_RNG"))
+	{
+	  // A single seed value is much smaller than the mt19937 state size,
+	  // but we're not trying to be cryptographically secure here.
+	  int s = std::atoi(v);
+	  if (s == 0)
+	    s = (int)std::random_device{}();
+	  std::printf("Using random seed %d\n", s);
+	  std::fflush(stdout);
+	  engine.seed((unsigned)s);
+	}
+      return engine;
+    }
+
     // 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::__throw_out_of_range_fmt(__N("setup_base::generate\n"
-					  "random number generated is: %zu "
-					  "out of range [%zu, %zu]\n"),
-				      (size_t)random,
-				      (size_t)distribution.min(),
-				      (size_t)distribution.max());
-      return random;
+      using param_type = typename distribution_type::param_type;
+
+      // Make the engine and distribution static...
+      static engine_type engine = get_engine();
+      static distribution_type distribution;
+      return distribution(engine, param_type{0, __max_size});
     }
 
     // Given an instantiating type, return a unique value.
@@ -309,10 +318,13 @@ namespace __gnu_test
 	      // computed with begin() and end().
 	      const size_type sz = std::distance(__container.begin(),
 						 __container.end());
+	      // Container::erase(pos) requires dereferenceable pos.
+	      if (sz == 0)
+		throw std::logic_error("erase_point: empty container");
 
 	      // NB: Lowest common denominator: use forward iterator operations.
 	      auto i = __container.begin();
-	      std::advance(i, generate(sz));
+	      std::advance(i, generate(sz - 1));
 
 	      // Makes it easier to think of this as __container.erase(i)
 	      (__container.*_F_erase_point)(i);
@@ -337,12 +349,15 @@ namespace __gnu_test
 	      // computed with begin() and end().
 	      const size_type sz = std::distance(__container.begin(),
 						 __container.end());
+	      // forward_list::erase_after(pos) requires dereferenceable pos.
+	      if (sz == 0)
+		throw std::logic_error("erase_point: empty container");
 
 	      // NB: Lowest common denominator: use forward iterator operations.
 	      auto i = __container.before_begin();
-	      std::advance(i, generate(sz));
+	      std::advance(i, generate(sz - 1));
 
-	      // Makes it easier to think of this as __container.erase(i)
+	      // Makes it easier to think of this as __container.erase_after(i)
 	      (__container.*_F_erase_point)(i);
 	    }
 	  catch(const __gnu_cxx::forced_error&)
@@ -405,14 +420,19 @@ namespace __gnu_test
 	    {
 	      const size_type sz = std::distance(__container.begin(),
 						 __container.end());
-	      size_type s1 = generate(sz);
-	      size_type s2 = generate(sz);
+	      // forward_list::erase_after(pos, last) requires a pos != last
+	      if (sz == 0)
+		return; // Caller doesn't check for this, not a logic error.
+
+	      size_type s1 = generate(sz - 1);
+	      size_type s2 = generate(sz - 1);
 	      auto i1 = __container.before_begin();
 	      auto i2 = __container.before_begin();
 	      std::advance(i1, std::min(s1, s2));
-	      std::advance(i2, std::max(s1, s2));
+	      std::advance(i2, std::max(s1, s2) + 1);
 
-	      // Makes it easier to think of this as __container.erase(i1, i2).
+	      // Makes it easier to think of this as
+	      // __container.erase_after(i1, i2).
 	      (__container.*_F_erase_range)(i1, i2);
 	    }
 	  catch(const __gnu_cxx::forced_error&)
@@ -1454,16 +1474,25 @@ namespace __gnu_test
 	  // constructor or assignment operator of value_type throws.
 	  if (!traits<container_type>::has_throwing_erase::value)
 	    {
-	      typename base_type::erase_point erasep;
-	      erasep(container);
+	      if (!container.empty())
+		{
+		  typename base_type::erase_point erasep;
+		  erasep(container);
+		}
 	      typename base_type::erase_range eraser;
 	      eraser(container);
 	    }
 
-	  typename base_type::pop_front popf;
-	  popf(container);
-	  typename base_type::pop_back popb;
-	  popb(container);
+	  if (!container.empty())
+	    {
+	      typename base_type::pop_front popf;
+	      popf(container);
+	    }
+	  if (!container.empty())
+	    {
+	      typename base_type::pop_back popb;
+	      popb(container);
+	    }
 
 	  typename base_type::iterator_ops iops;
 	  iops(container);
diff --git a/libstdc++-v3/testsuite/util/testsuite_containergen.h b/libstdc++-v3/testsuite/util/testsuite_containergen.h
index a2156733ec6..c468c7f4415 100644
--- a/libstdc++-v3/testsuite/util/testsuite_containergen.h
+++ b/libstdc++-v3/testsuite/util/testsuite_containergen.h
@@ -20,6 +20,8 @@
 
 #include <testsuite_container_traits.h>
 #include <random>
+#include <cstdlib> // getenv, atoi
+#include <cstdio>  // printf, fflush
 
 namespace __gnu_test
 {
@@ -63,6 +65,18 @@ namespace __gnu_test
     {
       std::mt19937_64 random_gen;
 
+      if (const char* v = std::getenv("GLIBCXX_SEED_TEST_RNG"))
+	{
+	  // A single seed value is much smaller than the mt19937 state size,
+	  // but we're not trying to be cryptographically secure here.
+	  int s = std::atoi(v);
+	  if (s == 0)
+	    s = (int)std::random_device{}();
+	  std::printf("Using random seed %d\n", s);
+	  std::fflush(stdout);
+	  random_gen.seed((unsigned)s);
+	}
+
 #ifdef SIMULATOR_TEST
       int loops = 10;
 #else


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-03-25 18:22 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-25 18:22 [gcc r11-7831] libstdc++: Allow seeding random engines in testsuite Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).