Author: julianfoad
Date: Fri Jan 17 17:34:10 2020
New Revision: 1872923

URL: http://svn.apache.org/viewvc?rev=1872923&view=rev
Log:
Fix weak test coverage.

In the tests for "semi-canonical" and "canonical" kinds of inputs to
svn_rangelist_merge2(), the input generator previously called "generate a
random non-validated input" repeatedly until it passed certain quality
tests.  That made the result heavily biased towards very simple cases.

As a result, the coverage of the "semi-canonical" and "canonical" tests
missed the failure mode of the original bug report #4840.

This generates "semi-canonical" and "canonical" inputs with a fairer
distribution, increasing their coverage to include that failure mode.

For issue 4840 "Merge assertion failure in svn_sort__array_insert"

* subversion/tests/libsvn_subr/mergeinfo-test.c
  (rand_interval_triangular): New.
  (rangelist_random_non_validated): Use it.
  (int_compare,
   ascending_values): New.
  (rangelist_random_semi_canonical,
   rangelist_random_canonical): Rewrite.
  (add_failure_mode): Deduplicate 'Attempted insert at index ...' messages.

Modified:
    subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c

Modified: subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c?rev=1872923&r1=1872922&r2=1872923&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c Fri Jan 17 
17:34:10 2020
@@ -34,6 +34,7 @@
 #include "svn_mergeinfo.h"
 #include "private/svn_mergeinfo_private.h"
 #include "private/svn_sorts_private.h"
+#include "private/svn_error_private.h"
 #include "../svn_test.h"
 
 /* A quick way to create error messages.  */
@@ -1965,19 +1966,32 @@ static int rand_less_than(int n, apr_uin
   return ((apr_uint64_t)next * n) >> 32;
 }
 
+/* Return a random integer in a triangular (centre-weighted) distribution in
+ * the inclusive interval [MIN, MAX]. */
+static int
+rand_interval_triangular(int min, int max, apr_uint32_t *seed)
+{
+  int span = max - min + 1;
+
+  return min + rand_less_than(span/2 + 1, seed)
+             + rand_less_than((span+1)/2, seed);
+}
+
 /* Generate a rangelist with a random number of random ranges.
+ * Choose from 0 to NON_V_MAX_RANGES ranges, biased towards the middle.
  */
+#define NON_V_MAX_RANGES 4  /* See "Random testing parameters and coverage" */
 static void
 rangelist_random_non_validated(svn_rangelist_t **rl,
                                apr_uint32_t *seed,
                                apr_pool_t *pool)
 {
-  svn_rangelist_t *r = apr_array_make(pool, 4, sizeof(svn_merge_range_t *));
+  svn_rangelist_t *r = apr_array_make(pool, NON_V_MAX_RANGES,
+                                      sizeof(svn_merge_range_t *));
+  int n_ranges = rand_interval_triangular(0, NON_V_MAX_RANGES, seed);
   int i;
 
-  /* Choose from 0 to 4 ranges, biased towards 2 ranges.
-     See comment "Random testing parameters and coverage" */
-  for (i = rand_less_than(3, seed) + rand_less_than(3, seed); i > 0; --i)
+  for (i = 0; i < n_ranges; i++)
     {
       svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange));
 
@@ -1989,30 +2003,86 @@ rangelist_random_non_validated(svn_range
   *rl = r;
 }
 
