From a4b5a5f904fe9f21697cd8fc8998c7e6e86306af Mon Sep 17 00:00:00 2001
From: Carlo Zancanaro <carlo@zancanaro.id.au>
Date: Sun, 31 May 2015 23:47:38 +1000
Subject: Lots more updates

+ add a list generator, and some more number generators.
+ bugfix the assumption checking stuff: if it failed once it would pretty
  likely continue to fail!
+ write some simple actualy properties:
  - reverse . reverse = id
  - sort . sort = sort
---
 src/main/java/au/id/zancanaro/Generators.java      | 37 ++++++++++++++++---
 .../java/au/id/zancanaro/PropertyTestRunner.java   | 27 ++++++++------
 src/main/java/au/id/zancanaro/RoseTree.java        |  1 +
 src/test/java/au/id/zancanaro/PropertyTests.java   | 43 +++++++++++++++++-----
 4 files changed, 80 insertions(+), 28 deletions(-)

diff --git a/src/main/java/au/id/zancanaro/Generators.java b/src/main/java/au/id/zancanaro/Generators.java
index 5b025fa..852a290 100644
--- a/src/main/java/au/id/zancanaro/Generators.java
+++ b/src/main/java/au/id/zancanaro/Generators.java
@@ -1,6 +1,8 @@
 package au.id.zancanaro;
 
+import java.util.Arrays;
 import java.util.Iterator;
+import java.util.List;
 import java.util.function.Predicate;
 
 public class Generators {
@@ -15,28 +17,51 @@ public class Generators {
         };
     }
 
+    @SafeVarargs
+    public static <T> Generator<T> oneOf(Generator<T>... gens) {
+        return integer(0, gens.length).flatMap(index -> gens[index]);
+    }
+
     public static Generator<Integer> integer() {
+        return (random, size) -> integer(-size, size).generate(random, size);
+    }
+
+    public static Generator<Integer> natural() {
+        return (random, size) -> integer(0, size).generate(random, size);
+    }
+
+    public static Generator<Integer> integer(int lower, int upper) {
         return (random, size) -> {
-            int value = random.nextInt(size);
-            return new RoseTree<>(value, intShrinkingIterable(value));
+            int value = lower + random.nextInt(upper - lower);
+            int bound = lower > 0 ? lower : (upper < 0 ? upper : 0);
+            return new RoseTree<>(value, intShrinkingIterable(value, bound));
         };
     }
 
-    private static Iterable<RoseTree<Integer>> intShrinkingIterable(final int value) {
+    private static Iterable<RoseTree<Integer>> intShrinkingIterable(final int value, final int bound) {
         return () -> new Iterator<RoseTree<Integer>>() {
-            int curr = value;
+            int curr = value - bound;
 
             @Override
             public boolean hasNext() {
-                return curr > 0;
+                return curr != 0;
             }
 
             @Override
             public RoseTree<Integer> next() {
                 int prevCurr = curr;
                 curr = curr / 2;
-                return new RoseTree<>(value - prevCurr, intShrinkingIterable(value - prevCurr));
+                return new RoseTree<>(value - prevCurr, intShrinkingIterable(value - prevCurr, bound));
             }
         };
     }
+
+    public static <T> Generator<List<T>> listOf(Generator<T> gen) {
+        return (random, size) -> integer(0, size).flatMap(count -> {
+            @SuppressWarnings("unchecked")
+            Generator<T>[] gens = (Generator<T>[]) new Generator[count];
+            Arrays.fill(gens, gen);
+            return Generator.tuple(gens).fmap(Arrays::asList);
+        }).generate(random, size);
+    }
 }
