Skip to content

Commit

Permalink
added a lot more tests and fixed some issues triggered by those tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jurgenvinju committed Oct 10, 2023
1 parent 1789dec commit 46fb81b
Show file tree
Hide file tree
Showing 14 changed files with 179 additions and 24 deletions.
10 changes: 10 additions & 0 deletions src/main/java/io/usethesource/vallang/IBool.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@
import io.usethesource.vallang.visitors.IValueVisitor;

public interface IBool extends IValue {
@Override
default int getMatchFingerprint() {
if (getValue()) {
return 3569038; /* "true".hashCode() */
}
else {
return 97196323; /* "false".hashCode() */
}
}

boolean getValue();
String getStringRepresentation();
IBool and(IBool other);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/usethesource/vallang/IConstructor.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
public interface IConstructor extends INode {

@Override
default int getPatternMatchFingerprint() {
default int getMatchFingerprint() {
return getName().hashCode() << 2 + arity();

// TODO: this would distinguish more constructors based on incomparable argument types:
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/usethesource/vallang/IExternalValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public interface IExternalValue extends IValue {
* instead of returning `IValue.hashCode()` automatically.
*/
@Override
int getPatternMatchFingerprint();
int getMatchFingerprint();

/**
* @return an ExternalType
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/io/usethesource/vallang/IInteger.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@
import io.usethesource.vallang.visitors.IValueVisitor;

public interface IInteger extends INumber {
@Override
default int getMatchFingerprint() {
if (signum() == 0) {
return 104431; /* "int".hashCode() */
}
else {
return hashCode();
}
}

/**
* @return this + other;
*/
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/usethesource/vallang/IList.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
public interface IList extends ICollection<IList> {

@Override
default int getPatternMatchFingerprint() {
default int getMatchFingerprint() {
return 3322014; // "list".hashCode()
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/usethesource/vallang/IMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
public interface IMap extends ICollection<IMap> {

@Override
default int getPatternMatchFingerprint() {
default int getMatchFingerprint() {
return 107868; // "map".hashCode()
}

Expand Down
6 changes: 4 additions & 2 deletions src/main/java/io/usethesource/vallang/INode.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@
public interface INode extends IValue, Iterable<IValue> {

@Override
default int getPatternMatchFingerprint() {
return getName().hashCode() << 2 + arity();
default int getMatchFingerprint() {
int hash = getName().hashCode();

return hash == 0 ? 3386882 /* node.hashCode() */ + arity() : hash << 2 + arity();
}

/**
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/io/usethesource/vallang/IReal.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
import io.usethesource.vallang.visitors.IValueVisitor;

public interface IReal extends INumber {
@Override
default int getMatchFingerprint() {
int hash = hashCode();
return hash == 0 ? 3496350 /* real.hashCode() */ : hash;
}

/**
* @return this + other;
*/
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/usethesource/vallang/ISet.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
public interface ISet extends ICollection<ISet> {

@Override
default int getPatternMatchFingerprint() {
default int getMatchFingerprint() {
return 113762; // "set".hashCode()
}

Expand Down
10 changes: 10 additions & 0 deletions src/main/java/io/usethesource/vallang/IString.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@

public interface IString extends IValue, Iterable<Integer> {

@Override
default int getMatchFingerprint() {
if (length() == 0) {
return 114225; /* "str".hashCode() */
}
else {
return hashCode();
}
}

/**
* @return the Java string that this string represents
*/
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/usethesource/vallang/ITuple.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

public interface ITuple extends Iterable<IValue>, IValue {
@Override
default int getPatternMatchFingerprint() {
default int getMatchFingerprint() {
return 442900256 /* "tuple".hashCode() << 2 */ + arity();
}

Expand Down
14 changes: 13 additions & 1 deletion src/main/java/io/usethesource/vallang/IValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,22 @@ public interface IValue {
* * distinguishes maximally between different kinds of values
* * never makes the same or similar value have a different fingerprint
*/
default int getPatternMatchFingerprint() {
default int getMatchFingerprint() {
return hashCode();
}

/**
* This method is used exclusively by code generated by the Rascal compiler,
*
* @return an integer code that:
* * is guaranteed to be different from `getMatchFingerPrint`
* * is guaranteed to be constant
* * is guaranteed to be the same for every IValue
*/
default int getDefaultMatchFingerprint() {
return 0;
}

/**
* Execute the {@link IValueVisitor} on the current node
*
Expand Down
20 changes: 19 additions & 1 deletion src/test/java/io/usethesource/vallang/ValueProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,26 @@ private RandomTypesConfig configureRandomTypes(TypeConfig typeConfig, int depth)
* @return an instance assignable to `cl`
*/
private IValue generateValue(IValueFactory vf, TypeStore ts, Class<? extends IValue> cl, ExpectedType expected, int depth, int width) {
Type expectedType = expected != null ? readType(ts, expected) : types.getOrDefault(cl, (x, n) -> tf.valueType()).apply(ts, expected);
Type expectedType = tf.voidType();


// this should terminate through random selection.
// only tuple types with nested void arguments can reduce to void.
int i = 0;
while (expectedType.isBottom() && i++ < 1000) {
if (expected != null) {
expectedType = readType(ts, expected);
break;
}
else {
expectedType = types
.getOrDefault(cl, (x, n) -> tf.valueType())
.apply(ts, expected);
}
}

assert !expectedType.isBottom() : cl + " generated void type?";

if (previous != null && rnd.nextInt(4) == 0 && previous.getType().isSubtypeOf(expectedType)) {
return rnd.nextBoolean() ? previous : reinstantiate(vf, ts, previous);
}
Expand Down
115 changes: 101 additions & 14 deletions src/test/java/io/usethesource/vallang/specification/IValueTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.IOException;
Expand All @@ -18,6 +19,7 @@
import io.usethesource.vallang.IRational;
import io.usethesource.vallang.IReal;
import io.usethesource.vallang.ISet;
import io.usethesource.vallang.ISourceLocation;
import io.usethesource.vallang.IString;
import io.usethesource.vallang.ITuple;
import io.usethesource.vallang.IValue;
Expand Down Expand Up @@ -57,25 +59,110 @@ public void testHashCodeContract(IValue val1, IValue val2) {
@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintContract(IValue val1, IValue val2) {
if (val1.equals(val2)) {
assertEquals(val1.getPatternMatchFingerprint(), val2.getPatternMatchFingerprint(), "" + val1.toString() + " and " + val2.toString() + " are equal but do not have the same fingerprint?");
assertEquals(val1.getMatchFingerprint(), val2.getMatchFingerprint(), "" + val1.toString() + " and " + val2.toString() + " are equal but do not have the same fingerprint?");
}
assertTrue(!val1.equals(val2) || val1.getPatternMatchFingerprint() == val2.getPatternMatchFingerprint());
assertTrue(!val1.equals(val2) || val1.getMatchFingerprint() == val2.getMatchFingerprint());
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintStability(IInteger integer, IString string, IReal real, IRational rational, IList list, ISet set, IMap map, ITuple tuple, IConstructor constructor, INode node) {
// if we really want to change these codes, we should be aware that we are breaking all previously compiled and released Rascal code.
public void testDefaultFingerprintContracts(IValue val1) {
assertEquals(val1.getDefaultMatchFingerprint(), 0);

assertEquals(integer.hashCode(), integer.getPatternMatchFingerprint());
assertEquals(string.hashCode(), string.getPatternMatchFingerprint());
assertEquals(real.hashCode(), real.getPatternMatchFingerprint());
assertEquals(rational.hashCode(), rational.getPatternMatchFingerprint());
assertEquals("list".hashCode(), list.getPatternMatchFingerprint());
assertEquals("set".hashCode(), set.getPatternMatchFingerprint());
assertEquals("map".hashCode(), map.getPatternMatchFingerprint());
assertEquals("tuple".hashCode() << 2 + tuple.arity(), tuple.getPatternMatchFingerprint());
assertEquals(constructor.getName().hashCode() << 2 + constructor.arity(), constructor.getPatternMatchFingerprint());
assertEquals(node.getName().hashCode() << 2 + node.arity(), node.getPatternMatchFingerprint());
if (val1.getMatchFingerprint() == 0) {
System.err.println(val1 + " has a 0 fingerprint?");
}
assertNotEquals(val1.getDefaultMatchFingerprint(), val1.getMatchFingerprint());
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintStabilityIntegersDoNotChangeTheTest(IValueFactory vf, IInteger integer) {
assertEquals(integer.equals(vf.integer(0)) ? "int".hashCode() : integer.hashCode(), integer.getMatchFingerprint());
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintStabilityStringDoNotChangeTheTest(IString string) {
assertEquals(string.length() == 0 ? "str".hashCode() : string.hashCode(), string.getMatchFingerprint());
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintStabilityRealDoNotChangeTheTest(IReal real) {
assertEquals(real.hashCode() == 0 ? "real".hashCode() : real.hashCode(), real.getMatchFingerprint());
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintStabilityRationalDoNotChangeTheTest(IRational rational) {
assertEquals(rational.hashCode(), rational.getMatchFingerprint());
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintStabilityListDoNotChangeTheTest(IList list) {
assertEquals("list".hashCode(), list.getMatchFingerprint());
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintAllListsTheSameDoNotChangeTheTest(IList list1, IList list2) {
assertEquals(list1.getMatchFingerprint(), list2.getMatchFingerprint());
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintStabilitySetDoNotChangeTheTest(ISet set) {
assertEquals("set".hashCode(), set.getMatchFingerprint());
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintAllSetsTheSameDoNotChangeTheTest(ISet set1, ISet set2) {
assertEquals(set1.getMatchFingerprint(), set2.getMatchFingerprint());
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintStabilityMapDoNotChangeTheTest(IMap map) {
assertEquals("map".hashCode(), map.getMatchFingerprint());
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintAllMapsTheSameDoNotChangeTheTest(IMap map1, IMap map2) {
assertEquals(map1.getMatchFingerprint(), map2.getMatchFingerprint());
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintStabilityTupleDoNotChangeTheTest(ITuple tuple) {
assertEquals("tuple".hashCode() << 2 + tuple.arity(), tuple.getMatchFingerprint());
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintEqualArityTuplesTheSameDoNotChangeTheTest(ITuple tuple1, ITuple tuple2) {
if (tuple1.arity() == tuple2.arity()) {
assertEquals(tuple1.getMatchFingerprint(), tuple2.getMatchFingerprint());
}
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintStabilityNodeDoNotChangeTheTest(ISourceLocation node) {
assertEquals(node.hashCode(), node.getMatchFingerprint());
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintStabilityNodeDoNotChangeTheTest(INode node) {
assertEquals(node.hashCode() == 0 ? "node".hashCode() << 2 + node.arity() : node.hashCode() << 2 + node.arity(), node.getMatchFingerprint());
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintEqualArityNodesTheSameDoNotChangeTheTest(INode node1, INode node2) {
if (node1.arity() == node2.arity()) {
assertEquals(node1.getMatchFingerprint(), node2.getMatchFingerprint());
}
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintStabilityRationalDoNotChangeTheTest(IConstructor constructor) {
assertEquals(constructor.getName().hashCode() << 2 + constructor.arity(), constructor.getMatchFingerprint());
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
public void testFingerprintEqualArityConstructorsTheSameDoNotChangeTheTest(IConstructor node1, IConstructor node2) {
if (node1.arity() == node2.arity()) {
assertEquals(node1.getMatchFingerprint(), node2.getMatchFingerprint());
}
}

@ParameterizedTest @ArgumentsSource(ValueProvider.class)
Expand Down

0 comments on commit 46fb81b

Please sign in to comment.