Skip to content

Commit 48f93b4

Browse files
committed
test: new tests for Views
1 parent 9f2c50d commit 48f93b4

4 files changed

Lines changed: 170 additions & 0 deletions

File tree

src/test/java/ViewsTest.java

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import static org.variantsync.diffdetective.util.fide.FormulaUtils.and;
2+
import static org.variantsync.diffdetective.util.fide.FormulaUtils.negate;
3+
import static org.variantsync.diffdetective.util.fide.FormulaUtils.var;
4+
5+
import java.io.IOException;
6+
import java.nio.file.Path;
7+
import java.util.ArrayList;
8+
import java.util.Arrays;
9+
import java.util.List;
10+
11+
import org.junit.jupiter.params.ParameterizedTest;
12+
import org.junit.jupiter.params.provider.MethodSource;
13+
import org.prop4j.Literal;
14+
import org.prop4j.Node;
15+
import org.variantsync.diffdetective.diff.result.DiffParseException;
16+
import org.variantsync.diffdetective.util.Assert;
17+
import org.variantsync.diffdetective.variation.DiffLinesLabel;
18+
import org.variantsync.diffdetective.variation.diff.DiffNode;
19+
import org.variantsync.diffdetective.variation.diff.VariationDiff;
20+
import org.variantsync.diffdetective.variation.diff.parse.VariationDiffParseOptions;
21+
import org.variantsync.diffdetective.variation.diff.view.DiffView;
22+
import org.variantsync.diffdetective.variation.tree.VariationTree;
23+
import org.variantsync.diffdetective.variation.tree.view.TreeView;
24+
import org.variantsync.diffdetective.variation.tree.view.relevance.Configure;
25+
import org.variantsync.diffdetective.variation.tree.view.relevance.Relevance;
26+
import org.variantsync.diffdetective.variation.tree.view.relevance.spec.ConfigureSpec;
27+
28+
/**
29+
* Tests for views on variation trees and diffs.
30+
*/
31+
public class ViewsTest {
32+
private static final Path
33+
resDir = Constants.RESOURCE_DIR.resolve("diffs").resolve("views"),
34+
treeDir = resDir.resolve("tree"), // directory of test cases for variation trees
35+
diffDir = resDir.resolve("diff") // directory of test cases for variation diffs
36+
;
37+
38+
/**
39+
* This record holds the implementation of a relevance predicate and its specification.
40+
* Both predicates will be tested for semantic equivalence.
41+
* The idea is that the first relevance is an optimized implementation, whereas the second
42+
* relevance is an easy-to-understand and easy-to-verify but suboptimal implementation.
43+
**/
44+
private static record RelevanceSpec(Relevance impl, Relevance spec) {}
45+
46+
// These classes are test cases for testing consistency between a relevance predicate's implementation and specification
47+
// on trees and diffs respectively.
48+
private static record TreeConsistencyTestCase(VariationTree<DiffLinesLabel> tree, List<RelevanceSpec> relevances) {}
49+
private static record DiffConsistencyTestCase(VariationDiff<DiffLinesLabel> diff, List<RelevanceSpec> relevances) {}
50+
51+
/**
52+
* Creates a TreeConsistencyTestCase for the Configure relevance.
53+
* @param fileName The name of the file to parse as variation tree.
54+
* @param fs any number of formulas to test view generation with
55+
*/
56+
private static TreeConsistencyTestCase treeConf(String fileName, Node... fs) throws IOException, DiffParseException {
57+
return new TreeConsistencyTestCase(
58+
VariationTree.fromFile(treeDir.resolve(fileName), VariationDiffParseOptions.Default),
59+
Arrays.stream(fs).map(f -> new RelevanceSpec(new Configure(f), new ConfigureSpec(f))).toList()
60+
);
61+
}
62+
63+
/**
64+
* Creates a DiffConsistencyTestCase for the Configure relevance.
65+
* @param fileName The name of the file to parse as variation diff.
66+
* @param fs any number of formulas to test view generation with
67+
*/
68+
private static DiffConsistencyTestCase diffConf(String fileName, Node... fs) throws IOException, DiffParseException {
69+
return new DiffConsistencyTestCase(
70+
VariationDiff.fromFile(diffDir.resolve(fileName), VariationDiffParseOptions.Default),
71+
Arrays.stream(fs).map(f -> new RelevanceSpec(new Configure(f), new ConfigureSpec(f))).toList()
72+
);
73+
}
74+
75+
private static List<TreeConsistencyTestCase> treeConsistencyTestCases() throws IOException, DiffParseException {
76+
final Literal A = var("A"), B = var("B"), C = var("C"), D = var("D"), E = var("E");
77+
return List.of(
78+
treeConf("else.c", A, negate(A), C),
79+
treeConf("elif.c", A, negate(A), B, negate(B)),
80+
treeConf("elif-chain.c", negate(E), and(negate(A), negate(B), negate(C), negate(D)))
81+
);
82+
}
83+
84+
public static List<DiffConsistencyTestCase> diffConsistencyTestCases() throws IOException, DiffParseException {
85+
List<DiffConsistencyTestCase> testCases = new ArrayList<>();
86+
87+
// We reuse all test cases for variation trees for variation diffs.
88+
testCases.addAll(treeConsistencyTestCases().stream().map(
89+
treeTestCase -> new DiffConsistencyTestCase(treeTestCase.tree.toCompletelyUnchangedVariationDiff(), treeTestCase.relevances)).toList()
90+
);
91+
92+
// TODO: Create some additional test cases targeted specifically at variation diffs.
93+
// In these test cases, the projections should be different.
94+
95+
return testCases;
96+
}
97+
98+
/**
99+
* Tests that a {@link Relevance} predicate is consistent with its specification on all
100+
* supplied test cases.
101+
*/
102+
@ParameterizedTest
103+
@MethodSource("treeConsistencyTestCases")
104+
public void consistencyOnTrees(TreeConsistencyTestCase t) {
105+
for (RelevanceSpec relevance : t.relevances) {
106+
final VariationTree<DiffLinesLabel> impl = TreeView.tree(t.tree, relevance.impl);
107+
final VariationTree<DiffLinesLabel> spec = TreeView.tree(t.tree, relevance.spec);
108+
109+
// To compare the trees, we create a diff.
110+
// If there are no deleted or removed nodes, both trees are equal.
111+
final VariationDiff<DiffLinesLabel> d = VariationDiff.fromTrees(impl, spec);
112+
Assert.assertTrue(d.allMatch(DiffNode::isNon));
113+
}
114+
}
115+
116+
/**
117+
* Tests that
118+
* (1) a {@link Relevance} predicate is consistent with its specification, and
119+
* (2) the optimized generation of views on variation diffs
120+
* ({@link DiffView#optimized(VariationDiff, Relevance)}) is consistent
121+
* with the naive implementation ({@link DiffView#naive(VariationDiff, Relevance)})
122+
* which rather serves as a specification.
123+
*/
124+
@ParameterizedTest
125+
@MethodSource("diffConsistencyTestCases")
126+
public void consistencyOnDiffs(DiffConsistencyTestCase t) throws IOException, DiffParseException {
127+
for (RelevanceSpec relevance : t.relevances) {
128+
// All of these must be equal.
129+
final VariationDiff<DiffLinesLabel> naive_impl = DiffView.naive(t.diff, relevance.impl);
130+
final VariationDiff<DiffLinesLabel> naive_spec = DiffView.naive(t.diff, relevance.spec);
131+
final VariationDiff<DiffLinesLabel> opt_impl = DiffView.optimized(t.diff, relevance.impl);
132+
final VariationDiff<DiffLinesLabel> opt_spec = DiffView.optimized(t.diff, relevance.spec);
133+
134+
// First, we test consistency among relevance implementation and relevance specification.
135+
Assert.assertTrue(naive_impl.isSameAsIgnoringLineNumbers(naive_spec));
136+
Assert.assertTrue(opt_impl.isSameAsIgnoringLineNumbers(opt_spec));
137+
138+
// Second, assuming relevance consistency, we test consistency between optimized and naive view generation.
139+
// We get the last comparison Assert.assertTrue(opt_impl.isSameAsIgnoringLineNumbers(naive_impl)) by transitivity.
140+
Assert.assertTrue(opt_spec.isSameAsIgnoringLineNumbers(naive_spec));
141+
}
142+
}
143+
144+
// TODO: Test cases where we check the result of computing a view against a predefined ground truth.
145+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
int x =
2+
#ifdef A
3+
0
4+
#elif B
5+
1
6+
#elif C
7+
2
8+
#elif D
9+
3
10+
#elif E
11+
4
12+
#else
13+
5
14+
#endif
15+
;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#ifdef A
2+
foo
3+
#elif B
4+
bar
5+
#endif
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#ifdef A
2+
foo
3+
#else
4+
bar
5+
#endif

0 commit comments

Comments
 (0)