diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 682406e..ddd6083 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,23 +16,31 @@ jobs: os: [ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v6 - - uses: actions/setup-dotnet@v5 + - uses: actions/checkout@v7 + - uses: actions/setup-dotnet@v6 with: dotnet-version: | 9.x 10.x + - name: Derive CI package version + shell: pwsh + run: | + $version = dotnet msbuild src/OpenDisNet/OpenDisNet.csproj -getProperty:Version -nologo + "PACKAGE_VERSION=${version}-ci.${{ github.run_id }}" >> $env:GITHUB_ENV - run: dotnet restore --locked-mode - run: dotnet run --project tools/OpenDisNet.Generator/OpenDisNet.Generator.csproj --configuration Release --no-restore -- --verify - - run: dotnet build --configuration Release --no-restore + - run: dotnet build --configuration Release --no-restore --no-incremental -p:Version=${{ env.PACKAGE_VERSION }} - run: dotnet test --configuration Release --no-build - - run: dotnet pack src/OpenDisNet/OpenDisNet.csproj --configuration Release --no-build --output artifacts/packages -p:PackageVersion=0.0.0-ci.${{ github.run_id }} + - run: dotnet pack src/OpenDisNet/OpenDisNet.csproj --configuration Release --no-build --no-restore --output artifacts/packages -p:Version=${{ env.PACKAGE_VERSION }} -p:PackageVersion=${{ env.PACKAGE_VERSION }} + - name: Inspect package + shell: pwsh + run: ./eng/Validate-Package.ps1 -PackagePath ./artifacts/packages/OpenDisNet.${{ env.PACKAGE_VERSION }}.nupkg -ExpectedVersion ${{ env.PACKAGE_VERSION }} - name: Restore packed-package consumer - run: dotnet restore tests/OpenDisNet.PackageSmoke/OpenDisNet.PackageSmoke.csproj --source "${{ github.workspace }}/artifacts/packages" --force --no-cache -p:OpenDisNetPackageVersion=0.0.0-ci.${{ github.run_id }} + run: dotnet restore tests/OpenDisNet.PackageSmoke/OpenDisNet.PackageSmoke.csproj --source "${{ github.workspace }}/artifacts/packages" --force --no-cache -p:OpenDisNetPackageVersion=${{ env.PACKAGE_VERSION }} - name: Run packed-package consumer on .NET 9 - run: dotnet run --project tests/OpenDisNet.PackageSmoke/OpenDisNet.PackageSmoke.csproj --configuration Release --framework net9.0 --no-restore -p:OpenDisNetPackageVersion=0.0.0-ci.${{ github.run_id }} + run: dotnet run --project tests/OpenDisNet.PackageSmoke/OpenDisNet.PackageSmoke.csproj --configuration Release --framework net9.0 --no-restore -p:OpenDisNetPackageVersion=${{ env.PACKAGE_VERSION }} - name: Run packed-package consumer on .NET 10 - run: dotnet run --project tests/OpenDisNet.PackageSmoke/OpenDisNet.PackageSmoke.csproj --configuration Release --framework net10.0 --no-restore -p:OpenDisNetPackageVersion=0.0.0-ci.${{ github.run_id }} + run: dotnet run --project tests/OpenDisNet.PackageSmoke/OpenDisNet.PackageSmoke.csproj --configuration Release --framework net10.0 --no-restore -p:OpenDisNetPackageVersion=${{ env.PACKAGE_VERSION }} - uses: actions/upload-artifact@v7 if: matrix.os == 'ubuntu-latest' with: @@ -42,8 +50,8 @@ jobs: format: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 - - uses: actions/setup-dotnet@v5 + - uses: actions/checkout@v7 + - uses: actions/setup-dotnet@v6 with: dotnet-version: 10.x - run: dotnet restore --locked-mode diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index bd60e2c..a344f9d 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -15,7 +15,7 @@ jobs: analyze: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: github/codeql-action/init@v4 with: languages: csharp diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index a7579d6..53190c4 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -10,7 +10,7 @@ jobs: review: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Reject vulnerable dependency changes uses: actions/dependency-review-action@v5 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 68c60c3..44447e0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,10 +14,10 @@ jobs: runs-on: ubuntu-latest environment: release steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: fetch-depth: 0 - - uses: actions/setup-dotnet@v5 + - uses: actions/setup-dotnet@v6 with: dotnet-version: | 9.x @@ -29,8 +29,12 @@ jobs: shell: bash run: echo "PACKAGE_VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_ENV" - run: dotnet restore --locked-mode - - run: dotnet test --configuration Release --no-restore - - run: dotnet pack src/OpenDisNet/OpenDisNet.csproj --configuration Release --no-restore --output artifacts/packages -p:PackageVersion=${{ env.PACKAGE_VERSION }} + - run: dotnet build --configuration Release --no-restore --no-incremental -p:Version=${{ env.PACKAGE_VERSION }} + - run: dotnet test --configuration Release --no-build + - run: dotnet pack src/OpenDisNet/OpenDisNet.csproj --configuration Release --no-build --no-restore --output artifacts/packages -p:Version=${{ env.PACKAGE_VERSION }} -p:PackageVersion=${{ env.PACKAGE_VERSION }} + - name: Inspect package + shell: pwsh + run: ./eng/Validate-Package.ps1 -PackagePath ./artifacts/packages/OpenDisNet.${{ env.PACKAGE_VERSION }}.nupkg -ExpectedVersion ${{ env.PACKAGE_VERSION }} - name: Restore packed-package consumer run: dotnet restore tests/OpenDisNet.PackageSmoke/OpenDisNet.PackageSmoke.csproj --source "${{ github.workspace }}/artifacts/packages" --force --no-cache -p:OpenDisNetPackageVersion=${{ env.PACKAGE_VERSION }} - name: Run packed-package consumer on .NET 9 diff --git a/CHANGELOG.md b/CHANGELOG.md index 6067767..9f58bba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ incomplete. ## 1.0.2 - Unreleased +- Ensured NuGet package, assembly, file, and informational versions agree for + release and CI builds, with package-level validation for both target frameworks. +- Migrated the test suite to MSTest, refreshed the test platform and GitHub + Actions dependencies, and removed the unused code-coverage collector. - Expanded NuGet metadata and search terms for Distributed Interactive Simulation, IEEE 1278.1, DIS v7, SISO, PDU parsing, and binary serialization. - Added a package icon, explicit project/repository links, release-notes link, diff --git a/eng/Validate-Package.ps1 b/eng/Validate-Package.ps1 new file mode 100644 index 0000000..f03c94d --- /dev/null +++ b/eng/Validate-Package.ps1 @@ -0,0 +1,113 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory)] + [string] $PackagePath, + + [Parameter(Mandatory)] + [string] $ExpectedVersion, + + [string] $ExpectedPackageId = 'OpenDisNet' +) + +$ErrorActionPreference = 'Stop' +$resolvedPackage = (Resolve-Path -LiteralPath $PackagePath).Path + +Add-Type -AssemblyName System.IO.Compression.FileSystem +$archive = [System.IO.Compression.ZipFile]::OpenRead($resolvedPackage) + +try { + $entryNames = @($archive.Entries | ForEach-Object FullName) + $requiredEntries = @( + 'README.md', + 'opendisnet-icon.png', + 'lib/net9.0/OpenDisNet.dll', + 'lib/net10.0/OpenDisNet.dll' + ) + + foreach ($entry in $requiredEntries) { + if ($entryNames -cnotcontains $entry) { + throw "Package is missing required entry '$entry'." + } + } + + $nuspecEntry = $archive.Entries | + Where-Object FullName -Like '*.nuspec' | + Select-Object -First 1 + if ($null -eq $nuspecEntry) { + throw 'Package does not contain a .nuspec file.' + } + + $reader = [System.IO.StreamReader]::new($nuspecEntry.Open()) + try { + [xml] $nuspec = $reader.ReadToEnd() + } + finally { + $reader.Dispose() + } + + $metadata = $nuspec.package.metadata + if ($metadata.id -ne $ExpectedPackageId) { + throw "Expected package ID '$ExpectedPackageId', found '$($metadata.id)'." + } + + if ($metadata.version -ne $ExpectedVersion) { + throw "Expected package version '$ExpectedVersion', found '$($metadata.version)'." + } + + if ($ExpectedVersion -notmatch '^(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?$') { + throw "Expected version '$ExpectedVersion' is not a supported semantic version." + } + + $expectedAssemblyVersion = "$($Matches[1]).$($Matches[2]).$($Matches[3]).0" + $assemblyEntries = @( + 'lib/net9.0/OpenDisNet.dll', + 'lib/net10.0/OpenDisNet.dll' + ) + $temporaryDirectory = Join-Path ([System.IO.Path]::GetTempPath()) ( + 'opendisnet-package-validation-' + [System.Guid]::NewGuid().ToString('N')) + [void] [System.IO.Directory]::CreateDirectory($temporaryDirectory) + + try { + foreach ($entryName in $assemblyEntries) { + $assemblyEntry = $archive.GetEntry($entryName) + $temporaryAssembly = Join-Path $temporaryDirectory ($entryName.Replace('/', '_')) + $sourceStream = $assemblyEntry.Open() + $destinationStream = [System.IO.File]::Create($temporaryAssembly) + + try { + $sourceStream.CopyTo($destinationStream) + } + finally { + $destinationStream.Dispose() + $sourceStream.Dispose() + } + + $assemblyVersion = [System.Reflection.AssemblyName]::GetAssemblyName( + $temporaryAssembly).Version.ToString() + $fileVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo( + $temporaryAssembly) + + if ($assemblyVersion -ne $expectedAssemblyVersion) { + throw "Expected '$entryName' assembly version '$expectedAssemblyVersion', found '$assemblyVersion'." + } + + if ($fileVersion.FileVersion -ne $expectedAssemblyVersion) { + throw "Expected '$entryName' file version '$expectedAssemblyVersion', found '$($fileVersion.FileVersion)'." + } + + if ($fileVersion.ProductVersion -ne $ExpectedVersion -and + -not $fileVersion.ProductVersion.StartsWith( + "$ExpectedVersion+", [System.StringComparison]::Ordinal)) { + throw "Expected '$entryName' product version '$ExpectedVersion', found '$($fileVersion.ProductVersion)'." + } + } + } + finally { + [System.IO.Directory]::Delete($temporaryDirectory, $true) + } + + Write-Host "Validated $ExpectedPackageId $ExpectedVersion ($($entryNames.Count) entries)." +} +finally { + $archive.Dispose() +} diff --git a/tests/OpenDisNet.Tests/Binary/DisHeaderCodecTests.cs b/tests/OpenDisNet.Tests/Binary/DisHeaderCodecTests.cs index 711ade6..ae3dc5f 100644 --- a/tests/OpenDisNet.Tests/Binary/DisHeaderCodecTests.cs +++ b/tests/OpenDisNet.Tests/Binary/DisHeaderCodecTests.cs @@ -3,9 +3,10 @@ namespace OpenDisNet.Tests.Binary; +[TestClass] public sealed class DisHeaderCodecTests { - [Fact] + [TestMethod] public void ReadsVersion7HeaderInNetworkByteOrder() { byte[] bytes = [7, 3, 1, 1, 0x01, 0x02, 0x03, 0x04, 0, 12, 0xA5, 0]; @@ -13,16 +14,16 @@ public void ReadsVersion7HeaderInNetworkByteOrder() DisHeader header = DisHeaderCodec.Read(ref reader); - Assert.Equal(DisProtocolVersion.Ieee1278_1_2012, header.ProtocolVersion); - Assert.Equal((byte)3, header.ExerciseId); - Assert.Equal(PduType.EntityState, header.PduType); - Assert.Equal(0x01020304u, header.Timestamp); - Assert.Equal((ushort)12, header.Length); - Assert.Equal((byte)0xA5, header.PduStatus); - Assert.Equal(12, reader.Offset); + Assert.AreEqual(DisProtocolVersion.Ieee1278_1_2012, header.ProtocolVersion); + Assert.AreEqual((byte)3, header.ExerciseId); + Assert.AreEqual(PduType.EntityState, header.PduType); + Assert.AreEqual(0x01020304u, header.Timestamp); + Assert.AreEqual((ushort)12, header.Length); + Assert.AreEqual((byte)0xA5, header.PduStatus); + Assert.AreEqual(12, reader.Offset); } - [Fact] + [TestMethod] public void WriterRoundTripsHeader() { var expected = new DisHeader(DisProtocolVersion.Ieee1278_1_2012, 9, PduType.Fire, ProtocolFamily.Warfare, 42, 12, 0, 0); @@ -31,6 +32,6 @@ public void WriterRoundTripsHeader() DisHeaderCodec.Write(ref writer, expected); var reader = new DisBinaryReader(bytes); - Assert.Equal(expected, DisHeaderCodec.Read(ref reader)); + Assert.AreEqual(expected, DisHeaderCodec.Read(ref reader)); } } diff --git a/tests/OpenDisNet.Tests/Conformance/ConformanceAuditTests.cs b/tests/OpenDisNet.Tests/Conformance/ConformanceAuditTests.cs index f3c2cdc..310f5d7 100644 --- a/tests/OpenDisNet.Tests/Conformance/ConformanceAuditTests.cs +++ b/tests/OpenDisNet.Tests/Conformance/ConformanceAuditTests.cs @@ -6,6 +6,7 @@ namespace OpenDisNet.Tests.Conformance; +[TestClass] public sealed class ConformanceAuditTests { private static readonly JsonSerializerOptions JsonOptions = new() @@ -13,45 +14,45 @@ public sealed class ConformanceAuditTests PropertyNameCaseInsensitive = true, }; - [Fact] + [TestMethod] public void AuditedInputsHaveNotChanged() { string root = FindRepositoryRoot(); AuditManifest manifest = ReadManifest(root); - Assert.Equal("1.0.1", manifest.AuditVersion); - Assert.Equal(72, manifest.PduTypeCount); - Assert.Equal(233, manifest.ConcreteWireClassCount); - Assert.Equal(17, manifest.Artifacts.Length); + Assert.AreEqual("1.0.1", manifest.AuditVersion); + Assert.AreEqual(72, manifest.PduTypeCount); + Assert.AreEqual(233, manifest.ConcreteWireClassCount); + Assert.AreEqual(17, manifest.Artifacts.Length); foreach (AuditArtifact artifact in manifest.Artifacts) { string path = Path.Combine(root, artifact.Path.Replace('/', Path.DirectorySeparatorChar)); - Assert.True(File.Exists(path), $"Audited input is missing: {artifact.Path}"); + Assert.IsTrue(File.Exists(path), $"Audited input is missing: {artifact.Path}"); byte[] normalized = Encoding.UTF8.GetBytes(File.ReadAllText(path).ReplaceLineEndings("\n")); string actual = Convert.ToHexStringLower(SHA256.HashData(normalized)); - Assert.Equal(artifact.Sha256, actual); + Assert.AreEqual(artifact.Sha256, actual); } } - [Fact] + [TestMethod] public void AuditAccountsForEveryStandardPduAndFamily() { string root = FindRepositoryRoot(); AuditManifest manifest = ReadManifest(root); int[] auditedTypes = manifest.Families.SelectMany(x => x.Types).Order().ToArray(); - Assert.Equal(Enumerable.Range(1, 72), auditedTypes); - Assert.Equal(72, manifest.Families.Sum(x => x.Count)); - Assert.Equal(12, manifest.Families.Length); + Assert.AreSequenceEqual(Enumerable.Range(1, 72), auditedTypes); + Assert.AreEqual(72, manifest.Families.Sum(x => x.Count)); + Assert.AreEqual(12, manifest.Families.Length); foreach (AuditFamily family in manifest.Families) { - Assert.Equal(family.Count, family.Types.Length); + Assert.AreEqual(family.Count, family.Types.Length); foreach (int type in family.Types) { Pdu pdu = PduFactory.Create((PduType)type); - Assert.Equal((ProtocolFamily)family.Value, pdu.ProtocolFamily); + Assert.AreEqual((ProtocolFamily)family.Value, pdu.ProtocolFamily); } } } diff --git a/tests/OpenDisNet.Tests/Conformance/FamilyConformanceVectorTests.cs b/tests/OpenDisNet.Tests/Conformance/FamilyConformanceVectorTests.cs index e942295..ea27d9f 100644 --- a/tests/OpenDisNet.Tests/Conformance/FamilyConformanceVectorTests.cs +++ b/tests/OpenDisNet.Tests/Conformance/FamilyConformanceVectorTests.cs @@ -4,6 +4,7 @@ namespace OpenDisNet.Tests.Conformance; +[TestClass] public sealed class FamilyConformanceVectorTests { // These fixtures expose known defects in the reference generator's @@ -15,89 +16,76 @@ public sealed class FamilyConformanceVectorTests 43, 44, 45, 56, 57, 58, 59, 60, 61, 62, 63, 64, 68, 72, ]; - public static TheoryData AllFamilies => new() - { - { 1, "0.3 Entity Information" }, { 2, "0.3 Warfare" }, - { 3, "0.4 Logistics" }, { 5, "0.4 Simulation Management" }, - { 6, "0.5 Distributed Emissions" }, { 4, "0.5 Radio Communications" }, - { 7, "0.6 Entity Management" }, { 8, "0.6 Minefield" }, - { 9, "0.7 Synthetic Environment" }, { 10, "0.7 Simulation Management with Reliability" }, - { 11, "0.8 Live Entity" }, { 13, "0.8 Information Operations" }, - }; - - public static TheoryData StandardPduTypes => - new(Enumerable.Range(1, 72).Select(x => (byte)x)); - - public static TheoryData OpenDisJavaVectors - { - get - { - var data = new TheoryData(); - foreach (OpenDisJavaVector vector in ReadOpenDisJavaVectors().Where(x => x.Type is not 43 and not 45)) - data.Add((byte)vector.Type, vector.Name, vector.Hex); - return data; - } - } + public static IEnumerable AllFamilies => + [ + [(byte)1, "0.3 Entity Information"], [(byte)2, "0.3 Warfare"], + [(byte)3, "0.4 Logistics"], [(byte)5, "0.4 Simulation Management"], + [(byte)6, "0.5 Distributed Emissions"], [(byte)4, "0.5 Radio Communications"], + [(byte)7, "0.6 Entity Management"], [(byte)8, "0.6 Minefield"], + [(byte)9, "0.7 Synthetic Environment"], [(byte)10, "0.7 Simulation Management with Reliability"], + [(byte)11, "0.8 Live Entity"], [(byte)13, "0.8 Information Operations"], + ]; - public static TheoryData OpenDisJavaPopulatedVectors - { - get - { - var data = new TheoryData(); - foreach (OpenDisJavaVector vector in ReadOpenDisJavaVectors("opendis7-java-populated.json") - .Where(x => !PopulatedReferenceIncompatibilities.Contains(x.Type))) - data.Add((byte)vector.Type, vector.Name, vector.Hex); - return data; - } - } + public static IEnumerable StandardPduTypes => + Enumerable.Range(1, 72).Select(x => new object[] { (byte)x }); + + public static IEnumerable OpenDisJavaVectors => + ReadOpenDisJavaVectors() + .Where(x => x.Type is not 43 and not 45) + .Select(x => new object[] { (byte)x.Type, x.Name, x.Hex }); + + public static IEnumerable OpenDisJavaPopulatedVectors => + ReadOpenDisJavaVectors("opendis7-java-populated.json") + .Where(x => !PopulatedReferenceIncompatibilities.Contains(x.Type)) + .Select(x => new object[] { (byte)x.Type, x.Name, x.Hex }); - [Theory] - [MemberData(nameof(StandardPduTypes))] + [TestMethod] + [DynamicData(nameof(StandardPduTypes))] public void PopulatedPduRoundTripsByteIdentically(byte value) { Pdu original = DeterministicPduFixture.Create((PduType)value); byte[] bytes = DisSerializer.Serialize(original); - Pdu decoded = Assert.IsAssignableFrom(DisSerializer.Deserialize(bytes)); + Pdu decoded = Assert.IsInstanceOfType(DisSerializer.Deserialize(bytes)); - Assert.Equal(original.GetType(), decoded.GetType()); - Assert.Equal(bytes, DisSerializer.Serialize(decoded)); - Assert.True(bytes.Length > 12); + Assert.AreEqual(original.GetType(), decoded.GetType()); + Assert.AreSequenceEqual(bytes, DisSerializer.Serialize(decoded)); + Assert.IsTrue(bytes.Length > 12); } - [Theory] - [MemberData(nameof(OpenDisJavaVectors))] + [TestMethod] + [DynamicData(nameof(OpenDisJavaVectors))] public void IndependentOpenDisJavaVectorRoundTrips(byte value, string referenceName, string hex) { byte[] bytes = Convert.FromHexString(hex); - Pdu decoded = Assert.IsAssignableFrom(DisSerializer.Deserialize(bytes)); + Pdu decoded = Assert.IsInstanceOfType(DisSerializer.Deserialize(bytes)); - Assert.Equal((PduType)value, decoded.PduType); - Assert.Equal(bytes, DisSerializer.Serialize(decoded)); - Assert.False(string.IsNullOrWhiteSpace(referenceName)); + Assert.AreEqual((PduType)value, decoded.PduType); + Assert.AreSequenceEqual(bytes, DisSerializer.Serialize(decoded)); + Assert.IsFalse(string.IsNullOrWhiteSpace(referenceName)); } - [Theory] - [MemberData(nameof(OpenDisJavaPopulatedVectors))] + [TestMethod] + [DynamicData(nameof(OpenDisJavaPopulatedVectors))] public void IndependentPopulatedOpenDisJavaVectorRoundTrips(byte value, string referenceName, string hex) { byte[] bytes = Convert.FromHexString(hex); - Pdu decoded = Assert.IsAssignableFrom(DisSerializer.Deserialize(bytes)); + Pdu decoded = Assert.IsInstanceOfType(DisSerializer.Deserialize(bytes)); - Assert.Equal((PduType)value, decoded.PduType); - Assert.Equal(bytes, DisSerializer.Serialize(decoded)); - Assert.False(string.IsNullOrWhiteSpace(referenceName)); + Assert.AreEqual((PduType)value, decoded.PduType); + Assert.AreSequenceEqual(bytes, DisSerializer.Serialize(decoded)); + Assert.IsFalse(string.IsNullOrWhiteSpace(referenceName)); } - [Theory] - [MemberData(nameof(AllFamilies))] + [TestMethod] + [DynamicData(nameof(AllFamilies))] public void EveryByteBoundaryRejectsTruncation(byte family, string milestone) { Pdu[] pdus = Enumerable.Range(1, 72) .Select(x => DeterministicPduFixture.Create((PduType)x)) .Where(x => (byte)x.ProtocolFamily == family) .ToArray(); - Assert.NotEmpty(pdus); + Assert.IsTrue(pdus.Length > 0); foreach (Pdu pdu in pdus) { @@ -105,42 +93,42 @@ public void EveryByteBoundaryRejectsTruncation(byte family, string milestone) for (int length = 0; length < bytes.Length; length++) { bool parsed = DisSerializer.TryDeserialize(bytes.AsSpan(0, length), out _, out DisParseError error); - Assert.False(parsed, $"{milestone}: {pdu.GetType().Name} accepted truncation at byte {length}."); - Assert.NotEqual(DisParseErrorCode.None, error.Code); + Assert.IsFalse(parsed, $"{milestone}: {pdu.GetType().Name} accepted truncation at byte {length}."); + Assert.AreNotEqual(DisParseErrorCode.None, error.Code); } } } - [Fact] + [TestMethod] public void IndependentVectorSetCoversEveryStandardPduExactlyOnce() { OpenDisJavaVector[] vectors = ReadOpenDisJavaVectors(); - Assert.Equal(72, vectors.Length); - Assert.Equal(Enumerable.Range(1, 72), vectors.Select(x => x.Type).Order()); + Assert.AreEqual(72, vectors.Length); + Assert.AreSequenceEqual(Enumerable.Range(1, 72), vectors.Select(x => x.Type).Order()); } - [Fact] + [TestMethod] public void CompatiblePopulatedReferenceVectorsCoverEveryPduFamily() { OpenDisJavaVector[] vectors = ReadOpenDisJavaVectors("opendis7-java-populated.json") .Where(x => !PopulatedReferenceIncompatibilities.Contains(x.Type)) .ToArray(); - Assert.Equal(44, vectors.Length); - Assert.Equal( + Assert.AreEqual(44, vectors.Length); + Assert.AreSequenceEqual( AllFamilies.Select(row => (byte)row[0]).Order(), vectors.Select(x => Convert.FromHexString(x.Hex)[3]).Distinct().Order()); } - [Theory] - [InlineData(43)] - [InlineData(45)] + [TestMethod] + [DataRow(43)] + [DataRow(45)] public void OpenDisJavaBitfieldWidthDifferencesRemainExplicit(int type) { - OpenDisJavaVector vector = Assert.Single(ReadOpenDisJavaVectors(), x => x.Type == type); + OpenDisJavaVector vector = ReadOpenDisJavaVectors().Single(x => x.Type == type); - Assert.False(DisSerializer.TryDeserialize(Convert.FromHexString(vector.Hex), out _, out DisParseError error)); - Assert.Equal(DisParseErrorCode.InvalidField, error.Code); + Assert.IsFalse(DisSerializer.TryDeserialize(Convert.FromHexString(vector.Hex), out _, out DisParseError error)); + Assert.AreEqual(DisParseErrorCode.InvalidField, error.Code); } private static OpenDisJavaVector[] ReadOpenDisJavaVectors(string fileName = "opendis7-java-default.json") diff --git a/tests/OpenDisNet.Tests/Conformance/PduCodecConformanceTests.cs b/tests/OpenDisNet.Tests/Conformance/PduCodecConformanceTests.cs index 51f7d2c..70cdaab 100644 --- a/tests/OpenDisNet.Tests/Conformance/PduCodecConformanceTests.cs +++ b/tests/OpenDisNet.Tests/Conformance/PduCodecConformanceTests.cs @@ -4,26 +4,27 @@ namespace OpenDisNet.Tests.Conformance; +[TestClass] public sealed class PduCodecConformanceTests { - public static TheoryData StandardPduTypes => - new(Enumerable.Range(1, 72).Select(x => (byte)x)); + public static IEnumerable StandardPduTypes => + Enumerable.Range(1, 72).Select(x => new object[] { (byte)x }); - [Theory] - [MemberData(nameof(StandardPduTypes))] + [TestMethod] + [DynamicData(nameof(StandardPduTypes))] public void DefaultPduRoundTripsWithoutUnreadBytes(byte value) { Pdu original = PduFactory.Create((PduType)value, exerciseId: 4); byte[] encoded = DisSerializer.Serialize(original); - Pdu decoded = Assert.IsAssignableFrom(DisSerializer.Deserialize(encoded)); + Pdu decoded = Assert.IsInstanceOfType(DisSerializer.Deserialize(encoded)); - Assert.Equal(value, (byte)decoded.PduType); - Assert.Equal(encoded.Length, original.Length); - Assert.Equal(encoded, DisSerializer.Serialize(decoded)); + Assert.AreEqual(value, (byte)decoded.PduType); + Assert.AreEqual(encoded.Length, original.Length); + Assert.AreSequenceEqual(encoded, DisSerializer.Serialize(decoded)); } - [Fact] + [TestMethod] public void PublicSerializerHandlesTypedPdus() { var original = (SignalPdu)PduFactory.Create(PduType.Signal, exerciseId: 9); @@ -35,15 +36,15 @@ public void PublicSerializerHandlesTypedPdus() original.Data = [0xA5, 0xE0]; byte[] encoded = DisSerializer.Serialize(original); - Assert.True(DisSerializer.TryDeserialize(encoded, out global::OpenDisNet.Pdus.IDisPdu? parsed, out DisParseError error), error.Message); + Assert.IsTrue(DisSerializer.TryDeserialize(encoded, out global::OpenDisNet.Pdus.IDisPdu? parsed, out DisParseError error), error.Message); - SignalPdu signal = Assert.IsType(parsed); - Assert.Equal(original.Data, signal.Data); - Assert.Equal((ushort)13, signal.DataBitLength); - Assert.Equal(encoded, DisSerializer.Serialize(signal)); + SignalPdu signal = Assert.IsInstanceOfType(parsed); + Assert.AreSequenceEqual(original.Data, signal.Data); + Assert.AreEqual((ushort)13, signal.DataBitLength); + Assert.AreSequenceEqual(encoded, DisSerializer.Serialize(signal)); } - [Fact] + [TestMethod] public void SerializerFacadeHidesCodecImplementation() { Pdu original = PduFactory.Create(PduType.Acknowledge, exerciseId: 3); @@ -51,11 +52,11 @@ public void SerializerFacadeHidesCodecImplementation() byte[] encoded = DisSerializer.Serialize(original); global::OpenDisNet.Pdus.IDisPdu decoded = DisSerializer.Deserialize(encoded); - Assert.Equal(PduType.Acknowledge, decoded.Header.PduType); - Assert.Equal(encoded, DisSerializer.Serialize(decoded)); + Assert.AreEqual(PduType.Acknowledge, decoded.Header.PduType); + Assert.AreSequenceEqual(encoded, DisSerializer.Serialize(decoded)); } - [Fact] + [TestMethod] public void RejectsDeclaredListThatExceedsBody() { var original = (SignalPdu)PduFactory.Create(PduType.Signal); @@ -66,11 +67,11 @@ public void RejectsDeclaredListThatExceedsBody() truncated[8] = (byte)(truncated.Length >> 8); truncated[9] = (byte)truncated.Length; - Assert.False(DisSerializer.TryDeserialize(truncated, out _, out DisParseError error)); - Assert.Equal(DisParseErrorCode.InvalidField, error.Code); + Assert.IsFalse(DisSerializer.TryDeserialize(truncated, out _, out DisParseError error)); + Assert.AreEqual(DisParseErrorCode.InvalidField, error.Code); } - [Fact] + [TestMethod] public void TransmitterUsesOctetLengthsAndSynchronizesThemAutomatically() { var original = (TransmitterPdu)PduFactory.Create(PduType.Transmitter); @@ -78,14 +79,14 @@ public void TransmitterUsesOctetLengthsAndSynchronizesThemAutomatically() original.AntennaPatternParameters = Enumerable.Range(0, 40).Select(x => (byte)x).ToArray(); byte[] encoded = DisSerializer.Serialize(original); - var decoded = Assert.IsType(DisSerializer.Deserialize(encoded)); + var decoded = Assert.IsInstanceOfType(DisSerializer.Deserialize(encoded)); - Assert.Equal(original.ModulationParameters, decoded.ModulationParameters); - Assert.Equal(original.AntennaPatternParameters, decoded.AntennaPatternParameters); - Assert.Equal(encoded, DisSerializer.Serialize(decoded)); + Assert.AreSequenceEqual(original.ModulationParameters, decoded.ModulationParameters); + Assert.AreSequenceEqual(original.AntennaPatternParameters, decoded.AntennaPatternParameters); + Assert.AreSequenceEqual(encoded, DisSerializer.Serialize(decoded)); } - [Fact] + [TestMethod] public void IntercomParameterByteLengthFramesMultipleRecords() { var original = (IntercomControlPdu)PduFactory.Create(PduType.IntercomControl); @@ -96,15 +97,15 @@ public void IntercomParameterByteLengthFramesMultipleRecords() ]; byte[] encoded = DisSerializer.Serialize(original); - var decoded = Assert.IsType(DisSerializer.Deserialize(encoded)); + var decoded = Assert.IsInstanceOfType(DisSerializer.Deserialize(encoded)); - Assert.Equal(2, decoded.IntercomParameters.Count); - Assert.Equal([1, 2, 3, 4], decoded.IntercomParameters[0].RecordSpecificField); - Assert.Equal([5, 6, 7, 8], decoded.IntercomParameters[1].RecordSpecificField); - Assert.Equal(encoded, DisSerializer.Serialize(decoded)); + Assert.AreEqual(2, decoded.IntercomParameters.Count); + Assert.AreSequenceEqual(new byte[] { 1, 2, 3, 4 }, decoded.IntercomParameters[0].RecordSpecificField); + Assert.AreSequenceEqual(new byte[] { 5, 6, 7, 8 }, decoded.IntercomParameters[1].RecordSpecificField); + Assert.AreSequenceEqual(encoded, DisSerializer.Serialize(decoded)); } - [Fact] + [TestMethod] public void MinefieldDataFilterControlsOptionalPerMineFields() { var original = (MinefieldDataPdu)PduFactory.Create(PduType.MinefieldData); @@ -118,12 +119,12 @@ public void MinefieldDataFilterControlsOptionalPerMineFields() original.MineEntityNumber = [101, 102]; byte[] encoded = DisSerializer.Serialize(original); - var decoded = Assert.IsType(DisSerializer.Deserialize(encoded)); + var decoded = Assert.IsInstanceOfType(DisSerializer.Deserialize(encoded)); - Assert.Equal(1u, original.DataFilter.BitFlags); - Assert.Equal(original.GroundBurialDepthOffset, decoded.GroundBurialDepthOffset); - Assert.Empty(decoded.WaterBurialDepthOffset); - Assert.Equal(original.MineEntityNumber, decoded.MineEntityNumber); - Assert.Equal(encoded, DisSerializer.Serialize(decoded)); + Assert.AreEqual(1u, original.DataFilter.BitFlags); + Assert.AreSequenceEqual(original.GroundBurialDepthOffset, decoded.GroundBurialDepthOffset); + Assert.AreEqual(0, decoded.WaterBurialDepthOffset.Length); + Assert.AreSequenceEqual(original.MineEntityNumber, decoded.MineEntityNumber); + Assert.AreSequenceEqual(encoded, DisSerializer.Serialize(decoded)); } } diff --git a/tests/OpenDisNet.Tests/Conformance/PduFactoryTests.cs b/tests/OpenDisNet.Tests/Conformance/PduFactoryTests.cs index 778de22..8c69370 100644 --- a/tests/OpenDisNet.Tests/Conformance/PduFactoryTests.cs +++ b/tests/OpenDisNet.Tests/Conformance/PduFactoryTests.cs @@ -3,20 +3,21 @@ namespace OpenDisNet.Tests.Conformance; +[TestClass] public sealed class PduFactoryTests { - [Fact] + [TestMethod] public void CreatesEveryStandardizedPduWithVersionAndFamily() { for (byte value = 1; value <= 72; value++) { Pdu pdu = PduFactory.Create((PduType)value, exerciseId: 19); - Assert.IsAssignableFrom(pdu); - Assert.Equal(value, (byte)pdu.Header.PduType); - Assert.Equal(DisProtocolVersion.Ieee1278_1_2012, pdu.Header.ProtocolVersion); - Assert.Equal((byte)19, pdu.Header.ExerciseId); - Assert.NotEqual(ProtocolFamily.Other, pdu.Header.ProtocolFamily); + Assert.IsInstanceOfType(pdu); + Assert.AreEqual(value, (byte)pdu.Header.PduType); + Assert.AreEqual(DisProtocolVersion.Ieee1278_1_2012, pdu.Header.ProtocolVersion); + Assert.AreEqual((byte)19, pdu.Header.ExerciseId); + Assert.AreNotEqual(ProtocolFamily.Other, pdu.Header.ProtocolFamily); } } } diff --git a/tests/OpenDisNet.Tests/Conformance/SchemaManifestTests.cs b/tests/OpenDisNet.Tests/Conformance/SchemaManifestTests.cs index 1a8d478..1edcae4 100644 --- a/tests/OpenDisNet.Tests/Conformance/SchemaManifestTests.cs +++ b/tests/OpenDisNet.Tests/Conformance/SchemaManifestTests.cs @@ -4,20 +4,21 @@ namespace OpenDisNet.Tests.Conformance; +[TestClass] public sealed class SchemaManifestTests { - [Fact] + [TestMethod] public void ManifestCoversEveryStandardizedDis7PduType() { - Assert.Equal(233, Dis7SchemaManifest.ClassCount); - Assert.Equal(72, Dis7SchemaManifest.PduCount); - Assert.Equal(Enumerable.Range(1, 72).Select(x => (byte)x), Dis7SchemaManifest.Pdus.ToArray().Select(x => x.Type)); - Assert.Equal(72, Dis7SchemaManifest.Pdus.ToArray().Select(x => x.ModelName).Distinct(StringComparer.Ordinal).Count()); + Assert.AreSequenceEqual( + Enumerable.Range(1, 72).Select(x => (byte)x), + Dis7SchemaManifest.Pdus.ToArray().Select(x => x.Type)); + Assert.AreEqual(72, Dis7SchemaManifest.Pdus.ToArray().Select(x => x.ModelName).Distinct(StringComparer.Ordinal).Count()); foreach (var descriptor in Dis7SchemaManifest.Pdus) - Assert.Equal(descriptor.ModelName, PduFactory.Create((PduType)descriptor.Type).GetType().Name); + Assert.AreEqual(descriptor.ModelName, PduFactory.Create((PduType)descriptor.Type).GetType().Name); } - [Fact] + [TestMethod] public void EverySchemaClassHasAGeneratedPublicModel() { Type[] models = typeof(OpenDisNet.Pdus.EntityStatePdu).Assembly @@ -27,8 +28,8 @@ public void EverySchemaClassHasAGeneratedPublicModel() .Where(x => !(x.IsAbstract && x.IsSealed)) .ToArray(); - Assert.Equal(233, models.Length); + Assert.AreEqual(233, models.Length); foreach (var descriptor in Dis7SchemaManifest.Pdus) - Assert.Contains(models, x => x.Name == descriptor.ModelName); + Assert.IsTrue(models.Any(x => x.Name == descriptor.ModelName)); } } diff --git a/tests/OpenDisNet.Tests/DisSerializerTests.cs b/tests/OpenDisNet.Tests/DisSerializerTests.cs index ced01a8..246ee1d 100644 --- a/tests/OpenDisNet.Tests/DisSerializerTests.cs +++ b/tests/OpenDisNet.Tests/DisSerializerTests.cs @@ -3,20 +3,21 @@ namespace OpenDisNet.Tests; +[TestClass] public sealed class DisSerializerTests { - [Fact] + [TestMethod] public void DirectConstructionInitializesWireIdentity() { var signal = new SignalPdu { ExerciseId = 7 }; - Assert.Equal(DisProtocolVersion.Ieee1278_1_2012, signal.Header.ProtocolVersion); - Assert.Equal(PduType.Signal, signal.Header.PduType); - Assert.Equal(ProtocolFamily.RadioCommunications, signal.Header.ProtocolFamily); - Assert.Equal((byte)7, signal.Header.ExerciseId); + Assert.AreEqual(DisProtocolVersion.Ieee1278_1_2012, signal.Header.ProtocolVersion); + Assert.AreEqual(PduType.Signal, signal.Header.PduType); + Assert.AreEqual(ProtocolFamily.RadioCommunications, signal.Header.ProtocolFamily); + Assert.AreEqual((byte)7, signal.Header.ExerciseId); } - [Fact] + [TestMethod] public void GenericDeserializeReturnsExpectedPdu() { var signal = new SignalPdu { Radio = new RadioId(new EntityId(1, 2, 3), 4) }; @@ -24,32 +25,32 @@ public void GenericDeserializeReturnsExpectedPdu() SignalPdu parsed = DisSerializer.Deserialize(DisSerializer.Serialize(signal)); - Assert.Equal((ushort)3, parsed.Radio.Entity.EntityNumber); - Assert.Equal((ushort)10, parsed.DataBitLength); - Assert.Equal(new byte[] { 0xaa, 0xc0 }, parsed.Data); + Assert.AreEqual((ushort)3, parsed.Radio.Entity.EntityNumber); + Assert.AreEqual((ushort)10, parsed.DataBitLength); + Assert.AreSequenceEqual(new byte[] { 0xaa, 0xc0 }, parsed.Data); } - [Fact] + [TestMethod] public void GenericTryDeserializeReportsTypeMismatch() { byte[] bytes = DisSerializer.Serialize(new SignalPdu()); - Assert.False(DisSerializer.TryDeserialize(bytes, out _, out DisParseError error)); - Assert.Equal(DisParseErrorCode.UnexpectedPduType, error.Code); + Assert.IsFalse(DisSerializer.TryDeserialize(bytes, out _, out DisParseError error)); + Assert.AreEqual(DisParseErrorCode.UnexpectedPduType, error.Code); } - [Fact] + [TestMethod] public void RejectsTruncatedHeader() { - Assert.False(DisSerializer.TryDeserialize(new byte[11], out _, out DisParseError error)); - Assert.Equal(DisParseErrorCode.TruncatedHeader, error.Code); + Assert.IsFalse(DisSerializer.TryDeserialize(new byte[11], out _, out DisParseError error)); + Assert.AreEqual(DisParseErrorCode.TruncatedHeader, error.Code); } - [Fact] + [TestMethod] public void PreservesUnknownPduBody() { byte[] bytes = [7, 1, 200, 0, 0, 0, 0, 0, 0, 15, 0, 0, 1, 2, 3]; - Assert.True(DisSerializer.TryDeserialize(bytes, out IDisPdu? parsed, out DisParseError error), error.Message); - var unknown = Assert.IsType(parsed); - Assert.Equal(new byte[] { 1, 2, 3 }, unknown.Body.ToArray()); + Assert.IsTrue(DisSerializer.TryDeserialize(bytes, out IDisPdu? parsed, out DisParseError error), error.Message); + var unknown = Assert.IsInstanceOfType(parsed); + Assert.AreSequenceEqual(new byte[] { 1, 2, 3 }, unknown.Body.ToArray()); } } diff --git a/tests/OpenDisNet.Tests/Enumerations/SisoEnumerationTests.cs b/tests/OpenDisNet.Tests/Enumerations/SisoEnumerationTests.cs index e3f9c94..51fa537 100644 --- a/tests/OpenDisNet.Tests/Enumerations/SisoEnumerationTests.cs +++ b/tests/OpenDisNet.Tests/Enumerations/SisoEnumerationTests.cs @@ -5,9 +5,10 @@ namespace OpenDisNet.Tests.Enumerations; +[TestClass] public sealed class SisoEnumerationTests { - [Fact] + [TestMethod] public void SignalUsesNamedSisoValuesOnTheWire() { var original = new SignalPdu @@ -20,27 +21,27 @@ public void SignalUsesNamedSisoValuesOnTheWire() original.SetData("test"u8); byte[] bytes = DisSerializer.Serialize(original); - var decoded = Assert.IsType(DisSerializer.Deserialize(bytes)); + var decoded = Assert.IsInstanceOfType(DisSerializer.Deserialize(bytes)); - Assert.Equal(SignalTdlType.Link16StandardizedFormatJtidsMidsTadilJ, decoded.TdlType); - Assert.Equal(SignalEncodingClass.EncodedAudio, decoded.EncodingScheme.Class); - Assert.Equal(SignalEncodingType.Opus, decoded.EncodingScheme.AudioType); - Assert.Equal("test", Encoding.UTF8.GetString(decoded.Data)); - Assert.Equal(bytes, DisSerializer.Serialize(decoded)); + Assert.AreEqual(SignalTdlType.Link16StandardizedFormatJtidsMidsTadilJ, decoded.TdlType); + Assert.AreEqual(SignalEncodingClass.EncodedAudio, decoded.EncodingScheme.Class); + Assert.AreEqual(SignalEncodingType.Opus, decoded.EncodingScheme.AudioType); + Assert.AreEqual("test", Encoding.UTF8.GetString(decoded.Data)); + Assert.AreSequenceEqual(bytes, DisSerializer.Serialize(decoded)); } - [Fact] + [TestMethod] public void SignalEncodingSchemeSeparatesClassAndFourteenBitDetail() { SignalEncodingScheme value = SignalEncodingScheme.Data(SignalEncodingClass.RawBinaryData, messageCount: 12); - Assert.Equal(SignalEncodingClass.RawBinaryData, value.Class); - Assert.Equal((ushort)12, value.TypeOrMessageCount); - Assert.Equal((ushort)0x400C, value.Value); - Assert.Throws(() => value.WithTypeOrMessageCount(0x4000)); + Assert.AreEqual(SignalEncodingClass.RawBinaryData, value.Class); + Assert.AreEqual((ushort)12, value.TypeOrMessageCount); + Assert.AreEqual((ushort)0x400C, value.Value); + Assert.ThrowsExactly(() => value.WithTypeOrMessageCount(0x4000)); } - [Fact] + [TestMethod] public void UnknownEnumerationValuesRoundTripLosslessly() { var original = new SignalPdu @@ -49,13 +50,13 @@ public void UnknownEnumerationValuesRoundTripLosslessly() }; byte[] bytes = DisSerializer.Serialize(original); - var decoded = Assert.IsType(DisSerializer.Deserialize(bytes)); + var decoded = Assert.IsInstanceOfType(DisSerializer.Deserialize(bytes)); - Assert.Equal((SignalTdlType)0xFEED, decoded.TdlType); - Assert.Equal(bytes, DisSerializer.Serialize(decoded)); + Assert.AreEqual((SignalTdlType)0xFEED, decoded.TdlType); + Assert.AreSequenceEqual(bytes, DisSerializer.Serialize(decoded)); } - [Fact] + [TestMethod] public void BitfieldHelpersPreserveReservedBitsAndRoundTrip() { StopFreezeFrozenBehavior behavior = new StopFreezeFrozenBehavior(0x80) @@ -68,16 +69,16 @@ public void BitfieldHelpersPreserveReservedBitsAndRoundTrip() }; byte[] bytes = DisSerializer.Serialize(original); - var decoded = Assert.IsType(DisSerializer.Deserialize(bytes)); + var decoded = Assert.IsInstanceOfType(DisSerializer.Deserialize(bytes)); - Assert.True(decoded.FrozenBehavior.RunSimulationClock); - Assert.False(decoded.FrozenBehavior.TransmitUpdates); - Assert.True(decoded.FrozenBehavior.ProcessUpdates); - Assert.Equal((byte)0x85, decoded.FrozenBehavior.Value); - Assert.Equal(bytes, DisSerializer.Serialize(decoded)); + Assert.IsTrue(decoded.FrozenBehavior.RunSimulationClock); + Assert.IsFalse(decoded.FrozenBehavior.TransmitUpdates); + Assert.IsTrue(decoded.FrozenBehavior.ProcessUpdates); + Assert.AreEqual((byte)0x85, decoded.FrozenBehavior.Value); + Assert.AreSequenceEqual(bytes, DisSerializer.Serialize(decoded)); } - [Fact] + [TestMethod] public void MultiBitFieldsHaveValidatedImmutableSetters() { MinefieldDataFusing value = MinefieldDataFusing.None @@ -85,9 +86,9 @@ public void MultiBitFieldsHaveValidatedImmutableSetters() .WithSecondary(7) .WithHasAntiHandlingDevice(true); - Assert.Equal((ushort)42, value.Primary); - Assert.Equal((ushort)7, value.Secondary); - Assert.True(value.HasAntiHandlingDevice); - Assert.Throws(() => value.WithPrimary(128)); + Assert.AreEqual((ushort)42, value.Primary); + Assert.AreEqual((ushort)7, value.Secondary); + Assert.IsTrue(value.HasAntiHandlingDevice); + Assert.ThrowsExactly(() => value.WithPrimary(128)); } } diff --git a/tests/OpenDisNet.Tests/OpenDisNet.Tests.csproj b/tests/OpenDisNet.Tests/OpenDisNet.Tests.csproj index e9d739d..bed6838 100644 --- a/tests/OpenDisNet.Tests/OpenDisNet.Tests.csproj +++ b/tests/OpenDisNet.Tests/OpenDisNet.Tests.csproj @@ -8,14 +8,13 @@ - - - - + + + - + diff --git a/tests/OpenDisNet.Tests/Pdus/EntityStatePduTests.cs b/tests/OpenDisNet.Tests/Pdus/EntityStatePduTests.cs index 148278c..e1dc681 100644 --- a/tests/OpenDisNet.Tests/Pdus/EntityStatePduTests.cs +++ b/tests/OpenDisNet.Tests/Pdus/EntityStatePduTests.cs @@ -5,9 +5,10 @@ namespace OpenDisNet.Tests.Pdus; +[TestClass] public sealed class EntityStatePduTests { - [Fact] + [TestMethod] public void EntityStatePduUsesObjectInitializerAndRoundTrips() { var expected = (EntityStatePdu)PduFactory.Create(PduType.EntityState, exerciseId: 4); @@ -19,11 +20,11 @@ public void EntityStatePduUsesObjectInitializerAndRoundTrips() Encoding.ASCII.GetBytes("EAGLE-1").CopyTo(expected.Marking.Characters, 0); byte[] bytes = DisSerializer.Serialize(expected); - var actual = Assert.IsType(DisSerializer.Deserialize(bytes)); + var actual = Assert.IsInstanceOfType(DisSerializer.Deserialize(bytes)); - Assert.Equal(144, bytes.Length); - Assert.Equal((ushort)3, actual.EntityId.EntityNumber); - Assert.Equal(300, actual.EntityLocation.Z); - Assert.StartsWith("EAGLE-1", Encoding.ASCII.GetString(actual.Marking.Characters)); + Assert.AreEqual(144, bytes.Length); + Assert.AreEqual((ushort)3, actual.EntityId.EntityNumber); + Assert.AreEqual(300, actual.EntityLocation.Z); + StringAssert.StartsWith(Encoding.ASCII.GetString(actual.Marking.Characters), "EAGLE-1"); } } diff --git a/tests/OpenDisNet.Tests/Pdus/WarfarePduTests.cs b/tests/OpenDisNet.Tests/Pdus/WarfarePduTests.cs index b856144..132bc82 100644 --- a/tests/OpenDisNet.Tests/Pdus/WarfarePduTests.cs +++ b/tests/OpenDisNet.Tests/Pdus/WarfarePduTests.cs @@ -4,9 +4,10 @@ namespace OpenDisNet.Tests.Pdus; +[TestClass] public sealed class WarfarePduTests { - [Fact] + [TestMethod] public void FirePduRoundTrips() { var expected = (FirePdu)PduFactory.Create(PduType.Fire, exerciseId: 1); @@ -17,14 +18,14 @@ public void FirePduRoundTrips() expected.Range = 5_000; byte[] bytes = DisSerializer.Serialize(expected); - var actual = Assert.IsType(DisSerializer.Deserialize(bytes)); + var actual = Assert.IsInstanceOfType(DisSerializer.Deserialize(bytes)); - Assert.Equal(96, bytes.Length); - Assert.Equal((ushort)3, actual.FiringEntityId.EntityNumber); - Assert.Equal(5_000, actual.Range); + Assert.AreEqual(96, bytes.Length); + Assert.AreEqual((ushort)3, actual.FiringEntityId.EntityNumber); + Assert.AreEqual(5_000, actual.Range); } - [Fact] + [TestMethod] public void DetonationPduRoundTripsVariableParameters() { var expected = (DetonationPdu)PduFactory.Create(PduType.Detonation); @@ -35,9 +36,11 @@ public void DetonationPduRoundTripsVariableParameters() }); byte[] bytes = DisSerializer.Serialize(expected); - var actual = Assert.IsType(DisSerializer.Deserialize(bytes)); + var actual = Assert.IsInstanceOfType(DisSerializer.Deserialize(bytes)); - Assert.Equal(120, bytes.Length); - Assert.Equal(expected.VariableParameters[0].RecordSpecificFields, actual.VariableParameters[0].RecordSpecificFields); + Assert.AreEqual(120, bytes.Length); + Assert.AreSequenceEqual( + expected.VariableParameters[0].RecordSpecificFields, + actual.VariableParameters[0].RecordSpecificFields); } } diff --git a/tests/OpenDisNet.Tests/PublicApiTests.cs b/tests/OpenDisNet.Tests/PublicApiTests.cs index 8913652..4ac7edf 100644 --- a/tests/OpenDisNet.Tests/PublicApiTests.cs +++ b/tests/OpenDisNet.Tests/PublicApiTests.cs @@ -3,37 +3,39 @@ namespace OpenDisNet.Tests; +[TestClass] public sealed class PublicApiTests { - [Fact] + [TestMethod] public void ProtocolModelsUseOneConsumerNamespace() { Type[] exported = typeof(Pdu).Assembly.GetExportedTypes(); - Assert.DoesNotContain(exported, x => x.Namespace is "OpenDisNet.Dis7" or "OpenDisNet.Generated" or "OpenDisNet.Records"); - Assert.Equal(72, exported.Count(x => x.Namespace == "OpenDisNet.Pdus" && typeof(Pdu).IsAssignableFrom(x) && !x.IsAbstract)); + Assert.IsFalse(exported.Any( + x => x.Namespace is "OpenDisNet.Dis7" or "OpenDisNet.Generated" or "OpenDisNet.Records")); + Assert.AreEqual(72, exported.Count(x => x.Namespace == "OpenDisNet.Pdus" && typeof(Pdu).IsAssignableFrom(x) && !x.IsAbstract)); } - [Fact] + [TestMethod] public void DerivedWireCountsAreNotPartOfThePublicModel() { - Assert.Null(typeof(EntityStatePdu).GetProperty("NumberOfVariableParameters")); - Assert.Null(typeof(SignalPdu).GetProperty("DataLength")); - Assert.Null(typeof(TransmitterPdu).GetProperty("ModulationParameterCount")); - Assert.NotNull(typeof(SignalPdu).GetProperty("DataBitLength")); + Assert.IsNull(typeof(EntityStatePdu).GetProperty("NumberOfVariableParameters")); + Assert.IsNull(typeof(SignalPdu).GetProperty("DataLength")); + Assert.IsNull(typeof(TransmitterPdu).GetProperty("ModulationParameterCount")); + Assert.IsNotNull(typeof(SignalPdu).GetProperty("DataBitLength")); } - [Fact] + [TestMethod] public void EveryConcretePduCanBeConstructedWithCorrectWireIdentity() { foreach (byte value in Enumerable.Range(1, 72).Select(x => (byte)x)) { Pdu factoryPdu = PduFactory.Create((PduType)value); - var directlyCreated = Assert.IsAssignableFrom(Activator.CreateInstance(factoryPdu.GetType())); + var directlyCreated = Assert.IsInstanceOfType(Activator.CreateInstance(factoryPdu.GetType())); - Assert.Equal((PduType)value, directlyCreated.Header.PduType); - Assert.Equal(DisProtocolVersion.Ieee1278_1_2012, directlyCreated.Header.ProtocolVersion); - Assert.NotEqual(ProtocolFamily.Other, directlyCreated.Header.ProtocolFamily); + Assert.AreEqual((PduType)value, directlyCreated.Header.PduType); + Assert.AreEqual(DisProtocolVersion.Ieee1278_1_2012, directlyCreated.Header.ProtocolVersion); + Assert.AreNotEqual(ProtocolFamily.Other, directlyCreated.Header.ProtocolFamily); } } } diff --git a/tests/OpenDisNet.Tests/Security/ParserSecurityTests.cs b/tests/OpenDisNet.Tests/Security/ParserSecurityTests.cs index c3ec45d..324b510 100644 --- a/tests/OpenDisNet.Tests/Security/ParserSecurityTests.cs +++ b/tests/OpenDisNet.Tests/Security/ParserSecurityTests.cs @@ -4,6 +4,7 @@ namespace OpenDisNet.Tests.Security; +[TestClass] public sealed class ParserSecurityTests { private static readonly DisParseOptions BoundedOptions = new() @@ -13,7 +14,7 @@ public sealed class ParserSecurityTests RequireVersion7 = true, }; - [Fact] + [TestMethod] public void DeterministicRandomDatagramsNeverEscapeTryDeserialize() { var random = new Random(0x1278_0001); @@ -26,19 +27,19 @@ public void DeterministicRandomDatagramsNeverEscapeTryDeserialize() if (DisSerializer.TryDeserialize(datagram, out IDisPdu? pdu, out DisParseError error, BoundedOptions)) { - Assert.NotNull(pdu); + Assert.IsNotNull(pdu); byte[] canonical = DisSerializer.Serialize(pdu); - Assert.InRange(canonical.Length, DisHeader.MinimumSize, ushort.MaxValue); + Assert.IsTrue(canonical.Length is >= DisHeader.MinimumSize and <= ushort.MaxValue); } else { - Assert.NotEqual(DisParseErrorCode.None, error.Code); - Assert.InRange(error.Offset, 0, Math.Max(datagram.Length, DisHeader.Size)); + Assert.AreNotEqual(DisParseErrorCode.None, error.Code); + Assert.IsTrue(error.Offset >= 0 && error.Offset <= Math.Max(datagram.Length, DisHeader.Size)); } } } - [Fact] + [TestMethod] public void BitMutationsAcrossEveryPduNeverEscapeTryDeserialize() { byte[] masks = [0x01, 0x80, 0xFF]; @@ -55,27 +56,27 @@ public void BitMutationsAcrossEveryPduNeverEscapeTryDeserialize() if (DisSerializer.TryDeserialize(mutated, out IDisPdu? parsed, out _, BoundedOptions)) { - Assert.NotNull(parsed); + Assert.IsNotNull(parsed); byte[] canonical = DisSerializer.Serialize(parsed); - Assert.InRange(canonical.Length, DisHeader.MinimumSize, ushort.MaxValue); + Assert.IsTrue(canonical.Length is >= DisHeader.MinimumSize and <= ushort.MaxValue); } } } } } - [Fact] + [TestMethod] public void ConfiguredLengthLimitIsEnforcedBeforeBodyParsing() { byte[] datagram = DisSerializer.Serialize(DeterministicPduFixture.Create(PduType.EntityState)); var options = new DisParseOptions { MaximumPduLength = datagram.Length - 1 }; - Assert.False(DisSerializer.TryDeserialize(datagram, out _, out DisParseError error, options)); - Assert.Equal(DisParseErrorCode.InvalidLength, error.Code); - Assert.Equal(8, error.Offset); + Assert.IsFalse(DisSerializer.TryDeserialize(datagram, out _, out DisParseError error, options)); + Assert.AreEqual(DisParseErrorCode.InvalidLength, error.Code); + Assert.AreEqual(8, error.Offset); } - [Fact] + [TestMethod] public void MaximumDeclaredLengthCannotAllocateFromATruncatedDatagram() { byte[] datagram = @@ -86,7 +87,7 @@ public void MaximumDeclaredLengthCannotAllocateFromATruncatedDatagram() 0, 0, ]; - Assert.False(DisSerializer.TryDeserialize(datagram, out _, out DisParseError error, BoundedOptions)); - Assert.Equal(DisParseErrorCode.InvalidLength, error.Code); + Assert.IsFalse(DisSerializer.TryDeserialize(datagram, out _, out DisParseError error, BoundedOptions)); + Assert.AreEqual(DisParseErrorCode.InvalidLength, error.Code); } } diff --git a/tests/OpenDisNet.Tests/packages.lock.json b/tests/OpenDisNet.Tests/packages.lock.json index b56f16c..de091f6 100644 --- a/tests/OpenDisNet.Tests/packages.lock.json +++ b/tests/OpenDisNet.Tests/packages.lock.json @@ -2,107 +2,106 @@ "version": 1, "dependencies": { "net10.0": { - "coverlet.collector": { - "type": "Direct", - "requested": "[6.0.4, )", - "resolved": "6.0.4", - "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" - }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.14.1, )", - "resolved": "17.14.1", - "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "requested": "[18.8.1, )", + "resolved": "18.8.1", + "contentHash": "dknJL3/9Y3t4XuCBqnc0PevPxgLsUMmVhjwup/b1HNovA8zWcj3XsfIf7c6p05363DWcqL7X/YhDL9B+Zymv1w==", "dependencies": { - "Microsoft.CodeCoverage": "17.14.1", - "Microsoft.TestPlatform.TestHost": "17.14.1" + "Microsoft.CodeCoverage": "18.8.1", + "Microsoft.TestPlatform.TestHost": "18.8.1" } }, - "xunit": { + "MSTest.TestAdapter": { "type": "Direct", - "requested": "[2.9.3, )", - "resolved": "2.9.3", - "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", + "requested": "[4.3.2, )", + "resolved": "4.3.2", + "contentHash": "uqpjJqQ3ws5z7Pq6BfMFpuqdrN2YIfe0Gfww91LRVXwxBdLqUaJd4JbHUImOSQvmK5m3PqW285kPjKbb69QFoA==", "dependencies": { - "xunit.analyzers": "1.18.0", - "xunit.assert": "2.9.3", - "xunit.core": "[2.9.3]" + "MSTest.TestFramework": "4.3.2", + "Microsoft.Testing.Extensions.VSTestBridge": "2.3.2", + "Microsoft.Testing.Platform.MSBuild": "2.3.2" } }, - "xunit.runner.visualstudio": { + "MSTest.TestFramework": { "type": "Direct", - "requested": "[3.1.4, )", - "resolved": "3.1.4", - "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + "requested": "[4.3.2, )", + "resolved": "4.3.2", + "contentHash": "Vlj3THt/BcOV/93k0Cdq29RNEsrY3TvUV6mF13rOuY8Gx7Ltd+LrOYId8AYu0cH9ioCYY/Sx1hppfI9p/vP+aw==", + "dependencies": { + "MSTest.Analyzers": "4.3.2" + } }, - "Microsoft.CodeCoverage": { + "Microsoft.ApplicationInsights": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + "resolved": "2.23.0", + "contentHash": "nWArUZTdU7iqZLycLKWe0TDms48KKGE6pONH2terYNa8REXiqixrMOkf1sk5DHGMaUTqONU2YkS4SAXBhLStgw==" }, - "Microsoft.TestPlatform.ObjectModel": { + "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==" + "resolved": "18.8.1", + "contentHash": "Eclse/ZZjr4lmWzZFNN9h/OluhKL+SK/QbUyKUewgX139aGeyMEO/DkMPwuFs2MixvanTnz6891rF8UHDg+W4Q==" }, - "Microsoft.TestPlatform.TestHost": { + "Microsoft.Testing.Extensions.Telemetry": { "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "resolved": "2.3.2", + "contentHash": "Q1iapTk1DwFFT9aPtv4wxJHe22++NLxsg7IekEujac9RDAugxGQdCN0SQ1b9ryF78cdidhSBB17pHdCANurOyQ==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.14.1", - "Newtonsoft.Json": "13.0.3" + "Microsoft.ApplicationInsights": "2.23.0", + "Microsoft.Testing.Platform": "2.3.2" } }, - "Newtonsoft.Json": { + "Microsoft.Testing.Extensions.TrxReport.Abstractions": { "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "xunit.abstractions": { - "type": "Transitive", - "resolved": "2.0.3", - "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + "resolved": "2.3.2", + "contentHash": "frw+Qa6gUxOZSw2Mw6SPtJT9gPzcP4nS9TfQhS0cHXKL6Rcp/CqLG+o4Psc1bHAhxW04rpzTBhjpNMnw2L7BuQ==", + "dependencies": { + "Microsoft.Testing.Platform": "2.3.2" + } }, - "xunit.analyzers": { + "Microsoft.Testing.Extensions.VSTestBridge": { "type": "Transitive", - "resolved": "1.18.0", - "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + "resolved": "2.3.2", + "contentHash": "DXJIaLlwt+6GnPurlf7eVMI0wAZV5KCZrgGPibhx9i9LNxAkwEOK2jVzdVjLvvZ/SX9MUUaGJB9yXuF/VoT4/w==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "18.4.0", + "Microsoft.Testing.Extensions.Telemetry": "2.3.2", + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.3.2", + "Microsoft.Testing.Platform": "2.3.2" + } }, - "xunit.assert": { + "Microsoft.Testing.Platform": { "type": "Transitive", - "resolved": "2.9.3", - "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + "resolved": "2.3.2", + "contentHash": "FuJgIEfNU+rFHJYGhuqA8uup+3cryJ1N67Tijqo01Jd5xAr28ptz0aXkFfSqK3MhAUmGsrPpgQ92+3+0oXv8AA==" }, - "xunit.core": { + "Microsoft.Testing.Platform.MSBuild": { "type": "Transitive", - "resolved": "2.9.3", - "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "resolved": "2.3.2", + "contentHash": "guFvvaE8FGUlB6h24jN+P+NBz/P5x4On9sk4n5UapUAPq4fr0y1AgUe4sXmItp7r2NMe3rhZYLE1teP9T9sVbg==", "dependencies": { - "xunit.extensibility.core": "[2.9.3]", - "xunit.extensibility.execution": "[2.9.3]" + "Microsoft.Testing.Platform": "2.3.2" } }, - "xunit.extensibility.core": { + "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "2.9.3", - "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", - "dependencies": { - "xunit.abstractions": "2.0.3" - } + "resolved": "18.8.1", + "contentHash": "qLbktNB1+b1XZLNJBTzaWVVJAd6PEzD7cgD406geMb6PcFZhp3EDNa1tctWx1+mtMU6MP/6ozVvFPC9vs2a9rw==" }, - "xunit.extensibility.execution": { + "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "2.9.3", - "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "resolved": "18.8.1", + "contentHash": "FaQHPDTUOcE+SFTjssNPfrub2lT9Zyon4J2W/KLHt/efLJACb1TCeWXyOgh0D/4Q1e4n+S3E6mOKud+9nLZlEA==", "dependencies": { - "xunit.extensibility.core": "[2.9.3]" + "Microsoft.TestPlatform.ObjectModel": "18.8.1" } }, - "opendisnet": { - "type": "Project" + "MSTest.Analyzers": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "GNvrVI6/Y5pNoKnK1/VELBls3XiHNZaj+zh14LtwlCqFI8zXTW3vuBe0Jv2qEr9wxHlOAQQBTJPRoNRjneGrKQ==" }, - "opendisnet.reference": { + "opendisnet": { "type": "Project" } }