├── .gitignore
├── LICENSE
├── README.rst
├── pom.xml
└── src
├── main
└── java
│ └── works
│ └── hypothesis
│ ├── ByteUtils.java
│ ├── DistributionalTestData.java
│ ├── Flaky.java
│ ├── FloatRangeGenerator.java
│ ├── Frozen.java
│ ├── Hypothesis.java
│ ├── HypothesisDataDistribution.java
│ ├── HypothesisException.java
│ ├── HypothesisPredicate.java
│ ├── HypothesisSettings.java
│ ├── HypothesisTestFunction.java
│ ├── HypothesisUniformDataDistributions.java
│ ├── Interval.java
│ ├── NoSuchExample.java
│ ├── Status.java
│ ├── StopTest.java
│ ├── TestData.java
│ ├── TestDataForBuffer.java
│ ├── TestDataRule.java
│ ├── TestRunner.java
│ └── strategies
│ ├── BooleanStrategy.java
│ ├── BytesStrategy.java
│ ├── IntegerStrategy.java
│ ├── ListStrategy.java
│ ├── Strategies.java
│ └── Strategy.java
└── test
└── java
└── works
└── hypothesis
├── TestBooleansAndIntegers.java
├── TestByteUtils.java
├── TestDataRuleTest.java
├── TestExampleQuality.java
├── TestFloatRange.java
├── TestSortingAList.java
└── TestTestData.java
/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | *.swp
3 | *.swo
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) 2016 David R. MacIver
2 |
3 | This program is free software: you can redistribute it and/or modify
4 | it under the terms of the GNU Affero General Public License as published by
5 | the Free Software Foundation, either version 3 of the License, or
6 | (at your option) any later version.
7 |
8 | This program is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU Affero General Public License for more details.
12 |
13 | You should have received a copy of the GNU Affero General Public License
14 | along with this program. If not, see .
15 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | =========================================
2 | Hypothesis for Java feasibility prototype
3 | =========================================
4 |
5 | `Hypothesis `_ is a modern property based testing system designed for
6 | mainstream languages. The original version is for Python, where it works extremely well.
7 |
8 | This is a very rough prototype of what Hypothesis could look like in Java. It's not even close to being production
9 | ready software. It implements the core algorithm and a very crude API. It has only been lightly tested. It exists to
10 | prove that the concept is feasible, and it achieves that.
11 |
12 | Things that it currently supports:
13 |
14 | 1. The core Hypothesis engine with example generation and shrinking.
15 | 2. Working JUnit integration.
16 | 3. A very small prototype data generator library.
17 |
18 | Things that are currently unimplemented:
19 |
20 | 1. The example database. The model used is the Hypothesis one where data is represented in an easy to serialize format,
21 | so implementing this is a Simple Matter Of Code, but it doesn't affect the feasibility so I haven't bothered yet.
22 | 2. The example mutator and shrinker are significantly less sophisticated than the Python one. There's no technical
23 | reason for this to be case, they're just based on an earlier prototype of the concept that works well enough for a
24 | tech demo.
25 | 3. Most of the data generation library. This is in many ways where the bulk of the work in porting an implementation
26 | lies.
27 | 4. Decent example printing. Right now it just uses toString, which is not ideal. Additionally it's not obvious what
28 | the best way to hook printing into JUnit is.
29 | 5. A license that you would want to use it under.
30 |
31 | Example usage
32 | -------------
33 |
34 | The Hypothesis Java prototype is build on JUnit's `Rules `_
35 | functionality.
36 |
37 | You use a TestDataRule as a source of data. Within a test you can then call draw on it with a strategy
38 | argument to get an example drawn from that strategy. You can call draw as many times as you want for
39 | tests that need multiple values.
40 |
41 | Here's an example of testing a sorting function using this:
42 |
43 | .. code-block:: java
44 |
45 | import org.junit.Rule;
46 | import org.junit.Test;
47 |
48 | import java.util.Comparator;
49 | import java.util.Iterator;
50 | import java.util.List;
51 |
52 | import static com.drmaciver.hypothesis.generators.Generators.integers;
53 | import static com.drmaciver.hypothesis.generators.Generators.lists;
54 | import static org.junit.Assert.assertTrue;
55 |
56 | public class TestSortingAList {
57 | @Rule
58 | public final TestDataRule data = new TestDataRule();
59 |
60 | @Test
61 | public void testIsSortedAfterSorting(){
62 | List ls = data.draw(lists(integers()));
63 | ls.sort(Comparator.naturalOrder());
64 | assertSorted(ls);
65 | }
66 |
67 | // Utility assertion function. Doesn't use any Hypothesis functionality.
68 | private > void assertSorted(List elements){
69 | if(elements.isEmpty()) return;
70 | Iterator it = elements.iterator();
71 | T previous = it.next();
72 | while(it.hasNext()){
73 | T current = it.next();
74 | assertTrue(previous.compareTo(current) <= 0);
75 | previous = current;
76 | }
77 | }
78 |
79 | }
80 |
81 |
82 | Licensing
83 | ---------
84 |
85 | This is currently under the AGPL. That is not intended as a long term licensing strategy but gives me more
86 | flexibility for future plans. The most likely future licensing path is that there will be a core version available
87 | under the MPLv2 or Apache, with a proprietary extension library containing most of the strategies and some useful
88 | extensions. I could be persuaded to make the whole thing open source under Apache/MPLv2 but the persuasion would have
89 | to be financial.
90 |
91 | Future Plans
92 | ------------
93 |
94 | I'm not very interested in supporting another fully fledged property based testing implementation of the calibre
95 | of the Python version of Hypothesis for free. As such this library is likely to remain a prototype without some
96 | financial backing to change that, in the form of paid development and/or ongoing support contracts.
97 |
98 | If you are interested in a fully fledged Hypothesis for Java, please `contact me `_ to
99 | discuss terms.
100 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | Hypothesis
4 | Hypothesis
5 | 0.0.1-SNAPSHOT
6 |
7 | src/main/java
8 | src/test/java
9 |
10 |
11 | maven-compiler-plugin
12 | 3.3
13 |
14 | 1.8
15 | 1.8
16 |
17 |
18 |
19 |
20 |
21 |
22 | junit
23 | junit
24 | 4.12
25 | compile
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/ByteUtils.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | public class ByteUtils {
4 | static byte[] deleteInterval(byte[] x, int start, int end) {
5 | final byte[] result = new byte[x.length - (end - start)];
6 | System.arraycopy(x, 0, result, 0, start);
7 | System.arraycopy(x, end, result, start, x.length - end);
8 | return result;
9 | }
10 |
11 | static byte[] sortInterval(byte[] x, int start, int end) {
12 | assert start <= end;
13 | assert end <= x.length;
14 | final byte[] result = x.clone();
15 | final int[] counter = new int[256];
16 | for (int i = start; i < end; i++) {
17 | final int b = unsigned(x[i]);
18 | counter[b]++;
19 | }
20 | int index = start;
21 | for (int i = 0; i < 256; i++) {
22 | for (int j = 0; j < counter[i]; j++)
23 | result[index++] = (byte) i;
24 | }
25 | return result;
26 | }
27 |
28 | public static byte[] swap(byte[] buffer, int i, int j) {
29 | final byte[] result = buffer.clone();
30 | final byte c = result[i];
31 | result[i] = result[j];
32 | result[j] = c;
33 | return result;
34 | }
35 |
36 | static int unsigned(byte b) {
37 | return b & 0xff;
38 | }
39 |
40 | static byte[] zeroInterval(byte[] x, int start, int end) {
41 | final byte[] result = x.clone();
42 | for (int i = start; i < end; i++)
43 | result[i] = (byte) 0;
44 | return result;
45 | }
46 |
47 | public static int intFromBytes(byte[] bytes, int i) {
48 | int result = 0;
49 | for (int j = 0; j < 4; j++) {
50 | result *= 256;
51 | result += ByteUtils.unsigned(bytes[i + j]);
52 | }
53 | return result;
54 | }
55 |
56 | public static void intToBytes(byte[] bytes, int index, int value) {
57 | for (int j = 3; j >= 0; j--) {
58 | bytes[index + j] = (byte) (value & 0xff);
59 | value >>>= 8;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/DistributionalTestData.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | import java.util.Random;
4 |
5 | /**
6 | * Created by david on 4/9/16.
7 | */
8 | public class DistributionalTestData extends TestData {
9 | private final Random random;
10 |
11 | public DistributionalTestData(int maxSize, Random random) {
12 | super(maxSize);
13 | this.random = random;
14 | }
15 |
16 | @Override
17 | protected byte[] doDrawBytes(int n, HypothesisDataDistribution distribution) {
18 | byte[] result = new byte[n];
19 | distribution.generateData(random, result, 0, n);
20 | return result;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/Flaky.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | public class Flaky extends RuntimeException {
4 |
5 | /**
6 | *
7 | */
8 | private static final long serialVersionUID = 1L;
9 |
10 | public Flaky(String string) {
11 | super(string);
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/FloatRangeGenerator.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | import works.hypothesis.strategies.Strategy;
4 |
5 | import java.util.Random;
6 |
7 | /**
8 | * Created by david on 4/9/16.
9 | */
10 | public class FloatRangeGenerator implements Strategy {
11 | private final float right;
12 | private final float left;
13 | private final HypothesisDataDistribution distribution;
14 |
15 | FloatRangeGenerator(float left, float right) {
16 | this.left = left;
17 | this.right = right;
18 | this.distribution = new HypothesisDataDistribution() {
19 |
20 | @Override
21 | public void generateData(Random random, byte[] target, int index, int nbytes) {
22 | if (nbytes != 4) {
23 | throw new RuntimeException("Bad number of bytes " + nbytes);
24 | }
25 | float f = random.nextFloat() * (right - left) + left;
26 | ByteUtils.intToBytes(target, index, Float.floatToIntBits(f));
27 | }
28 | };
29 | }
30 |
31 |
32 | @Override
33 | public Float doDraw(TestData data) {
34 | byte[] value = data.drawBytes(4, this.distribution);
35 | float f = Float.intBitsToFloat(ByteUtils.intFromBytes(value, 0));
36 | data.assume(f >= left);
37 | data.assume(f <= right);
38 | return f;
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/Frozen.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | /**
4 | * Created by david on 4/9/16.
5 | */
6 | class Frozen extends HypothesisException {
7 | /**
8 | * A mutation method has been called on a frozen TestDataForBuffer.
9 | */
10 | private static final long serialVersionUID = 1L;
11 |
12 | public Frozen(String arg0) {
13 | super(arg0);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/Hypothesis.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | import works.hypothesis.strategies.Strategy;
4 |
5 | public class Hypothesis {
6 | public static T find(Strategy generator, HypothesisPredicate condition) throws NoSuchExample {
7 | return Hypothesis.find(generator, condition, null);
8 | }
9 |
10 | public static T find(final Strategy generator, final HypothesisPredicate condition,
11 | final HypothesisSettings settings) throws NoSuchExample {
12 | final byte[] buffer = TestRunner.findInterestingBuffer(new HypothesisTestFunction() {
13 | public void runTest(TestData data) {
14 | final T value = generator.doDraw(data);
15 | if (condition.test(value)) {
16 | data.markInteresting();
17 | }
18 | }
19 | }, settings);
20 | if (buffer == null) {
21 | throw new NoSuchExample();
22 | } else {
23 | try {
24 | final T result = generator.doDraw(new TestDataForBuffer(buffer));
25 | if (!condition.test(result)) {
26 | throw new Flaky("Result " + result + " did not satisfy condition.");
27 | }
28 | return result;
29 | } catch (StopTest e) {
30 | throw new Flaky("Generator drew a different amount of data on retry");
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/HypothesisDataDistribution.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | import java.util.Random;
4 |
5 | /**
6 | * Created by david on 4/9/16.
7 | */
8 | public interface HypothesisDataDistribution {
9 | void generateData(Random random, byte[] target, int index, int nbytes);
10 |
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/HypothesisException.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | public class HypothesisException extends RuntimeException {
4 |
5 | /**
6 | *
7 | */
8 | private static final long serialVersionUID = 1L;
9 |
10 | public HypothesisException() {
11 | super();
12 | }
13 |
14 | public HypothesisException(String arg0) {
15 | super(arg0);
16 | }
17 |
18 | public HypothesisException(String arg0, Throwable arg1) {
19 | super(arg0, arg1);
20 | }
21 |
22 | public HypothesisException(String arg0, Throwable arg1, boolean arg2, boolean arg3) {
23 | super(arg0, arg1, arg2, arg3);
24 | }
25 |
26 | public HypothesisException(Throwable arg0) {
27 | super(arg0);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/HypothesisPredicate.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | public interface HypothesisPredicate {
4 | boolean test(T value);
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/HypothesisSettings.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | public class HypothesisSettings {
4 | private int bufferSize = 8 * 1024;
5 |
6 | private int mutations = 50;
7 |
8 | private int maxExamples = 200;
9 |
10 | private int maxShrinks = 2000;
11 |
12 | private boolean debug = false;
13 |
14 | public int getBufferSize() {
15 | return bufferSize;
16 | }
17 |
18 | public int getMaxExamples() {
19 | return maxExamples;
20 | }
21 |
22 | public int getMaxShrinks() {
23 | return maxShrinks;
24 | }
25 |
26 | public int getMutations() {
27 | return mutations;
28 | }
29 |
30 | public boolean isDebug() {
31 | return debug;
32 | }
33 |
34 | public void setBufferSize(int bufferSize) {
35 | this.bufferSize = bufferSize;
36 | }
37 |
38 | public void setDebug(boolean debug) {
39 | this.debug = debug;
40 | }
41 |
42 | public void setMaxExamples(int maxExamples) {
43 | this.maxExamples = maxExamples;
44 | }
45 |
46 | public void setMaxShrinks(int maxShrinks) {
47 | this.maxShrinks = maxShrinks;
48 | }
49 |
50 | public void setMutations(int mutations) {
51 | this.mutations = mutations;
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/HypothesisTestFunction.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | public interface HypothesisTestFunction {
4 | void runTest(TestData data);
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/HypothesisUniformDataDistributions.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | import java.util.Random;
4 |
5 | /**
6 | * Created by david on 4/9/16.
7 | */
8 | public class HypothesisUniformDataDistributions implements HypothesisDataDistribution {
9 | public static final HypothesisUniformDataDistributions INSTANCE = new HypothesisUniformDataDistributions();
10 |
11 | @Override
12 | public void generateData(Random random, byte[] target, int index, int nbytes) {
13 | for (int j = 0; j < nbytes; j++) {
14 | target[index + j] = (byte) random.nextInt(256);
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/Interval.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | /**
4 | * Created by david on 4/9/16.
5 | */
6 | class Interval implements Comparable {
7 | final int start;
8 |
9 | final int end;
10 |
11 | public Interval(int start, int end) {
12 | super();
13 | this.start = start;
14 | this.end = end;
15 | }
16 |
17 | public int compareTo(Interval other) {
18 | if (length() < other.length())
19 | return -1;
20 | if (length() > other.length())
21 | return 1;
22 | if (start < other.start)
23 | return -1;
24 | if (start > other.start)
25 | return 1;
26 | return 0;
27 | }
28 |
29 | @Override
30 | public boolean equals(Object obj) {
31 | if (this == obj)
32 | return true;
33 | if (obj == null)
34 | return false;
35 | if (getClass() != obj.getClass())
36 | return false;
37 | final Interval other = (Interval) obj;
38 | return end == other.end && start == other.start;
39 | }
40 |
41 | @Override
42 | public int hashCode() {
43 | final int prime = 31;
44 | int result = 1;
45 | result = prime * result + end;
46 | result = prime * result + start;
47 | return result;
48 | }
49 |
50 | int length() {
51 | return end - start;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/NoSuchExample.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | public class NoSuchExample extends Exception {
4 |
5 | /**
6 | *
7 | */
8 | private static final long serialVersionUID = 1;
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/Status.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | /**
4 | * Created by david on 4/9/16.
5 | */
6 | enum Status {
7 | OVERRUN, INVALID, VALID, INTERESTING
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/StopTest.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | /**
4 | * Created by david on 4/9/16.
5 | */
6 | class StopTest extends HypothesisException {
7 | /**
8 | * A condition that ends the current test has occurred.
9 | */
10 | private static final long serialVersionUID = 1L;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/TestData.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | import works.hypothesis.strategies.Strategy;
4 |
5 | import java.util.ArrayList;
6 | import java.util.Arrays;
7 | import java.util.Collections;
8 | import java.util.List;
9 |
10 | /**
11 | * Created by david on 4/9/16.
12 | */
13 | public abstract class TestData implements Comparable {
14 | final List intervalStarts = new ArrayList();
15 | final List intervals = new ArrayList();
16 | byte[] record;
17 | boolean frozen = false;
18 | int index = 0;
19 | Status status = Status.VALID;
20 | int checksum = -1;
21 |
22 | public TestData(int maxSize) {
23 | this.record = new byte[maxSize];
24 | }
25 |
26 | @Override
27 | public String toString() {
28 | return "TestData{" +
29 | "record=" + Arrays.toString(record) +
30 | ", frozen=" + frozen +
31 | ", index=" + index +
32 | '}';
33 | }
34 |
35 | public int compareTo(TestData other) {
36 | if (!(frozen && other.frozen)) {
37 | throw new RuntimeException("Cannot compare non frozen TestDataForBuffer");
38 | }
39 | if (intervals.size() < other.intervals.size())
40 | return -1;
41 | if (intervals.size() > other.intervals.size())
42 | return 1;
43 | if (this.index < other.index)
44 | return -1;
45 | if (this.index > other.index)
46 | return 1;
47 | for (int i = 0; i < index; i++) {
48 | final int c = ByteUtils.unsigned(record[i]);
49 | final int d = ByteUtils.unsigned(other.record[i]);
50 | if (c < d)
51 | return -1;
52 | if (c > d)
53 | return 1;
54 | }
55 | return 0;
56 | }
57 |
58 | private void assertNotFrozen(String name) {
59 | if (frozen) {
60 | throw new Frozen("Cannot call " + name + " on a frozen TestDataForBuffer.");
61 | }
62 | }
63 |
64 | public T draw(Strategy generator) {
65 | startExample();
66 | final T result = generator.doDraw(this);
67 | stopExample();
68 | return result;
69 | }
70 |
71 | public byte drawByte() {
72 | return drawBytes(1)[0];
73 | }
74 |
75 | public byte[] drawBytes(int n) {
76 | return this.drawBytes(n, HypothesisUniformDataDistributions.INSTANCE);
77 | }
78 |
79 | public byte[] drawBytes(int n, HypothesisDataDistribution distribution) {
80 | assertNotFrozen("drawBytes");
81 | startExample();
82 | if (index + n > record.length) {
83 | status = Status.OVERRUN;
84 | freeze();
85 | throw new StopTest();
86 | }
87 | final byte[] result = doDrawBytes(n, distribution);
88 | System.arraycopy(result, 0, record, index, n);
89 | index += n;
90 | stopExample();
91 | return result;
92 | }
93 |
94 | void checkIntegrity() {
95 | if (!frozen) return;
96 | final int hashCode = Arrays.hashCode(record);
97 | if (hashCode != checksum) {
98 | throw new RuntimeException("TestData.record has been modified.");
99 | }
100 | }
101 |
102 | protected abstract byte[] doDrawBytes(int n, HypothesisDataDistribution distribution);
103 |
104 | public void freeze() {
105 | if (frozen)
106 | return;
107 | frozen = true;
108 | record = Arrays.copyOfRange(record, 0, index);
109 | checksum = Arrays.hashCode(record);
110 | Collections.sort(intervals);
111 | }
112 |
113 | public Status getStatus() {
114 | return status;
115 | }
116 |
117 | public int index() {
118 | return index;
119 | }
120 |
121 | public boolean isFrozen() {
122 | return frozen;
123 | }
124 |
125 | public void markInteresting() {
126 | assertNotFrozen("markInteresting");
127 | if (status == Status.VALID) {
128 | status = Status.INTERESTING;
129 | }
130 | throw new StopTest();
131 | }
132 |
133 | public void markInvalid() {
134 | assertNotFrozen("markInvalid");
135 | if (status == Status.VALID) {
136 | status = Status.INVALID;
137 | }
138 | throw new StopTest();
139 | }
140 |
141 | public void assume(boolean condition) {
142 | if (!condition) this.markInvalid();
143 | }
144 |
145 | public void startExample() {
146 | assertNotFrozen("startExample");
147 | intervalStarts.add(index);
148 | }
149 |
150 | public void stopExample() {
151 | assertNotFrozen("stopExample");
152 | final int k = intervalStarts.remove(intervalStarts.size() - 1);
153 | if (k != index) {
154 | final Interval interval = new Interval(k, index);
155 | if (intervals.size() == 0 || intervals.get(intervals.size() - 1) != interval)
156 | intervals.add(interval);
157 | }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/TestDataForBuffer.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | import java.util.Arrays;
4 |
5 | public class TestDataForBuffer extends TestData {
6 |
7 | private final byte[] buffer;
8 |
9 | public TestDataForBuffer(byte[] buffer) {
10 | super(buffer.length);
11 | this.buffer = buffer.clone();
12 | }
13 |
14 | @Override
15 | protected byte[] doDrawBytes(int n, HypothesisDataDistribution distribution) {
16 | return Arrays.copyOfRange(buffer, index(), index() + n);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/TestDataRule.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | import works.hypothesis.strategies.Strategy;
4 | import org.junit.AssumptionViolatedException;
5 | import org.junit.rules.TestRule;
6 | import org.junit.runner.Description;
7 | import org.junit.runners.model.Statement;
8 |
9 | import java.util.logging.Logger;
10 |
11 | public class TestDataRule extends Object implements TestRule {
12 | private final HypothesisSettings settings;
13 |
14 | private TestData data = null;
15 |
16 | private Throwable lastError = null;
17 |
18 | private Logger logger;
19 | private int index = 0;
20 |
21 | public TestDataRule() {
22 | this(new HypothesisSettings());
23 | }
24 |
25 | public TestDataRule(HypothesisSettings settings) {
26 | super();
27 | this.settings = settings;
28 | }
29 |
30 | public Statement apply(final Statement base, final Description description) {
31 | return new Statement() {
32 | @Override
33 | public void evaluate() throws Throwable {
34 | logger = null;
35 | index = 0;
36 | final TestRunner runner = new TestRunner(new HypothesisTestFunction() {
37 | public void runTest(TestData d) {
38 | data = d;
39 | try {
40 | base.evaluate();
41 | } catch (final AssumptionViolatedException e) {
42 | d.markInvalid();
43 | } catch (final HypothesisException e) {
44 | throw e;
45 | } catch (final Throwable t) {
46 | if (!d.isFrozen()) {
47 | lastError = t;
48 | d.markInteresting();
49 | }
50 | }
51 | }
52 | }, settings);
53 | runner.run();
54 | if (runner.lastData.getStatus() == Status.INTERESTING) {
55 | logger = Logger
56 | .getLogger(description.getTestClass().getName() + '.' + description.getDisplayName());
57 | index = 0;
58 | assert lastError != null;
59 | data = new TestDataForBuffer(runner.lastData.record);
60 | base.evaluate();
61 | throw new Flaky("Expected error: " + lastError.toString());
62 | }
63 | }
64 | };
65 | }
66 |
67 | public T draw(Strategy generator) {
68 | final T result = getData().draw(generator);
69 | final String string = result.toString();
70 | if (logger != null) {
71 | logger.info("Draw #" + (++index) + ": " + string);
72 | }
73 | return result;
74 | }
75 |
76 | TestData getData() {
77 | assert data != null;
78 | return data;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/TestRunner.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.List;
6 | import java.util.Random;
7 |
8 | class TestRunner {
9 | final Random random;
10 | final HypothesisTestFunction _testFunction;
11 | final HypothesisSettings settings;
12 | int changed = 0;
13 | int shrinks = 0;
14 | int validExamples = 0;
15 | TestData lastData = null;
16 |
17 | public TestRunner(HypothesisTestFunction _testFunction, HypothesisSettings settings) {
18 | super();
19 | this._testFunction = _testFunction;
20 | if (settings == null)
21 | settings = new HypothesisSettings();
22 | this.settings = settings;
23 | random = new Random();
24 | }
25 |
26 | static byte[] findInterestingBuffer(HypothesisTestFunction test, HypothesisSettings settings) {
27 | final TestRunner runner = new TestRunner(test, settings);
28 | runner.run();
29 | if (runner.lastData.getStatus() == Status.INTERESTING) {
30 | runner.lastData.checkIntegrity();
31 | assert runner.lastData.frozen;
32 | return runner.lastData.record;
33 | }
34 | return null;
35 | }
36 |
37 | private void _run() {
38 | newBuffer();
39 | int mutations = 0;
40 | int generation = 0;
41 | while (validExamples <= settings.getMaxExamples() && lastData.getStatus() != Status.INTERESTING && generation <= settings.getMutations()) {
42 | if (mutations >= settings.getMutations()) {
43 | generation += 1;
44 | mutations = 0;
45 | incorporateNewBuffer(mutateDataToNewBuffer());
46 | } else {
47 | newBuffer();
48 | }
49 | }
50 | if (lastData.getStatus() != Status.INTERESTING) {
51 | return;
52 | }
53 |
54 | // We have successfully found an interesting buffer and shrinking starts
55 | // here
56 |
57 | int changeCounter = -1;
58 | while (changed > changeCounter) {
59 | changeCounter = changed;
60 | assert lastData.getStatus() == Status.INTERESTING;
61 | for (int i = 0; i < lastData.intervals.size(); ) {
62 | final Interval interval = lastData.intervals.get(i);
63 | if (!incorporateNewBuffer(ByteUtils.deleteInterval(lastData.record, interval.start, interval.end))) {
64 | i++;
65 | }
66 | }
67 | if (changed > changeCounter)
68 | continue;
69 | for (int i = 0; i < lastData.intervals.size(); i++) {
70 | final Interval interval = lastData.intervals.get(i);
71 | incorporateNewBuffer(ByteUtils.zeroInterval(lastData.record, interval.start, interval.end));
72 | }
73 | for (int i = 0; i < lastData.intervals.size(); i++) {
74 | final Interval interval = lastData.intervals.get(i);
75 | incorporateNewBuffer(ByteUtils.sortInterval(lastData.record, interval.start, interval.end));
76 | }
77 | if (changed > changeCounter)
78 | continue;
79 |
80 | for (int i = 0; i < lastData.record.length - 8; i++) {
81 | incorporateNewBuffer(ByteUtils.zeroInterval(lastData.record, i, i + 8));
82 | }
83 | for (int i = 0; i < lastData.record.length; i++) {
84 | if (lastData.record[i] == 0)
85 | continue;
86 | final byte[] buf = lastData.record.clone();
87 | buf[i]--;
88 | if (!incorporateNewBuffer(buf))
89 | break;
90 | for (int c = 0; c < ByteUtils.unsigned(lastData.record[i]) - 1; c++) {
91 | buf[i] = (byte) c;
92 | if (incorporateNewBuffer(buf))
93 | break;
94 | }
95 | }
96 |
97 | for (int i = 0; i < lastData.record.length - 1; i++) {
98 | incorporateNewBuffer(ByteUtils.sortInterval(lastData.record, i, i + 2));
99 | }
100 | if (changed > changeCounter)
101 | continue;
102 | for (int i = 0; i < lastData.record.length; i++) {
103 | incorporateNewBuffer(ByteUtils.deleteInterval(lastData.record, i, i + 1));
104 | }
105 | for (int i = 0; i < lastData.record.length; i++) {
106 | if (lastData.record[i] == 0) {
107 | final byte[] buf = lastData.record.clone();
108 | for (int j = i; j >= 0; j--) {
109 | if (buf[j] != 0) {
110 | buf[j]--;
111 | incorporateNewBuffer(buf);
112 | break;
113 | } else {
114 | buf[j] = (byte) 255;
115 | }
116 | }
117 | }
118 | }
119 | if (changed > changeCounter)
120 | continue;
121 | final List> buckets = new ArrayList>();
122 | for (int i = 0; i < 256; i++)
123 | buckets.add(new ArrayList());
124 | for (int i = 0; i < lastData.record.length; i++) {
125 | buckets.get(ByteUtils.unsigned(lastData.record[i])).add(i);
126 | }
127 | for (final List bucket : buckets) {
128 | for (final int j : bucket) {
129 | for (final int k : bucket) {
130 | if (j < k) {
131 | final byte[] buf = lastData.record.clone();
132 | if (buf[j] == buf[k]) {
133 | if (buf[j] == 0) {
134 | if (j > 0 && buf[j - 1] != 0 && buf[k - 1] != 0) {
135 | buf[j - 1]--;
136 | buf[k - 1]--;
137 | buf[j] = (byte) 255;
138 | buf[k] = (byte) 255;
139 | incorporateNewBuffer(buf);
140 | continue;
141 | }
142 | } else {
143 | for (byte c = 0; c < buf[j]; c++) {
144 | buf[j] = c;
145 | buf[k] = c;
146 | if (incorporateNewBuffer(buf))
147 | break;
148 | }
149 | }
150 | }
151 | }
152 | }
153 | }
154 | }
155 | if (changed > changeCounter)
156 | continue;
157 | for (int i = 0; i < lastData.record.length; i++) {
158 | if (lastData.record[i] == 0)
159 | continue;
160 | for (int j = i + 1; j < lastData.record.length; j++) {
161 | if (ByteUtils.unsigned(lastData.record[i]) > ByteUtils.unsigned(lastData.record[j])) {
162 | incorporateNewBuffer(ByteUtils.swap(lastData.record, i, j));
163 | }
164 | if (lastData.record[i] != 0 && lastData.record[j] != 0) {
165 | final byte[] buf = lastData.record.clone();
166 | buf[i]--;
167 | buf[j]--;
168 | incorporateNewBuffer(buf);
169 | }
170 | }
171 | }
172 | }
173 | }
174 |
175 | boolean considerNewTestData(TestData data) {
176 | if (lastData.getStatus().compareTo(data.getStatus()) < 0) {
177 | return true;
178 | }
179 | if (lastData.getStatus().compareTo(data.getStatus()) > 0) {
180 | return false;
181 | }
182 | if (data.getStatus() == Status.INVALID) {
183 | return data.index() >= lastData.index();
184 | }
185 | if (data.getStatus() == Status.OVERRUN) {
186 | return data.index() <= lastData.index();
187 | }
188 | return data.getStatus() != Status.INTERESTING || lastData.compareTo(data) > 0;
189 | }
190 |
191 | boolean incorporateNewBuffer(byte[] buffer) {
192 | assert buffer.length <= settings.getBufferSize();
193 | if (Arrays.equals(buffer, lastData.record))
194 | return false;
195 | final TestData data = new TestDataForBuffer(buffer);
196 | testFunction(data);
197 | if (considerNewTestData(data)) {
198 | if (lastData.getStatus() == Status.INTERESTING) {
199 | shrinks++;
200 | }
201 | changed++;
202 | lastData = data;
203 | if (settings.isDebug())
204 | if (shrinks >= settings.getMaxShrinks())
205 | throw new StopShrinking();
206 | return true;
207 | }
208 | return false;
209 | }
210 |
211 | private byte[] mutateDataToNewBuffer() {
212 | final int n = Math.min(lastData.record.length, lastData.index());
213 | if (n == 0) {
214 | return new byte[0];
215 | }
216 | if (n == 1) {
217 | final byte[] result = new byte[1];
218 | random.nextBytes(result);
219 | return result;
220 | }
221 | final byte[] result = lastData.record.clone();
222 | if (lastData.getStatus() == Status.OVERRUN) {
223 | for (int i = 0; i < result.length; i++) {
224 | if (result[i] == 0)
225 | continue;
226 | switch (random.nextInt(3)) {
227 | case 0:
228 | result[i] = 0;
229 | break;
230 | case 1:
231 | result[i] = (byte) random.nextInt(ByteUtils.unsigned(result[i]));
232 | break;
233 | case 2:
234 | continue;
235 | }
236 | }
237 | return result;
238 | }
239 | if (lastData.intervals.size() <= 1 || random.nextInt(3) == 0) {
240 | int u, v;
241 | if (random.nextBoolean() || lastData.intervals.size() <= 1) {
242 | u = random.nextInt(lastData.record.length);
243 | v = u + random.nextInt(lastData.record.length - u);
244 | } else {
245 | final Interval in = lastData.intervals.get(random.nextInt(lastData.intervals.size()));
246 | u = in.start;
247 | v = in.end;
248 | }
249 | switch (random.nextInt(3)) {
250 | case 0:
251 | for (int i = u; i < v; i++) {
252 | result[i] = 0;
253 | }
254 | break;
255 | case 1:
256 | for (int i = u; i < v; i++) {
257 | result[i] = (byte) 255;
258 | }
259 | break;
260 | case 2:
261 | for (int i = u; i < v; i++) {
262 | result[i] = (byte) random.nextInt(256);
263 | }
264 | break;
265 | }
266 | } else {
267 | final int i = random.nextInt(lastData.intervals.size() - 1);
268 | final int j = i + 1 + random.nextInt(lastData.intervals.size() - 1 - i);
269 | final Interval int1 = lastData.intervals.get(i);
270 | final Interval int2 = lastData.intervals.get(j);
271 | assert int2.length() <= int1.length();
272 | System.arraycopy(lastData.record, int2.start, result, int1.start, int2.length());
273 | if (int1.length() != int2.length()) {
274 | System.arraycopy(lastData.record, int1.end, result, int1.start + int2.length(),
275 | lastData.record.length - int1.end);
276 | }
277 | }
278 | return result;
279 | }
280 |
281 | void newBuffer() {
282 | final TestData data = new DistributionalTestData(settings.getBufferSize(), this.random);
283 | testFunction(data);
284 | data.freeze();
285 | lastData = data;
286 | }
287 |
288 | void run() {
289 | try {
290 | _run();
291 | } catch (final StopShrinking ignored) {
292 |
293 | }
294 | }
295 |
296 | private void testFunction(TestData data) {
297 | try {
298 | _testFunction.runTest(data);
299 | } catch (final StopTest ignored) {
300 | }
301 | data.freeze();
302 | if (data.status.compareTo(Status.VALID) >= 0)
303 | validExamples++;
304 | }
305 |
306 | static class StopShrinking extends RuntimeException {
307 |
308 | /**
309 | *
310 | */
311 | private static final long serialVersionUID = 1L;
312 |
313 | }
314 | }
315 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/strategies/BooleanStrategy.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis.strategies;
2 |
3 | import works.hypothesis.TestData;
4 |
5 | /**
6 | * Created by david on 15/08/2016.
7 | */
8 | public class BooleanStrategy implements Strategy {
9 | @Override
10 | public Boolean doDraw(TestData data) {
11 | return (data.drawByte() & 1) != 0;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/strategies/BytesStrategy.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis.strategies;
2 |
3 | import works.hypothesis.TestData;
4 |
5 | public class BytesStrategy implements Strategy {
6 | private final int n;
7 |
8 | public BytesStrategy(int n) {
9 | super();
10 | this.n = n;
11 | }
12 |
13 | public byte[] doDraw(TestData data) {
14 | return data.drawBytes(n);
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/strategies/IntegerStrategy.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis.strategies;
2 |
3 | import works.hypothesis.ByteUtils;
4 | import works.hypothesis.TestData;
5 |
6 | public class IntegerStrategy implements Strategy {
7 | IntegerStrategy() {
8 | }
9 |
10 | public Integer doDraw(TestData data) {
11 | return ByteUtils.intFromBytes(data.drawBytes(4), 0);
12 | }
13 | }
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/strategies/ListStrategy.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis.strategies;
2 |
3 | import works.hypothesis.TestData;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | public class ListStrategy implements Strategy> {
9 |
10 | private final Strategy elementGenerator;
11 |
12 | ListStrategy(Strategy elementGenerator) {
13 | this.elementGenerator = elementGenerator;
14 | }
15 |
16 | public List doDraw(TestData data) {
17 | final List result = new ArrayList();
18 | while (true) {
19 | data.startExample();
20 | if (data.drawByte() <= 50) {
21 | data.stopExample();
22 | break;
23 | }
24 | result.add(this.elementGenerator.doDraw(data));
25 | data.stopExample();
26 | }
27 | return result;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/strategies/Strategies.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis.strategies;
2 |
3 | /**
4 | * Created by david on 4/18/16.
5 | */
6 | public interface Strategies {
7 | static ListStrategy lists(Strategy elementGenerator) {
8 | return new ListStrategy(elementGenerator);
9 | }
10 |
11 | static Strategy integers() {
12 | return new IntegerStrategy();
13 | }
14 | static Strategy booleans() { return new BooleanStrategy(); }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/works/hypothesis/strategies/Strategy.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis.strategies;
2 |
3 | import works.hypothesis.TestData;
4 |
5 | @FunctionalInterface
6 | public interface Strategy {
7 | T doDraw(TestData data);
8 | }
9 |
--------------------------------------------------------------------------------
/src/test/java/works/hypothesis/TestBooleansAndIntegers.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | import org.junit.Rule;
4 | import org.junit.Test;
5 |
6 | import static works.hypothesis.strategies.Strategies.booleans;
7 | import static works.hypothesis.strategies.Strategies.integers;
8 | import static junit.framework.TestCase.assertFalse;
9 |
10 | public class TestBooleansAndIntegers {
11 | @Rule
12 | public final TestDataRule data = new TestDataRule();
13 |
14 | @Test
15 | public void testBooleansAreNotIntegers(){
16 | Integer anInt = data.draw(integers());
17 | Boolean aBool = data.draw(booleans());
18 | assertFalse(anInt.equals(aBool));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/test/java/works/hypothesis/TestByteUtils.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | import works.hypothesis.strategies.BytesStrategy;
4 | import works.hypothesis.strategies.Strategy;
5 | import org.junit.Rule;
6 | import org.junit.Test;
7 |
8 | import static org.junit.Assert.assertArrayEquals;
9 |
10 | /**
11 | * Created by david on 4/9/16.
12 | */
13 | public class TestByteUtils {
14 | @Rule
15 | public final TestDataRule data = new TestDataRule();
16 |
17 | public final Strategy INTBYTES = new BytesStrategy(4);
18 |
19 | @Test
20 | public void testBidirectionalConversion() {
21 | byte[] bytes = data.draw(INTBYTES);
22 | int i = ByteUtils.intFromBytes(bytes, 0);
23 | byte[] rebytes = new byte[4];
24 | ByteUtils.intToBytes(rebytes, 0, i);
25 | assertArrayEquals(bytes, rebytes);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/test/java/works/hypothesis/TestDataRuleTest.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | import works.hypothesis.strategies.Strategy;
4 | import works.hypothesis.strategies.Strategies;
5 | import org.junit.Assert;
6 | import org.junit.Rule;
7 | import org.junit.Test;
8 |
9 | import java.util.List;
10 |
11 | public class TestDataRuleTest {
12 | private static final Strategy INTEGER = Strategies.integers();
13 | private static final Strategy> INTEGERS = Strategies.lists(INTEGER);
14 |
15 | @Rule
16 | public final TestDataRule data = new TestDataRule();
17 |
18 | @Test
19 | public void testAssociative3() {
20 | int x = data.draw(INTEGER);
21 | int y = data.draw(INTEGER);
22 | int z = data.draw(INTEGER);
23 |
24 | Assert.assertEquals((x + y) + z, x + (y + z));
25 | }
26 |
27 |
28 | @Test
29 | public void testReversibleLists() {
30 | List xs = data.draw(INTEGERS);
31 | int sumLeft = 0;
32 | int sumRight = 0;
33 | for(int i = 0; i < xs.size(); i++){
34 | sumLeft += xs.get(i);
35 | sumRight += xs.get(xs.size() - i - 1);
36 | }
37 | Assert.assertEquals(sumLeft, sumRight);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/test/java/works/hypothesis/TestExampleQuality.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | import works.hypothesis.strategies.BytesStrategy;
4 | import works.hypothesis.strategies.Strategies;
5 | import org.junit.Test;
6 |
7 | import java.util.List;
8 |
9 | import static org.junit.Assert.assertEquals;
10 | import static org.junit.Assert.assertTrue;
11 |
12 | public class TestExampleQuality {
13 |
14 | static int sum(List t) {
15 | int c = 0;
16 | for (final int i : t)
17 | c += i;
18 | return c;
19 | }
20 |
21 | @Test
22 | public void testLexicographicBytes() throws NoSuchExample {
23 | final byte[] data = Hypothesis.find(new BytesStrategy(1000), new HypothesisPredicate() {
24 | public boolean test(byte[] t) {
25 | int c = 0;
26 | for (final byte b : t) {
27 | if (b > 0)
28 | c++;
29 | }
30 | return c >= 100;
31 | }
32 | });
33 | assertEquals(1000, data.length);
34 | for (int i = 0; i < 1000; i++) {
35 | if (i < 900)
36 | assertEquals(0, data[i]);
37 | else
38 | assertEquals(1, data[i]);
39 | }
40 |
41 | }
42 |
43 | @Test
44 | public void testSummingIntegers() throws NoSuchExample {
45 | final int n = 1000000;
46 | final HypothesisSettings settings = new HypothesisSettings();
47 | settings.setDebug(true);
48 | final List result = Hypothesis.find(Strategies.lists(Strategies.integers()),
49 | new HypothesisPredicate>() {
50 | public boolean test(List t) {
51 | return sum(t) >= n;
52 | }
53 | }, settings);
54 | assertEquals(n, sum(result));
55 | }
56 |
57 |
58 | @Test
59 | public void testFloatShrinksTowardsZero() throws NoSuchExample {
60 | float result = Hypothesis.find(new FloatRangeGenerator(0, 1), new HypothesisPredicate() {
61 | @Override
62 | public boolean test(Float value) {
63 | return value > 0;
64 | }
65 | });
66 | assertEquals(0.0, result, 0.01);
67 | }
68 |
69 |
70 | @Test
71 | public void testNonAssociativeFloats() throws NoSuchExample {
72 | final HypothesisSettings settings = new HypothesisSettings();
73 | settings.setMaxExamples(10000);
74 |
75 | final List result = Hypothesis.find(Strategies.lists(new FloatRangeGenerator(0, 1)),
76 | new HypothesisPredicate>() {
77 | public boolean test(List t) {
78 | if (t.size() < 3) return false;
79 | float x = t.get(0);
80 | float y = t.get(1);
81 | float z = t.get(2);
82 | float u = (x + y) + z;
83 | float v = x + (y + z);
84 | return u != v;
85 | }
86 | }, settings);
87 | assertEquals(3, result.size());
88 | assertTrue(result.get(0) + result.get(1) + result.get(2) < 1);
89 |
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/test/java/works/hypothesis/TestFloatRange.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | import works.hypothesis.strategies.BytesStrategy;
4 | import works.hypothesis.strategies.Strategy;
5 | import org.junit.Rule;
6 | import org.junit.Test;
7 |
8 | import static org.junit.Assert.assertEquals;
9 | import static org.junit.Assert.assertTrue;
10 |
11 | /**
12 | * Created by david on 4/9/16.
13 | */
14 | public class TestFloatRange {
15 | @Rule
16 | public final TestDataRule data = new TestDataRule();
17 |
18 | public final Strategy FLOATBYTES = new BytesStrategy(4);
19 | public final Strategy FLOAT01 = new FloatRangeGenerator(0, 1);
20 |
21 |
22 | @Test
23 | public void testDrawIsStable() {
24 | byte[] bytes = data.draw(FLOATBYTES);
25 | float x = new TestDataForBuffer(bytes).draw(FLOAT01);
26 | float y = new TestDataForBuffer(bytes).draw(FLOAT01);
27 | assertEquals(x, y, 0.0);
28 | }
29 |
30 | @Test
31 | public void testDrawTrivial() {
32 | float x = data.draw(FLOAT01);
33 | assertTrue(x >= 0.0);
34 | assertTrue(x <= 1.0);
35 | }
36 |
37 | @Test
38 | public void testDrawTrivialOtherRange() {
39 | float x = data.draw(new FloatRangeGenerator(10, 12));
40 | assertTrue(x >= 10.0);
41 | assertTrue(x <= 12.0);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/test/java/works/hypothesis/TestSortingAList.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | import org.junit.Rule;
4 | import org.junit.Test;
5 | import works.hypothesis.strategies.Strategies;
6 |
7 | import java.util.Comparator;
8 | import java.util.Iterator;
9 | import java.util.List;
10 |
11 | import static org.junit.Assert.assertTrue;
12 |
13 | public class TestSortingAList {
14 | @Rule
15 | public final TestDataRule data = new TestDataRule();
16 |
17 | @Test
18 | public void testIsSortedAfterSorting(){
19 | List ls = data.draw(Strategies.lists(Strategies.integers()));
20 | ls.sort(Comparator.naturalOrder());
21 | assertSorted(ls);
22 | }
23 |
24 | // Utility assertion function. Doesn't use any Hypothesis functionality.
25 | private > void assertSorted(List elements){
26 | if(elements.isEmpty()) return;
27 | Iterator it = elements.iterator();
28 | T previous = it.next();
29 | while(it.hasNext()){
30 | T current = it.next();
31 | assertTrue(previous.compareTo(current) <= 0);
32 | previous = current;
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/test/java/works/hypothesis/TestTestData.java:
--------------------------------------------------------------------------------
1 | package works.hypothesis;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Test;
5 |
6 | /**
7 | * Created by david on 4/9/16.
8 | */
9 | public class TestTestData {
10 |
11 | @Test
12 | public void testRecordsBufferDraw() {
13 | byte[] buffer = new byte[256];
14 | for (int i = 0; i < buffer.length; i++) buffer[i] = (byte) i;
15 | TestData data = new TestDataForBuffer(buffer);
16 | Assert.assertArrayEquals(new byte[]{0}, data.drawBytes(1));
17 | Assert.assertArrayEquals(new byte[]{1, 2}, data.drawBytes(2));
18 | Assert.assertArrayEquals(new byte[]{3, 4, 5}, data.drawBytes(3));
19 | Assert.assertEquals(6, data.index);
20 | data.freeze();
21 | Assert.assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5}, data.record);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------