diff --git a/src/main/java/au/id/zancanaro/PropertyTestRunner.java b/src/main/java/au/id/zancanaro/PropertyTestRunner.java
index b3563ff..ea36341 100644
--- a/src/main/java/au/id/zancanaro/PropertyTestRunner.java
+++ b/src/main/java/au/id/zancanaro/PropertyTestRunner.java
@@ -178,7 +178,7 @@ public class PropertyTestRunner extends BlockJUnit4ClassRunner {
                         runTest(tree.getValue());
                         assumptionsViolated = 0;
                     } catch (AssumptionViolatedException ex) {
-                        i--;
+                        numTests++;
                         if (assumptionsViolated++ == 50) {
                             throw new Error("Violated 50 assumptions in a row: failing test");
                         }
@@ -193,19 +193,22 @@ public class PropertyTestRunner extends BlockJUnit4ClassRunner {
         private ShrinkResult shrink(RoseTree<Object[]> failed, Throwable originalEx) {
             ShrinkResult smallest = new ShrinkResult(failed.getValue(), originalEx);
             Iterator<RoseTree<Object[]>> trees = failed.getChildren();
+            Set<List<Object>> seenArgs = new HashSet<>();
             while (trees.hasNext()) {
                 RoseTree<Object[]> tree = trees.next();
-                try {
-                    runTest(tree.getValue());
-                } catch (AssumptionViolatedException ex) {
-                    // ignore, because it's not useful
-                } catch (Throwable ex) {
-                    smallest = new ShrinkResult(tree.getValue(), ex);
-                    Iterator<RoseTree<Object[]>> children = tree.getChildren();
-                    if (children.hasNext()) {
-                        trees = children;
-                    } else {
-                        break;
+                if (seenArgs.add(Arrays.asList(tree.getValue()))) {
+                    try {
+                        runTest(tree.getValue());
+                    } catch (AssumptionViolatedException ex) {
+                        // ignore, because it's not useful
+                    } catch (Throwable ex) {
+                        smallest = new ShrinkResult(tree.getValue(), ex);
+                        Iterator<RoseTree<Object[]>> children = tree.getChildren();
+                        if (children.hasNext()) {
+                            trees = children;
+                        } else {
+                            break;
+                        }
                     }
                 }
             }
diff --git a/src/main/java/au/id/zancanaro/RoseTree.java b/src/main/java/au/id/zancanaro/RoseTree.java
index bbcb949..6ba5c1c 100644
--- a/src/main/java/au/id/zancanaro/RoseTree.java
+++ b/src/main/java/au/id/zancanaro/RoseTree.java
@@ -1,5 +1,6 @@
 package au.id.zancanaro;
 
+import java.io.OutputStream;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Iterator;
diff --git a/src/test/java/au/id/zancanaro/PropertyTests.java b/src/test/java/au/id/zancanaro/PropertyTests.java
index 374a974..dd595e1 100644
--- a/src/test/java/au/id/zancanaro/PropertyTests.java
+++ b/src/test/java/au/id/zancanaro/PropertyTests.java
@@ -1,35 +1,58 @@
 package au.id.zancanaro;
 
 import au.id.zancanaro.annotations.Property;
+import au.id.zancanaro.annotations.Seed;
+import org.hamcrest.CoreMatchers;
 import org.junit.*;
 import org.junit.experimental.theories.DataPoint;
+import org.junit.matchers.JUnitMatchers;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 @RunWith(PropertyTestRunner.class)
 public class PropertyTests {
     public static Generator<Integer> gen = Generators.integer();
+    public static Generator<List<Integer>> list = Generators.listOf(Generators.integer());
 
-    @Test
-    public void testblah() throws Exception {
-        Assert.assertEquals(10, 11);
-
-    }
-
+    @Ignore
     @Property
     public void aIsNotOdd(int a, int b) {
-        Assume.assumeFalse(a % 2 == 1);
         Assert.assertFalse(a % 2 == 1);
     }
 
+    @Ignore
     @Property
     public void aIsNotLessThanB(int a, int b) {
-        Assume.assumeFalse(a < b);
         Assert.assertFalse(a < b);
     }
 
+    @Ignore
     @Property
     public void aPlusBLessThanOneHundred(int a, int b) {
-        Assert.assertTrue("a plus b is five", a + b == 5);
-        Assert.assertEquals(a + b, 5);
+        Assert.assertTrue(a + b < 100);
+    }
+
+    @Property
+    public void sortingIsIdempotent(List<Integer> list) {
+        List<Integer> left = new ArrayList<>(list);
+        Collections.sort(left);
+
+        List<Integer> right = new ArrayList<>(list);
+        Collections.sort(right);
+        Collections.sort(right);
+
+        Assert.assertEquals(left, right);
+    }
+
+    @Property
+    public void reverseIsItsOwnInverse(List<Integer> list) {
+        List<Integer> reversed = new ArrayList<>(list);
+        Collections.reverse(reversed);
+        Collections.reverse(reversed);
+
+        Assert.assertEquals(list, reversed);
     }
 }
-- 
cgit v1.2.3