diff options
Diffstat (limited to 'jgvariant-core')
| -rw-r--r-- | jgvariant-core/pom.xml | 5 | ||||
| -rw-r--r-- | jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java | 18 | ||||
| -rw-r--r-- | jgvariant-core/src/test/java/eu/mulk/jgvariant/core/DecoderPropertyTest.java | 89 | 
3 files changed, 107 insertions, 5 deletions
| diff --git a/jgvariant-core/pom.xml b/jgvariant-core/pom.xml index b37b65c..da34551 100644 --- a/jgvariant-core/pom.xml +++ b/jgvariant-core/pom.xml @@ -61,6 +61,11 @@ SPDX-License-Identifier: LGPL-3.0-or-later        <artifactId>junit-jupiter-api</artifactId>        <scope>test</scope>      </dependency> +    <dependency> +      <groupId>net.jqwik</groupId> +      <artifactId>jqwik</artifactId> +      <scope>test</scope> +    </dependency>    </dependencies>  </project> 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 9362487..fcbb639 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 @@ -342,14 +342,22 @@ public abstract class Decoder<T> {    }    private static int getIntN(ByteBuffer byteSlice) { -    var intBytes = new byte[4]; -    byteSlice.get(intBytes, 0, Math.min(4, byteSlice.limit())); -    return ByteBuffer.wrap(intBytes).order(LITTLE_ENDIAN).getInt(); +    return switch (byteSlice.limit()) { +      case 0 -> 0; +      case 1 -> +        Byte.toUnsignedInt(byteSlice.order(LITTLE_ENDIAN).get()); +      case 2 -> +        Short.toUnsignedInt(byteSlice.order(LITTLE_ENDIAN).getShort()); +      case 4 -> +        byteSlice.order(LITTLE_ENDIAN).getInt(); +      default -> +        throw new IllegalArgumentException("invalid byte count: %d".formatted(byteSlice.limit())); +    };    }    @SuppressWarnings("java:S3358")    private static int byteCount(int n) { -    return n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4; +    return n == 0 ? 0 : n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4;    }    private static int computeFramingOffsetSize(int elementsRelativeEnd, List<Integer> framingOffsets) { @@ -404,7 +412,7 @@ public abstract class Decoder<T> {          elements = List.of();        } else {          // An array with aligned elements and a vector of framing offsets in the end. -        int framingOffsetSize = byteCount(byteSlice.limit()); +        int framingOffsetSize = max(1, byteCount(byteSlice.limit()));          int lastFramingOffset =              getIntN(byteSlice.slice(byteSlice.limit() - framingOffsetSize, framingOffsetSize));          int elementCount = (byteSlice.limit() - lastFramingOffset) / framingOffsetSize; diff --git a/jgvariant-core/src/test/java/eu/mulk/jgvariant/core/DecoderPropertyTest.java b/jgvariant-core/src/test/java/eu/mulk/jgvariant/core/DecoderPropertyTest.java new file mode 100644 index 0000000..5e07ea0 --- /dev/null +++ b/jgvariant-core/src/test/java/eu/mulk/jgvariant/core/DecoderPropertyTest.java @@ -0,0 +1,89 @@ +package eu.mulk.jgvariant.core; + +import java.text.ParseException; +import java.util.Optional; +import net.jqwik.api.*; + +@SuppressWarnings("java:S2187") +class DecoderPropertyTest { + +  @Group +  class VariantRoundtripLaw implements RoundtripLaw<Variant> { + +    @Override +    public Decoder<Variant> decoder() { +      return Decoder.ofVariant(); +    } + +    @Override +    public Arbitrary<Variant> anyT() { +      return anyVariant(); +    } +  } + +  interface RoundtripLaw<T> { + +    @Property +    default boolean roundtripsWell(@ForAll(value = "anyT") T entityLeft) { +      var decoder = decoder(); +      var bytes = decoder.encode(entityLeft); +      var entityRight = decoder.decode(bytes); +      return entityLeft.equals(entityRight); +    } + +    Decoder<T> decoder(); + +    @Provide +    Arbitrary<T> anyT(); +  } + +  @Provide +  Arbitrary<Variant> anyVariant() { +    var anyString = Arbitraries.strings().map(s -> new Variant(parseSignature("s"), s)); +    var anyInt = Arbitraries.integers().map(i -> new Variant(parseSignature("i"), i)); +    var anyLong = Arbitraries.longs().map(l -> new Variant(parseSignature("x"), l)); +    var anyDouble = Arbitraries.doubles().map(d -> new Variant(parseSignature("d"), d)); +    var anyBoolean = +        Arbitraries.of(Boolean.TRUE, Boolean.FALSE).map(b -> new Variant(parseSignature("b"), b)); +    var anyByte = Arbitraries.bytes().map(b -> new Variant(parseSignature("y"), b)); +    var anyShort = Arbitraries.shorts().map(s -> new Variant(parseSignature("n"), s)); +    var anyByteArray = Arbitraries.bytes().list().map(b -> new Variant(parseSignature("ay"), b)); +    var anySome = +        Arbitraries.lazyOf( +            () -> +                anyVariant() +                    .map( +                        x -> +                            new Variant( +                                parseSignature("m" + x.signature().toString()), +                                Optional.of(x.value())))); +    var anyNone = +        Arbitraries.lazyOf( +            () -> +                anyVariant() +                    .map( +                        x -> +                            new Variant( +                                parseSignature("m" + x.signature().toString()), Optional.empty()))); +    // FIXME missing: list, tuple, dictionary, variant +    return Arbitraries.oneOf( +        anyString, +        anyInt, +        anyLong, +        anyDouble, +        anyBoolean, +        anyByte, +        anyShort, +        anyByteArray, +        anySome, +        anyNone); +  } + +  private Signature parseSignature(String s) { +    try { +      return Signature.parse(s); +    } catch (ParseException e) { +      throw new AssertionError(e); +    } +  } +} | 
