未验证 提交 efd5565f 编写于 作者: E Eirik Tsarpalis 提交者: GitHub

Add CBOR property-based tests (#39828)

* Add CBOR property-based tests

* address feedback
上级 2b2eb5ad
......@@ -142,6 +142,7 @@
<TraceEventVersion>2.0.5</TraceEventVersion>
<NewtonsoftJsonVersion>12.0.3</NewtonsoftJsonVersion>
<MoqVersion>4.12.0</MoqVersion>
<FsCheckVersion>2.14.3</FsCheckVersion>
<!-- Docs -->
<MicrosoftPrivateIntellisenseVersion>3.0.0-preview-20200715.1</MicrosoftPrivateIntellisenseVersion>
<!-- ILLink -->
......
......@@ -14,6 +14,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Formats.Cbor", "ref\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Formats.Cbor.Tests", "tests\System.Formats.Cbor.Tests.csproj", "{1C84C43C-69A8-493E-AC11-0DA9C22BFB46}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "System.Formats.Cbor.Tests.DataModel", "tests\CborDocument\System.Formats.Cbor.Tests.DataModel.fsproj", "{3BF538B4-0985-4918-90A7-8F15BA9E661C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -32,6 +34,10 @@ Global
{1C84C43C-69A8-493E-AC11-0DA9C22BFB46}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1C84C43C-69A8-493E-AC11-0DA9C22BFB46}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1C84C43C-69A8-493E-AC11-0DA9C22BFB46}.Release|Any CPU.Build.0 = Release|Any CPU
{3BF538B4-0985-4918-90A7-8F15BA9E661C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3BF538B4-0985-4918-90A7-8F15BA9E661C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3BF538B4-0985-4918-90A7-8F15BA9E661C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3BF538B4-0985-4918-90A7-8F15BA9E661C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......@@ -40,6 +46,7 @@ Global
{57B5AD1E-D0BA-4811-9276-9BBAFF44AB31} = {59B37ECC-D5BB-488A-9571-1E239EDF19A9}
{B17130D2-0A75-464A-A3E7-67123C567CAF} = {B3417D0B-D64E-4CC8-AEAA-F0C7C963248F}
{1C84C43C-69A8-493E-AC11-0DA9C22BFB46} = {00675670-C8E7-4428-A16F-9E6B933586A8}
{3BF538B4-0985-4918-90A7-8F15BA9E661C} = {00675670-C8E7-4428-A16F-9E6B933586A8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {36EE2FB8-4CD8-4A12-8FD5-9FDF3D0F6D40}
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace System.Formats.Cbor.Tests.DataModel
open System
// --
// Models a CBOR document type using a discriminated union.
// Discriminated unions have structural equality for free
// and random instances can be generated using FsCheck.
//
// The type and serialization helpers are used by the
// property tests in the main test project.
//
type CborDocument =
// Major type 0
| UnsignedInteger of uint64
// Major type 1
| NegativeInteger of uint64
// Major type 2
| ByteString of byte[]
| ByteStringIndefiniteLength of byte[][]
// Major type 3
| TextString of string
| TextStringIndefiniteLength of string[]
// Major type 4
| Array of isDefiniteLength:bool * CborDocument[]
// Major type 5
| Map of isDefiniteLength:bool * Map<CborDocument, CborDocument>
// Major type 6
| Tag of tag:uint64 * CborDocument
| SemanticValue of CborSemanticValue
// Major type 7
| Double of double
| SimpleValue of value:byte
// Defines a set of semantic values drawing either from the CBOR spec
// or invented here for the purposes of this test.
and CborSemanticValue =
| Null
| Bool of bool
| Int32 of int
| Int64 of int64
| Half of Half
| Single of single
| DateTimeOffset of DateTimeOffset
| UnixTimeSeconds of seconds:int64
| BigInt of bigint
| Decimal of decimal
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
module rec System.Formats.Cbor.Tests.DataModel.CborDocumentSerializer
open System
open System.Formats.Cbor
open System.Formats.Cbor.Tests.DataModel
let createWriter (context : CborPropertyTestContext) =
new CborWriter(
conformanceMode = context.ConformanceMode,
convertIndefiniteLengthEncodings = context.ConvertIndefiniteLengthItems,
allowMultipleRootLevelValues = (context.RootDocuments.Length > 1))
let createReader (context : CborPropertyTestContext) (encoding : byte[]) =
new CborReader(
data = ReadOnlyMemory<byte>.op_Implicit encoding,
conformanceMode = context.ConformanceMode,
allowMultipleRootLevelValues = (context.RootDocuments.Length > 1))
let encode (context : CborPropertyTestContext) =
let writer = createWriter context
for doc in context.RootDocuments do
write writer doc
writer.Encode()
let decode (context : CborPropertyTestContext) (data : byte[]) =
let reader = createReader context data
let values = new System.Collections.Generic.List<_>()
while reader.PeekState() <> CborReaderState.Finished do
values.Add(read reader)
values.ToArray()
let rec write (writer : CborWriter) (doc : CborDocument) =
match doc with
| UnsignedInteger i -> writer.WriteUInt64 i
| NegativeInteger i -> writer.WriteCborNegativeIntegerRepresentation i
| ByteString bs -> writer.WriteByteString bs
| TextString ts -> writer.WriteTextString ts
| ByteStringIndefiniteLength bss ->
writer.WriteStartIndefiniteLengthByteString()
for bs in bss do
writer.WriteByteString bs
writer.WriteEndIndefiniteLengthByteString()
| TextStringIndefiniteLength tss ->
writer.WriteStartIndefiniteLengthTextString()
for ts in tss do
writer.WriteTextString ts
writer.WriteEndIndefiniteLengthTextString()
| Array (isDefiniteLength, elements) ->
writer.WriteStartArray (if isDefiniteLength then Nullable elements.Length else Nullable())
for elem in elements do
write writer elem
writer.WriteEndArray()
| Map (isDefiniteLength, pairs) ->
writer.WriteStartMap (if isDefiniteLength then Nullable pairs.Count else Nullable())
for kv in pairs do
write writer kv.Key ; write writer kv.Value
writer.WriteEndMap()
| Tag (tag, nested) ->
writer.WriteTag (LanguagePrimitives.EnumOfValue tag)
write writer nested
| Double d -> writer.WriteDouble d
| SimpleValue v -> writer.WriteSimpleValue(LanguagePrimitives.EnumOfValue v)
| SemanticValue value ->
writer.WriteTag CborTagExt.SemValueGuard
writeSemanticValue writer value
let private writeSemanticValue (writer : CborWriter) (s : CborSemanticValue) =
match s with
| Null -> writer.WriteTag CborTagExt.Null; writer.WriteNull()
| Bool b -> writer.WriteTag CborTagExt.Bool; writer.WriteBoolean b
| Int32 i -> writer.WriteTag CborTagExt.Int32; writer.WriteInt32 i
| Int64 i -> writer.WriteTag CborTagExt.Int64; writer.WriteInt64 i
| Half f -> writer.WriteTag CborTagExt.Half; writer.WriteHalf f
| Single f -> writer.WriteTag CborTagExt.Single; writer.WriteSingle f
| DateTimeOffset d -> writer.WriteDateTimeOffset d
| UnixTimeSeconds s -> writer.WriteUnixTimeSeconds s
| BigInt i -> writer.WriteBigInteger i
| Decimal d -> writer.WriteDecimal d
let rec read (reader : CborReader) : CborDocument =
match reader.PeekState() with
| CborReaderState.UnsignedInteger -> UnsignedInteger(reader.ReadUInt64())
| CborReaderState.NegativeInteger -> NegativeInteger(reader.ReadCborNegativeIntegerRepresentation())
| CborReaderState.ByteString -> ByteString(reader.ReadByteString())
| CborReaderState.TextString -> TextString(reader.ReadTextString())
| CborReaderState.StartArray ->
let length = reader.ReadStartArray()
if length.HasValue then
let results = Array.zeroCreate<CborDocument> length.Value
for i = 0 to results.Length - 1 do
results.[i] <- read reader
reader.ReadEndArray()
Array(true, results)
else
let results = new System.Collections.Generic.List<_>()
while reader.PeekState() <> CborReaderState.EndArray do
results.Add(read reader)
reader.ReadEndArray()
Array(false, results.ToArray())
| CborReaderState.StartIndefiniteLengthByteString ->
reader.ReadStartIndefiniteLengthByteString()
let chunks = new System.Collections.Generic.List<byte[]>()
while reader.PeekState() <> CborReaderState.EndIndefiniteLengthByteString do
chunks.Add(reader.ReadByteString())
reader.ReadEndIndefiniteLengthByteString()
ByteStringIndefiniteLength(chunks.ToArray())
| CborReaderState.StartIndefiniteLengthTextString ->
reader.ReadStartIndefiniteLengthTextString()
let chunks = new System.Collections.Generic.List<string>()
while reader.PeekState() <> CborReaderState.EndIndefiniteLengthTextString do
chunks.Add(reader.ReadTextString())
reader.ReadEndIndefiniteLengthTextString()
TextStringIndefiniteLength(chunks.ToArray())
| CborReaderState.StartMap ->
let length = reader.ReadStartMap()
if length.HasValue then
let results = Array.zeroCreate<CborDocument * CborDocument> length.Value
for i = 0 to results.Length - 1 do
results.[i] <- (read reader, read reader)
reader.ReadEndMap()
Map(true, Map.ofArray results)
else
let results = new System.Collections.Generic.List<_>()
while reader.PeekState() <> CborReaderState.EndMap do
results.Add(read reader, read reader)
reader.ReadEndMap()
Map(false, Map.ofSeq results)
| CborReaderState.Tag ->
let tag = reader.ReadTag()
if tag = CborTagExt.SemValueGuard then SemanticValue(readSemanticValue reader)
else Tag (LanguagePrimitives.EnumToValue tag, read reader)
| CborReaderState.HalfPrecisionFloat
| CborReaderState.SinglePrecisionFloat
| CborReaderState.DoublePrecisionFloat -> Double (reader.ReadDouble())
| CborReaderState.Null
| CborReaderState.Boolean
| CborReaderState.SimpleValue ->
let value = reader.ReadSimpleValue()
SimpleValue(LanguagePrimitives.EnumToValue value)
| state -> failwithf "Unrecognized reader state %O" state
let private readSemanticValue (reader : CborReader) : CborSemanticValue =
match reader.PeekTag() with
| CborTagExt.Null -> let _ = reader.ReadTag() in reader.ReadNull(); Null
| CborTagExt.Bool -> let _ = reader.ReadTag() in Bool(reader.ReadBoolean())
| CborTagExt.Int32 -> let _ = reader.ReadTag() in Int32(reader.ReadInt32())
| CborTagExt.Int64 -> let _ = reader.ReadTag() in Int64(reader.ReadInt64())
| CborTagExt.Half -> let _ = reader.ReadTag() in Half(reader.ReadHalf())
| CborTagExt.Single -> let _ = reader.ReadTag() in Single(reader.ReadSingle())
| CborTag.DateTimeString -> DateTimeOffset(reader.ReadDateTimeOffset())
| CborTag.UnixTimeSeconds -> let dto = reader.ReadUnixTimeSeconds() in UnixTimeSeconds(dto.ToUnixTimeSeconds())
| CborTag.UnsignedBigNum
| CborTag.NegativeBigNum -> BigInt(reader.ReadBigInteger())
| CborTag.DecimalFraction -> Decimal(reader.ReadDecimal())
| tag -> failwithf "Unrecognized tag %O" tag
// defines a set of custom CBOR tags for semantic value encodings
module private CborTagExt =
let [<Literal>] SemValueGuard : CborTag = LanguagePrimitives.EnumOfValue 50015001uL
let [<Literal>] Null : CborTag = LanguagePrimitives.EnumOfValue 5001uL
let [<Literal>] Bool : CborTag = LanguagePrimitives.EnumOfValue 5002uL
let [<Literal>] Int32 : CborTag = LanguagePrimitives.EnumOfValue 5003uL
let [<Literal>] Int64 : CborTag = LanguagePrimitives.EnumOfValue 5004uL
let [<Literal>] Half : CborTag = LanguagePrimitives.EnumOfValue 5005uL
let [<Literal>] Single : CborTag = LanguagePrimitives.EnumOfValue 5006uL
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace System.Formats.Cbor.Tests.DataModel
open System.Formats.Cbor
open System.Formats.Cbor.Tests.DataModel
/// Randomly generated record containing parameters for a CBOR property-based test
[<CLIMutable>]
type CborPropertyTestContext =
{
RootDocuments : CborDocument[]
ConformanceMode : CborConformanceMode
ConvertIndefiniteLengthItems : bool
}
module rec CborPropertyTestContextHelper =
/// Identifies & transforms documents that might not be accepted under the supplied conformance mode
let create (mode : CborConformanceMode) (convertIndefiniteLengthItems : bool) (docs : CborDocument[]) =
{
RootDocuments = Array.map (normalize mode convertIndefiniteLengthItems) docs
ConvertIndefiniteLengthItems = convertIndefiniteLengthItems
ConformanceMode = mode
}
/// Gets the expected value that we would expected to see after a full serialization/deserialization roundtrip
let getExpectedRoundtripValues (context : CborPropertyTestContext) =
Array.map (getExpectedRoundtripValue context.ConvertIndefiniteLengthItems) context.RootDocuments
/// Identifies & transforms documents that might not be accepted under the supplied conformance mode
let private normalize (mode : CborConformanceMode) (convertIndefiniteLengthItems : bool) (doc : CborDocument) =
// normalization can lead to collisions in conformance modes that require field uniqueness
// mitigate by wrapping with a unique CBOR node
let mutable counter = 19000uL
let createUniqueWrapper (doc : CborDocument) =
counter <- counter + 1uL
Array(true, [|UnsignedInteger counter; doc|])
// not accepted by strict & canonical conformance modes
let trimNonCanonicalSimpleValue doc =
match doc with
| SimpleValue value when value >= 24uy && value < 32uy -> createUniqueWrapper(SimpleValue 0uy)
| _ -> doc
// completely replace indefinite-length nodes in canonical conformance modes
let trimIndefiniteLengthNode doc =
match doc with
| ByteStringIndefiniteLength bss -> createUniqueWrapper(ByteString (Array.concat bss))
| TextStringIndefiniteLength tss -> createUniqueWrapper(TextString (String.concat "" tss))
| Array(false, elems) -> createUniqueWrapper(Array(true, elems))
| Map(false, fields) -> createUniqueWrapper(Map(true, fields))
| _ -> doc
// CTAP2 does not allow major type 6, at all
let trimTagNode doc =
match doc with
| Tag (tag, doc) -> Array(true, [| UnsignedInteger (uint64 tag) ; doc|])
| SemanticValue _ -> createUniqueWrapper(UnsignedInteger 0uL)
| _ -> doc
match mode with
| CborConformanceMode.Lax -> doc
| CborConformanceMode.Strict
| CborConformanceMode.Canonical -> map (trimNonCanonicalSimpleValue << trimIndefiniteLengthNode) doc
| CborConformanceMode.Ctap2Canonical -> map (trimNonCanonicalSimpleValue << trimIndefiniteLengthNode << trimTagNode) doc
| _ -> doc
/// Gets the expected value that we would expected to see after a full serialization/deserialization roundtrip
let private getExpectedRoundtripValue (convertIndefiniteLengthItems : bool) (doc : CborDocument) =
if not convertIndefiniteLengthItems then doc else
let trimIndefiniteLengthNode doc =
match doc with
| ByteStringIndefiniteLength bss -> ByteString (Array.concat bss)
| TextStringIndefiniteLength tss -> TextString (String.concat "" tss)
| Array(false, elems) -> Array(true, elems)
| Map(false, fields) -> Map(true, fields)
| _ -> doc
map trimIndefiniteLengthNode doc
/// Depth-first application of update function on a CBOR doc tree
let rec private map (updater : CborDocument -> CborDocument) (doc : CborDocument) =
match updater doc with
| UnsignedInteger _
| NegativeInteger _
| ByteString _
| ByteStringIndefiniteLength _
| TextString _
| TextStringIndefiniteLength _
| SemanticValue _
| SimpleValue _
| Double _ as updated -> updated
| Tag(tag, value) -> Tag(tag, map updater value)
| Array(isDefiniteLength, elems) -> Array(isDefiniteLength, Array.map (map updater) elems)
| Map(isDefiniteLength, fields) ->
let updatedFields =
fields
|> Map.toSeq
|> Seq.map (fun (k,v) -> map updater k, map updater v)
|> Map.ofSeq
Map(isDefiniteLength, updatedFields)
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="CborDocument.fs" />
<Compile Include="CborPropertyTestContext.fs" />
<Compile Include="CborDocumentSerializer.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\System.Formats.Cbor.csproj" />
</ItemGroup>
</Project>
\ No newline at end of file
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Linq;
using Test.Cryptography;
using Xunit;
#if CBOR_PROPERTY_TESTS
using FsCheck.Xunit;
#endif
namespace System.Formats.Cbor.Tests
{
public partial class CborRoundtripTests
{
#if CBOR_PROPERTY_TESTS
private const string ReplaySeed = "(0,0)"; // set a seed for deterministic runs
private const int MaxTests = 10_000;
#endif
#if CBOR_PROPERTY_TESTS
[Property(Replay = ReplaySeed, MaxTest = MaxTests)]
#else
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(10)]
[InlineData(23)]
[InlineData(24)]
[InlineData(25)]
[InlineData(100)]
[InlineData(1000)]
[InlineData(1000000)]
[InlineData(1000000000000)]
[InlineData(-1)]
[InlineData(-10)]
[InlineData(-100)]
[InlineData(-1000)]
[InlineData(byte.MaxValue)]
[InlineData(byte.MaxValue + 1)]
[InlineData(-1 - byte.MaxValue)]
[InlineData(-2 - byte.MaxValue)]
[InlineData(ushort.MaxValue)]
[InlineData(ushort.MaxValue + 1)]
[InlineData(-1 - ushort.MaxValue)]
[InlineData(-2 - ushort.MaxValue)]
[InlineData(uint.MaxValue)]
[InlineData((long)uint.MaxValue + 1)]
[InlineData(-1 - uint.MaxValue)]
[InlineData(-2 - uint.MaxValue)]
[InlineData(long.MinValue)]
[InlineData(long.MaxValue)]
#endif
public static void Roundtrip_Int64(long input)
{
var writer = new CborWriter();
writer.WriteInt64(input);
byte[] encoding = writer.Encode();
var reader = new CborReader(encoding);
long result = reader.ReadInt64();
Assert.Equal(input, result);
}
#if CBOR_PROPERTY_TESTS
[Property(Replay = ReplaySeed, MaxTest = MaxTests)]
#else
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(10)]
[InlineData(23)]
[InlineData(24)]
[InlineData(25)]
[InlineData(100)]
[InlineData(1000)]
[InlineData(1000000)]
[InlineData(1000000000000)]
[InlineData(byte.MaxValue)]
[InlineData(byte.MaxValue + 1)]
[InlineData(ushort.MaxValue)]
[InlineData(ushort.MaxValue + 1)]
[InlineData(uint.MaxValue)]
[InlineData((long)uint.MaxValue + 1)]
[InlineData(long.MaxValue)]
[InlineData(ulong.MaxValue)]
#endif
public static void Roundtrip_UInt64(ulong input)
{
var writer = new CborWriter();
writer.WriteUInt64(input);
byte[] encoding = writer.Encode();
var reader = new CborReader(encoding);
ulong result = reader.ReadUInt64();
Assert.Equal(input, result);
}
#if CBOR_PROPERTY_TESTS
[Property(Replay = ReplaySeed, MaxTest = MaxTests)]
public static void Roundtrip_ByteString(byte[] input)
{
#else
[Theory]
[InlineData("")]
[InlineData("01020304")]
[InlineData("ffffffffffffffffffffffffffff")]
public static void Roundtrip_ByteString(string hexInput)
{
byte[] input = hexInput.HexToByteArray();
#endif
var writer = new CborWriter();
writer.WriteByteString(input);
byte[] encoding = writer.Encode();
var reader = new CborReader(encoding);
byte[] result = reader.ReadByteString();
AssertHelper.HexEqual(input ?? Array.Empty<byte>(), result);
}
#if CBOR_PROPERTY_TESTS
[Property(Replay = ReplaySeed, MaxTest = MaxTests)]
#else
[Theory]
[InlineData("")]
[InlineData("a")]
[InlineData("IETF")]
[InlineData("\"\\")]
[InlineData("\u00fc")]
[InlineData("\u6c34")]
[InlineData("\ud800\udd51")]
#endif
public static void Roundtrip_TextString(string input)
{
var writer = new CborWriter();
writer.WriteTextString(input);
byte[] encoding = writer.Encode();
var reader = new CborReader(encoding);
string result = reader.ReadTextString();
Assert.Equal(input ?? "", result);
}
#if CBOR_PROPERTY_TESTS
[Property(Replay = ReplaySeed, MaxTest = MaxTests)]
public static void ByteString_Encoding_ShouldContainInputBytes(byte[] input)
{
#else
[Theory]
[InlineData("")]
[InlineData("01020304")]
[InlineData("ffffffffffffffffffffffffffff")]
public static void ByteString_Encoding_ShouldContainInputBytes(string hexInput)
{
byte[] input = hexInput.HexToByteArray();
#endif
var writer = new CborWriter();
writer.WriteByteString(input);
byte[] encoding = writer.Encode();
int length = input?.Length ?? 0;
int lengthEncodingLength = GetLengthEncodingLength(length);
Assert.Equal(lengthEncodingLength + length, encoding.Length);
AssertHelper.HexEqual(input ?? Array.Empty<byte>(), encoding.Skip(lengthEncodingLength).ToArray());
static int GetLengthEncodingLength(int length)
{
return length switch
{
_ when (length < 24) => 1,
_ when (length < byte.MaxValue) => 1 + sizeof(byte),
_ when (length < ushort.MaxValue) => 1 + sizeof(ushort),
_ => 1 + sizeof(uint)
};
}
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Formats.Cbor.Tests.DataModel;
using System.Linq;
using FsCheck;
using FsCheck.Xunit;
using Xunit;
namespace System.Formats.Cbor.Tests
{
public static class CborPropertyTests
{
private const string? ReplaySeed = "(42,42)"; // set a seed for deterministic runs, null for randomized runs
private const int MaxTests = 10_000;
[Property(Replay = ReplaySeed, MaxTest = MaxTests, Arbitrary = new[] { typeof(CborRandomGenerators) })]
public static void Roundtrip_Int64(CborConformanceMode mode, long input)
{
var writer = new CborWriter(mode);
writer.WriteInt64(input);
byte[] encoding = writer.Encode();
var reader = new CborReader(encoding, mode);
long result = reader.ReadInt64();
Assert.Equal(input, result);
}
[Property(Replay = ReplaySeed, MaxTest = MaxTests, Arbitrary = new[] { typeof(CborRandomGenerators) })]
public static void Roundtrip_UInt64(CborConformanceMode mode, ulong input)
{
var writer = new CborWriter(mode);
writer.WriteUInt64(input);
byte[] encoding = writer.Encode();
var reader = new CborReader(encoding, mode);
ulong result = reader.ReadUInt64();
Assert.Equal(input, result);
}
[Property(Replay = ReplaySeed, MaxTest = MaxTests, Arbitrary = new[] { typeof(CborRandomGenerators) })]
public static void Roundtrip_NegativeInteger(CborConformanceMode mode, ulong input)
{
var writer = new CborWriter(mode);
writer.WriteCborNegativeIntegerRepresentation(input);
byte[] encoding = writer.Encode();
var reader = new CborReader(encoding, mode);
ulong result = reader.ReadCborNegativeIntegerRepresentation();
Assert.Equal(input, result);
}
[Property(Replay = ReplaySeed, MaxTest = MaxTests, Arbitrary = new[] { typeof(CborRandomGenerators) })]
public static void Roundtrip_ByteString(CborConformanceMode mode, byte[] input)
{
var writer = new CborWriter(mode);
writer.WriteByteString(input);
byte[] encoding = writer.Encode();
var reader = new CborReader(encoding, mode);
byte[] result = reader.ReadByteString();
AssertHelper.HexEqual(input, result);
}
[Property(Replay = ReplaySeed, MaxTest = MaxTests, Arbitrary = new[] { typeof(CborRandomGenerators) })]
public static void Roundtrip_TextString(CborConformanceMode mode, string input)
{
var writer = new CborWriter(mode);
writer.WriteTextString(input);
byte[] encoding = writer.Encode();
var reader = new CborReader(encoding, mode);
string result = reader.ReadTextString();
Assert.Equal(input, result);
}
[Property(Replay = ReplaySeed, MaxTest = MaxTests, Arbitrary = new[] { typeof(CborRandomGenerators) })]
public static void Roundtrip_IndefiniteByteString(CborConformanceMode mode, byte[][] chunks)
{
bool convertIndefiniteLengthEncodings = mode is CborConformanceMode.Canonical or CborConformanceMode.Ctap2Canonical;
var writer = new CborWriter(convertIndefiniteLengthEncodings: convertIndefiniteLengthEncodings);
writer.WriteStartIndefiniteLengthByteString();
foreach (byte[] chunk in chunks)
{
writer.WriteByteString(chunk);
}
writer.WriteEndIndefiniteLengthByteString();
byte[] encoding = writer.Encode();
var reader = new CborReader(encoding);
byte[] expected = chunks.SelectMany(ch => ch).ToArray();
byte[] result = reader.ReadByteString();
AssertHelper.HexEqual(expected, result);
}
[Property(Replay = ReplaySeed, MaxTest = MaxTests, Arbitrary = new[] { typeof(CborRandomGenerators) })]
public static void Roundtrip_IndefiniteTextString(CborConformanceMode mode, string[] chunks)
{
bool convertIndefiniteLengthEncodings = mode is CborConformanceMode.Canonical or CborConformanceMode.Ctap2Canonical;
var writer = new CborWriter(convertIndefiniteLengthEncodings: convertIndefiniteLengthEncodings);
writer.WriteStartIndefiniteLengthTextString();
foreach (string chunk in chunks)
{
writer.WriteTextString(chunk);
}
writer.WriteEndIndefiniteLengthTextString();
byte[] encoding = writer.Encode();
var reader = new CborReader(encoding);
string expected = String.Concat(chunks);
string result = reader.ReadTextString();
Assert.Equal(expected, result);
}
[Property(Replay = ReplaySeed, MaxTest = MaxTests, Arbitrary = new[] { typeof(CborRandomGenerators) })]
public static void Roundtrip_Half(CborConformanceMode mode, Half input)
{
var writer = new CborWriter(mode);
writer.WriteHalf(input);
byte[] encoding = writer.Encode();
var reader = new CborReader(encoding, mode);
Half result = reader.ReadHalf();
Assert.Equal(input, result);
}
[Property(Replay = ReplaySeed, MaxTest = MaxTests, Arbitrary = new[] { typeof(CborRandomGenerators) })]
public static void Roundtrip_Double(CborConformanceMode mode, double input)
{
var writer = new CborWriter();
writer.WriteDouble(input);
byte[] encoding = writer.Encode();
var reader = new CborReader(encoding);
double result = reader.ReadDouble();
Assert.Equal(input, result);
}
[Property(Replay = ReplaySeed, MaxTest = MaxTests, Arbitrary = new[] { typeof(CborRandomGenerators) })]
public static void Roundtrip_Decimal(CborConformanceMode mode, decimal input)
{
var writer = new CborWriter();
writer.WriteDecimal(input);
byte[] encoding = writer.Encode();
var reader = new CborReader(encoding);
decimal result = reader.ReadDecimal();
Assert.Equal(input, result);
}
[Property(Replay = ReplaySeed, MaxTest = MaxTests, Arbitrary = new[] { typeof(CborRandomGenerators) })]
public static void ByteString_Encoding_ShouldContainInputBytes(CborConformanceMode mode, byte[] input)
{
var writer = new CborWriter(mode);
writer.WriteByteString(input);
byte[] encoding = writer.Encode();
int length = input?.Length ?? 0;
int lengthEncodingLength = GetLengthEncodingLength(length);
Assert.Equal(lengthEncodingLength + length, encoding.Length);
AssertHelper.HexEqual(input ?? Array.Empty<byte>(), encoding.Skip(lengthEncodingLength).ToArray());
static int GetLengthEncodingLength(int length)
{
return length switch
{
_ when (length < 24) => 1,
_ when (length < byte.MaxValue) => 1 + sizeof(byte),
_ when (length < ushort.MaxValue) => 1 + sizeof(ushort),
_ => 1 + sizeof(uint)
};
}
}
[Property(Replay = ReplaySeed, MaxTest = MaxTests, Arbitrary = new[] { typeof(CborRandomGenerators) })]
public static void PropertyTest_Roundtrip(CborPropertyTestContext input)
{
byte[] encoding = CborDocumentSerializer.encode(input);
CborDocument[] expectedResults = CborPropertyTestContextHelper.getExpectedRoundtripValues(input);
CborDocument[] roundtrippedDocuments = CborDocumentSerializer.decode(input, encoding);
Assert.Equal(expectedResults, roundtrippedDocuments);
}
[Property(Replay = ReplaySeed, MaxTest = MaxTests, Arbitrary = new[] { typeof(CborRandomGenerators) })]
public static void PropertyTest_SkipValue(CborPropertyTestContext input)
{
int length = input.RootDocuments.Length;
input.RootDocuments = new[] { CborDocument.NewArray(_isDefiniteLength: true, input.RootDocuments) };
byte[] encoding = CborDocumentSerializer.encode(input);
CborReader reader = CborDocumentSerializer.createReader(input, encoding);
reader.ReadStartArray();
for (int i = 0; i < length; i++)
{
reader.SkipValue();
}
reader.ReadEndArray();
Assert.Equal(CborReaderState.Finished, reader.PeekState());
}
[Property(Replay = ReplaySeed, MaxTest = MaxTests, Arbitrary = new[] { typeof(CborRandomGenerators) })]
public static void PropertyTest_SkipToParent(CborPropertyTestContext input)
{
input.RootDocuments = new[] { CborDocument.NewArray(_isDefiniteLength: true, input.RootDocuments) };
byte[] encoding = CborDocumentSerializer.encode(input);
CborReader reader = CborDocumentSerializer.createReader(input, encoding);
reader.ReadStartArray();
reader.SkipToParent();
Assert.Equal(CborReaderState.Finished, reader.PeekState());
}
}
}
using System.Collections.Generic;
using System.Formats.Cbor.Tests.DataModel;
using System.Linq;
using FsCheck;
namespace System.Formats.Cbor.Tests
{
public static class CborRandomGenerators
{
public static Arbitrary<CborPropertyTestContext> PropertyTestInput()
{
Arbitrary<NonEmptyArray<CborDocument>> documentArb = Arb.Default.NonEmptyArray<CborDocument>();
Arbitrary<bool> convertArb = Arb.Default.Bool();
Gen<CborConformanceMode> conformanceModes = Gen.Elements(
CborConformanceMode.Lax,
CborConformanceMode.Strict,
CborConformanceMode.Canonical,
CborConformanceMode.Ctap2Canonical);
Gen<CborPropertyTestContext> inputGen =
from docs in documentArb.Generator
from convert in convertArb.Generator
from mode in conformanceModes
select CborPropertyTestContextHelper.create(mode, convert, docs.Get);
IEnumerable<CborPropertyTestContext> Shrinker(CborPropertyTestContext input)
{
var nonEmptyArrayInput = NonEmptyArray<CborDocument>.NewNonEmptyArray(input.RootDocuments);
foreach (NonEmptyArray<CborDocument> shrunkDoc in documentArb.Shrinker(nonEmptyArrayInput))
{
yield return CborPropertyTestContextHelper.create(input.ConformanceMode, input.ConvertIndefiniteLengthItems, input.RootDocuments);
}
}
return Arb.From(inputGen, Shrinker);
}
// Do not generate null strings and byte arrays
public static Arbitrary<string> String() => Arb.Default.String().Filter(s => s is not null);
public static Arbitrary<byte[]> ByteArray() => Arb.Default.Array<byte>().Filter(s => s is not null);
// forgo NaN value generation in order to simplify equality checks
public static Arbitrary<float> Single() => Arb.Default.Float32().Filter(s => !float.IsNaN(s));
public static Arbitrary<double> Double() => Arb.Default.Float().Filter(s => !double.IsNaN(s));
// FsCheck has no built-in System.Half generator, define one here
public static Arbitrary<Half> Half()
{
Arbitrary<float> singleArb = Arb.Default.Float32();
Gen<Half> generator =
from f in singleArb.Generator
where !float.IsNaN(f)
select (Half)f;
IEnumerable<Half> Shrinker(Half h)
{
foreach (float shrunk in singleArb.Shrinker((float)h))
{
yield return (Half)shrunk;
}
}
return Arb.From(generator, Shrinker);
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
<nullable>enable</nullable>
<CborPropertyTests>false</CborPropertyTests>
</PropertyGroup>
<PropertyGroup Condition="'$(CborPropertyTests)' == 'True'">
<DefineConstants>$(DefineConstants),CBOR_PROPERTY_TESTS</DefineConstants>
<!-- Referenced assembly 'FsCheck' does not have a strong name.-->
<NoWarn>CS8002</NoWarn>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(CommonTestPath)System\Security\Cryptography\ByteUtils.cs">
<Link>CommonTest\System\Security\Cryptography\ByteUtils.cs</Link>
......@@ -26,10 +34,20 @@
<Compile Include="Writer\CborWriterTests.Integer.cs" />
<Compile Include="Writer\CborWriterTests.ByteString.cs" />
<Compile Include="Writer\CborWriterTests.TextString.cs" />
<Compile Include="CborRoundtripTests.cs" />
<Compile Include="CoseKeyHelpers.cs" />
</ItemGroup>
<ItemGroup Condition="'$(CborPropertyTests)' == 'True'">
<Compile Include="PropertyTests\CborPropertyTests.cs" />
<Compile Include="PropertyTests\CborRandomGenerators.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\src\System.Formats.Cbor.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(CborPropertyTests)' == 'True'">
<ProjectReference Include="CborDocument\System.Formats.Cbor.Tests.DataModel.fsproj" />
<PackageReference Include="FsCheck.Xunit" Version="$(FsCheckVersion)" />
</ItemGroup>
</Project>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册