From 22925124ca31db8dd0f67c2977a42f2a4a1d6038 Mon Sep 17 00:00:00 2001 From: mikera Date: Sun, 22 Dec 2024 17:33:05 +0000 Subject: [PATCH] More generative testing, include extension values --- .../main/java/convex/core/cvm/Address.java | 12 +------- .../main/java/convex/core/cvm/ops/Local.java | 10 ++++++- .../java/convex/core/cvm/ops/Special.java | 7 +++++ .../convex/core/data/AExtensionValue.java | 17 +++++++++++ .../java/convex/core/data/ExtensionValue.java | 12 +------- .../java/convex/core/lang/impl/CoreFn.java | 11 +++++++- .../java/convex/core/message/Message.java | 1 - .../convex/core/data/GenTestAnyValue.java | 2 +- .../{data => message}/GenTestMessages.java | 10 +++++-- .../convex/core/message}/MessageTest.java | 26 +++++++++-------- .../convex/test/generators/AnyMapGen.java | 28 +++++++++++++++++-- .../convex/test/generators/CollectionGen.java | 3 +- .../test/generators/ExtensionValueGen.java | 26 +++++++++++++++++ .../test/java/convex/test/generators/Gen.java | 2 ++ .../convex/test/generators/HashMapGen.java | 22 +++++++++++++++ .../convex/test/generators/IntegerGen.java | 4 +-- .../java/convex/test/generators/ValueGen.java | 11 +++++--- 17 files changed, 152 insertions(+), 52 deletions(-) rename convex-core/src/test/java/convex/core/{data => message}/GenTestMessages.java (82%) rename {convex-peer/src/test/java/convex/net => convex-core/src/test/java/convex/core/message}/MessageTest.java (94%) create mode 100644 convex-core/src/test/java/convex/test/generators/ExtensionValueGen.java diff --git a/convex-core/src/main/java/convex/core/cvm/Address.java b/convex-core/src/main/java/convex/core/cvm/Address.java index 6afca2c8f..68b9b029f 100644 --- a/convex-core/src/main/java/convex/core/cvm/Address.java +++ b/convex-core/src/main/java/convex/core/cvm/Address.java @@ -46,14 +46,9 @@ public final class Address extends AExtensionValue { * The maximum possible Address */ public static final Address MAX_VALUE = Address.create(Long.MAX_VALUE); - - /** - * 64-bit address value - */ - private long value; private Address(long value) { - this.value=value; + super(value); } /** @@ -265,11 +260,6 @@ public final int getBytes(byte[] bs, int pos) { return pos; } - @Override - public long longValue() { - return value; - } - @Override public boolean equalsBytes(ABlob b) { if (b.count()!=LENGTH) return false; diff --git a/convex-core/src/main/java/convex/core/cvm/ops/Local.java b/convex-core/src/main/java/convex/core/cvm/ops/Local.java index 5f57cacab..6540a0969 100644 --- a/convex-core/src/main/java/convex/core/cvm/ops/Local.java +++ b/convex-core/src/main/java/convex/core/cvm/ops/Local.java @@ -11,6 +11,7 @@ import convex.core.data.IRefFunction; import convex.core.data.util.BlobBuilder; import convex.core.exceptions.InvalidDataException; +import convex.core.util.Bits; import convex.core.util.ErrorMessages; /** @@ -62,7 +63,7 @@ public byte getTag() { @Override public int encodeRaw(byte[] bs, int pos) { - pos=Format.writeVLQLong(bs, pos, position); + pos=Format.writeVLQCount(bs, pos, position); return pos; } @@ -82,6 +83,13 @@ public void validateCell() throws InvalidDataException { throw new InvalidDataException("Invalid Local position "+position, this); } } + + @Override + public final int hashCode() { + // Needed for hashCode equivalence with extension values + return Bits.hash32(position); + } + @Override public int getRefCount() { diff --git a/convex-core/src/main/java/convex/core/cvm/ops/Special.java b/convex-core/src/main/java/convex/core/cvm/ops/Special.java index f02a16cfb..95e1a2cc8 100644 --- a/convex-core/src/main/java/convex/core/cvm/ops/Special.java +++ b/convex-core/src/main/java/convex/core/cvm/ops/Special.java @@ -18,6 +18,7 @@ import convex.core.data.util.BlobBuilder; import convex.core.exceptions.BadFormatException; import convex.core.exceptions.InvalidDataException; +import convex.core.util.Bits; import convex.core.util.ErrorMessages; import convex.core.util.Utils; @@ -206,6 +207,12 @@ public static Special forSymbol(Symbol sym) { public static Special get(String string) { return forSymbol(Symbol.create(string)); } + + @Override + public final int hashCode() { + // Needed for hashCode equivalence with extension values + return Bits.hash32((long)specialCode); + } @SuppressWarnings("unchecked") diff --git a/convex-core/src/main/java/convex/core/data/AExtensionValue.java b/convex-core/src/main/java/convex/core/data/AExtensionValue.java index 4cdbb87e6..9bfa8623d 100644 --- a/convex-core/src/main/java/convex/core/data/AExtensionValue.java +++ b/convex-core/src/main/java/convex/core/data/AExtensionValue.java @@ -2,6 +2,7 @@ import convex.core.data.impl.LongBlob; import convex.core.data.prim.CVMLong; +import convex.core.util.Bits; import convex.core.util.ErrorMessages; import convex.core.util.Utils; @@ -14,7 +15,13 @@ public abstract class AExtensionValue extends ABlobLike { * Length of an extension value in bytes (when considered as a Blob) */ protected static final int BYTE_LENGTH = 8; + + protected final long value; + protected AExtensionValue(long value) { + this.value=value; + } + @Override public final long count() { return BYTE_LENGTH; @@ -36,6 +43,11 @@ public ABlob toBlob() { return LongBlob.create(longValue()); } + @Override + public final long longValue() { + return value; + } + protected static void checkIndex(long i) { if ((i < 0) || (i >= BYTE_LENGTH)) throw new IndexOutOfBoundsException(ErrorMessages.badIndex(i)); } @@ -88,6 +100,11 @@ public AExtensionValue empty() { return null; } + @Override + public final int hashCode() { + return Bits.hash32(value); + } + @Override public boolean isEmbedded() { return true; diff --git a/convex-core/src/main/java/convex/core/data/ExtensionValue.java b/convex-core/src/main/java/convex/core/data/ExtensionValue.java index 31ac7f284..afef19b6d 100644 --- a/convex-core/src/main/java/convex/core/data/ExtensionValue.java +++ b/convex-core/src/main/java/convex/core/data/ExtensionValue.java @@ -3,17 +3,15 @@ import convex.core.data.util.BlobBuilder; import convex.core.exceptions.InvalidDataException; import convex.core.lang.RT; -import convex.core.util.Bits; import convex.core.util.Utils; public class ExtensionValue extends AExtensionValue { protected final byte tag; - protected final long value; protected ExtensionValue(byte tag,long value) { + super(value); this.tag=tag; - this.value=value; } /** @@ -37,10 +35,6 @@ public void validateCell() throws InvalidDataException { } } - @Override - public int hashCode() { - return Bits.hash32(value); - } @Override public final byte byteAt(long i) { @@ -59,10 +53,6 @@ public final int getBytes(byte[] bs, int pos) { return pos; } - @Override - public long longValue() { - return value; - } @Override public byte getTag() { diff --git a/convex-core/src/main/java/convex/core/lang/impl/CoreFn.java b/convex-core/src/main/java/convex/core/lang/impl/CoreFn.java index c0ec6c830..f7be4480c 100644 --- a/convex-core/src/main/java/convex/core/lang/impl/CoreFn.java +++ b/convex-core/src/main/java/convex/core/lang/impl/CoreFn.java @@ -4,12 +4,14 @@ import convex.core.cvm.CVMTag; import convex.core.cvm.Context; import convex.core.data.ACell; +import convex.core.data.Cells; import convex.core.data.Format; import convex.core.data.IRefFunction; import convex.core.data.Ref; import convex.core.data.Symbol; import convex.core.data.util.BlobBuilder; import convex.core.exceptions.InvalidDataException; +import convex.core.util.Bits; /** * Abstract base class for core language functions implemented in the Runtime @@ -110,6 +112,12 @@ public int getRefCount() { return 0; } + @Override + public final int hashCode() { + // This is needed to match hash behaviour of extension values + return Bits.hash32(code); + } + @Override public Ref getRef(int i) { throw new IndexOutOfBoundsException("Bad ref index: "+i); @@ -145,7 +153,8 @@ protected final long calcMemorySize() { @Override public boolean equals(ACell o) { // This is OK since these are guaranteed to be singleton instances! - return o==this; + if (o instanceof CoreFn) return o==this; + return Cells.equalsGeneric(this, o); } @Override diff --git a/convex-core/src/main/java/convex/core/message/Message.java b/convex-core/src/main/java/convex/core/message/Message.java index ac3ce4ba4..cea403750 100644 --- a/convex-core/src/main/java/convex/core/message/Message.java +++ b/convex-core/src/main/java/convex/core/message/Message.java @@ -32,7 +32,6 @@ import convex.core.lang.RT; import convex.core.lang.Reader; import convex.core.store.AStore; -import convex.core.text.PrintUtils; import convex.core.util.Utils; /** diff --git a/convex-core/src/test/java/convex/core/data/GenTestAnyValue.java b/convex-core/src/test/java/convex/core/data/GenTestAnyValue.java index 9360caae2..ff8c00fcb 100644 --- a/convex-core/src/test/java/convex/core/data/GenTestAnyValue.java +++ b/convex-core/src/test/java/convex/core/data/GenTestAnyValue.java @@ -118,8 +118,8 @@ public void dataRoundTrip(@From(ValueGen.class) ACell o) throws BadFormatExcepti // equality checks assertEquals(o,o2); - if (o!=null) assertEquals(o.hashCode(),o2.hashCode()); assertEquals(hash,Hash.get(o2)); + if (o!=null) assertEquals(o.hashCode(),o2.hashCode()); // re-encoding AArrayBlob data2=Cells.encode(o2); diff --git a/convex-core/src/test/java/convex/core/data/GenTestMessages.java b/convex-core/src/test/java/convex/core/message/GenTestMessages.java similarity index 82% rename from convex-core/src/test/java/convex/core/data/GenTestMessages.java rename to convex-core/src/test/java/convex/core/message/GenTestMessages.java index d3246e847..8bb363ca8 100644 --- a/convex-core/src/test/java/convex/core/data/GenTestMessages.java +++ b/convex-core/src/test/java/convex/core/message/GenTestMessages.java @@ -1,4 +1,4 @@ -package convex.core.data; +package convex.core.message; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -11,9 +11,10 @@ import com.pholser.junit.quickcheck.Property; import com.pholser.junit.quickcheck.runner.JUnitQuickcheck; +import convex.core.data.ACell; +import convex.core.data.Blob; +import convex.core.data.Format; import convex.core.exceptions.BadFormatException; -import convex.core.message.Message; -import convex.core.message.MessageType; import convex.test.generators.ValueGen; @RunWith(JUnitQuickcheck.class) @@ -29,8 +30,11 @@ public void messageLengthVLQ(Integer a) throws BadFormatException { assertEquals(bb.remaining(), Format.getVLQCountLength(a)); } + // @When(seed = -1436059931257127601l) + // @From(ValueGen.class) @Property public void testCAD3Message(@From(ValueGen.class) ACell a) throws BadFormatException { + // Any CAD3 object should encode as a complete message Message m=Message.create(null,a); MessageType mtype=m.getType(); diff --git a/convex-peer/src/test/java/convex/net/MessageTest.java b/convex-core/src/test/java/convex/core/message/MessageTest.java similarity index 94% rename from convex-peer/src/test/java/convex/net/MessageTest.java rename to convex-core/src/test/java/convex/core/message/MessageTest.java index a1a7746b7..f7c123669 100644 --- a/convex-peer/src/test/java/convex/net/MessageTest.java +++ b/convex-core/src/test/java/convex/core/message/MessageTest.java @@ -1,4 +1,4 @@ -package convex.net; +package convex.core.message; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -9,15 +9,16 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; -import convex.core.data.Format; - import java.util.Arrays; import java.util.Random; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.Test; +import convex.core.Result; +import convex.core.cpos.Belief; +import convex.core.cpos.Block; +import convex.core.cpos.CPoSConstants; import convex.core.cpos.Order; - import convex.core.crypto.AKeyPair; import convex.core.cvm.Address; import convex.core.cvm.Symbols; @@ -28,20 +29,15 @@ import convex.core.data.Blob; import convex.core.data.Blobs; import convex.core.data.Cells; +import convex.core.data.Format; import convex.core.data.Hash; import convex.core.data.SignedData; import convex.core.data.Vectors; import convex.core.data.prim.CVMLong; import convex.core.exceptions.BadFormatException; import convex.core.lang.RT; -import convex.core.message.Message; -import convex.core.message.MessageTag; -import convex.core.message.MessageType; +import convex.core.lang.Reader; import convex.core.store.Stores; -import convex.core.Result; -import convex.core.cpos.Belief; -import convex.core.cpos.Block; -import convex.core.cpos.CPoSConstants; public class MessageTest { static Hash BAD_HASH=Hash.EMPTY_HASH; @@ -181,6 +177,12 @@ public void testStatusMessage() { doMessageTest(m); } + @Test public void testCoreDefExtension() throws BadFormatException { + // This was a regression at one point where Local was badly re-encoded + Message m=Message.create(null,Reader.read("#[e645]")); + doMessageTest(m); + } + /** * Generic tests for any valid message * @param m diff --git a/convex-core/src/test/java/convex/test/generators/AnyMapGen.java b/convex-core/src/test/java/convex/test/generators/AnyMapGen.java index f6b3f3dac..e0a934864 100644 --- a/convex-core/src/test/java/convex/test/generators/AnyMapGen.java +++ b/convex-core/src/test/java/convex/test/generators/AnyMapGen.java @@ -1,5 +1,9 @@ package convex.test.generators; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import com.pholser.junit.quickcheck.generator.GenerationStatus; import com.pholser.junit.quickcheck.random.SourceOfRandomness; @@ -26,7 +30,7 @@ public AnyMapGen() { public AMap generate(SourceOfRandomness r, GenerationStatus status) { int type = r.nextInt(); - switch (type % 8) { + switch (type % 10) { case 0: return Maps.empty(); case 1: @@ -48,12 +52,32 @@ public AMap generate(SourceOfRandomness r, GenerationStatus status) { return Index.create(o1, o2); } - default: { + case 7: { AVector vec = gen().make(VectorGen.class).generate(r, status); vec = (AVector) vec.subList(0, vec.size() & (~1)); Object[] os = vec.toArray(); return Maps.of(os); } + + default: + return Gen.HASHMAP.generate(r, status); + } + } + + @SuppressWarnings("unchecked") + @Override + public List doShrink(SourceOfRandomness r, AMap m) { + long n=m.count(); + if (n==0) return Collections.EMPTY_LIST; + if (n==1) return Collections.singletonList(Maps.empty()); + ArrayList al=new ArrayList<>(); + + for (int i=0; i> { +public class CollectionGen extends AGenerator> { @SuppressWarnings("rawtypes") private static final Class cls = (Class) ACollection.class; diff --git a/convex-core/src/test/java/convex/test/generators/ExtensionValueGen.java b/convex-core/src/test/java/convex/test/generators/ExtensionValueGen.java new file mode 100644 index 000000000..e74ea6a51 --- /dev/null +++ b/convex-core/src/test/java/convex/test/generators/ExtensionValueGen.java @@ -0,0 +1,26 @@ +package convex.test.generators; + +import com.pholser.junit.quickcheck.generator.GenerationStatus; +import com.pholser.junit.quickcheck.random.SourceOfRandomness; + +import convex.core.data.ExtensionValue; +import convex.core.data.Tag; + +/** + * Generator for CAD3 dense records + * + */ +public class ExtensionValueGen extends AGenerator { + public ExtensionValueGen() { + super(ExtensionValue.class); + } + + @Override + public ExtensionValue generate(SourceOfRandomness r, GenerationStatus status) { + + byte type = (byte)(r.nextInt(16)+Tag.EXTENSION_VALUE_BASE); + long val=r.nextLong(0, 1+status.size()); + + return ExtensionValue.create(type, val); + } +} diff --git a/convex-core/src/test/java/convex/test/generators/Gen.java b/convex-core/src/test/java/convex/test/generators/Gen.java index 19e4d7179..068809f70 100644 --- a/convex-core/src/test/java/convex/test/generators/Gen.java +++ b/convex-core/src/test/java/convex/test/generators/Gen.java @@ -47,4 +47,6 @@ public class Gen { public static final DenseRecordGen DENSE_RECORD = new DenseRecordGen(); public static final OpGen OP = new OpGen(); + + public static final ExtensionValueGen EXTENSION_VALUE = new ExtensionValueGen(); } diff --git a/convex-core/src/test/java/convex/test/generators/HashMapGen.java b/convex-core/src/test/java/convex/test/generators/HashMapGen.java index 27531e011..3ec50833e 100644 --- a/convex-core/src/test/java/convex/test/generators/HashMapGen.java +++ b/convex-core/src/test/java/convex/test/generators/HashMapGen.java @@ -1,5 +1,9 @@ package convex.test.generators; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import com.pholser.junit.quickcheck.generator.GenerationStatus; import com.pholser.junit.quickcheck.generator.Generator; import com.pholser.junit.quickcheck.random.SourceOfRandomness; @@ -7,6 +11,7 @@ import convex.core.data.ACell; import convex.core.data.AHashMap; import convex.core.data.AVector; +import convex.core.data.Cells; import convex.core.data.Maps; import convex.test.Samples; @@ -45,4 +50,21 @@ public HashMapGen() { } } } + + @SuppressWarnings("unchecked") + @Override + public List doShrink(SourceOfRandomness r, AHashMap m) { + long n=m.count(); + if (n==0) return Collections.EMPTY_LIST; + if (n==1) return Collections.singletonList(Maps.empty()); + ArrayList al=new ArrayList<>(); + + for (int i=0; i { +public class IntegerGen extends AGenerator { public IntegerGen() { super(AInteger.class); } @@ -34,7 +33,6 @@ public AInteger generate(SourceOfRandomness r, GenerationStatus status) { // fallthorugh if null default: return Gen.LONG.generate(r, status); - } } } diff --git a/convex-core/src/test/java/convex/test/generators/ValueGen.java b/convex-core/src/test/java/convex/test/generators/ValueGen.java index d795cc38a..35b95da25 100644 --- a/convex-core/src/test/java/convex/test/generators/ValueGen.java +++ b/convex-core/src/test/java/convex/test/generators/ValueGen.java @@ -1,7 +1,6 @@ package convex.test.generators; import com.pholser.junit.quickcheck.generator.GenerationStatus; -import com.pholser.junit.quickcheck.generator.Generator; import com.pholser.junit.quickcheck.random.SourceOfRandomness; import convex.core.cvm.Syntax; @@ -10,14 +9,14 @@ /** * Generator for arbitrary CVM values */ -public class ValueGen extends Generator { +public class ValueGen extends AGenerator { public ValueGen() { super(ACell.class); } @Override public ACell generate(SourceOfRandomness r, GenerationStatus status) { - int type = r.nextInt(15); + int type = r.nextInt(20); switch (type) { case 0: return null; @@ -49,9 +48,13 @@ public ACell generate(SourceOfRandomness r, GenerationStatus status) { return Gen.RECORD.generate(r, status); case 14: return Syntax.create(generate(r, status)); + case 15: + return Gen.EXTENSION_VALUE.generate(r, status); + case 16: + return Gen.CHAR.generate(r, status); default: - throw new Error("Unexpected type: " + type); + return Gen.LONG.generate(r, status); } } }