+/* Compare two integers pointed to by A_P and B_P, for use with qsort(). */
+static int
+int_compare(const void *a_p, const void *b_p)
+{
+  const int *a = a_p, *b = b_p;
+
+  return (*a < *b) ? -1 : (*a > *b) ? 1 : 0;
+}
+
+/* Fill an ARRAY[ARRAY_LENGTH] with values each in the inclusive range
+ * [0, MAX].  The values are in ascending order, possibly with the same
+ * value repeated any number of times.
+ */
+static void
+ascending_values(int *array,
+                 int array_length,
+                 int max,
+                 apr_uint32_t *seed)
+{
+  int i;
+
+  for (i = 0; i < array_length; i++)
+    array[i] = rand_less_than(max + 1, seed);
+  /* Sort them. (Some values will be repeated.) */
+  qsort(array, array_length, sizeof(*array), int_compare);
+}
+
 /* Generate a random rangelist that is not necessarily canonical
  * but is at least sorted according to svn_sort_compare_ranges()
- * and on which svn_rangelist__canonicalize() would succeed. */
+ * and on which svn_rangelist__canonicalize() would succeed.
+ * Choose from 0 to SEMI_C_MAX_RANGES ranges, biased towards the middle.
+ */
+#define SEMI_C_MAX_RANGES 8
 static void
 rangelist_random_semi_canonical(svn_rangelist_t **rl,
                                 apr_uint32_t *seed,
                                 apr_pool_t *pool)
 {
-  do
+  svn_rangelist_t *r = apr_array_make(pool, 4, sizeof(svn_merge_range_t *));
+  int n_ranges = rand_interval_triangular(0, SEMI_C_MAX_RANGES, seed);
+  int start_and_end_revs[SEMI_C_MAX_RANGES * 2];
+  int i;
+
+  /* Choose start and end revs of the ranges. To end up with ranges evenly
+   * distributed up to RANGELIST_TESTS_MAX_REV, we start with them evenly
+   * distributed up to RANGELIST_TESTS_MAX_REV - N_RANGES, before spreading. */
+  ascending_values(start_and_end_revs, n_ranges * 2,
+                   RANGELIST_TESTS_MAX_REV - n_ranges,
+                   seed);
+  /* Some values will be repeated. Within one range, that is not allowed,
+   * so add 1 to the length of each range, spreading the ranges out so they
+   * still don't overlap:
+   * [(6,6), (6,8)] becomes [(6,7), (7, 10)] */
+  for (i = 0; i < n_ranges; i++)
     {
-      svn_rangelist_t *dup;
-      svn_error_t *err;
+      start_and_end_revs[i*2] += i;
+      start_and_end_revs[i*2 + 1] += i+1;
+    }
 
-      rangelist_random_non_validated(rl, seed, pool);
-      if (!rangelist_is_sorted(*rl))
-        continue;
-      dup = svn_rangelist_dup(*rl, pool);
-      if ((err = svn_rangelist__canonicalize(dup, pool)))
-        {
-          svn_error_clear(err);
-          continue;
-        }
-      break;
-    } while (1);
+  for (i = 0; i < n_ranges; i++)
+    {
+      svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange));
+
+      mrange->start = start_and_end_revs[i * 2];
+      mrange->end = start_and_end_revs[i * 2 + 1];
+      mrange->inheritable = rand_less_than(2, seed);
+      APR_ARRAY_PUSH(r, svn_merge_range_t *) = mrange;
+    }
+  *rl = r;
+
+  /* check postconditions */
+  {
+    svn_rangelist_t *dup;
+    svn_error_t *err;
+
+    SVN_ERR_ASSERT_NO_RETURN(rangelist_is_sorted(*rl));
+    dup = svn_rangelist_dup(*rl, pool);
+    err = svn_rangelist__canonicalize(dup, pool);
+    SVN_ERR_ASSERT_NO_RETURN(!err);
+  }
 }
 
 /* Generate a random rangelist that satisfies svn_rangelist__is_canonical().
@@ -2022,9 +2092,25 @@ rangelist_random_canonical(svn_rangelist
                            apr_uint32_t *seed,
                            apr_pool_t *pool)
 {
-  do {
-    rangelist_random_non_validated(rl, seed, pool);
-  } while (! svn_rangelist__is_canonical(*rl));
+  svn_rangelist_t *r;
+  int i;
+
+  rangelist_random_semi_canonical(&r, seed, pool);
+  for (i = 1; i < r->nelts; i++)
+    {
+      svn_merge_range_t *prev_mrange = APR_ARRAY_IDX(r, i-1, svn_merge_range_t 
*);
+      svn_merge_range_t *mrange = APR_ARRAY_IDX(r, i, svn_merge_range_t *);
+
+      /* to be canonical: adjacent ranges need differing inheritability */
+      if (mrange->start == prev_mrange->end)
+        {
+          mrange->inheritable = !prev_mrange->inheritable;
+        }
+    }
+  *rl = r;
+
+  /* check postconditions */
+  SVN_ERR_ASSERT_NO_RETURN(svn_rangelist__is_canonical(*rl));
 }
 
 static const char *
@@ -2108,9 +2194,11 @@ add_failure_mode(svn_error_t *err_chain,
 
   /* For deduplication, ignore case-specific data in certain messages. */
   if (strstr(message, "Unable to parse overlapping revision ranges '"))
-    message = "Unable to parse overlapping revision ranges '...";
+            message = "Unable to parse overlapping revision ranges '...";
   if (strstr(message, "wrong result: (c?"))
-    message = "wrong result: (c?...";
+            message = "wrong result: (c?...";
+  if (strstr(message, "svn_sort__array_insert2: Attempted insert at index "))
+            message = "svn_sort__array_insert2: Attempted insert at index ...";
 
   if (!svn_hash_gets(failure_modes, message))
     {
@@ -2173,6 +2261,9 @@ test_rangelist_merge_random_canonical_in
   return SVN_NO_ERROR;
 }
 
+/* Test svn_rangelist_merge2() with inputs that confirm to its doc-string.
+ * Fail if any errors are produced.
+ */
 static svn_error_t *
 test_rangelist_merge_random_semi_c_inputs(apr_pool_t *pool)
 {


Reply via email to