From 147a1c19c1f7bfe8d0939618d3c8dc2bb4e59fb7 Mon Sep 17 00:00:00 2001 From: Matthias Andreas Benkard Date: Sun, 10 Dec 2023 20:54:33 +0100 Subject: Add more property-based tests and fix more bugs. Change-Id: I8deb1a7d75078c037714541d8f6f656052c2476c --- .../main/java/eu/mulk/jgvariant/core/Decoder.java | 4 +- .../eu/mulk/jgvariant/ostree/DeltaOperation.java | 4 +- .../ostree/OstreeDecoderPropertyTest.java | 225 ++++++++++++++++++++- 3 files changed, 227 insertions(+), 6 deletions(-) diff --git a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java index fcbb639..2121cfb 100644 --- a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java +++ b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java @@ -364,7 +364,7 @@ public abstract class Decoder { // Determining the framing offset size requires trial and error. int framingOffsetSize; for (framingOffsetSize = 0;; framingOffsetSize = max(1, framingOffsetSize << 1)) { - if (elementsRelativeEnd + framingOffsetSize* framingOffsets.size() >= 1 << (8*framingOffsetSize)) { + if (elementsRelativeEnd + (long)framingOffsetSize * framingOffsets.size() >= 1L << (8*framingOffsetSize)) { continue; } @@ -457,7 +457,7 @@ public abstract class Decoder { } // Write the framing offsets. - int framingOffsetSize = computeFramingOffsetSize(byteWriter.position() - startOffset, framingOffsets); + int framingOffsetSize = max(1, computeFramingOffsetSize(byteWriter.position() - startOffset, framingOffsets)); for (var framingOffset : framingOffsets) { byteWriter.writeIntN(framingOffset, framingOffsetSize); } diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaOperation.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaOperation.java index 42b7056..bb31e50 100644 --- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaOperation.java +++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaOperation.java @@ -130,6 +130,7 @@ public sealed interface DeltaOperation { * @see #readVarint64 */ private static void writeVarint64(ByteArrayOutputStream output, long value) { + int n = 0; do { byte b = (byte) (value & 0x7F); value >>= 7; @@ -137,6 +138,7 @@ public sealed interface DeltaOperation { b |= (byte) 0x80; } output.write(b); - } while (value != 0); + ++n; + } while (value != 0 && n < 10); } } diff --git a/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderPropertyTest.java b/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderPropertyTest.java index acd11c4..da15ff2 100644 --- a/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderPropertyTest.java +++ b/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderPropertyTest.java @@ -1,6 +1,9 @@ package eu.mulk.jgvariant.ostree; +import static org.junit.jupiter.api.Assertions.assertEquals; + import eu.mulk.jgvariant.core.Decoder; +import java.util.List; import java.util.Map; import net.jqwik.api.*; @@ -21,14 +24,88 @@ class OstreeDecoderPropertyTest { } } + @Group + class DeltaSuperblockRoundtripLaw implements RoundtripLaw { + + @Override + public Decoder decoder() { + return DeltaSuperblock.decoder(); + } + + @Override + public Arbitrary anyT() { + return anyDeltaSuperblock(); + } + } + + @Group + @Disabled( + "Not implemented correctly: Requires enough file entries to parse all the delta operations.") + class DeltaPartPayloadRoundtripLaw implements RoundtripLaw { + + @Override + public Decoder decoder() { + // FIXME + var deltaMetaEntry = new DeltaMetaEntry(0, Checksum.zero(), 0, 0, List.of()); + return DeltaPartPayload.decoder(deltaMetaEntry); + } + + @Override + public Arbitrary anyT() { + return anyDeltaPartPayload(); + } + } + + @Group + class DirTreeRoundtripLaw implements RoundtripLaw { + + @Override + public Decoder decoder() { + return DirTree.decoder(); + } + + @Override + public Arbitrary anyT() { + return anyDirTree(); + } + } + + @Group + class DirMetaRoundtripLaw implements RoundtripLaw { + + @Override + public Decoder decoder() { + return DirMeta.decoder(); + } + + @Override + public Arbitrary anyT() { + return anyDirMeta(); + } + } + + @Group + class CommitRoundtripLaw implements RoundtripLaw { + + @Override + public Decoder decoder() { + return Commit.decoder(); + } + + @Override + public Arbitrary anyT() { + return anyCommit(); + } + } + interface RoundtripLaw { @Property - default boolean roundtripsWell(@ForAll(value = "anyT") T entityLeft) { + default void roundtripsWell(@ForAll(value = "anyT") T entityLeft) { var decoder = decoder(); var bytes = decoder.encode(entityLeft); var entityRight = decoder.decode(bytes); - return entityLeft.equals(entityRight); + assertEquals(entityLeft, entityRight); } Decoder decoder(); @@ -61,6 +138,148 @@ class OstreeDecoderPropertyTest { @Provide Arbitrary anyChecksum() { - return Arbitraries.of(new Checksum(new ByteString(new byte[32]))); + return Arbitraries.bytes() + .array(byte[].class) + .ofSize(32) + .map(ByteString::new) + .map(Checksum::new); + } + + @Provide + Arbitrary anyDeltaSuperblock() { + return Combinators.combine( + anyMetadata(), + Arbitraries.longs(), + anyChecksum(), + anyChecksum(), + anyCommit(), + anyDeltaName().list(), + anyDeltaMetaEntry().list(), + anyDeltaFallback().list()) + .as(DeltaSuperblock::new); + } + + @Provide + Arbitrary anyDeltaPartPayload() { + return Combinators.combine( + anyFileMode().list(), + anyXattr().list().list(), + anyByteString(), + anyDeltaOperation().list()) + .as(DeltaPartPayload::new); + } + + @Provide + Arbitrary anyDeltaOperation() { + return Arbitraries.oneOf( + Combinators.combine(Arbitraries.longs(), Arbitraries.longs()) + .as(DeltaOperation.OpenSpliceAndCloseMeta::new), + Combinators.combine( + Arbitraries.longs(), Arbitraries.longs(), Arbitraries.longs(), Arbitraries.longs()) + .as(DeltaOperation.OpenSpliceAndCloseReal::new), + Combinators.combine(Arbitraries.longs(), Arbitraries.longs(), Arbitraries.longs()) + .as(DeltaOperation.Open::new), + Combinators.combine(Arbitraries.longs(), Arbitraries.longs()).as(DeltaOperation.Write::new), + Arbitraries.longs().map(DeltaOperation.SetReadSource::new), + Arbitraries.of(new DeltaOperation.UnsetReadSource()), + Arbitraries.of(new DeltaOperation.Close()), + Combinators.combine(Arbitraries.longs(), Arbitraries.longs()) + .as(DeltaOperation.BsPatch::new)); + } + + @Provide + Arbitrary anyFileMode() { + return Combinators.combine( + Arbitraries.integers(), Arbitraries.integers(), Arbitraries.integers()) + .as(DeltaPartPayload.FileMode::new); + } + + @Provide + Arbitrary anyXattr() { + return Combinators.combine(anyByteString(), anyByteString()).as(Xattr::new); + } + + @Provide + Arbitrary anyByteString() { + return Arbitraries.bytes().array(byte[].class).map(ByteString::new); + } + + @Provide + Arbitrary anyDirTree() { + return Combinators.combine(anyDirTreeFile().list(), anyDirTreeDirectory().list()) + .as(DirTree::new); + } + + @Provide + Arbitrary anyDirMeta() { + return Combinators.combine( + Arbitraries.integers(), + Arbitraries.integers(), + Arbitraries.integers(), + anyXattr().list()) + .as(DirMeta::new); + } + + @Provide + Arbitrary anyCommit() { + return Combinators.combine( + anyMetadata(), + anyChecksum(), + anyRelatedObject().list(), + Arbitraries.strings(), + Arbitraries.strings(), + Arbitraries.longs(), + anyChecksum(), + anyChecksum()) + .as(Commit::new); + } + + @Provide + Arbitrary anyRelatedObject() { + return Combinators.combine(Arbitraries.strings(), anyChecksum()).as(Commit.RelatedObject::new); + } + + @Provide + Arbitrary anyDeltaName() { + return Combinators.combine(anyChecksum(), anyChecksum()).as(DeltaSuperblock.DeltaName::new); + } + + @Provide + Arbitrary anyDeltaMetaEntry() { + return Combinators.combine( + Arbitraries.integers(), + anyChecksum(), + Arbitraries.longs(), + Arbitraries.longs(), + anyDeltaObject().list()) + .as(DeltaMetaEntry::new); + } + + @Provide + Arbitrary anyDeltaObject() { + return Combinators.combine(anyObjectType(), anyChecksum()).as(DeltaMetaEntry.DeltaObject::new); + } + + @Provide + Arbitrary anyObjectType() { + return Arbitraries.of(ObjectType.values()); + } + + @Provide + Arbitrary anyDeltaFallback() { + return Combinators.combine( + anyObjectType(), anyChecksum(), Arbitraries.longs(), Arbitraries.longs()) + .as(DeltaFallback::new); + } + + @Provide + Arbitrary anyDirTreeDirectory() { + return Combinators.combine(Arbitraries.strings(), anyChecksum(), anyChecksum()) + .as(DirTree.Directory::new); + } + + @Provide + Arbitrary anyDirTreeFile() { + return Combinators.combine(Arbitraries.strings(), anyChecksum()).as(DirTree.File::new); } } -- cgit v1.2.3