diff options
author | 2021-12-12 17:48:47 +0100 | |
---|---|---|
committer | 2021-12-12 17:48:47 +0100 | |
commit | fc7e9312d00e60171ed77c477ed91231d3dbfff9 (patch) | |
tree | cc185dd088b5fa6e9357aab3c9062a70626d1953 /subprojects/store/src/test | |
parent | build: refactor java-application conventions (diff) | |
download | refinery-fc7e9312d00e60171ed77c477ed91231d3dbfff9.tar.gz refinery-fc7e9312d00e60171ed77c477ed91231d3dbfff9.tar.zst refinery-fc7e9312d00e60171ed77c477ed91231d3dbfff9.zip |
build: move modules into subproject directory
Diffstat (limited to 'subprojects/store/src/test')
18 files changed, 2180 insertions, 0 deletions
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java new file mode 100644 index 00000000..f0d5d927 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java | |||
@@ -0,0 +1,22 @@ | |||
1 | package tools.refinery.store.map.tests; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | |||
5 | import org.junit.jupiter.api.Test; | ||
6 | |||
7 | import tools.refinery.store.map.VersionedMapStore; | ||
8 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
9 | import tools.refinery.store.model.Tuple; | ||
10 | import tools.refinery.store.model.TupleHashProvider; | ||
11 | |||
12 | class MapUnitTests { | ||
13 | @Test | ||
14 | void defaultTest() { | ||
15 | VersionedMapStore<Tuple, Boolean> store = new VersionedMapStoreImpl<Tuple, Boolean>(TupleHashProvider.singleton(), false); | ||
16 | var map = store.createMap(); | ||
17 | var out1 = map.put(Tuple.of(0), true); | ||
18 | assertEquals(false, out1); | ||
19 | var out2 = map.put(Tuple.of(1), true); | ||
20 | assertEquals(false, out2); | ||
21 | } | ||
22 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java new file mode 100644 index 00000000..1f9d022f --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java | |||
@@ -0,0 +1,96 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.fail; | ||
4 | |||
5 | import java.util.Random; | ||
6 | import java.util.stream.Stream; | ||
7 | |||
8 | import org.junit.jupiter.api.Tag; | ||
9 | import org.junit.jupiter.api.Timeout; | ||
10 | import org.junit.jupiter.params.ParameterizedTest; | ||
11 | import org.junit.jupiter.params.provider.Arguments; | ||
12 | import org.junit.jupiter.params.provider.MethodSource; | ||
13 | |||
14 | import tools.refinery.store.map.ContinousHashProvider; | ||
15 | import tools.refinery.store.map.VersionedMapStore; | ||
16 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
17 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
18 | import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; | ||
19 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
20 | |||
21 | class CommitFuzzTest { | ||
22 | private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency, | ||
23 | boolean evilHash) { | ||
24 | String[] values = MapTestEnvironment.prepareValues(maxValue); | ||
25 | ContinousHashProvider<Integer> chp = MapTestEnvironment.prepareHashProvider(evilHash); | ||
26 | |||
27 | VersionedMapStore<Integer, String> store = new VersionedMapStoreImpl<Integer, String>(chp, values[0]); | ||
28 | VersionedMapImpl<Integer, String> sut = (VersionedMapImpl<Integer, String>) store.createMap(); | ||
29 | MapTestEnvironment<Integer, String> e = new MapTestEnvironment<Integer, String>(sut); | ||
30 | |||
31 | Random r = new Random(seed); | ||
32 | |||
33 | iterativeRandomPutsAndCommits(scenario, steps, maxKey, values, e, r, commitFrequency); | ||
34 | } | ||
35 | |||
36 | private void iterativeRandomPutsAndCommits(String scenario, int steps, int maxKey, String[] values, | ||
37 | MapTestEnvironment<Integer, String> e, Random r, int commitFrequency) { | ||
38 | int stopAt = -1; | ||
39 | for (int i = 0; i < steps; i++) { | ||
40 | int index = i + 1; | ||
41 | int nextKey = r.nextInt(maxKey); | ||
42 | String nextValue = values[r.nextInt(values.length)]; | ||
43 | if (index == stopAt) { | ||
44 | System.out.println("issue!"); | ||
45 | System.out.println("State before:"); | ||
46 | e.printComparison(); | ||
47 | e.sut.prettyPrint(); | ||
48 | System.out.println("Next: put(" + nextKey + "," + nextValue + ")"); | ||
49 | } | ||
50 | try { | ||
51 | e.put(nextKey, nextValue); | ||
52 | if (index == stopAt) { | ||
53 | e.sut.prettyPrint(); | ||
54 | } | ||
55 | e.checkEquivalence(scenario + ":" + index); | ||
56 | } catch (Exception exception) { | ||
57 | exception.printStackTrace(); | ||
58 | fail(scenario + ":" + index + ": exception happened: " + exception); | ||
59 | } | ||
60 | MapTestEnvironment.printStatus(scenario, index, steps, null); | ||
61 | if (index % commitFrequency == 0) { | ||
62 | e.sut.commit(); | ||
63 | } | ||
64 | } | ||
65 | } | ||
66 | |||
67 | @ParameterizedTest(name = "Commit {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") | ||
68 | @MethodSource | ||
69 | @Timeout(value = 10) | ||
70 | @Tag("fuzz") | ||
71 | void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, | ||
72 | boolean evilHash) { | ||
73 | runFuzzTest("CommitS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, | ||
74 | commitFrequency, evilHash); | ||
75 | } | ||
76 | |||
77 | static Stream<Arguments> parametrizedFastFuzz() { | ||
78 | return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, | ||
79 | new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, | ||
80 | new Object[] { false, true }); | ||
81 | } | ||
82 | |||
83 | @ParameterizedTest(name = "Commit {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") | ||
84 | @MethodSource | ||
85 | @Tag("fuzz") | ||
86 | @Tag("slow") | ||
87 | void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, | ||
88 | boolean evilHash) { | ||
89 | runFuzzTest("CommitS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, | ||
90 | commitFrequency, evilHash); | ||
91 | } | ||
92 | |||
93 | static Stream<Arguments> parametrizedSlowFuzz() { | ||
94 | return FuzzTestUtils.changeStepCount(parametrizedFastFuzz(), 1); | ||
95 | } | ||
96 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java new file mode 100644 index 00000000..263cb2cd --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java | |||
@@ -0,0 +1,143 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | import static org.junit.jupiter.api.Assertions.fail; | ||
5 | |||
6 | import java.util.AbstractMap.SimpleEntry; | ||
7 | import java.util.Collections; | ||
8 | import java.util.LinkedList; | ||
9 | import java.util.List; | ||
10 | import java.util.Random; | ||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import org.junit.jupiter.api.Tag; | ||
14 | import org.junit.jupiter.api.Timeout; | ||
15 | import org.junit.jupiter.params.ParameterizedTest; | ||
16 | import org.junit.jupiter.params.provider.Arguments; | ||
17 | import org.junit.jupiter.params.provider.MethodSource; | ||
18 | |||
19 | import tools.refinery.store.map.ContinousHashProvider; | ||
20 | import tools.refinery.store.map.Cursor; | ||
21 | import tools.refinery.store.map.VersionedMap; | ||
22 | import tools.refinery.store.map.VersionedMapStore; | ||
23 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
24 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
25 | import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; | ||
26 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
27 | |||
28 | class ContentEqualsFuzzTest { | ||
29 | private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency, | ||
30 | boolean evilHash) { | ||
31 | String[] values = MapTestEnvironment.prepareValues(maxValue); | ||
32 | ContinousHashProvider<Integer> chp = MapTestEnvironment.prepareHashProvider(evilHash); | ||
33 | |||
34 | Random r = new Random(seed); | ||
35 | |||
36 | iterativeRandomPutsAndCommitsThenCompare(scenario, chp, steps, maxKey, values, r, commitFrequency); | ||
37 | } | ||
38 | |||
39 | private void iterativeRandomPutsAndCommitsThenCompare(String scenario, ContinousHashProvider<Integer> chp, int steps, int maxKey, String[] values, Random r, int commitFrequency) { | ||
40 | |||
41 | VersionedMapStore<Integer, String> store1 = new VersionedMapStoreImpl<Integer, String>(chp, values[0]); | ||
42 | VersionedMap<Integer, String> sut1 = store1.createMap(); | ||
43 | |||
44 | // Fill one map | ||
45 | for (int i = 0; i < steps; i++) { | ||
46 | int index1 = i + 1; | ||
47 | int nextKey = r.nextInt(maxKey); | ||
48 | String nextValue = values[r.nextInt(values.length)]; | ||
49 | try { | ||
50 | sut1.put(nextKey, nextValue); | ||
51 | } catch (Exception exception) { | ||
52 | exception.printStackTrace(); | ||
53 | fail(scenario + ":" + index1 + ": exception happened: " + exception); | ||
54 | } | ||
55 | MapTestEnvironment.printStatus(scenario, index1, steps, "Fill"); | ||
56 | if (index1 % commitFrequency == 0) { | ||
57 | sut1.commit(); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | // Get the content of the first map | ||
62 | List<SimpleEntry<Integer, String>> content = new LinkedList<>(); | ||
63 | Cursor<Integer, String> cursor = sut1.getAll(); | ||
64 | while (cursor.move()) { | ||
65 | content.add(new SimpleEntry<>(cursor.getKey(), cursor.getValue())); | ||
66 | } | ||
67 | |||
68 | // Randomize the order of the content | ||
69 | Collections.shuffle(content, r); | ||
70 | |||
71 | VersionedMapStore<Integer, String> store2 = new VersionedMapStoreImpl<Integer, String>(chp, values[0]); | ||
72 | VersionedMap<Integer, String> sut2 = store2.createMap(); | ||
73 | int index2 = 1; | ||
74 | for (SimpleEntry<Integer, String> entry : content) { | ||
75 | sut2.put(entry.getKey(), entry.getValue()); | ||
76 | if(index2++%commitFrequency == 0) | ||
77 | sut2.commit(); | ||
78 | } | ||
79 | |||
80 | // Check the integrity of the maps | ||
81 | ((VersionedMapImpl<Integer,String>) sut1).checkIntegrity(); | ||
82 | ((VersionedMapImpl<Integer,String>) sut2).checkIntegrity(); | ||
83 | |||
84 | // // Compare the two maps | ||
85 | // By size | ||
86 | assertEquals(sut1.getSize(), content.size()); | ||
87 | assertEquals(sut2.getSize(), content.size()); | ||
88 | |||
89 | |||
90 | |||
91 | // By cursors | ||
92 | Cursor<Integer, String> cursor1 = sut1.getAll(); | ||
93 | Cursor<Integer, String> cursor2 = sut2.getAll(); | ||
94 | int index3 = 1; | ||
95 | boolean canMove = true; | ||
96 | do{ | ||
97 | boolean canMove1 = cursor1.move(); | ||
98 | boolean canMove2 = cursor2.move(); | ||
99 | assertEquals(canMove1, canMove2, scenario + ":" + index3 +" Cursors stopped at different times!"); | ||
100 | assertEquals(cursor1.getKey(), cursor2.getKey(), scenario + ":" + index3 +" Cursors have different keys!"); | ||
101 | assertEquals(cursor1.getValue(), cursor2.getValue(), scenario + ":" + index3 +" Cursors have different values!"); | ||
102 | |||
103 | canMove = canMove1; | ||
104 | MapTestEnvironment.printStatus(scenario, index3++, content.size(), "Compare"); | ||
105 | } while (canMove); | ||
106 | |||
107 | // By hashcode | ||
108 | assertEquals(sut1.hashCode(), sut2.hashCode(), "Hash codes are not equal!"); | ||
109 | |||
110 | // By equals | ||
111 | assertEquals(sut1, sut2, "Maps are not equals"); | ||
112 | } | ||
113 | |||
114 | @ParameterizedTest(name = "Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") | ||
115 | @MethodSource | ||
116 | @Timeout(value = 10) | ||
117 | @Tag("fuzz") | ||
118 | void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, | ||
119 | boolean evilHash) { | ||
120 | runFuzzTest("CompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, | ||
121 | commitFrequency, evilHash); | ||
122 | } | ||
123 | |||
124 | static Stream<Arguments> parametrizedFastFuzz() { | ||
125 | return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, | ||
126 | new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, | ||
127 | new Object[] { false, true }); | ||
128 | } | ||
129 | |||
130 | @ParameterizedTest(name = "Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") | ||
131 | @MethodSource | ||
132 | @Tag("fuzz") | ||
133 | @Tag("slow") | ||
134 | void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, | ||
135 | boolean evilHash) { | ||
136 | runFuzzTest("CompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, | ||
137 | commitFrequency, evilHash); | ||
138 | } | ||
139 | |||
140 | static Stream<Arguments> parametrizedSlowFuzz() { | ||
141 | return FuzzTestUtils.changeStepCount(parametrizedFastFuzz(), 1); | ||
142 | } | ||
143 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java new file mode 100644 index 00000000..e6334224 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java | |||
@@ -0,0 +1,117 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.fail; | ||
4 | |||
5 | import java.util.Random; | ||
6 | import java.util.stream.Stream; | ||
7 | |||
8 | import org.junit.jupiter.api.Tag; | ||
9 | import org.junit.jupiter.api.Timeout; | ||
10 | import org.junit.jupiter.params.ParameterizedTest; | ||
11 | import org.junit.jupiter.params.provider.Arguments; | ||
12 | import org.junit.jupiter.params.provider.MethodSource; | ||
13 | |||
14 | import tools.refinery.store.map.ContinousHashProvider; | ||
15 | import tools.refinery.store.map.DiffCursor; | ||
16 | import tools.refinery.store.map.VersionedMapStore; | ||
17 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
18 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
19 | import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; | ||
20 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
21 | |||
22 | class DiffCursorFuzzTest { | ||
23 | private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency, | ||
24 | boolean evilHash) { | ||
25 | String[] values = MapTestEnvironment.prepareValues(maxValue); | ||
26 | ContinousHashProvider<Integer> chp = MapTestEnvironment.prepareHashProvider(evilHash); | ||
27 | |||
28 | VersionedMapStore<Integer, String> store = new VersionedMapStoreImpl<Integer, String>(chp, values[0]); | ||
29 | iterativeRandomPutsAndCommitsThenDiffcursor(scenario, store, steps, maxKey, values, seed, commitFrequency); | ||
30 | } | ||
31 | |||
32 | private void iterativeRandomPutsAndCommitsThenDiffcursor(String scenario, VersionedMapStore<Integer, String> store, | ||
33 | int steps, int maxKey, String[] values, int seed, int commitFrequency) { | ||
34 | // 1. build a map with versions | ||
35 | Random r = new Random(seed); | ||
36 | VersionedMapImpl<Integer, String> versioned = (VersionedMapImpl<Integer, String>) store.createMap(); | ||
37 | int largestCommit = -1; | ||
38 | |||
39 | for (int i = 0; i < steps; i++) { | ||
40 | int index = i + 1; | ||
41 | int nextKey = r.nextInt(maxKey); | ||
42 | String nextValue = values[r.nextInt(values.length)]; | ||
43 | try { | ||
44 | versioned.put(nextKey, nextValue); | ||
45 | } catch (Exception exception) { | ||
46 | exception.printStackTrace(); | ||
47 | fail(scenario + ":" + index + ": exception happened: " + exception); | ||
48 | } | ||
49 | if (index % commitFrequency == 0) { | ||
50 | long version = versioned.commit(); | ||
51 | largestCommit = (int) version; | ||
52 | } | ||
53 | if (index % 10000 == 0) | ||
54 | System.out.println(scenario + ":" + index + "/" + steps + " building finished"); | ||
55 | } | ||
56 | // 2. create a non-versioned map, | ||
57 | VersionedMapImpl<Integer, String> moving = (VersionedMapImpl<Integer, String>) store.createMap(); | ||
58 | Random r2 = new Random(seed + 1); | ||
59 | |||
60 | final int diffTravelFrequency = commitFrequency * 2; | ||
61 | for (int i = 0; i < steps; i++) { | ||
62 | int index = i + 1; | ||
63 | if (index % diffTravelFrequency == 0) { | ||
64 | // difftravel | ||
65 | long travelToVersion = r2.nextInt(largestCommit + 1); | ||
66 | DiffCursor<Integer, String> diffCursor = moving.getDiffCursor(travelToVersion); | ||
67 | moving.putAll(diffCursor); | ||
68 | |||
69 | } else { | ||
70 | // random puts | ||
71 | int nextKey = r2.nextInt(maxKey); | ||
72 | String nextValue = values[r2.nextInt(values.length)]; | ||
73 | try { | ||
74 | moving.put(nextKey, nextValue); | ||
75 | } catch (Exception exception) { | ||
76 | exception.printStackTrace(); | ||
77 | fail(scenario + ":" + index + ": exception happened: " + exception); | ||
78 | } | ||
79 | if (index % commitFrequency == 0) { | ||
80 | versioned.commit(); | ||
81 | } | ||
82 | if (index % 10000 == 0) | ||
83 | System.out.println(scenario + ":" + index + "/" + steps + " building finished"); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | } | ||
88 | |||
89 | @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") | ||
90 | @MethodSource | ||
91 | @Timeout(value = 10) | ||
92 | @Tag("fuzz") | ||
93 | void parametrizedFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, | ||
94 | boolean evilHash) { | ||
95 | runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, | ||
96 | noKeys, noValues, commitFrequency, evilHash); | ||
97 | } | ||
98 | |||
99 | static Stream<Arguments> parametrizedFuzz() { | ||
100 | return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, | ||
101 | new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, | ||
102 | new Object[] { false, true }); | ||
103 | } | ||
104 | @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") | ||
105 | @MethodSource | ||
106 | @Tag("fuzz") | ||
107 | @Tag("slow") | ||
108 | void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, | ||
109 | boolean evilHash) { | ||
110 | runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, | ||
111 | commitFrequency, evilHash); | ||
112 | } | ||
113 | |||
114 | static Stream<Arguments> parametrizedSlowFuzz() { | ||
115 | return FuzzTestUtils.changeStepCount(parametrizedFuzz(), 1); | ||
116 | } | ||
117 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java new file mode 100644 index 00000000..1ab431a8 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java | |||
@@ -0,0 +1,97 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | import static org.junit.jupiter.api.Assertions.fail; | ||
5 | |||
6 | import java.util.Collections; | ||
7 | import java.util.LinkedList; | ||
8 | import java.util.List; | ||
9 | import java.util.stream.Stream; | ||
10 | |||
11 | import org.junit.jupiter.api.Tag; | ||
12 | import org.junit.jupiter.api.Timeout; | ||
13 | import org.junit.jupiter.params.ParameterizedTest; | ||
14 | import org.junit.jupiter.params.provider.Arguments; | ||
15 | import org.junit.jupiter.params.provider.MethodSource; | ||
16 | |||
17 | import tools.refinery.store.map.ContinousHashProvider; | ||
18 | import tools.refinery.store.map.VersionedMapStore; | ||
19 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
20 | import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; | ||
21 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
22 | |||
23 | class MultiThreadFuzzTest { | ||
24 | public static final int noThreads = 32; | ||
25 | |||
26 | private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency, | ||
27 | boolean evilHash) { | ||
28 | String[] values = MapTestEnvironment.prepareValues(maxValue); | ||
29 | ContinousHashProvider<Integer> chp = MapTestEnvironment.prepareHashProvider(evilHash); | ||
30 | |||
31 | VersionedMapStore<Integer, String> store = new VersionedMapStoreImpl<Integer, String>(chp, values[0]); | ||
32 | |||
33 | // initialize runnables | ||
34 | MultiThreadTestRunnable[] runnables = new MultiThreadTestRunnable[noThreads]; | ||
35 | for(int i = 0; i<noThreads; i++) { | ||
36 | runnables[i] = new MultiThreadTestRunnable(scenario+"-T"+(i+1), store, steps, maxKey, values, seed, commitFrequency); | ||
37 | } | ||
38 | |||
39 | // initialize threads | ||
40 | Thread[] threads = new Thread[noThreads]; | ||
41 | for(int i = 0; i<noThreads; i++) { | ||
42 | threads[i] = new Thread(runnables[i]); | ||
43 | } | ||
44 | |||
45 | // start threads; | ||
46 | for(int i = 0; i<noThreads; i++) { | ||
47 | threads[i].start(); | ||
48 | } | ||
49 | |||
50 | // wait all the threads; | ||
51 | for(int i = 0; i<noThreads; i++) { | ||
52 | try { | ||
53 | threads[i].join(); | ||
54 | } catch (InterruptedException e) { | ||
55 | fail("Thread "+i+" interrupted."); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | // collect errors | ||
60 | List<Throwable> errors = new LinkedList<>(); | ||
61 | for(int i = 0; i<noThreads; i++) { | ||
62 | errors.addAll(runnables[i].getErrors()); | ||
63 | } | ||
64 | |||
65 | assertEquals(Collections.EMPTY_LIST, errors); | ||
66 | } | ||
67 | |||
68 | @ParameterizedTest(name = "Multithread {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") | ||
69 | @MethodSource | ||
70 | @Timeout(value = 10) | ||
71 | @Tag("fuzz") | ||
72 | void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, | ||
73 | boolean evilHash) { | ||
74 | runFuzzTest("MultithreadS" + steps + "K" + noKeys + "V" + noValues + "CF" + commitFrequency + "s" + seed, seed, steps, noKeys, noValues, | ||
75 | commitFrequency, evilHash); | ||
76 | } | ||
77 | |||
78 | static Stream<Arguments> parametrizedFastFuzz() { | ||
79 | return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, | ||
80 | new Object[] { 2, 3 }, new Object[] { 10, 100 }, new Object[] { 1, 2, 3 }, | ||
81 | new Object[] { false, true }); | ||
82 | } | ||
83 | |||
84 | @ParameterizedTest(name = "Multithread {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") | ||
85 | @MethodSource | ||
86 | @Tag("fuzz") | ||
87 | @Tag("slow") | ||
88 | void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, | ||
89 | boolean evilHash) { | ||
90 | runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, | ||
91 | commitFrequency, evilHash); | ||
92 | } | ||
93 | |||
94 | static Stream<Arguments> parametrizedSlowFuzz() { | ||
95 | return FuzzTestUtils.changeStepCount(RestoreFuzzTest.parametrizedFastFuzz(), 1); | ||
96 | } | ||
97 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java new file mode 100644 index 00000000..f77f9ee5 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java | |||
@@ -0,0 +1,101 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import java.util.ArrayList; | ||
4 | import java.util.Collections; | ||
5 | import java.util.HashMap; | ||
6 | import java.util.LinkedList; | ||
7 | import java.util.List; | ||
8 | import java.util.Map; | ||
9 | import java.util.Random; | ||
10 | |||
11 | import tools.refinery.store.map.VersionedMapStore; | ||
12 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
13 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
14 | |||
15 | public class MultiThreadTestRunnable implements Runnable { | ||
16 | String scenario; | ||
17 | VersionedMapStore<Integer, String> store; | ||
18 | int steps; | ||
19 | int maxKey; | ||
20 | String[] values; | ||
21 | int seed; | ||
22 | int commitFrequency; | ||
23 | List<Throwable> errors = new LinkedList<>(); | ||
24 | |||
25 | public MultiThreadTestRunnable(String scenario, VersionedMapStore<Integer, String> store, int steps, | ||
26 | int maxKey, String[] values, int seed, int commitFrequency) { | ||
27 | super(); | ||
28 | this.scenario = scenario; | ||
29 | this.store = store; | ||
30 | this.steps = steps; | ||
31 | this.maxKey = maxKey; | ||
32 | this.values = values; | ||
33 | this.seed = seed; | ||
34 | this.commitFrequency = commitFrequency; | ||
35 | } | ||
36 | |||
37 | private void logAndThrowError(String message) { | ||
38 | AssertionError error = new AssertionError(message); | ||
39 | errors.add(error); | ||
40 | } | ||
41 | |||
42 | public List<Throwable> getErrors() { | ||
43 | return errors; | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public void run() { | ||
48 | // 1. build a map with versions | ||
49 | Random r = new Random(seed); | ||
50 | VersionedMapImpl<Integer, String> versioned = (VersionedMapImpl<Integer, String>) store.createMap(); | ||
51 | Map<Integer, Long> index2Version = new HashMap<>(); | ||
52 | |||
53 | for (int i = 0; i < steps; i++) { | ||
54 | int index = i + 1; | ||
55 | int nextKey = r.nextInt(maxKey); | ||
56 | String nextValue = values[r.nextInt(values.length)]; | ||
57 | try { | ||
58 | versioned.put(nextKey, nextValue); | ||
59 | } catch (Exception exception) { | ||
60 | exception.printStackTrace(); | ||
61 | logAndThrowError(scenario + ":" + index + ": exception happened: " + exception); | ||
62 | } | ||
63 | if (index % commitFrequency == 0) { | ||
64 | long version = versioned.commit(); | ||
65 | index2Version.put(i, version); | ||
66 | } | ||
67 | MapTestEnvironment.printStatus(scenario, index, steps, "building"); | ||
68 | } | ||
69 | // 2. create a non-versioned | ||
70 | VersionedMapImpl<Integer, String> reference = (VersionedMapImpl<Integer, String>) store.createMap(); | ||
71 | r = new Random(seed); | ||
72 | Random r2 = new Random(seed+1); | ||
73 | |||
74 | for (int i = 0; i < steps; i++) { | ||
75 | int index = i + 1; | ||
76 | int nextKey = r.nextInt(maxKey); | ||
77 | String nextValue = values[r.nextInt(values.length)]; | ||
78 | try { | ||
79 | reference.put(nextKey, nextValue); | ||
80 | } catch (Exception exception) { | ||
81 | exception.printStackTrace(); | ||
82 | logAndThrowError(scenario + ":" + index + ": exception happened: " + exception); | ||
83 | } | ||
84 | // go back to an existing state and compare to the reference | ||
85 | if (index % (commitFrequency) == 0) { | ||
86 | versioned.restore(index2Version.get(i)); | ||
87 | MapTestEnvironment.compareTwoMaps(scenario + ":" + index, reference, versioned,errors); | ||
88 | |||
89 | // go back to a random state (probably created by another thread) | ||
90 | List<Long> states = new ArrayList<>(store.getStates()); | ||
91 | Collections.shuffle(states, r2); | ||
92 | for(Long state : states.subList(0, Math.min(states.size(), 100))) { | ||
93 | versioned.restore(state); | ||
94 | } | ||
95 | versioned.restore(index2Version.get(i)); | ||
96 | } | ||
97 | |||
98 | MapTestEnvironment.printStatus(scenario, index, steps, "comparison"); | ||
99 | } | ||
100 | } | ||
101 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java new file mode 100644 index 00000000..d40c49c4 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java | |||
@@ -0,0 +1,92 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.fail; | ||
4 | |||
5 | import java.util.Random; | ||
6 | import java.util.stream.Stream; | ||
7 | |||
8 | import org.junit.jupiter.api.Tag; | ||
9 | import org.junit.jupiter.api.Timeout; | ||
10 | import org.junit.jupiter.params.ParameterizedTest; | ||
11 | import org.junit.jupiter.params.provider.Arguments; | ||
12 | import org.junit.jupiter.params.provider.MethodSource; | ||
13 | |||
14 | import tools.refinery.store.map.ContinousHashProvider; | ||
15 | import tools.refinery.store.map.VersionedMapStore; | ||
16 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
17 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
18 | import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; | ||
19 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
20 | |||
21 | class MutableFuzzTest { | ||
22 | private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean evilHash) { | ||
23 | String[] values = MapTestEnvironment.prepareValues(maxValue); | ||
24 | ContinousHashProvider<Integer> chp = MapTestEnvironment.prepareHashProvider(evilHash); | ||
25 | |||
26 | VersionedMapStore<Integer, String> store = new VersionedMapStoreImpl<Integer, String>(chp, values[0]); | ||
27 | VersionedMapImpl<Integer, String> sut = (VersionedMapImpl<Integer, String>) store.createMap(); | ||
28 | MapTestEnvironment<Integer, String> e = new MapTestEnvironment<Integer, String>(sut); | ||
29 | |||
30 | Random r = new Random(seed); | ||
31 | |||
32 | iterativeRandomPuts(scenario, steps, maxKey, values, e, r); | ||
33 | } | ||
34 | |||
35 | private void iterativeRandomPuts(String scenario, int steps, int maxKey, String[] values, | ||
36 | MapTestEnvironment<Integer, String> e, Random r) { | ||
37 | int stopAt = -1; | ||
38 | for (int i = 0; i < steps; i++) { | ||
39 | int index = i + 1; | ||
40 | int nextKey = r.nextInt(maxKey); | ||
41 | String nextValue = values[r.nextInt(values.length)]; | ||
42 | if (index == stopAt) { | ||
43 | System.out.println("issue!"); | ||
44 | System.out.println("State before:"); | ||
45 | e.printComparison(); | ||
46 | e.sut.prettyPrint(); | ||
47 | System.out.println("Next: put(" + nextKey + "," + nextValue + ")"); | ||
48 | } | ||
49 | try { | ||
50 | e.put(nextKey, nextValue); | ||
51 | if (index == stopAt) { | ||
52 | e.sut.prettyPrint(); | ||
53 | } | ||
54 | e.checkEquivalence(scenario + ":" + index); | ||
55 | } catch (Exception exception) { | ||
56 | exception.printStackTrace(); | ||
57 | fail(scenario + ":" + index + ": exception happened: " + exception); | ||
58 | } | ||
59 | MapTestEnvironment.printStatus(scenario, index, steps, null); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | @ParameterizedTest(name = "Mutable {index}/{0} Steps={1} Keys={2} Values={3} seed={4} evil-hash={5}") | ||
64 | @MethodSource | ||
65 | @Timeout(value = 10) | ||
66 | @Tag("fuzz") | ||
67 | void parametrizedFuzz(int test, int steps, int noKeys, int noValues, int seed, boolean evilHash) { | ||
68 | runFuzzTest( | ||
69 | "MutableS" + steps + "K" + noKeys + "V" + noValues + "s" + seed + "H" + (evilHash ? "Evil" : "Normal"), | ||
70 | seed, steps, noKeys, noValues, evilHash); | ||
71 | } | ||
72 | |||
73 | static Stream<Arguments> parametrizedFuzz() { | ||
74 | return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, | ||
75 | new Object[] { 3, 32, 32 * 32, 32 * 32 * 32 * 32 }, new Object[] { 2, 3 }, new Object[] { 1, 2, 3 }, | ||
76 | new Object[] { false, true }); | ||
77 | } | ||
78 | |||
79 | @ParameterizedTest(name = "Mutable {index}/{0} Steps={1} Keys={2} Values={3} seed={4} evil-hash={5}") | ||
80 | @MethodSource | ||
81 | @Tag("fuzz") | ||
82 | @Tag("slow") | ||
83 | void parametrizedSlowFuzz(int test, int steps, int noKeys, int noValues, int seed, boolean evilHash) { | ||
84 | runFuzzTest( | ||
85 | "MutableS" + steps + "K" + noKeys + "V" + noValues + "s" + seed + "H" + (evilHash ? "Evil" : "Normal"), | ||
86 | seed, steps, noKeys, noValues, evilHash); | ||
87 | } | ||
88 | |||
89 | static Stream<Arguments> parametrizedSlowFuzz() { | ||
90 | return FuzzTestUtils.changeStepCount(parametrizedFuzz(), 1); | ||
91 | } | ||
92 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java new file mode 100644 index 00000000..410705a2 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java | |||
@@ -0,0 +1,89 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.fail; | ||
4 | |||
5 | import java.util.Random; | ||
6 | import java.util.stream.Stream; | ||
7 | |||
8 | import org.junit.jupiter.api.Tag; | ||
9 | import org.junit.jupiter.api.Timeout; | ||
10 | import org.junit.jupiter.params.ParameterizedTest; | ||
11 | import org.junit.jupiter.params.provider.Arguments; | ||
12 | import org.junit.jupiter.params.provider.MethodSource; | ||
13 | |||
14 | import tools.refinery.store.map.ContinousHashProvider; | ||
15 | import tools.refinery.store.map.VersionedMapStore; | ||
16 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
17 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
18 | import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; | ||
19 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
20 | |||
21 | class MutableImmutableCompareFuzzTest { | ||
22 | private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency, | ||
23 | boolean evilHash) { | ||
24 | String[] values = MapTestEnvironment.prepareValues(maxValue); | ||
25 | ContinousHashProvider<Integer> chp = MapTestEnvironment.prepareHashProvider(evilHash); | ||
26 | |||
27 | VersionedMapStore<Integer, String> store = new VersionedMapStoreImpl<Integer, String>(chp, values[0]); | ||
28 | VersionedMapImpl<Integer, String> immutable = (VersionedMapImpl<Integer, String>) store.createMap(); | ||
29 | VersionedMapImpl<Integer, String> mutable = (VersionedMapImpl<Integer, String>) store.createMap(); | ||
30 | |||
31 | Random r = new Random(seed); | ||
32 | |||
33 | iterativeRandomPutsAndCommitsAndCompare(scenario, immutable, mutable, steps, maxKey, values, r, | ||
34 | commitFrequency); | ||
35 | } | ||
36 | |||
37 | private void iterativeRandomPutsAndCommitsAndCompare(String scenario, VersionedMapImpl<Integer, String> immutable, | ||
38 | VersionedMapImpl<Integer, String> mutable, int steps, int maxKey, String[] values, Random r, | ||
39 | int commitFrequency) { | ||
40 | for (int i = 0; i < steps; i++) { | ||
41 | int index = i + 1; | ||
42 | int nextKey = r.nextInt(maxKey); | ||
43 | String nextValue = values[r.nextInt(values.length)]; | ||
44 | try { | ||
45 | immutable.put(nextKey, nextValue); | ||
46 | mutable.put(nextKey, nextValue); | ||
47 | } catch (Exception exception) { | ||
48 | exception.printStackTrace(); | ||
49 | fail(scenario + ":" + index + ": exception happened: " + exception); | ||
50 | } | ||
51 | if (index % commitFrequency == 0) { | ||
52 | immutable.commit(); | ||
53 | } | ||
54 | MapTestEnvironment.compareTwoMaps(scenario + ":" + index, immutable, mutable); | ||
55 | |||
56 | MapTestEnvironment.printStatus(scenario, index, steps, null); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") | ||
61 | @MethodSource | ||
62 | @Timeout(value = 10) | ||
63 | @Tag("fuzz") | ||
64 | void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, | ||
65 | boolean evilHash) { | ||
66 | runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, | ||
67 | noKeys, noValues, commitFrequency, evilHash); | ||
68 | } | ||
69 | |||
70 | static Stream<Arguments> parametrizedFastFuzz() { | ||
71 | return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, | ||
72 | new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, | ||
73 | new Object[] { false, true }); | ||
74 | } | ||
75 | |||
76 | @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") | ||
77 | @MethodSource | ||
78 | @Tag("fuzz") | ||
79 | @Tag("slow") | ||
80 | void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, | ||
81 | boolean evilHash) { | ||
82 | runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, | ||
83 | noKeys, noValues, commitFrequency, evilHash); | ||
84 | } | ||
85 | |||
86 | static Stream<Arguments> parametrizedSlowFuzz() { | ||
87 | return FuzzTestUtils.changeStepCount(MutableImmutableCompareFuzzTest.parametrizedFastFuzz(), 1); | ||
88 | } | ||
89 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java new file mode 100644 index 00000000..2e29a03f --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java | |||
@@ -0,0 +1,109 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.fail; | ||
4 | |||
5 | import java.util.HashMap; | ||
6 | import java.util.Map; | ||
7 | import java.util.Random; | ||
8 | import java.util.stream.Stream; | ||
9 | |||
10 | import org.junit.jupiter.api.Tag; | ||
11 | import org.junit.jupiter.api.Timeout; | ||
12 | import org.junit.jupiter.params.ParameterizedTest; | ||
13 | import org.junit.jupiter.params.provider.Arguments; | ||
14 | import org.junit.jupiter.params.provider.MethodSource; | ||
15 | |||
16 | import tools.refinery.store.map.ContinousHashProvider; | ||
17 | import tools.refinery.store.map.VersionedMapStore; | ||
18 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
19 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
20 | import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; | ||
21 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
22 | |||
23 | class RestoreFuzzTest { | ||
24 | private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency, | ||
25 | boolean evilHash) { | ||
26 | String[] values = MapTestEnvironment.prepareValues(maxValue); | ||
27 | ContinousHashProvider<Integer> chp = MapTestEnvironment.prepareHashProvider(evilHash); | ||
28 | |||
29 | VersionedMapStore<Integer, String> store = new VersionedMapStoreImpl<Integer, String>(chp, values[0]); | ||
30 | |||
31 | iterativeRandomPutsAndCommitsThenRestore(scenario, store, steps, maxKey, values, seed, commitFrequency); | ||
32 | } | ||
33 | |||
34 | private void iterativeRandomPutsAndCommitsThenRestore(String scenario, VersionedMapStore<Integer, String> store, | ||
35 | int steps, int maxKey, String[] values, int seed, int commitFrequency) { | ||
36 | // 1. build a map with versions | ||
37 | Random r = new Random(seed); | ||
38 | VersionedMapImpl<Integer, String> versioned = (VersionedMapImpl<Integer, String>) store.createMap(); | ||
39 | Map<Integer, Long> index2Version = new HashMap<>(); | ||
40 | |||
41 | for (int i = 0; i < steps; i++) { | ||
42 | int index = i + 1; | ||
43 | int nextKey = r.nextInt(maxKey); | ||
44 | String nextValue = values[r.nextInt(values.length)]; | ||
45 | try { | ||
46 | versioned.put(nextKey, nextValue); | ||
47 | } catch (Exception exception) { | ||
48 | exception.printStackTrace(); | ||
49 | fail(scenario + ":" + index + ": exception happened: " + exception); | ||
50 | } | ||
51 | if (index % commitFrequency == 0) { | ||
52 | long version = versioned.commit(); | ||
53 | index2Version.put(i, version); | ||
54 | } | ||
55 | MapTestEnvironment.printStatus(scenario, index, steps, "building"); | ||
56 | } | ||
57 | // 2. create a non-versioned and | ||
58 | VersionedMapImpl<Integer, String> reference = (VersionedMapImpl<Integer, String>) store.createMap(); | ||
59 | r = new Random(seed); | ||
60 | |||
61 | for (int i = 0; i < steps; i++) { | ||
62 | int index = i + 1; | ||
63 | int nextKey = r.nextInt(maxKey); | ||
64 | String nextValue = values[r.nextInt(values.length)]; | ||
65 | try { | ||
66 | reference.put(nextKey, nextValue); | ||
67 | } catch (Exception exception) { | ||
68 | exception.printStackTrace(); | ||
69 | fail(scenario + ":" + index + ": exception happened: " + exception); | ||
70 | } | ||
71 | if (index % commitFrequency == 0) { | ||
72 | versioned.restore(index2Version.get(i)); | ||
73 | MapTestEnvironment.compareTwoMaps(scenario + ":" + index, reference, versioned); | ||
74 | } | ||
75 | MapTestEnvironment.printStatus(scenario, index, steps, "comparison"); | ||
76 | } | ||
77 | |||
78 | } | ||
79 | |||
80 | @ParameterizedTest(name = "Restore {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") | ||
81 | @MethodSource | ||
82 | @Timeout(value = 10) | ||
83 | @Tag("smoke") | ||
84 | void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, | ||
85 | boolean evilHash) { | ||
86 | runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, | ||
87 | commitFrequency, evilHash); | ||
88 | } | ||
89 | |||
90 | static Stream<Arguments> parametrizedFastFuzz() { | ||
91 | return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, | ||
92 | new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, | ||
93 | new Object[] { false, true }); | ||
94 | } | ||
95 | |||
96 | @ParameterizedTest(name = "Restore {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") | ||
97 | @MethodSource | ||
98 | @Tag("smoke") | ||
99 | @Tag("slow") | ||
100 | void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, | ||
101 | boolean evilHash) { | ||
102 | runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, | ||
103 | commitFrequency, evilHash); | ||
104 | } | ||
105 | |||
106 | static Stream<Arguments> parametrizedSlowFuzz() { | ||
107 | return FuzzTestUtils.changeStepCount(RestoreFuzzTest.parametrizedFastFuzz(), 1); | ||
108 | } | ||
109 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java new file mode 100644 index 00000000..914a0f63 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java | |||
@@ -0,0 +1,113 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz; | ||
2 | |||
3 | import java.util.HashMap; | ||
4 | import java.util.LinkedList; | ||
5 | import java.util.List; | ||
6 | import java.util.Map; | ||
7 | import java.util.Random; | ||
8 | import java.util.stream.Stream; | ||
9 | |||
10 | import org.junit.jupiter.api.Tag; | ||
11 | import org.junit.jupiter.api.Timeout; | ||
12 | import org.junit.jupiter.params.ParameterizedTest; | ||
13 | import org.junit.jupiter.params.provider.Arguments; | ||
14 | import org.junit.jupiter.params.provider.MethodSource; | ||
15 | |||
16 | import tools.refinery.store.map.ContinousHashProvider; | ||
17 | import tools.refinery.store.map.VersionedMapStore; | ||
18 | import tools.refinery.store.map.VersionedMapStoreImpl; | ||
19 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
20 | import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; | ||
21 | import tools.refinery.store.map.tests.utils.MapTestEnvironment; | ||
22 | |||
23 | class SharedStoreFuzzTest { | ||
24 | private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency, | ||
25 | boolean evilHash) { | ||
26 | String[] values = MapTestEnvironment.prepareValues(maxValue); | ||
27 | ContinousHashProvider<Integer> chp = MapTestEnvironment.prepareHashProvider(evilHash); | ||
28 | |||
29 | List<VersionedMapStore<Integer, String>> stores = VersionedMapStoreImpl.createSharedVersionedMapStores(5, chp, values[0]); | ||
30 | |||
31 | iterativeRandomPutsAndCommitsThenRestore(scenario, stores, steps, maxKey, values, seed, commitFrequency); | ||
32 | } | ||
33 | |||
34 | private void iterativeRandomPutsAndCommitsThenRestore(String scenario, List<VersionedMapStore<Integer, String>> stores, | ||
35 | int steps, int maxKey, String[] values, int seed, int commitFrequency) { | ||
36 | // 1. maps with versions | ||
37 | Random r = new Random(seed); | ||
38 | List<VersionedMapImpl<Integer, String>> versioneds = new LinkedList<>(); | ||
39 | for(VersionedMapStore<Integer, String> store : stores) { | ||
40 | versioneds.add((VersionedMapImpl<Integer, String>) store.createMap()); | ||
41 | } | ||
42 | |||
43 | List<Map<Integer, Long>> index2Version = new LinkedList<>(); | ||
44 | for(int i = 0; i<stores.size(); i++) { | ||
45 | index2Version.add(new HashMap<>()); | ||
46 | } | ||
47 | |||
48 | for (int i = 0; i < steps; i++) { | ||
49 | int stepIndex = i + 1; | ||
50 | for (int storeIndex = 0; storeIndex<versioneds.size(); storeIndex++) { | ||
51 | int nextKey = r.nextInt(maxKey); | ||
52 | String nextValue = values[r.nextInt(values.length)]; | ||
53 | versioneds.get(storeIndex).put(nextKey, nextValue); | ||
54 | if (stepIndex % commitFrequency == 0) { | ||
55 | long version = versioneds.get(storeIndex).commit(); | ||
56 | index2Version.get(storeIndex).put(i, version); | ||
57 | } | ||
58 | MapTestEnvironment.printStatus(scenario, stepIndex, steps, "building"); | ||
59 | } | ||
60 | } | ||
61 | // 2. create a non-versioned and | ||
62 | List<VersionedMapImpl<Integer, String>> reference = new LinkedList<>(); | ||
63 | for(VersionedMapStore<Integer, String> store : stores) { | ||
64 | reference.add((VersionedMapImpl<Integer, String>) store.createMap()); | ||
65 | } | ||
66 | r = new Random(seed); | ||
67 | |||
68 | for (int i = 0; i < steps; i++) { | ||
69 | int index = i + 1; | ||
70 | for (int storeIndex = 0; storeIndex<versioneds.size(); storeIndex++) { | ||
71 | int nextKey = r.nextInt(maxKey); | ||
72 | String nextValue = values[r.nextInt(values.length)]; | ||
73 | reference.get(storeIndex).put(nextKey, nextValue); | ||
74 | if (index % commitFrequency == 0) { | ||
75 | versioneds.get(storeIndex).restore(index2Version.get(storeIndex).get(i)); | ||
76 | MapTestEnvironment.compareTwoMaps(scenario + ":" + index, reference.get(storeIndex), versioneds.get(storeIndex)); | ||
77 | } | ||
78 | } | ||
79 | MapTestEnvironment.printStatus(scenario, index, steps, "comparison"); | ||
80 | } | ||
81 | |||
82 | } | ||
83 | |||
84 | @ParameterizedTest(name = "Shared Store {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") | ||
85 | @MethodSource | ||
86 | @Timeout(value = 10) | ||
87 | @Tag("smoke") | ||
88 | void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, | ||
89 | boolean evilHash) { | ||
90 | runFuzzTest("SharedS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, | ||
91 | commitFrequency, evilHash); | ||
92 | } | ||
93 | |||
94 | static Stream<Arguments> parametrizedFastFuzz() { | ||
95 | return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, | ||
96 | new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, | ||
97 | new Object[] { false, true }); | ||
98 | } | ||
99 | |||
100 | @ParameterizedTest(name = "Shared Store {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") | ||
101 | @MethodSource | ||
102 | @Tag("smoke") | ||
103 | @Tag("slow") | ||
104 | void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, | ||
105 | boolean evilHash) { | ||
106 | runFuzzTest("SharedS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, | ||
107 | commitFrequency, evilHash); | ||
108 | } | ||
109 | |||
110 | static Stream<Arguments> parametrizedSlowFuzz() { | ||
111 | return FuzzTestUtils.changeStepCount(RestoreFuzzTest.parametrizedFastFuzz(), 1); | ||
112 | } | ||
113 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtils.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtils.java new file mode 100644 index 00000000..e75d7f5a --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtils.java | |||
@@ -0,0 +1,64 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz.utils; | ||
2 | |||
3 | import java.util.Arrays; | ||
4 | import java.util.LinkedList; | ||
5 | import java.util.List; | ||
6 | import java.util.stream.Stream; | ||
7 | |||
8 | import org.junit.jupiter.params.provider.Arguments; | ||
9 | |||
10 | public final class FuzzTestUtils { | ||
11 | public static final int FAST_STEP_COUNT = 500; | ||
12 | public static final int SLOW_STEP_COUNT = 32 * 32 * 32 * 32; | ||
13 | |||
14 | private FuzzTestUtils() { | ||
15 | throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); | ||
16 | } | ||
17 | |||
18 | public static Stream<Arguments> changeStepCount(Stream<Arguments> arguments, int parameterIndex) { | ||
19 | return arguments.map(x -> Arguments.of(updatedStepCount(x.get(), parameterIndex))); | ||
20 | } | ||
21 | |||
22 | public static Object[] updatedStepCount(Object[] arguments, int parameterIndex) { | ||
23 | Object[] copy = Arrays.copyOf(arguments, arguments.length); | ||
24 | copy[parameterIndex] = SLOW_STEP_COUNT; | ||
25 | return copy; | ||
26 | } | ||
27 | |||
28 | static List<List<Object>> permutationInternal(int from, Object[]... valueOption) { | ||
29 | if (valueOption.length == from) { | ||
30 | return List.of(List.of()); | ||
31 | } else { | ||
32 | Object[] permuteThis = valueOption[from]; | ||
33 | List<List<Object>> otherCombination = permutationInternal(from + 1, valueOption); | ||
34 | List<List<Object>> result = new LinkedList<>(); | ||
35 | for (Object permuteThisElement : permuteThis) { | ||
36 | for (List<Object> otherCombinationList : otherCombination) { | ||
37 | List<Object> newResult = new LinkedList<>(); | ||
38 | newResult.add(permuteThisElement); | ||
39 | newResult.addAll(otherCombinationList); | ||
40 | result.add(newResult); | ||
41 | } | ||
42 | } | ||
43 | return result; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | public static Stream<Arguments> permutation(Object[]... valueOption) { | ||
48 | List<List<Object>> permutations = permutationInternal(0, valueOption); | ||
49 | return permutations.stream().map(x -> Arguments.of(x.toArray())); | ||
50 | } | ||
51 | |||
52 | public static Stream<Arguments> permutationWithSize(Object[]... valueOption) { | ||
53 | int size = 1; | ||
54 | for (int i = 0; i < valueOption.length; i++) { | ||
55 | size *= valueOption[i].length; | ||
56 | } | ||
57 | Object[][] newValueOption = new Object[valueOption.length + 1][]; | ||
58 | newValueOption[0] = new Object[] { size }; | ||
59 | for (int i = 1; i < newValueOption.length; i++) { | ||
60 | newValueOption[i] = valueOption[i - 1]; | ||
61 | } | ||
62 | return permutation(newValueOption); | ||
63 | } | ||
64 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtilsTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtilsTest.java new file mode 100644 index 00000000..72f2a46c --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtilsTest.java | |||
@@ -0,0 +1,33 @@ | |||
1 | package tools.refinery.store.map.tests.fuzz.utils; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | |||
5 | import java.util.List; | ||
6 | |||
7 | import org.junit.jupiter.api.Test; | ||
8 | |||
9 | class FuzzTestUtilsTest { | ||
10 | @Test | ||
11 | void permutationInternalTest() { | ||
12 | List<List<Object>> res = FuzzTestUtils.permutationInternal(0, new Object[] { 1, 2, 3 }, | ||
13 | new Object[] { 'a', 'b', 'c' }, new Object[] { "alpha", "beta", "gamma", "delta" }); | ||
14 | assertEquals(3 * 3 * 4, res.size()); | ||
15 | } | ||
16 | |||
17 | @Test | ||
18 | void permutationTest1() { | ||
19 | var res = FuzzTestUtils.permutation(new Object[] { 1, 2, 3 }, new Object[] { 'a', 'b', 'c' }, | ||
20 | new Object[] { "alpha", "beta", "gamma", "delta" }); | ||
21 | assertEquals(3 * 3 * 4, res.count()); | ||
22 | } | ||
23 | |||
24 | @Test | ||
25 | void permutationTest2() { | ||
26 | var res = FuzzTestUtils.permutation(new Object[] { 1, 2, 3 }, new Object[] { 'a', 'b', 'c' }, | ||
27 | new Object[] { "alpha", "beta", "gamma", "delta" }); | ||
28 | var arguments = res.findFirst().get().get(); | ||
29 | assertEquals(1, arguments[0]); | ||
30 | assertEquals('a', arguments[1]); | ||
31 | assertEquals("alpha", arguments[2]); | ||
32 | } | ||
33 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java new file mode 100644 index 00000000..991b4f51 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java | |||
@@ -0,0 +1,214 @@ | |||
1 | package tools.refinery.store.map.tests.utils; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | import static org.junit.jupiter.api.Assertions.assertTrue; | ||
5 | import static org.junit.jupiter.api.Assertions.fail; | ||
6 | |||
7 | import java.util.HashMap; | ||
8 | import java.util.Iterator; | ||
9 | import java.util.List; | ||
10 | import java.util.Map; | ||
11 | import java.util.Map.Entry; | ||
12 | |||
13 | import tools.refinery.store.map.ContinousHashProvider; | ||
14 | import tools.refinery.store.map.Cursor; | ||
15 | import tools.refinery.store.map.VersionedMap; | ||
16 | import tools.refinery.store.map.internal.VersionedMapImpl; | ||
17 | |||
18 | import java.util.TreeMap; | ||
19 | |||
20 | public class MapTestEnvironment<K, V> { | ||
21 | public static String[] prepareValues(int maxValue) { | ||
22 | String[] values = new String[maxValue]; | ||
23 | values[0] = "DEFAULT"; | ||
24 | for (int i = 1; i < values.length; i++) { | ||
25 | values[i] = "VAL" + i; | ||
26 | } | ||
27 | return values; | ||
28 | } | ||
29 | |||
30 | public static ContinousHashProvider<Integer> prepareHashProvider(final boolean evil) { | ||
31 | // Use maxPrime = 2147483629 | ||
32 | |||
33 | ContinousHashProvider<Integer> chp = new ContinousHashProvider<Integer>() { | ||
34 | |||
35 | @Override | ||
36 | public int getHash(Integer key, int index) { | ||
37 | if (evil && index < 15 && index < key / 3) { | ||
38 | return 7; | ||
39 | } | ||
40 | int result = 1; | ||
41 | final int prime = 31; | ||
42 | |||
43 | result = prime * result + key; | ||
44 | result = prime * result + index; | ||
45 | |||
46 | return result; | ||
47 | } | ||
48 | }; | ||
49 | return chp; | ||
50 | } | ||
51 | |||
52 | public static void printStatus(String scenario, int actual, int max, String stepName) { | ||
53 | if (actual % 10000 == 0) { | ||
54 | String printStepName = stepName == null ? "" : stepName; | ||
55 | System.out.format(scenario + ":%d/%d (%d%%) " + printStepName + "%n", actual, max, actual * 100 / max); | ||
56 | } | ||
57 | |||
58 | } | ||
59 | |||
60 | public static <K, V> void compareTwoMaps(String title, VersionedMapImpl<K, V> map1, | ||
61 | VersionedMapImpl<K, V> map2) { | ||
62 | compareTwoMaps(title, map1, map2, null); | ||
63 | } | ||
64 | public static <K, V> void compareTwoMaps(String title, VersionedMapImpl<K, V> map1, | ||
65 | VersionedMapImpl<K, V> map2, List<Throwable> errors) { | ||
66 | // 1. Comparing cursors. | ||
67 | Cursor<K, V> cursor1 = map1.getAll(); | ||
68 | Cursor<K, V> cursor2 = map2.getAll(); | ||
69 | while (!cursor1.isTerminated()) { | ||
70 | if (cursor2.isTerminated()) { | ||
71 | fail("cursor 2 terminated before cursor1"); | ||
72 | } | ||
73 | assertEqualsList(cursor1.getKey(), cursor2.getKey(),"Keys not equal", errors); | ||
74 | assertEqualsList(cursor2.getValue(), cursor2.getValue(), "Values not equal", errors); | ||
75 | cursor1.move(); | ||
76 | cursor2.move(); | ||
77 | } | ||
78 | if (!cursor2.isTerminated()) | ||
79 | fail("cursor 1 terminated before cursor 2"); | ||
80 | |||
81 | // 2.1. comparing hash codes | ||
82 | assertEqualsList(map1.hashCode(), map2.hashCode(), title + ": hash code check",errors); | ||
83 | assertEqualsList(map1, map2, title + ": 1.equals(2)",errors); | ||
84 | assertEqualsList(map2, map1, title + ": 2.equals(1)",errors); | ||
85 | } | ||
86 | private static void assertEqualsList(Object o1, Object o2, String message, List<Throwable> errors) { | ||
87 | if(errors == null) { | ||
88 | assertEquals(o1, o2, message); | ||
89 | } else { | ||
90 | if(o1 != null) { | ||
91 | if(!(o1.equals(o2))) { | ||
92 | AssertionError error = new AssertionError((message != null ? message+" " : "") + "expected: " + o1 + " but was : " + o2); | ||
93 | errors.add(error); | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | |||
99 | public VersionedMapImpl<K, V> sut; | ||
100 | Map<K, V> oracle = new HashMap<K, V>(); | ||
101 | |||
102 | public MapTestEnvironment(VersionedMapImpl<K, V> sut) { | ||
103 | this.sut = sut; | ||
104 | } | ||
105 | |||
106 | public void put(K key, V value) { | ||
107 | V oldSutValue = sut.put(key, value); | ||
108 | V oldOracleValue; | ||
109 | if (value != sut.getDefaultValue()) { | ||
110 | oldOracleValue = oracle.put(key, value); | ||
111 | } else { | ||
112 | oldOracleValue = oracle.remove(key); | ||
113 | } | ||
114 | if(oldSutValue == sut.getDefaultValue() && oldOracleValue != null) { | ||
115 | fail("After put, SUT old value was default, but oracle old walue was " + oldOracleValue); | ||
116 | } | ||
117 | if(oldSutValue != sut.getDefaultValue()) { | ||
118 | assertEquals(oldOracleValue, oldSutValue); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | public void checkEquivalence(String title) { | ||
123 | // 0. Checking integrity | ||
124 | try { | ||
125 | sut.checkIntegrity(); | ||
126 | } catch (IllegalStateException e) { | ||
127 | fail(title + ": " + e.getMessage()); | ||
128 | } | ||
129 | |||
130 | // 1. Checking: if Reference contains <key,value> pair, then SUT contains | ||
131 | // <key,value> pair. | ||
132 | // Tests get functions | ||
133 | for (Entry<K, V> entry : oracle.entrySet()) { | ||
134 | V sutValue = sut.get(entry.getKey()); | ||
135 | V oracleValue = entry.getValue(); | ||
136 | if (sutValue != oracleValue) { | ||
137 | printComparison(); | ||
138 | fail(title + ": Non-equivalent get(" + entry.getKey() + ") results: SUT=" + sutValue + ", Oracle=" | ||
139 | + oracleValue + "!"); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | // 2. Checking: if SUT contains <key,value> pair, then Reference contains | ||
144 | // <key,value> pair. | ||
145 | // Tests iterators | ||
146 | int elementsInSutEntrySet = 0; | ||
147 | Cursor<K, V> cursor = sut.getAll(); | ||
148 | while (cursor.move()) { | ||
149 | elementsInSutEntrySet++; | ||
150 | K key = cursor.getKey(); | ||
151 | V sutValue = cursor.getValue(); | ||
152 | // System.out.println(key + " -> " + sutValue); | ||
153 | V oracleValue = oracle.get(key); | ||
154 | if (sutValue != oracleValue) { | ||
155 | printComparison(); | ||
156 | fail(title + ": Non-equivalent entry in iterator: SUT=<" + key + "," + sutValue + ">, Oracle=<" + key | ||
157 | + "," + oracleValue + ">!"); | ||
158 | } | ||
159 | |||
160 | } | ||
161 | |||
162 | // 3. Checking sizes | ||
163 | // Counting of non-default value pairs. | ||
164 | int oracleSize = oracle.entrySet().size(); | ||
165 | long sutSize = sut.getSize(); | ||
166 | if (oracleSize != sutSize || oracleSize != elementsInSutEntrySet) { | ||
167 | printComparison(); | ||
168 | fail(title + ": Non-eqivalent size() result: SUT.getSize()=" + sutSize + ", SUT.entryset.size=" | ||
169 | + elementsInSutEntrySet + ", Oracle=" + oracleSize + "!"); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | public static <K,V> void checkOrder(String scenario, VersionedMap<K,V> versionedMap) { | ||
174 | K previous = null; | ||
175 | Cursor<K, V> cursor = versionedMap.getAll(); | ||
176 | while(cursor.move()) { | ||
177 | System.out.println(cursor.getKey() + " " + ((VersionedMapImpl<K, V>) versionedMap).getHashProvider().getHash(cursor.getKey(), 0)); | ||
178 | if(previous != null) { | ||
179 | int comparisonResult = ((VersionedMapImpl<K, V>) versionedMap).getHashProvider().compare(previous, cursor.getKey()); | ||
180 | assertTrue(comparisonResult<0,scenario+" Cursor order is not incremental!"); | ||
181 | } | ||
182 | previous = cursor.getKey(); | ||
183 | } | ||
184 | System.out.println(); | ||
185 | } | ||
186 | |||
187 | public void printComparison() { | ||
188 | System.out.println("SUT:"); | ||
189 | printEntrySet(sut.getAll()); | ||
190 | System.out.println("Oracle:"); | ||
191 | printEntrySet(oracle.entrySet().iterator()); | ||
192 | } | ||
193 | |||
194 | private void printEntrySet(Iterator<Entry<K, V>> iterator) { | ||
195 | TreeMap<K, V> treemap = new TreeMap<>(); | ||
196 | while (iterator.hasNext()) { | ||
197 | Entry<K, V> entry = iterator.next(); | ||
198 | treemap.put(entry.getKey(), entry.getValue()); | ||
199 | } | ||
200 | for (Entry<K, V> e : treemap.entrySet()) { | ||
201 | System.out.println("\t" + e.getKey() + " -> " + e.getValue()); | ||
202 | } | ||
203 | } | ||
204 | |||
205 | private void printEntrySet(Cursor<K, V> cursor) { | ||
206 | TreeMap<K, V> treemap = new TreeMap<>(); | ||
207 | while (cursor.move()) { | ||
208 | treemap.put(cursor.getKey(), cursor.getValue()); | ||
209 | } | ||
210 | for (Entry<K, V> e : treemap.entrySet()) { | ||
211 | System.out.println("\t" + e.getKey() + " -> " + e.getValue()); | ||
212 | } | ||
213 | } | ||
214 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/model/hashTests/HashEfficiencyTest.java b/subprojects/store/src/test/java/tools/refinery/store/model/hashTests/HashEfficiencyTest.java new file mode 100644 index 00000000..7d070380 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/model/hashTests/HashEfficiencyTest.java | |||
@@ -0,0 +1,161 @@ | |||
1 | package tools.refinery.store.model.hashTests; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | |||
5 | import java.util.ArrayList; | ||
6 | import java.util.LinkedList; | ||
7 | import java.util.List; | ||
8 | import java.util.Random; | ||
9 | |||
10 | import org.junit.jupiter.api.Test; | ||
11 | |||
12 | import tools.refinery.store.map.ContinousHashProvider; | ||
13 | import tools.refinery.store.model.Tuple; | ||
14 | import tools.refinery.store.model.TupleHashProvider; | ||
15 | import tools.refinery.store.model.TupleHashProviderBitMagic; | ||
16 | |||
17 | class HashEfficiencyTest { | ||
18 | |||
19 | private static List<Tuple> permutations(int range, int arity) { | ||
20 | if(arity == 1) { | ||
21 | List<Tuple> result = new ArrayList<>(range); | ||
22 | for(int i=0; i<range; i++) { | ||
23 | result.add(Tuple.of(i)); | ||
24 | } | ||
25 | return result; | ||
26 | } else if(arity > 1) { | ||
27 | List<Tuple> smallers = permutations(range, arity-1); | ||
28 | List<Tuple> result = new ArrayList<>(range*smallers.size()); | ||
29 | for(Tuple smaller : smallers) { | ||
30 | for(int i=0; i<range; i++) { | ||
31 | int[] larger = new int[arity]; | ||
32 | for(int x = 0; x<smaller.getSize(); x++) { | ||
33 | larger[x] = smaller.get(x); | ||
34 | } | ||
35 | larger[arity-1] = i; | ||
36 | result.add(Tuple.of(larger)); | ||
37 | } | ||
38 | } | ||
39 | return result; | ||
40 | } else throw new IllegalArgumentException(); | ||
41 | } | ||
42 | |||
43 | private static int amountToRange(int arity, int n) { | ||
44 | int range = 1; | ||
45 | while(Math.pow(range,arity)<n+0.1) { | ||
46 | range++; | ||
47 | } | ||
48 | return 1024; | ||
49 | } | ||
50 | |||
51 | public static List<Tuple> nPermutations(int arity, int n) { | ||
52 | int range = amountToRange(arity, n); | ||
53 | List<Tuple> permutations = permutations(range, arity); | ||
54 | return permutations.subList(0, n); | ||
55 | } | ||
56 | |||
57 | public static List<Tuple> nRandoms(int arity, int n, int seed) { | ||
58 | int range = amountToRange(arity, n); | ||
59 | List<Tuple> permutations = new ArrayList<>(n); | ||
60 | Random r = new Random(seed); | ||
61 | for(int i = 0; i<n; i++) { | ||
62 | int[] tuple = new int[arity]; | ||
63 | for(int j=0; j<arity; j++) { | ||
64 | tuple[j] = r.nextInt(range); | ||
65 | } | ||
66 | permutations.add(Tuple.of(tuple)); | ||
67 | } | ||
68 | return permutations; | ||
69 | } | ||
70 | |||
71 | @Test | ||
72 | void permutationTest() { | ||
73 | List<Tuple> p = permutations(10, 2); | ||
74 | assertEquals(p.size(),10*10); | ||
75 | } | ||
76 | // private void printTuples(List<Tuple> p) { | ||
77 | // for(Tuple element : p) { | ||
78 | // System.out.println(element); | ||
79 | // } | ||
80 | // } | ||
81 | @Test | ||
82 | void nPermutationTest() { | ||
83 | final int amount = 500; | ||
84 | List<Tuple> p = nPermutations(2, amount); | ||
85 | assertEquals(amount,p.size()); | ||
86 | } | ||
87 | @Test | ||
88 | void nRandomTest() { | ||
89 | final int amount = 500; | ||
90 | List<Tuple> p = nRandoms(2, amount, 1);; | ||
91 | assertEquals(amount,p.size()); | ||
92 | } | ||
93 | private static double calculateHashClashes(List<Tuple> tuples, ContinousHashProvider<Tuple> chp) { | ||
94 | int sumClashes = 0; | ||
95 | |||
96 | for(int i = 0; i<tuples.size(); i++) { | ||
97 | int height = 0; | ||
98 | for(int j=0; j<tuples.size(); j++) { | ||
99 | int clashes = calculateHashClash(chp, tuples.get(i), tuples.get(j)); | ||
100 | height = Math.max(height, clashes); | ||
101 | } | ||
102 | sumClashes += height; | ||
103 | } | ||
104 | return (sumClashes+0.0) / tuples.size(); | ||
105 | } | ||
106 | private static int calculateHashClash(ContinousHashProvider<Tuple> chp, Tuple a, Tuple b) { | ||
107 | if(a.equals(b)) return 0; | ||
108 | final int bits = 5; | ||
109 | final int segments = Integer.SIZE/bits; | ||
110 | final int mask = (1<<bits)-1; | ||
111 | for(int i = 0;;i++) { | ||
112 | int index = i/segments; | ||
113 | int depth = i%segments; | ||
114 | int aHash = (chp.getHash(a, index)>>(depth*5))&mask; | ||
115 | int bHash = (chp.getHash(b, index)>>(depth*5))&mask; | ||
116 | if(aHash != bHash) { | ||
117 | return i+1; | ||
118 | } | ||
119 | if(i>400) { | ||
120 | throw new IllegalStateException(a+" vs "+b); | ||
121 | } | ||
122 | } | ||
123 | } | ||
124 | private static double caclulateOptimalHashClash(int size) { | ||
125 | return (Math.log(size)/Math.log(32)); | ||
126 | } | ||
127 | public static void main(String[] args) { | ||
128 | List<String> hashNames = new LinkedList<>(); | ||
129 | List<ContinousHashProvider<Tuple>> hashes = new LinkedList<>(); | ||
130 | hashNames.add("PrimeGroup"); | ||
131 | hashes.add(new TupleHashProvider()); | ||
132 | hashNames.add("BitMagic"); | ||
133 | hashes.add(new TupleHashProviderBitMagic()); | ||
134 | |||
135 | int[] arities = new int[] {2,3,4,5}; | ||
136 | int[] sizes = new int[] {32*32,32*32*8}; | ||
137 | |||
138 | System.out.println("Size,Arity,DataSource,Hash,Chashes,Optimal,Badness"); | ||
139 | for(int size : sizes) { | ||
140 | double optimalClashes = caclulateOptimalHashClash(size); | ||
141 | for(int arity : arities) { | ||
142 | List<String> dataSourceNames = new LinkedList<>(); | ||
143 | List<List<Tuple>> dataSources = new LinkedList<>(); | ||
144 | |||
145 | // dataSourceNames.add("Permutation"); | ||
146 | // dataSources.add(nPermutations(arity, size)); | ||
147 | dataSourceNames.add("Random"); | ||
148 | dataSources.add(nRandoms(arity, size, 0)); | ||
149 | |||
150 | for(int dataSourceIndex = 0; dataSourceIndex<dataSourceNames.size(); dataSourceIndex++) { | ||
151 | for(int hashIndex = 0; hashIndex<hashNames.size(); hashIndex++) { | ||
152 | double clashes = calculateHashClashes(dataSources.get(dataSourceIndex),hashes.get(hashIndex)); | ||
153 | System.out.println( | ||
154 | size+","+arity+","+dataSourceNames.get(dataSourceIndex)+","+hashNames.get(hashIndex)+","+ | ||
155 | clashes+","+optimalClashes+","+(clashes+0.0)/optimalClashes); | ||
156 | } | ||
157 | } | ||
158 | } | ||
159 | } | ||
160 | } | ||
161 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java b/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java new file mode 100644 index 00000000..9d90b1e1 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java | |||
@@ -0,0 +1,148 @@ | |||
1 | package tools.refinery.store.model.tests; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | import static org.junit.jupiter.api.Assertions.assertFalse; | ||
5 | import static org.junit.jupiter.api.Assertions.assertTrue; | ||
6 | |||
7 | import java.util.Set; | ||
8 | |||
9 | import org.junit.jupiter.api.Assertions; | ||
10 | import org.junit.jupiter.api.Test; | ||
11 | |||
12 | import tools.refinery.store.model.Model; | ||
13 | import tools.refinery.store.model.ModelStore; | ||
14 | import tools.refinery.store.model.ModelStoreImpl; | ||
15 | import tools.refinery.store.model.Tuple; | ||
16 | import tools.refinery.store.model.representation.Relation; | ||
17 | |||
18 | class ModelTest { | ||
19 | |||
20 | @Test | ||
21 | void modelConstructionTest() { | ||
22 | Relation<Boolean> person = new Relation<>("Person", 1, false); | ||
23 | Relation<Boolean> friend = new Relation<>("friend", 2, false); | ||
24 | |||
25 | ModelStore store = new ModelStoreImpl(Set.of(person, friend)); | ||
26 | Model model = store.createModel(); | ||
27 | |||
28 | assertTrue(store.getDataRepresentations().contains(person)); | ||
29 | assertTrue(store.getDataRepresentations().contains(friend)); | ||
30 | assertTrue(model.getDataRepresentations().contains(person)); | ||
31 | assertTrue(model.getDataRepresentations().contains(friend)); | ||
32 | |||
33 | Relation<Integer> other = new Relation<Integer>("other", 2, null); | ||
34 | assertFalse(model.getDataRepresentations().contains(other)); | ||
35 | } | ||
36 | |||
37 | @Test | ||
38 | void modelBuildingTest() { | ||
39 | Relation<Boolean> person = new Relation<>("Person", 1, false); | ||
40 | Relation<Integer> age = new Relation<Integer>("age", 1, null); | ||
41 | Relation<Boolean> friend = new Relation<>("friend", 2, false); | ||
42 | |||
43 | ModelStore store = new ModelStoreImpl(Set.of(person, age, friend)); | ||
44 | Model model = store.createModel(); | ||
45 | |||
46 | model.put(person, Tuple.of(0), true); | ||
47 | model.put(person, Tuple.of(1), true); | ||
48 | model.put(age, Tuple.of(0), 3); | ||
49 | model.put(age, Tuple.of(1), 1); | ||
50 | model.put(friend, Tuple.of(0, 1), true); | ||
51 | model.put(friend, Tuple.of(1, 0), true); | ||
52 | |||
53 | assertTrue(model.get(person, Tuple.of(0))); | ||
54 | assertTrue(model.get(person, Tuple.of(1))); | ||
55 | assertFalse(model.get(person, Tuple.of(2))); | ||
56 | |||
57 | assertEquals(3, model.get(age, Tuple.of(0))); | ||
58 | assertEquals(1, model.get(age, Tuple.of(1))); | ||
59 | assertEquals(null, model.get(age, Tuple.of(2))); | ||
60 | |||
61 | assertTrue(model.get(friend, Tuple.of(0, 1))); | ||
62 | assertFalse(model.get(friend, Tuple.of(0, 5))); | ||
63 | } | ||
64 | |||
65 | @Test | ||
66 | void modelBuildingArityFailTest() { | ||
67 | Relation<Boolean> person = new Relation<>("Person", 1, false); | ||
68 | ModelStore store = new ModelStoreImpl(Set.of(person)); | ||
69 | Model model = store.createModel(); | ||
70 | |||
71 | final Tuple tuple3 = Tuple.of(1, 1, 1); | ||
72 | Assertions.assertThrows(IllegalArgumentException.class, () -> model.put(person, tuple3, true)); | ||
73 | Assertions.assertThrows(IllegalArgumentException.class, () -> model.get(person, tuple3)); | ||
74 | } | ||
75 | |||
76 | @Test | ||
77 | void modelBuildingNullFailTest() { | ||
78 | Relation<Integer> age = new Relation<Integer>("age", 1, null); | ||
79 | ModelStore store = new ModelStoreImpl(Set.of(age)); | ||
80 | Model model = store.createModel(); | ||
81 | |||
82 | model.put(age, Tuple.of(1), null); // valid | ||
83 | Assertions.assertThrows(IllegalArgumentException.class, () -> model.put(age, null, 1)); | ||
84 | Assertions.assertThrows(IllegalArgumentException.class, () -> model.get(age, null)); | ||
85 | |||
86 | } | ||
87 | |||
88 | @Test | ||
89 | void modelUpdateTest() { | ||
90 | Relation<Boolean> person = new Relation<>("Person", 1, false); | ||
91 | Relation<Integer> age = new Relation<Integer>("age", 1, null); | ||
92 | Relation<Boolean> friend = new Relation<>("friend", 2, false); | ||
93 | |||
94 | ModelStore store = new ModelStoreImpl(Set.of(person, age, friend)); | ||
95 | Model model = store.createModel(); | ||
96 | |||
97 | model.put(person, Tuple.of(0), true); | ||
98 | model.put(person, Tuple.of(1), true); | ||
99 | model.put(age, Tuple.of(0), 3); | ||
100 | model.put(age, Tuple.of(1), 1); | ||
101 | model.put(friend, Tuple.of(0, 1), true); | ||
102 | model.put(friend, Tuple.of(1, 0), true); | ||
103 | |||
104 | assertEquals(3, model.get(age, Tuple.of(0))); | ||
105 | assertTrue(model.get(friend, Tuple.of(0, 1))); | ||
106 | |||
107 | model.put(age, Tuple.of(0), 4); | ||
108 | model.put(friend, Tuple.of(0, 1), false); | ||
109 | |||
110 | assertEquals(4, model.get(age, Tuple.of(0))); | ||
111 | assertFalse(model.get(friend, Tuple.of(0, 1))); | ||
112 | } | ||
113 | |||
114 | @Test | ||
115 | void restoreTest() { | ||
116 | Relation<Boolean> person = new Relation<Boolean>("Person", 1, false); | ||
117 | Relation<Boolean> friend = new Relation<Boolean>("friend", 2, false); | ||
118 | |||
119 | ModelStore store = new ModelStoreImpl(Set.of(person, friend)); | ||
120 | Model model = store.createModel(); | ||
121 | |||
122 | model.put(person, Tuple.of(0), true); | ||
123 | model.put(person, Tuple.of(1), true); | ||
124 | model.put(friend, Tuple.of(0, 1), true); | ||
125 | model.put(friend, Tuple.of(1, 0), true); | ||
126 | long state1 = model.commit(); | ||
127 | |||
128 | assertFalse(model.get(person, Tuple.of(2))); | ||
129 | assertFalse(model.get(friend, Tuple.of(0, 2))); | ||
130 | |||
131 | model.put(person, Tuple.of(2), true); | ||
132 | model.put(friend, Tuple.of(0, 2), true); | ||
133 | long state2 = model.commit(); | ||
134 | |||
135 | assertTrue(model.get(person, Tuple.of(2))); | ||
136 | assertTrue(model.get(friend, Tuple.of(0, 2))); | ||
137 | |||
138 | model.restore(state1); | ||
139 | |||
140 | assertFalse(model.get(person, Tuple.of(2))); | ||
141 | assertFalse(model.get(friend, Tuple.of(0, 2))); | ||
142 | |||
143 | model.restore(state2); | ||
144 | |||
145 | assertTrue(model.get(person, Tuple.of(2))); | ||
146 | assertTrue(model.get(friend, Tuple.of(0, 2))); | ||
147 | } | ||
148 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/query/test/QueryTest.java b/subprojects/store/src/test/java/tools/refinery/store/query/test/QueryTest.java new file mode 100644 index 00000000..02381bcd --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/query/test/QueryTest.java | |||
@@ -0,0 +1,445 @@ | |||
1 | package tools.refinery.store.query.test; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | |||
5 | import java.util.ArrayList; | ||
6 | import java.util.Arrays; | ||
7 | import java.util.Collections; | ||
8 | import java.util.HashSet; | ||
9 | import java.util.List; | ||
10 | import java.util.Set; | ||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import org.junit.jupiter.api.Test; | ||
14 | |||
15 | import tools.refinery.store.model.Tuple; | ||
16 | import tools.refinery.store.model.representation.Relation; | ||
17 | import tools.refinery.store.model.representation.TruthValue; | ||
18 | import tools.refinery.store.query.QueriableModel; | ||
19 | import tools.refinery.store.query.QueriableModelStore; | ||
20 | import tools.refinery.store.query.QueriableModelStoreImpl; | ||
21 | import tools.refinery.store.query.building.DNFAnd; | ||
22 | import tools.refinery.store.query.building.DNFPredicate; | ||
23 | import tools.refinery.store.query.building.EquivalenceAtom; | ||
24 | import tools.refinery.store.query.building.PredicateAtom; | ||
25 | import tools.refinery.store.query.building.RelationAtom; | ||
26 | import tools.refinery.store.query.building.Variable; | ||
27 | import tools.refinery.store.query.view.FilteredRelationView; | ||
28 | import tools.refinery.store.query.view.KeyOnlyRelationView; | ||
29 | import tools.refinery.store.query.view.RelationView; | ||
30 | |||
31 | class QueryTest { | ||
32 | |||
33 | static void compareMatchSets(Stream<Object[]> matchSet, Set<List<Tuple>> expected) { | ||
34 | Set<List<Tuple>> translatedMatchSet = new HashSet<>(); | ||
35 | var interator = matchSet.iterator(); | ||
36 | while (interator.hasNext()) { | ||
37 | var element = interator.next(); | ||
38 | List<Tuple> elementToTranslatedMatchSet = new ArrayList<>(); | ||
39 | for (int i = 0; i < element.length; i++) { | ||
40 | elementToTranslatedMatchSet.add((Tuple) element[i]); | ||
41 | } | ||
42 | translatedMatchSet.add(elementToTranslatedMatchSet); | ||
43 | } | ||
44 | |||
45 | assertEquals(expected, translatedMatchSet); | ||
46 | } | ||
47 | |||
48 | @Test | ||
49 | void typeConstraintTest() { | ||
50 | Relation<Boolean> person = new Relation<>("Person", 1, false); | ||
51 | Relation<Boolean> asset = new Relation<>("Asset", 1, false); | ||
52 | RelationView<Boolean> persionView = new KeyOnlyRelationView(person); | ||
53 | |||
54 | List<Variable> parameters = Arrays.asList(new Variable("p1")); | ||
55 | RelationAtom personRelationAtom = new RelationAtom(persionView, parameters); | ||
56 | DNFAnd clause = new DNFAnd(Collections.emptySet(), Arrays.asList(personRelationAtom)); | ||
57 | DNFPredicate predicate = new DNFPredicate("TypeConstraint", parameters, Arrays.asList(clause)); | ||
58 | |||
59 | QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, asset), Set.of(persionView), | ||
60 | Set.of(predicate)); | ||
61 | QueriableModel model = store.createModel(); | ||
62 | |||
63 | model.put(person, Tuple.of(0), true); | ||
64 | model.put(person, Tuple.of(1), true); | ||
65 | model.put(asset, Tuple.of(1), true); | ||
66 | model.put(asset, Tuple.of(2), true); | ||
67 | |||
68 | model.flushChanges(); | ||
69 | assertEquals(2, model.countResults(predicate)); | ||
70 | compareMatchSets(model.allResults(predicate), Set.of(List.of(Tuple.of(0)), List.of(Tuple.of(1)))); | ||
71 | } | ||
72 | |||
73 | @Test | ||
74 | void relationConstraintTest() { | ||
75 | Relation<Boolean> person = new Relation<Boolean>("Person", 1, false); | ||
76 | Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE); | ||
77 | RelationView<Boolean> persionView = new KeyOnlyRelationView(person); | ||
78 | RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must()); | ||
79 | |||
80 | Variable p1 = new Variable("p1"); | ||
81 | Variable p2 = new Variable("p2"); | ||
82 | List<Variable> parameters = Arrays.asList(p1, p2); | ||
83 | |||
84 | RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1)); | ||
85 | RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2)); | ||
86 | RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2)); | ||
87 | DNFAnd clause = new DNFAnd(Collections.emptySet(), | ||
88 | Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom)); | ||
89 | DNFPredicate predicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause)); | ||
90 | |||
91 | QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend), | ||
92 | Set.of(persionView, friendMustView), Set.of(predicate)); | ||
93 | QueriableModel model = store.createModel(); | ||
94 | |||
95 | assertEquals(0, model.countResults(predicate)); | ||
96 | |||
97 | model.put(person, Tuple.of(0), true); | ||
98 | model.put(person, Tuple.of(1), true); | ||
99 | model.put(person, Tuple.of(2), true); | ||
100 | model.put(friend, Tuple.of(0, 1), TruthValue.TRUE); | ||
101 | model.put(friend, Tuple.of(1, 0), TruthValue.TRUE); | ||
102 | model.put(friend, Tuple.of(1, 2), TruthValue.TRUE); | ||
103 | |||
104 | assertEquals(0, model.countResults(predicate)); | ||
105 | |||
106 | model.flushChanges(); | ||
107 | assertEquals(3, model.countResults(predicate)); | ||
108 | compareMatchSets(model.allResults(predicate), Set.of(List.of(Tuple.of(0), Tuple.of(1)), | ||
109 | List.of(Tuple.of(1), Tuple.of(0)), List.of(Tuple.of(1), Tuple.of(2)))); | ||
110 | } | ||
111 | |||
112 | @Test | ||
113 | void andTest() { | ||
114 | Relation<Boolean> person = new Relation<Boolean>("Person", 1, false); | ||
115 | Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE); | ||
116 | RelationView<Boolean> persionView = new KeyOnlyRelationView(person); | ||
117 | RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must()); | ||
118 | |||
119 | Variable p1 = new Variable("p1"); | ||
120 | Variable p2 = new Variable("p2"); | ||
121 | List<Variable> parameters = Arrays.asList(p1, p2); | ||
122 | |||
123 | RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1)); | ||
124 | RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2)); | ||
125 | RelationAtom friendRelationAtom1 = new RelationAtom(friendMustView, Arrays.asList(p1, p2)); | ||
126 | RelationAtom friendRelationAtom2 = new RelationAtom(friendMustView, Arrays.asList(p2, p1)); | ||
127 | DNFAnd clause = new DNFAnd(Collections.emptySet(), | ||
128 | Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom1, friendRelationAtom2)); | ||
129 | DNFPredicate predicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause)); | ||
130 | |||
131 | QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend), | ||
132 | Set.of(persionView, friendMustView), Set.of(predicate)); | ||
133 | QueriableModel model = store.createModel(); | ||
134 | |||
135 | assertEquals(0, model.countResults(predicate)); | ||
136 | |||
137 | model.put(person, Tuple.of(0), true); | ||
138 | model.put(person, Tuple.of(1), true); | ||
139 | model.put(person, Tuple.of(2), true); | ||
140 | |||
141 | model.put(friend, Tuple.of(0, 1), TruthValue.TRUE); | ||
142 | model.put(friend, Tuple.of(0, 2), TruthValue.TRUE); | ||
143 | |||
144 | model.flushChanges(); | ||
145 | assertEquals(0, model.countResults(predicate)); | ||
146 | |||
147 | model.put(friend, Tuple.of(1, 0), TruthValue.TRUE); | ||
148 | model.flushChanges(); | ||
149 | assertEquals(2, model.countResults(predicate)); | ||
150 | compareMatchSets(model.allResults(predicate), | ||
151 | Set.of(List.of(Tuple.of(0), Tuple.of(1)), List.of(Tuple.of(1), Tuple.of(0)))); | ||
152 | |||
153 | model.put(friend, Tuple.of(2, 0), TruthValue.TRUE); | ||
154 | model.flushChanges(); | ||
155 | assertEquals(4, model.countResults(predicate)); | ||
156 | compareMatchSets(model.allResults(predicate), | ||
157 | Set.of(List.of(Tuple.of(0), Tuple.of(1)), List.of(Tuple.of(1), Tuple.of(0)), | ||
158 | List.of(Tuple.of(0), Tuple.of(2)), List.of(Tuple.of(2), Tuple.of(0)))); | ||
159 | } | ||
160 | |||
161 | @Test | ||
162 | void existTest() { | ||
163 | Relation<Boolean> person = new Relation<Boolean>("Person", 1, false); | ||
164 | Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE); | ||
165 | RelationView<Boolean> persionView = new KeyOnlyRelationView(person); | ||
166 | RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must()); | ||
167 | |||
168 | Variable p1 = new Variable("p1"); | ||
169 | Variable p2 = new Variable("p2"); | ||
170 | List<Variable> parameters = Arrays.asList(p1); | ||
171 | |||
172 | RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1)); | ||
173 | RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2)); | ||
174 | RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2)); | ||
175 | DNFAnd clause = new DNFAnd(Set.of(p2), | ||
176 | Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom)); | ||
177 | DNFPredicate predicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause)); | ||
178 | |||
179 | QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend), | ||
180 | Set.of(persionView, friendMustView), Set.of(predicate)); | ||
181 | QueriableModel model = store.createModel(); | ||
182 | |||
183 | assertEquals(0, model.countResults(predicate)); | ||
184 | |||
185 | model.put(person, Tuple.of(0), true); | ||
186 | model.put(person, Tuple.of(1), true); | ||
187 | model.put(person, Tuple.of(2), true); | ||
188 | model.put(friend, Tuple.of(0, 1), TruthValue.TRUE); | ||
189 | model.put(friend, Tuple.of(1, 0), TruthValue.TRUE); | ||
190 | model.put(friend, Tuple.of(1, 2), TruthValue.TRUE); | ||
191 | |||
192 | assertEquals(0, model.countResults(predicate)); | ||
193 | |||
194 | model.flushChanges(); | ||
195 | assertEquals(2, model.countResults(predicate)); | ||
196 | compareMatchSets(model.allResults(predicate), Set.of(List.of(Tuple.of(0)), List.of(Tuple.of(1)))); | ||
197 | } | ||
198 | |||
199 | @Test | ||
200 | void orTest() { | ||
201 | Relation<Boolean> person = new Relation<>("Person", 1, false); | ||
202 | Relation<Boolean> animal = new Relation<>("Animal", 1, false); | ||
203 | Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE); | ||
204 | RelationView<Boolean> persionView = new KeyOnlyRelationView(person); | ||
205 | RelationView<Boolean> animalView = new KeyOnlyRelationView(animal); | ||
206 | RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must()); | ||
207 | |||
208 | Variable p1 = new Variable("p1"); | ||
209 | Variable p2 = new Variable("p2"); | ||
210 | List<Variable> parameters = Arrays.asList(p1, p2); | ||
211 | |||
212 | // Person-Person friendship | ||
213 | RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1)); | ||
214 | RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2)); | ||
215 | RelationAtom friendRelationAtom1 = new RelationAtom(friendMustView, Arrays.asList(p1, p2)); | ||
216 | DNFAnd clause1 = new DNFAnd(Collections.emptySet(), | ||
217 | Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom1)); | ||
218 | |||
219 | // Animal-Animal friendship | ||
220 | RelationAtom animalRelationAtom1 = new RelationAtom(animalView, Arrays.asList(p1)); | ||
221 | RelationAtom animalRelationAtom2 = new RelationAtom(animalView, Arrays.asList(p2)); | ||
222 | RelationAtom friendRelationAtom2 = new RelationAtom(friendMustView, Arrays.asList(p1, p2)); | ||
223 | DNFAnd clause2 = new DNFAnd(Collections.emptySet(), | ||
224 | Arrays.asList(animalRelationAtom1, animalRelationAtom2, friendRelationAtom2)); | ||
225 | |||
226 | // No inter-species friendship | ||
227 | |||
228 | DNFPredicate predicate = new DNFPredicate("Or", parameters, Arrays.asList(clause1, clause2)); | ||
229 | |||
230 | QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, animal, friend), | ||
231 | Set.of(persionView, animalView, friendMustView), Set.of(predicate)); | ||
232 | QueriableModel model = store.createModel(); | ||
233 | |||
234 | model.put(person, Tuple.of(0), true); | ||
235 | model.put(person, Tuple.of(1), true); | ||
236 | model.put(animal, Tuple.of(2), true); | ||
237 | model.put(animal, Tuple.of(3), true); | ||
238 | model.put(friend, Tuple.of(0, 1), TruthValue.TRUE); | ||
239 | model.put(friend, Tuple.of(0, 2), TruthValue.TRUE); | ||
240 | model.put(friend, Tuple.of(2, 3), TruthValue.TRUE); | ||
241 | model.put(friend, Tuple.of(3, 0), TruthValue.TRUE); | ||
242 | |||
243 | model.flushChanges(); | ||
244 | assertEquals(2, model.countResults(predicate)); | ||
245 | compareMatchSets(model.allResults(predicate), | ||
246 | Set.of(List.of(Tuple.of(0), Tuple.of(1)), List.of(Tuple.of(2), Tuple.of(3)))); | ||
247 | } | ||
248 | |||
249 | @Test | ||
250 | void equalityTest() { | ||
251 | Relation<Boolean> person = new Relation<Boolean>("Person", 1, false); | ||
252 | RelationView<Boolean> persionView = new KeyOnlyRelationView(person); | ||
253 | |||
254 | Variable p1 = new Variable("p1"); | ||
255 | Variable p2 = new Variable("p2"); | ||
256 | List<Variable> parameters = Arrays.asList(p1, p2); | ||
257 | |||
258 | RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1)); | ||
259 | RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2)); | ||
260 | EquivalenceAtom equivalenceAtom = new EquivalenceAtom(true, p1, p2); | ||
261 | DNFAnd clause = new DNFAnd(Collections.emptySet(), | ||
262 | Arrays.asList(personRelationAtom1, personRelationAtom2, equivalenceAtom)); | ||
263 | DNFPredicate predicate = new DNFPredicate("Equality", parameters, Arrays.asList(clause)); | ||
264 | |||
265 | QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person), Set.of(persionView), Set.of(predicate)); | ||
266 | QueriableModel model = store.createModel(); | ||
267 | |||
268 | model.put(person, Tuple.of(0), true); | ||
269 | model.put(person, Tuple.of(1), true); | ||
270 | model.put(person, Tuple.of(2), true); | ||
271 | |||
272 | model.flushChanges(); | ||
273 | assertEquals(3, model.countResults(predicate)); | ||
274 | compareMatchSets(model.allResults(predicate), Set.of(List.of(Tuple.of(0), Tuple.of(0)), | ||
275 | List.of(Tuple.of(1), Tuple.of(1)), List.of(Tuple.of(2), Tuple.of(2)))); | ||
276 | } | ||
277 | |||
278 | @Test | ||
279 | void inequalityTest() { | ||
280 | Relation<Boolean> person = new Relation<Boolean>("Person", 1, false); | ||
281 | Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE); | ||
282 | RelationView<Boolean> persionView = new KeyOnlyRelationView(person); | ||
283 | RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must()); | ||
284 | |||
285 | Variable p1 = new Variable("p1"); | ||
286 | Variable p2 = new Variable("p2"); | ||
287 | Variable p3 = new Variable("p3"); | ||
288 | List<Variable> parameters = Arrays.asList(p1, p2, p3); | ||
289 | |||
290 | RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1)); | ||
291 | RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2)); | ||
292 | RelationAtom friendRelationAtom1 = new RelationAtom(friendMustView, Arrays.asList(p1, p3)); | ||
293 | RelationAtom friendRelationAtom2 = new RelationAtom(friendMustView, Arrays.asList(p2, p3)); | ||
294 | EquivalenceAtom inequivalenceAtom = new EquivalenceAtom(false, p1, p2); | ||
295 | DNFAnd clause = new DNFAnd(Collections.emptySet(), Arrays.asList(personRelationAtom1, personRelationAtom2, | ||
296 | friendRelationAtom1, friendRelationAtom2, inequivalenceAtom)); | ||
297 | DNFPredicate predicate = new DNFPredicate("Inequality", parameters, Arrays.asList(clause)); | ||
298 | |||
299 | QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend), | ||
300 | Set.of(persionView, friendMustView), Set.of(predicate)); | ||
301 | QueriableModel model = store.createModel(); | ||
302 | |||
303 | model.put(person, Tuple.of(0), true); | ||
304 | model.put(person, Tuple.of(1), true); | ||
305 | model.put(person, Tuple.of(2), true); | ||
306 | model.put(friend, Tuple.of(0, 2), TruthValue.TRUE); | ||
307 | model.put(friend, Tuple.of(1, 2), TruthValue.TRUE); | ||
308 | |||
309 | model.flushChanges(); | ||
310 | assertEquals(2, model.countResults(predicate)); | ||
311 | compareMatchSets(model.allResults(predicate), | ||
312 | Set.of(List.of(Tuple.of(0), Tuple.of(1), Tuple.of(2)), List.of(Tuple.of(1), Tuple.of(0), Tuple.of(2)))); | ||
313 | } | ||
314 | |||
315 | @Test | ||
316 | void patternCallTest() { | ||
317 | Relation<Boolean> person = new Relation<Boolean>("Person", 1, false); | ||
318 | Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE); | ||
319 | RelationView<Boolean> persionView = new KeyOnlyRelationView(person); | ||
320 | RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must()); | ||
321 | |||
322 | Variable p1 = new Variable("p1"); | ||
323 | Variable p2 = new Variable("p2"); | ||
324 | List<Variable> parameters = Arrays.asList(p1, p2); | ||
325 | |||
326 | RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1)); | ||
327 | RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2)); | ||
328 | RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2)); | ||
329 | DNFAnd clause = new DNFAnd(Collections.emptySet(), | ||
330 | Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom)); | ||
331 | DNFPredicate friendPredicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause)); | ||
332 | |||
333 | Variable p3 = new Variable("p3"); | ||
334 | Variable p4 = new Variable("p4"); | ||
335 | List<Variable> substitution = Arrays.asList(p3, p4); | ||
336 | RelationAtom personRelationAtom3 = new RelationAtom(persionView, Arrays.asList(p3)); | ||
337 | RelationAtom personRelationAtom4 = new RelationAtom(persionView, Arrays.asList(p4)); | ||
338 | PredicateAtom friendPredicateAtom = new PredicateAtom(true, false, friendPredicate, substitution); | ||
339 | DNFAnd patternCallClause = new DNFAnd(Collections.emptySet(), | ||
340 | Arrays.asList(personRelationAtom3, personRelationAtom4, friendPredicateAtom)); | ||
341 | DNFPredicate predicate = new DNFPredicate("PatternCall", substitution, Arrays.asList(patternCallClause)); | ||
342 | |||
343 | QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend), | ||
344 | Set.of(persionView, friendMustView), Set.of(friendPredicate, predicate)); | ||
345 | QueriableModel model = store.createModel(); | ||
346 | |||
347 | model.put(person, Tuple.of(0), true); | ||
348 | model.put(person, Tuple.of(1), true); | ||
349 | model.put(person, Tuple.of(2), true); | ||
350 | model.put(friend, Tuple.of(0, 1), TruthValue.TRUE); | ||
351 | model.put(friend, Tuple.of(1, 0), TruthValue.TRUE); | ||
352 | model.put(friend, Tuple.of(1, 2), TruthValue.TRUE); | ||
353 | |||
354 | model.flushChanges(); | ||
355 | |||
356 | assertEquals(3, model.countResults(friendPredicate)); | ||
357 | } | ||
358 | |||
359 | @Test | ||
360 | void negativePatternCallTest() { | ||
361 | Relation<Boolean> person = new Relation<Boolean>("Person", 1, false); | ||
362 | Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE); | ||
363 | RelationView<Boolean> persionView = new KeyOnlyRelationView(person); | ||
364 | RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must()); | ||
365 | |||
366 | Variable p1 = new Variable("p1"); | ||
367 | Variable p2 = new Variable("p2"); | ||
368 | List<Variable> parameters = Arrays.asList(p1, p2); | ||
369 | |||
370 | RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1)); | ||
371 | RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2)); | ||
372 | RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2)); | ||
373 | DNFAnd clause = new DNFAnd(Collections.emptySet(), | ||
374 | Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom)); | ||
375 | DNFPredicate friendPredicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause)); | ||
376 | |||
377 | Variable p3 = new Variable("p3"); | ||
378 | Variable p4 = new Variable("p4"); | ||
379 | List<Variable> substitution = Arrays.asList(p3, p4); | ||
380 | RelationAtom personRelationAtom3 = new RelationAtom(persionView, Arrays.asList(p3)); | ||
381 | RelationAtom personRelationAtom4 = new RelationAtom(persionView, Arrays.asList(p4)); | ||
382 | PredicateAtom friendPredicateAtom = new PredicateAtom(false, false, friendPredicate, substitution); | ||
383 | DNFAnd negativePatternCallClause = new DNFAnd(Collections.emptySet(), | ||
384 | Arrays.asList(personRelationAtom3, personRelationAtom4, friendPredicateAtom)); | ||
385 | DNFPredicate predicate = new DNFPredicate("NegativePatternCall", substitution, | ||
386 | Arrays.asList(negativePatternCallClause)); | ||
387 | |||
388 | QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend), | ||
389 | Set.of(persionView, friendMustView), Set.of(friendPredicate, predicate)); | ||
390 | QueriableModel model = store.createModel(); | ||
391 | |||
392 | model.put(person, Tuple.of(0), true); | ||
393 | model.put(person, Tuple.of(1), true); | ||
394 | model.put(person, Tuple.of(2), true); | ||
395 | model.put(friend, Tuple.of(0, 1), TruthValue.TRUE); | ||
396 | model.put(friend, Tuple.of(1, 0), TruthValue.TRUE); | ||
397 | model.put(friend, Tuple.of(1, 2), TruthValue.TRUE); | ||
398 | |||
399 | model.flushChanges(); | ||
400 | assertEquals(6, model.countResults(predicate)); | ||
401 | } | ||
402 | |||
403 | @Test | ||
404 | void transitivePatternCallTest() { | ||
405 | Relation<Boolean> person = new Relation<Boolean>("Person", 1, false); | ||
406 | Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE); | ||
407 | RelationView<Boolean> persionView = new KeyOnlyRelationView(person); | ||
408 | RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must()); | ||
409 | |||
410 | Variable p1 = new Variable("p1"); | ||
411 | Variable p2 = new Variable("p2"); | ||
412 | List<Variable> parameters = Arrays.asList(p1, p2); | ||
413 | |||
414 | RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1)); | ||
415 | RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2)); | ||
416 | RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2)); | ||
417 | DNFAnd clause = new DNFAnd(Collections.emptySet(), | ||
418 | Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom)); | ||
419 | DNFPredicate friendPredicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause)); | ||
420 | |||
421 | Variable p3 = new Variable("p3"); | ||
422 | Variable p4 = new Variable("p4"); | ||
423 | List<Variable> substitution = Arrays.asList(p3, p4); | ||
424 | RelationAtom personRelationAtom3 = new RelationAtom(persionView, Arrays.asList(p3)); | ||
425 | RelationAtom personRelationAtom4 = new RelationAtom(persionView, Arrays.asList(p4)); | ||
426 | PredicateAtom friendPredicateAtom = new PredicateAtom(true, true, friendPredicate, substitution); | ||
427 | DNFAnd patternCallClause = new DNFAnd(Collections.emptySet(), | ||
428 | Arrays.asList(personRelationAtom3, personRelationAtom4, friendPredicateAtom)); | ||
429 | DNFPredicate predicate = new DNFPredicate("TransitivePatternCall", substitution, | ||
430 | Arrays.asList(patternCallClause)); | ||
431 | |||
432 | QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend), | ||
433 | Set.of(persionView, friendMustView), Set.of(friendPredicate, predicate)); | ||
434 | QueriableModel model = store.createModel(); | ||
435 | |||
436 | model.put(person, Tuple.of(0), true); | ||
437 | model.put(person, Tuple.of(1), true); | ||
438 | model.put(person, Tuple.of(2), true); | ||
439 | model.put(friend, Tuple.of(0, 1), TruthValue.TRUE); | ||
440 | model.put(friend, Tuple.of(1, 2), TruthValue.TRUE); | ||
441 | |||
442 | model.flushChanges(); | ||
443 | assertEquals(3, model.countResults(predicate)); | ||
444 | } | ||
445 | } \ No newline at end of file | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/query/test/QueryTransactionTest.java b/subprojects/store/src/test/java/tools/refinery/store/query/test/QueryTransactionTest.java new file mode 100644 index 00000000..e72186b9 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/query/test/QueryTransactionTest.java | |||
@@ -0,0 +1,58 @@ | |||
1 | package tools.refinery.store.query.test; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | |||
5 | import java.util.Arrays; | ||
6 | import java.util.Collections; | ||
7 | import java.util.List; | ||
8 | import java.util.Set; | ||
9 | |||
10 | import org.junit.jupiter.api.Test; | ||
11 | |||
12 | import tools.refinery.store.model.Tuple; | ||
13 | import tools.refinery.store.model.representation.Relation; | ||
14 | import tools.refinery.store.query.QueriableModel; | ||
15 | import tools.refinery.store.query.QueriableModelStore; | ||
16 | import tools.refinery.store.query.QueriableModelStoreImpl; | ||
17 | import tools.refinery.store.query.building.DNFAnd; | ||
18 | import tools.refinery.store.query.building.DNFPredicate; | ||
19 | import tools.refinery.store.query.building.RelationAtom; | ||
20 | import tools.refinery.store.query.building.Variable; | ||
21 | import tools.refinery.store.query.view.KeyOnlyRelationView; | ||
22 | import tools.refinery.store.query.view.RelationView; | ||
23 | |||
24 | class QueryTransactionTest { | ||
25 | @Test | ||
26 | void flushTest() { | ||
27 | Relation<Boolean> person = new Relation<>("Person", 1, false); | ||
28 | Relation<Boolean> asset = new Relation<>("Asset", 1, false); | ||
29 | RelationView<Boolean> persionView = new KeyOnlyRelationView(person); | ||
30 | |||
31 | List<Variable> parameters = Arrays.asList(new Variable("p1")); | ||
32 | RelationAtom personRelationAtom = new RelationAtom(persionView, parameters); | ||
33 | DNFAnd clause = new DNFAnd(Collections.emptySet(), Arrays.asList(personRelationAtom)); | ||
34 | DNFPredicate predicate = new DNFPredicate("TypeConstraint", parameters, Arrays.asList(clause)); | ||
35 | |||
36 | QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, asset), Set.of(persionView), | ||
37 | Set.of(predicate)); | ||
38 | QueriableModel model = store.createModel(); | ||
39 | |||
40 | assertEquals(0, model.countResults(predicate)); | ||
41 | |||
42 | model.put(person, Tuple.of(0), true); | ||
43 | model.put(person, Tuple.of(1), true); | ||
44 | model.put(asset, Tuple.of(1), true); | ||
45 | model.put(asset, Tuple.of(2), true); | ||
46 | |||
47 | assertEquals(0, model.countResults(predicate)); | ||
48 | |||
49 | model.flushChanges(); | ||
50 | assertEquals(2, model.countResults(predicate)); | ||
51 | |||
52 | model.put(person, Tuple.of(4), true); | ||
53 | assertEquals(2, model.countResults(predicate)); | ||
54 | |||
55 | model.flushChanges(); | ||
56 | assertEquals(3, model.countResults(predicate)); | ||
57 | } | ||
58 | } | ||
diff --git a/subprojects/store/src/test/java/tools/refinery/store/util/CollectionsUtilTests.java b/subprojects/store/src/test/java/tools/refinery/store/util/CollectionsUtilTests.java new file mode 100644 index 00000000..171be0e5 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/util/CollectionsUtilTests.java | |||
@@ -0,0 +1,78 @@ | |||
1 | package tools.refinery.store.util; | ||
2 | |||
3 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
4 | import static tools.refinery.store.util.CollectionsUtil.filter; | ||
5 | import static tools.refinery.store.util.CollectionsUtil.map; | ||
6 | |||
7 | import java.util.ArrayList; | ||
8 | import java.util.Iterator; | ||
9 | import java.util.List; | ||
10 | import java.util.NoSuchElementException; | ||
11 | |||
12 | import org.junit.jupiter.api.Assertions; | ||
13 | import org.junit.jupiter.api.Test; | ||
14 | |||
15 | class CollectionsUtilTests { | ||
16 | List<Integer> list10 = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); | ||
17 | List<String> listTen = List.of("1", "2", "3", "4", "5", "6", "7", "8", "9", "10"); | ||
18 | |||
19 | private static <T> void compare(Iterable<T> a, Iterable<T> b) { | ||
20 | List<T> listA = toList(a); | ||
21 | List<T> listB = toList(b); | ||
22 | assertEquals(listA, listB); | ||
23 | } | ||
24 | |||
25 | private static <T> List<T> toList(Iterable<T> a) { | ||
26 | List<T> result = new ArrayList<T>(); | ||
27 | Iterator<T> iterator = a.iterator(); | ||
28 | while (iterator.hasNext()) { | ||
29 | result.add(iterator.next()); | ||
30 | } | ||
31 | return result; | ||
32 | } | ||
33 | |||
34 | @Test | ||
35 | void testFilterEven() { | ||
36 | compare(List.of(2, 4, 6, 8, 10), filter(list10, (x -> x % 2 == 0))); | ||
37 | } | ||
38 | |||
39 | @Test | ||
40 | void testFilterOdd() { | ||
41 | compare(List.of(1, 3, 5, 7, 9), filter(list10, (x -> x % 2 == 1))); | ||
42 | } | ||
43 | |||
44 | @Test | ||
45 | void testFilterFalse() { | ||
46 | compare(List.of(), filter(list10, (x -> false))); | ||
47 | } | ||
48 | |||
49 | @Test | ||
50 | void testFilterTrue() { | ||
51 | compare(list10, filter(list10, (x -> true))); | ||
52 | } | ||
53 | |||
54 | @Test | ||
55 | void testFilterEmpty() { | ||
56 | compare(List.of(), filter(List.of(), (x -> true))); | ||
57 | } | ||
58 | |||
59 | @Test() | ||
60 | void testNoSuchElement() { | ||
61 | Iterable<Integer> iterable = filter(list10, (x -> x % 2 == 0)); | ||
62 | Iterator<Integer> iterator = iterable.iterator(); | ||
63 | while (iterator.hasNext()) { | ||
64 | iterator.next(); | ||
65 | } | ||
66 | Assertions.assertThrows(NoSuchElementException.class, () -> iterator.next()); | ||
67 | } | ||
68 | |||
69 | @Test() | ||
70 | void mapTest() { | ||
71 | compare(listTen, map(list10, x -> x.toString())); | ||
72 | } | ||
73 | |||
74 | @Test() | ||
75 | void mapEmtyTest() { | ||
76 | compare(List.of(), map(List.of(), x -> x.toString())); | ||
77 | } | ||
78 | } | ||