diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml index f7e4e8842..c6ab60d2b 100644 --- a/.azure-pipelines/ci-build.yml +++ b/.azure-pipelines/ci-build.yml @@ -40,7 +40,6 @@ extends: os: windows demands: - msbuild - - vstest customBuildTags: - ES365AIMigrationTooling stages: @@ -81,7 +80,7 @@ extends: version: 8.x - task: UseDotNet@2 - displayName: 'Use .NET 10 for hidi tests' + displayName: 'Use .NET 10 for Microsoft.Testing.Platform' inputs: version: 10.x @@ -114,19 +113,20 @@ extends: projects: '$(Build.SourcesDirectory)\Microsoft.OpenApi.OData.sln' arguments: '--configuration $(BuildConfiguration) --no-incremental' - # Both test projects are in the solution, but SDK10 cannot run MTP via VSTest. - # Keep OData on VSTest and invoke the hidi MTP executable separately. - - task: DotNetCoreCLI@2 - displayName: 'test' - inputs: - command: test - projects: '$(Build.SourcesDirectory)\test\Microsoft.OpenAPI.OData.Reader.Tests\Microsoft.OpenAPI.OData.Reader.Tests.csproj' - arguments: '--configuration $(BuildConfiguration) --no-build' - - pwsh: | - dotnet run --project test\Microsoft.OpenApi.Hidi.Tests\Microsoft.OpenApi.Hidi.Tests.csproj -c $(buildConfiguration) --no-build -- --minimum-expected-tests 1 + dotnet test --solution Microsoft.OpenApi.OData.sln -c $(buildConfiguration) --no-build --results-directory '$(Agent.TempDirectory)\TestResults' -- --minimum-expected-tests 1 --report-trx --report-azdo if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } - displayName: 'Test hidi (Microsoft.Testing.Platform)' + displayName: 'Test solution (Microsoft.Testing.Platform)' + + - task: PublishTestResults@2 + displayName: 'Publish solution test results' + condition: succeededOrFailed() + inputs: + testResultsFormat: VSTest + testResultsFiles: '$(Agent.TempDirectory)\TestResults\*.trx' + mergeTestResults: true + failTaskOnFailedTests: true + failTaskOnMissingResultsFile: true - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 displayName: 'ESRP CodeSigning' diff --git a/.azure-pipelines/hidi-release.yml b/.azure-pipelines/hidi-release.yml index a11c53fb4..f21a3f590 100644 --- a/.azure-pipelines/hidi-release.yml +++ b/.azure-pipelines/hidi-release.yml @@ -115,8 +115,21 @@ extends: - pwsh: | dotnet build src\Microsoft.OpenApi.Hidi\Microsoft.OpenApi.Hidi.csproj -c $(buildConfiguration) -p:GeneratePackageOnBuild=false if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } - dotnet run --project test\Microsoft.OpenApi.Hidi.Tests\Microsoft.OpenApi.Hidi.Tests.csproj -c $(buildConfiguration) -- --minimum-expected-tests 1 + displayName: Build hidi + - pwsh: | + dotnet test --solution Microsoft.OpenApi.OData.sln -c $(buildConfiguration) --results-directory '$(Agent.TempDirectory)\TestResults' -- --minimum-expected-tests 1 --report-trx --report-azdo if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + displayName: Test solution (Microsoft.Testing.Platform) + - task: PublishTestResults@2 + displayName: Publish solution test results + condition: succeededOrFailed() + inputs: + testResultsFormat: VSTest + testResultsFiles: '$(Agent.TempDirectory)\TestResults\*.trx' + mergeTestResults: true + failTaskOnFailedTests: true + failTaskOnMissingResultsFile: true + - pwsh: | dotnet publish src\Microsoft.OpenApi.Hidi\Microsoft.OpenApi.Hidi.csproj -c $(buildConfiguration) -r win-x64 --self-contained true -p:PublishSingleFile=true -p:PackAsTool=false -p:GeneratePackageOnBuild=false -o artifacts\hidi\win-x64 if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } & .\artifacts\hidi\win-x64\Microsoft.OpenApi.Hidi.exe --help @@ -124,7 +137,7 @@ extends: New-Item -ItemType Directory -Force artifacts\hidi\signing | Out-Null Copy-Item src\Microsoft.OpenApi.Hidi\bin\$(buildConfiguration)\net8.0\Microsoft.OpenApi.Hidi.dll artifacts\hidi\signing Copy-Item artifacts\hidi\win-x64\Microsoft.OpenApi.Hidi.exe artifacts\hidi\signing - displayName: Build, test and stage hidi binaries + displayName: Stage hidi binaries - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 displayName: Sign hidi DLL and Windows executable inputs: diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 3fc947508..12b9e4bd0 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -88,21 +88,18 @@ jobs: id: run_unit_tests shell: pwsh run: | - $testProjectsArray = @( - '.\test\Microsoft.OpenAPI.OData.Reader.Tests\Microsoft.OpenAPI.OData.Reader.Tests.csproj' - ) - - $testProjectsArray | ForEach-Object { - dotnet test $PSItem ` - -c Release - } - - - name: Run hidi tests (Microsoft.Testing.Platform) - shell: pwsh - run: | - dotnet run --project .\test\Microsoft.OpenApi.Hidi.Tests\Microsoft.OpenApi.Hidi.Tests.csproj -c Release -- --minimum-expected-tests 1 + $resultsDirectory = Join-Path $PWD 'artifacts' 'test-results' + dotnet test --solution Microsoft.OpenApi.OData.sln -c Release -p:EnableWindowsTargeting=true --results-directory $resultsDirectory -- --minimum-expected-tests 1 --report-trx --report-gh if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + - name: Upload test results + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: test-results + path: artifacts/test-results + if-no-files-found: error + - name: Pack and install hidi locally shell: pwsh run: | diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 3681312cc..1c0f0b486 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -122,16 +122,25 @@ jobs: - name: Build and analyze env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any - CollectCoverage: true - CoverletOutputFormat: 'opencover' # https://github.com/microsoft/vstest/issues/4014#issuecomment-1307913682 shell: pwsh run: | - dotnet tool run dotnet-sonarscanner begin /k:"microsoft_OpenAPI.NET.OData" /o:"microsoft" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.opencover.reportsPaths="test/**/coverage.net8.0.opencover.xml,artifacts/hidi/coverage/*opencover*.xml" /d:sonar.coverageReportPaths="artifacts/hidi/powershell-coverage/sonar-coverage.xml" + dotnet tool run dotnet-sonarscanner begin /k:"microsoft_OpenAPI.NET.OData" /o:"microsoft" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.opencover.reportsPaths="artifacts/test-results/*opencover*.xml" /d:sonar.coverageReportPaths="artifacts/hidi/powershell-coverage/sonar-coverage.xml" + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } dotnet workload restore + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } dotnet build - dotnet test test\Microsoft.OpenAPI.OData.Reader.Tests\Microsoft.OpenAPI.OData.Reader.Tests.csproj --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } - dotnet run --project test\Microsoft.OpenApi.Hidi.Tests\Microsoft.OpenApi.Hidi.Tests.csproj --no-build -- --minimum-expected-tests 1 --coverlet --coverlet-output-format opencover --coverlet-include "[Microsoft.OpenApi.Hidi]*" --results-directory artifacts\hidi\coverage + dotnet test --solution Microsoft.OpenApi.OData.sln --no-build --results-directory artifacts\test-results -- --minimum-expected-tests 1 --report-trx --report-gh --coverlet --coverlet-output-format opencover --coverlet-include "[Microsoft.OpenApi.OData.Reader]*" --coverlet-include "[Microsoft.OpenApi.Hidi]*" if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } - if (-not (Get-ChildItem artifacts\hidi\coverage -Filter '*opencover*.xml')) { throw "Hidi OpenCover report was not generated." } + $reports = @(Get-ChildItem artifacts\test-results -Filter '*opencover*.xml') + if ($reports.Count -ne 2) { throw "Expected an OpenCover report from each test project." } dotnet tool run dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + + - name: Upload test and coverage results + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: sonar-test-results + path: artifacts/test-results + if-no-files-found: error diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 097a05672..4f31b9c3a 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -21,10 +21,21 @@ "group": "test", "args": [ "test", + "--solution", "${workspaceFolder}/Microsoft.OpenApi.OData.sln", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary", - "--collect:\"XPlat Code Coverage\"" + "-p:EnableWindowsTargeting=true", + "--results-directory", + "${workspaceFolder}/artifacts/test-results", + "--", + "--minimum-expected-tests", + "1", + "--coverlet", + "--coverlet-output-format", + "cobertura", + "--coverlet-include", + "[Microsoft.OpenApi.OData.Reader]*", + "--coverlet-include", + "[Microsoft.OpenApi.Hidi]*" ], "problemMatcher": "$msCompile" }, @@ -35,15 +46,15 @@ "linux": { "command": "rm", "args": [ - "-r", - "${workspaceFolder}/test/Microsoft.OpenAPI.OData.Reader.Tests/TestResults" + "-rf", + "${workspaceFolder}/artifacts/test-results" ] }, "windows": { "command": "powershell", "args": [ "-command", - "Remove-Item -Recurse -Include TestResults -Path ${workspaceFolder}/tests" + "if (Test-Path -LiteralPath '${workspaceFolder}/artifacts/test-results') { Remove-Item -Recurse -LiteralPath '${workspaceFolder}/artifacts/test-results' }" ] } }, @@ -54,14 +65,14 @@ "linux":{ "command": "reportgenerator", "args": [ - "-reports:${workspaceFolder}/tests/**/coverage.cobertura.xml", + "-reports:${workspaceFolder}/artifacts/test-results/*cobertura*.xml", "-targetdir:${workspaceFolder}/reports/coverage" ], }, "windows": { "command": "reportgenerator", "args": [ - "-reports:${workspaceFolder}\\test\\**\\coverage.cobertura.xml", + "-reports:${workspaceFolder}\\artifacts\\test-results\\*cobertura*.xml", "-targetdir:${workspaceFolder}\\reports\\coverage" ] }, @@ -72,7 +83,7 @@ "dependsOrder": "sequence" }, { - "label": "coverage:launch", + "label": "coverage:open", "type": "shell", "linux": { "command": "xdg-open", @@ -100,7 +111,7 @@ "dependsOrder": "sequence", "dependsOn": [ "coverage:unit", - "coverage:launch" + "coverage:open" ] }, { diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8fa33027d..22d010be1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,15 +12,29 @@ OpenAPI.net.OData is a mono-repo containing source code for the following packag [Microsoft.OpenApi.Hidi](src/Microsoft.OpenApi.Hidi/readme.md) is a .NET tool, not an OData library package. Install the .NET 10 SDK and .NET 8 runtime to build -and test both projects. Do not change the solution-wide test runner: OData uses -VSTest and hidi uses Microsoft.Testing.Platform. +and test both projects. Both test projects use xUnit v3 and +Microsoft.Testing.Platform, selected by `global.json`. Reader tests still target +.NET 8; Hidi tests target .NET 10. ```powershell dotnet build Microsoft.OpenApi.OData.sln -c Release -dotnet test test\Microsoft.OpenAPI.OData.Reader.Tests\Microsoft.OpenAPI.OData.Reader.Tests.csproj -c Release -dotnet run --project test\Microsoft.OpenApi.Hidi.Tests\Microsoft.OpenApi.Hidi.Tests.csproj -c Release -- --minimum-expected-tests 1 +dotnet test --solution Microsoft.OpenApi.OData.sln -c Release --no-build -- --minimum-expected-tests 1 ``` +On non-Windows hosts, pass `-p:EnableWindowsTargeting=true` to both commands +because the solution also contains the Windows-only GUI project. +Use `--project ` instead of `--solution ` for a single test project. +MTP options replace VSTest's `--logger`, `--collect`, and MSBuild coverage options: + +```powershell +dotnet test --solution Microsoft.OpenApi.OData.sln -c Release --no-build --results-directory artifacts\test-results -- --minimum-expected-tests 1 --report-trx --coverlet --coverlet-output-format cobertura --coverlet-include "[Microsoft.OpenApi.OData.Reader]*" --coverlet-include "[Microsoft.OpenApi.Hidi]*" +``` + +This produces separate TRX and coverage reports for each test assembly. +CI uses `--report-gh` for GitHub Actions and `--report-azdo` with TRX publication +for Azure DevOps. SonarCloud collects OpenCover reports for both projects in +`artifacts/test-results`, alongside the separately measured Pester helper coverage. + Hidi uses published OpenAPI core/YAML packages and the local OData project. Its public-only signing key preserves assembly identity and friend-assembly access; never add the source repository's private strong-name key. Production signing diff --git a/README.md b/README.md index 0d3d7651d..ec9266909 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,11 @@ The command remains `hidi`. The `support/v2` branch maintains hidi 2.x separately from the OData library version. Hidi requires .NET 8 to run; building the repository and running hidi -tests requires the .NET 10 SDK. Existing OData tests still use .NET 8 and VSTest. +tests requires the .NET 10 SDK. Both test projects use xUnit v3 and +Microsoft.Testing.Platform; OData tests still target .NET 8. Run them together +with `dotnet test --solution Microsoft.OpenApi.OData.sln -c Release -- --minimum-expected-tests 1` +(add `-p:EnableWindowsTargeting=true` on non-Windows hosts). See +[contributing](CONTRIBUTING.md#hidi) for coverage and reporting commands. Hidi's filtered history was imported with a merge, not squashed. **Merge this migration PR using a merge commit, not squash or rebase**, so the canonical diff --git a/build.ps1 b/build.ps1 index 1becaff9b..99a056e4c 100644 --- a/build.ps1 +++ b/build.ps1 @@ -103,8 +103,7 @@ ForEach ($ver in $SNVersions) } # Other variables -$ProductProj = $ENLISTMENT_ROOT + "\src\Microsoft.OpenAPI.OData.Reader\Microsoft.OpenApi.OData.Reader.csproj" -$TESTProj = $ENLISTMENT_ROOT + "\test\Microsoft.OpenAPI.OData.Reader.Tests\Microsoft.OpenApi.OData.Reader.Tests.csproj" +$Solution = $ENLISTMENT_ROOT + "\Microsoft.OpenApi.OData.sln" $TESTDIR = $ENLISTMENT_ROOT + "\bin\$Configuration\Test\net472" $PRODUCTDIR = $ENLISTMENT_ROOT + "\bin\$Configuration\net472" @@ -186,13 +185,10 @@ Function BuildProcess $script:BUILD_START_TIME = Get-Date - Write-Host "Build Product ..." + Write-Host "Build Solution ..." - & dotnet.exe build $ProductProj -c $Configuration - - Write-Host "Build Test ..." - - & dotnet.exe build $TESTProj -c $Configuration + & dotnet.exe build $Solution -c $Configuration + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } Write-Host "Build Done" -ForegroundColor $Success $script:BUILD_END_TIME = Get-Date @@ -204,7 +200,8 @@ Function TestProcess $script:TEST_START_TIME = Get-Date - & dotnet test $TESTProj -c $Configuration + & dotnet test --solution $Solution -c $Configuration --no-build -- --minimum-expected-tests 1 + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } Write-Host "Test Done" -ForegroundColor $Success $script:TEST_END_TIME = Get-Date diff --git a/global.json b/global.json index d46d21e50..df32a2e39 100644 --- a/global.json +++ b/global.json @@ -3,5 +3,8 @@ "version": "10.0.100", "rollForward": "latestFeature", "allowPrerelease": false + }, + "test": { + "runner": "Microsoft.Testing.Platform" } } diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Common/EdmModelHelper.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/Common/EdmModelHelper.cs index a7d5841c3..cbbeffbce 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Common/EdmModelHelper.cs +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Common/EdmModelHelper.cs @@ -15,7 +15,6 @@ using System.Collections.Generic; using System.Text; using Microsoft.OData.Edm.Validation; -using Xunit.Abstractions; namespace Microsoft.OpenApi.OData.Tests { diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Common/OpenApiTestBase.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/Common/OpenApiTestBase.cs index b944fe828..52b437b34 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Common/OpenApiTestBase.cs +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Common/OpenApiTestBase.cs @@ -3,7 +3,7 @@ // Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. // ------------------------------------------------------------ -using Xunit.Abstractions; +using Xunit; namespace Microsoft.OpenApi.OData.Tests { diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/EdmModelOpenApiExtensionsTest.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/EdmModelOpenApiExtensionsTest.cs index 3c1326e97..bf071c541 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/EdmModelOpenApiExtensionsTest.cs +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/EdmModelOpenApiExtensionsTest.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using Microsoft.OData.Edm; using Xunit; -using Xunit.Abstractions; namespace Microsoft.OpenApi.OData.Tests; public class EdmModelOpenApiExtensionsTest(ITestOutputHelper output) diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiEdmTypeSchemaGeneratorTest.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiEdmTypeSchemaGeneratorTest.cs index 52bc0a8e4..5e7a04f3b 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiEdmTypeSchemaGeneratorTest.cs +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiEdmTypeSchemaGeneratorTest.cs @@ -11,7 +11,6 @@ using Microsoft.OpenApi.OData.Edm; using Microsoft.OpenApi.OData.Generator; using Xunit; -using Xunit.Abstractions; namespace Microsoft.OpenApi.OData.Tests { diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiSchemaGeneratorTests.cs b/test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiSchemaGeneratorTests.cs index 162592951..db7471a02 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiSchemaGeneratorTests.cs +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiSchemaGeneratorTests.cs @@ -12,7 +12,6 @@ using Microsoft.OpenApi.OData.Edm; using Microsoft.OpenApi.OData.Generator; using Xunit; -using Xunit.Abstractions; namespace Microsoft.OpenApi.OData.Tests { diff --git a/test/Microsoft.OpenAPI.OData.Reader.Tests/Microsoft.OpenAPI.OData.Reader.Tests.csproj b/test/Microsoft.OpenAPI.OData.Reader.Tests/Microsoft.OpenAPI.OData.Reader.Tests.csproj index 44ec521d3..9dc4f6a2b 100644 --- a/test/Microsoft.OpenAPI.OData.Reader.Tests/Microsoft.OpenAPI.OData.Reader.Tests.csproj +++ b/test/Microsoft.OpenAPI.OData.Reader.Tests/Microsoft.OpenAPI.OData.Reader.Tests.csproj @@ -4,6 +4,10 @@ Microsoft.OpenApi.OData.Reader.Tests Microsoft.OpenApi.OData.Reader.Tests net8.0 + Exe + true + false + true latest true ..\..\tool\Microsoft.OpenApi.OData.snk @@ -80,21 +84,12 @@ - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - + + + + - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj b/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj index ab4d54206..2d7f1289a 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj +++ b/test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj @@ -2,6 +2,8 @@ net10.0 + Exe + true enable enable @@ -16,9 +18,9 @@ - +