diff --git a/.github/actions/setup-ibmmq/action.yml b/.github/actions/setup-ibmmq/action.yml new file mode 100644 index 0000000000..7f581ad722 --- /dev/null +++ b/.github/actions/setup-ibmmq/action.yml @@ -0,0 +1,93 @@ +name: Setup IBM MQ +description: > + Runs IBM MQ in a Linux container for the duration of the job. On Linux runners the container runs + directly through Docker. On Windows runners it runs inside the WSL2 Docker host provisioned by + Particular/setup-wsl-action, which must run first, because the IBM MQ image is Linux-only and Windows + runners can otherwise only run Windows containers. + +inputs: + connection-string-name: + description: The name of the environment variable to fill with the IBM MQ connection string. + required: true + queue-manager: + description: The name of the queue manager to create. + required: false + default: QM1 + admin-password: + description: The password for the admin user. + required: false + default: passw0rd + image: + description: The IBM MQ container image to run. + required: false + default: icr.io/ibm-messaging/mq:latest + +runs: + using: composite + steps: + - name: Run IBM MQ container + shell: pwsh + env: + CONNECTION_STRING_NAME: ${{ inputs.connection-string-name }} + QUEUE_MANAGER: ${{ inputs.queue-manager }} + ADMIN_PASSWORD: ${{ inputs.admin-password }} + IMAGE: ${{ inputs.image }} + run: | + $ErrorActionPreference = 'Stop' + + $onWindows = $Env:RUNNER_OS -eq 'Windows' + + if ($onWindows) { + if (-not $Env:WSL_TOOLS_MODULE_PATH) { + throw "WSL_TOOLS_MODULE_PATH is not set. Particular/setup-wsl-action must run before this action on Windows." + } + Import-Module $Env:WSL_TOOLS_MODULE_PATH + } + + function Invoke-Docker([string]$Arguments) { + if ($onWindows) { Invoke-Wsl -CheckExitCode -Command "docker $Arguments" } + else { Invoke-Expression "docker $Arguments" } + } + + $containerName = 'ibmmq' + + Write-Output "Starting $($Env:IMAGE) as $containerName" + Invoke-Docker ("run --name $containerName --detach --publish 1414:1414 --publish 9443:9443 " + + "--health-cmd dspmq --health-interval 10s --health-timeout 5s --health-retries 10 --health-start-period 30s " + + "-e LICENSE=accept -e MQ_QMGR_NAME=$($Env:QUEUE_MANAGER) -e MQ_ADMIN_PASSWORD=$($Env:ADMIN_PASSWORD) " + + $Env:IMAGE) + + # On Windows the queue manager listens inside the WSL VM, reachable on its gateway address. + $mqHost = if ($onWindows) { $Env:WSL_IP } else { 'localhost' } + + # Docker's health check runs dspmq inside the container, which reports the queue manager + # process rather than whether the listener is reachable through the WSL port forward. Probe + # the endpoint the tests actually connect to instead, the way setup-postgres-action does. + function Test-Endpoint([string]$TargetHost, [int]$Port) { + $client = [System.Net.Sockets.TcpClient]::new() + try { return $client.ConnectAsync($TargetHost, $Port).Wait(5000) -and $client.Connected } + catch { return $false } + finally { $client.Dispose() } + } + + Write-Output "::group::Waiting for IBM MQ to accept connections on ${mqHost}:1414" + + $deadline = (Get-Date).AddMinutes(5) + while (-not (Test-Endpoint $mqHost 1414)) { + if ((Get-Date) -gt $deadline) { + # Out-String because the WSL and native paths differ in whether they hand back a string + # or a collection of output lines. + $status = (Invoke-Docker "inspect --format '{{.State.Status}} {{.State.Health.Status}}' $containerName" | Out-String).Trim() + Write-Output "Container state: $status" + Invoke-Docker "ps --all --filter name=$containerName" + Invoke-Docker "logs $containerName" + throw "IBM MQ did not accept connections on ${mqHost}:1414 within 5 minutes." + } + Start-Sleep -Seconds 2 + } + + Write-Output "::endgroup::" + $connectionString = "mq://admin:$($Env:ADMIN_PASSWORD)@${mqHost}:1414/$($Env:QUEUE_MANAGER)?channel=DEV.ADMIN.SVRCONN&topicprefix=DEV" + + Write-Output "IBM MQ is healthy on $mqHost" + Write-Output "$($Env:CONNECTION_STRING_NAME)=$connectionString" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d3bcdfb490..46a2f113ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,13 +12,87 @@ defaults: run: shell: pwsh jobs: + # Compiles the whole solution once, on Linux because it is the faster runner, and publishes the + # output for every test job to reuse. Only bin/ is cached: obj/ holds absolute paths into the Linux + # NuGet cache, so each test job regenerates it with a local `dotnet restore` instead. + compile: + name: Compile + runs-on: ubuntu-latest + outputs: + cache-key: ${{ steps.key.outputs.value }} + steps: + - name: Check for secrets + env: + SECRETS_AVAILABLE: ${{ secrets.SECRETS_AVAILABLE }} + run: exit $(If ($env:SECRETS_AVAILABLE -eq 'true') { 0 } Else { 1 }) + - name: Checkout + uses: actions/checkout@v7.0.1 + with: + fetch-depth: 0 + - name: Setup .NET SDK + uses: actions/setup-dotnet@v6.0.0 + with: + global-json-file: global.json + - name: Compute cache key + id: key + run: echo "value=build-output-${{ runner.os }}-${{ github.sha }}" | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf-8 -Append + # ServiceControlInstaller.Packaging's zip targets fail without a self-contained server in deploy/, + # so the full-solution build needs this and every test job waits behind it. The server is pinned + # to the RavenDB.Embedded version, so cache it rather than re-downloading ~18s per run. + # Keyed on exactly what determines the download: the RavenDB.Embedded version and the OS (the + # script fetches a different archive per platform), plus the script itself so a change to how it + # extracts invalidates the entry. Hashing Directory.Packages.props instead would re-download on + # every unrelated package bump. + - name: Read RavenDB version + id: raven-version + run: | + $version = (Select-Xml -Path src/Directory.Packages.props -XPath "/Project/ItemGroup/PackageVersion[@Include='RavenDB.Embedded']/@Version").Node.Value + if (-not $version) { throw "Could not read the RavenDB.Embedded version from src/Directory.Packages.props" } + Write-Output "RavenDB.Embedded version: $version" + echo "value=$version" | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf-8 -Append + - name: Cache RavenDB Server + id: raven + uses: actions/cache@v6.1.0 + with: + path: deploy/RavenDBServer + key: ravendb-server-${{ runner.os }}-${{ steps.raven-version.outputs.value }}-${{ hashFiles('tools/download-ravendb-server.ps1') }} + - name: Download RavenDB Server + if: steps.raven.outputs.cache-hit != 'true' + run: ./tools/download-ravendb-server.ps1 + # EnableWindowsTargeting lets the net*-windows projects compile off Windows by restoring the + # targeting packs from NuGet. + - name: Build + run: dotnet build src --configuration Release -graph --property:EnableWindowsTargeting=true + # The headline number for this experiment: everything below is paid once here and then again, + # as a download, in every test job. + - name: Report build output size + run: | + # Resolve the bin directories first. `Get-ChildItem -Path src/*/bin -Recurse` does not work: + # with a wildcard in the path, PowerShell treats the leaf as a filter and looks for items + # *named* bin during recursion, which matches nothing. + $binDirs = Get-ChildItem -Path src -Directory | ForEach-Object { Join-Path $_.FullName 'bin' } | Where-Object { Test-Path $_ } + $files = Get-ChildItem -Path $binDirs -Recurse -File -ErrorAction SilentlyContinue + $gb = [math]::Round(($files | Measure-Object -Property Length -Sum).Sum / 1GB, 2) + Write-Output "Build output to cache: $gb GB uncompressed across $($files.Count) files" + - name: Save build output to cache + uses: actions/cache/save@v6.1.0 + with: + path: src/*/bin + key: ${{ steps.key.outputs.value }} + # Required for the Windows test jobs to restore a cache written on Linux. Without it the + # archive is platform-specific and every Windows restore misses. + enableCrossOsArchive: true + build: name: ${{ matrix.os-name }}-${{ matrix.test-category }} + needs: compile runs-on: ${{ matrix.os }} strategy: matrix: os: [windows-latest, ubuntu-latest] - test-category: [ Default, SqlServer, SqlServerPersistence, AzureServiceBus, RabbitMQ, AzureStorageQueues, MSMQ, SQS, PrimaryRavenAcceptance, PrimaryRavenPersistence, PostgreSQL, PostgreSQLPersistence, IBMMQ ] + # Categories are declared by the property in each test project. Default and + # RabbitMQ are split because each was a single job running several slow assemblies back to back. + test-category: [ DefaultCore, DefaultAudit, DefaultMonitoring, SqlServer, SqlServerPersistence, AzureServiceBus, RabbitMQClassicConventional, RabbitMQClassicDirect, RabbitMQQuorumConventional, RabbitMQQuorumDirect, AzureStorageQueues, MSMQ, SQS, PrimaryRavenAcceptance, PrimaryRavenPersistence, PostgreSql, PostgreSqlPersistence, IBMMQ ] include: - os: windows-latest os-name: Windows @@ -27,8 +101,6 @@ jobs: exclude: - os: ubuntu-latest test-category: MSMQ - - os: windows-latest - test-category: IBMMQ fail-fast: false steps: - name: Check for secrets @@ -43,30 +115,42 @@ jobs: uses: actions/setup-dotnet@v6.0.0 with: global-json-file: global.json - - parallel: - - name: Setup WSL - uses: Particular/setup-wsl-action@v1.1.0 - - name: Download RavenDB Server - run: ./tools/download-ravendb-server.ps1 - - name: Build - run: dotnet build src --configuration Release -graph - - name: Zip PowerShell module + - name: Select test projects + id: select + run: ./tools/select-test-projects.ps1 -Category ${{ matrix.test-category }} + # Both are backgrounded so they overlap the infrastructure steps below, which are an ordered + # chain (Setup WSL provisions the Docker host the database actions rely on). They are independent + # of each other: the cache supplies bin/, the restore regenerates obj/. Separate steps so the + # cache download time is attributable on its own. + - name: Download compiled output from cache + id: cache + background: true + uses: actions/cache/restore@v6.1.0 + with: + path: src/*/bin + key: ${{ needs.compile.outputs.cache-key }} + fail-on-cache-miss: true + # Must match the save side: the cache is written on Linux and restored on both OSes. + enableCrossOsArchive: true + # obj/ is deliberately not cached: it embeds absolute paths into the Linux runner's NuGet cache, + # so it has to be regenerated for this OS. dotnet test --no-build then uses the cached assemblies + # without recompiling. + - name: Restore NuGet packages + id: build + background: true run: | - New-Item assets\PowerShellModules -ItemType Directory - Compress-Archive -Path deploy\PowerShellModules\Particular.ServiceControl.Management\* -DestinationPath assets\PowerShellModules\Particular.ServiceControl.Management.zip - - name: Upload assets - uses: actions/upload-artifact@v7.0.1 - if: matrix.test-category == 'Default' - with: - name: ${{ matrix.os-name }}-assets - path: | - nugets/ - zip/ - assets/ - retention-days: 1 - - name: Smoke test PowerShell module import - if: matrix.os-name == 'Windows' - run: Import-Module ./deploy/PowerShellModules/Particular.ServiceControl.Management + dotnet restore src + if ($LASTEXITCODE -ne 0) { throw "dotnet restore failed with exit code $LASTEXITCODE" } + # Provisions the WSL2 Docker host that the SQL Server, PostgreSQL, RabbitMQ and IBM MQ containers + # run in on Windows. A no-op on Linux, but still run there so those actions see the same + # environment variables on both runners. + - name: Setup WSL + if: startsWith(matrix.test-category, 'RabbitMQ') || contains(fromJSON('["SqlServer", "SqlServerPersistence", "PostgreSql", "PostgreSqlPersistence", "IBMMQ"]'), matrix.test-category) + uses: Particular/setup-wsl-action@v1.1.0 + with: + # The action defaults to 4GB. The runner has 16GB and the build runs concurrently with the + # container starting up, so give the VM real headroom. + memory: 8GB # there is an issue with az cli and python 3.14, so for now we need to pin it # once the issue is resolved it should be able to be re-floated @@ -84,9 +168,11 @@ jobs: python -m pip install --user "azure-cli==2.64.0" echo "$HOME/.local/bin" >> "$GITHUB_PATH" + # Only the Azure Service Bus and Azure Storage Queues actions provision cloud resources. RabbitMQ, + # PostgreSQL and SQL Server all run as containers now, so they no longer need an Azure session. - name: Azure login uses: azure/login@v3.0.1 - if: matrix.test-category == 'AzureServiceBus' || matrix.test-category == 'AzureStorageQueues' || matrix.test-category == 'RabbitMQ' || matrix.test-category == 'PostgreSQL' || matrix.test-category == 'PostgreSQLPersistence' + if: matrix.test-category == 'AzureServiceBus' || matrix.test-category == 'AzureStorageQueues' with: creds: ${{ secrets.AZURE_ACI_CREDENTIALS }} - name: Setup SQL Server @@ -104,21 +190,21 @@ jobs: enable-full-text-search: true - name: Setup PostgreSQL uses: Particular/setup-postgres-action@v3.0.0 - if: matrix.test-category == 'PostgreSQL' + if: matrix.test-category == 'PostgreSql' with: connection-string-name: ServiceControl_TransportTests_PostgreSQL_ConnectionString registry-username: ${{ secrets.DOCKERHUB_USERNAME }} registry-password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Setup PostgreSQL persistence uses: Particular/setup-postgres-action@v3.0.0 - if: matrix.test-category == 'PostgreSQLPersistence' + if: matrix.test-category == 'PostgreSqlPersistence' with: connection-string-name: ServiceControl_Persistence_PostgreSql_ConnectionString registry-username: ${{ secrets.DOCKERHUB_USERNAME }} registry-password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Setup RabbitMQ uses: Particular/setup-rabbitmq-action@v2.0.0 - if: matrix.test-category == 'RabbitMQ' + if: startsWith(matrix.test-category, 'RabbitMQ') with: connection-string-name: ServiceControl_TransportTests_RabbitMQ_ConnectionString registry-username: ${{ secrets.DOCKERHUB_USERNAME }} @@ -138,17 +224,10 @@ jobs: azure-credentials: ${{ secrets.AZURE_ACI_CREDENTIALS }} tag: ServiceControl - name: Setup IBM MQ + uses: ./.github/actions/setup-ibmmq if: matrix.test-category == 'IBMMQ' - run: | - docker run --name ibmmq -d -p 1414:1414 -p 9443:9443 ` - --health-cmd "dspmq" --health-interval 10s --health-timeout 5s --health-retries 10 --health-start-period 30s ` - -e LICENSE=accept -e MQ_QMGR_NAME=QM1 -e MQ_ADMIN_PASSWORD=passw0rd ` - icr.io/ibm-messaging/mq:latest - # Wait for container health check to pass - while ((docker inspect --format '{{.State.Health.Status}}' ibmmq) -ne 'healthy') { - Start-Sleep -Seconds 2 - } - echo "ServiceControl_TransportTests_IBMMQ_ConnectionString=mq://admin:passw0rd@localhost:1414/QM1?channel=DEV.ADMIN.SVRCONN&topicprefix=DEV" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append + with: + connection-string-name: ServiceControl_TransportTests_IBMMQ_ConnectionString - name: Setup SQS environment variables if: matrix.test-category == 'SQS' run: | @@ -159,9 +238,12 @@ jobs: # Cleanup of queues starting with `GHA-` handled by https://github.com/Particular/NServiceBus.AmazonSQS/blob/master/.github/workflows/tests-cleanup.yml $connectString = "AccessKeyId=${{ secrets.AWS_ACCESS_KEY_ID }};SecretAccessKey=${{ secrets.AWS_SECRET_ACCESS_KEY }};Region=${{ secrets.AWS_REGION }};QueueNamePrefix=GHA-${{ github.run_id }}" echo "ServiceControl_TransportTests_SQS_ConnectionString=$connectString" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append + - name: Wait for compiled output and packages + wait: [cache, build] - name: Run tests - uses: Particular/run-tests-action@v1.7.0 + run: ./tools/run-tests.ps1 -Projects $Env:TEST_PROJECTS env: + TEST_PROJECTS: ${{ steps.select.outputs.test-projects }} ServiceControl_TESTS_FILTER: ${{ matrix.test-category }} PARTICULARSOFTWARE_LICENSE: ${{ secrets.LICENSETEXT }} AZURE_ACI_CREDENTIALS: ${{ secrets.AZURE_ACI_CREDENTIALS }} diff --git a/.gitignore b/.gitignore index 1653797127..b3b621401f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ /binaries /deploy /nugets +# Generated by tools/select-test-projects.ps1 +/tests.proj build32 *.vshost.* .nu diff --git a/README.md b/README.md index bdff9eb042..c0bde0c286 100644 --- a/README.md +++ b/README.md @@ -46,18 +46,37 @@ Testing using the [CI workflow](/.github/workflows/ci.yml) depends on the follow Running all tests all the times takes a lot of resources. Tests are filtered based on the `ServiceControl_TESTS_FILTER` environment variable. To run only a subset, e.g., SQS transport tests, define the variable as `ServiceControl_TESTS_FILTER=SQS`. The following list contains all the possible `ServiceControl_TESTS_FILTER` values: -- `Default` - runs only non-transport-specific tests +Non-transport-specific: + +- `DefaultCore` +- `DefaultAudit` +- `DefaultMonitoring` + +Transports: + - `AzureServiceBus` - `AzureStorageQueues` +- `IBMMQ` - `MSMQ` -- `RabbitMQ` +- `PostgreSql` +- `RabbitMQClassicConventional` +- `RabbitMQClassicDirect` +- `RabbitMQQuorumConventional` +- `RabbitMQQuorumDirect` - `SqlServer` -- `SqlServerPersistence` -- `PostgresSqlPersistence` - `SQS` +Persisters: + +- `PostgreSqlPersistence` +- `PrimaryRavenAcceptance` +- `PrimaryRavenPersistence` +- `SqlServerPersistence` + NOTE: If no variable is defined all tests will be executed. +Each category is declared by the `` property in the test project and by the matching assembly-level `IncludeInTestCategory` attribute. CI uses the property to build and run only that category's projects; the attribute is the runtime safety net. Run `./tools/select-test-projects.ps1 -List` to see every category and the projects it selects. + ## Security Configuration Documentation for configuring security features: diff --git a/src/Particular.LicensingComponent.UnitTests/Particular.LicensingComponent.UnitTests.csproj b/src/Particular.LicensingComponent.UnitTests/Particular.LicensingComponent.UnitTests.csproj index b0094aab94..529a3f226c 100644 --- a/src/Particular.LicensingComponent.UnitTests/Particular.LicensingComponent.UnitTests.csproj +++ b/src/Particular.LicensingComponent.UnitTests/Particular.LicensingComponent.UnitTests.csproj @@ -2,6 +2,7 @@ net10.0 + DefaultCore diff --git a/src/Particular.LicensingComponent.UnitTests/TestsFilter.cs b/src/Particular.LicensingComponent.UnitTests/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/Particular.LicensingComponent.UnitTests/TestsFilter.cs +++ b/src/Particular.LicensingComponent.UnitTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.AcceptanceTests.RavenDB/ServiceControl.AcceptanceTests.RavenDB.csproj b/src/ServiceControl.AcceptanceTests.RavenDB/ServiceControl.AcceptanceTests.RavenDB.csproj index 0cf6c7efa5..26650ebe99 100644 --- a/src/ServiceControl.AcceptanceTests.RavenDB/ServiceControl.AcceptanceTests.RavenDB.csproj +++ b/src/ServiceControl.AcceptanceTests.RavenDB/ServiceControl.AcceptanceTests.RavenDB.csproj @@ -2,6 +2,7 @@ net10.0 + PrimaryRavenAcceptance diff --git a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj index 223e117c20..f3834a4a81 100644 --- a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj +++ b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/ServiceControl.Audit.AcceptanceTests.RavenDB.csproj @@ -2,6 +2,7 @@ net10.0 + DefaultAudit diff --git a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/TestsFilter.cs b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/TestsFilter.cs index cbb64ab1fc..653413cf8e 100644 --- a/src/ServiceControl.Audit.AcceptanceTests.RavenDB/TestsFilter.cs +++ b/src/ServiceControl.Audit.AcceptanceTests.RavenDB/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultAudit")] \ No newline at end of file diff --git a/src/ServiceControl.Audit.AcceptanceTests/ServiceControl.Audit.AcceptanceTests.csproj b/src/ServiceControl.Audit.AcceptanceTests/ServiceControl.Audit.AcceptanceTests.csproj index 2bbf59bc45..2388115142 100644 --- a/src/ServiceControl.Audit.AcceptanceTests/ServiceControl.Audit.AcceptanceTests.csproj +++ b/src/ServiceControl.Audit.AcceptanceTests/ServiceControl.Audit.AcceptanceTests.csproj @@ -2,6 +2,7 @@ net10.0 + DefaultAudit diff --git a/src/ServiceControl.Audit.AcceptanceTests/TestsFilter.cs b/src/ServiceControl.Audit.AcceptanceTests/TestsFilter.cs index cbb64ab1fc..653413cf8e 100644 --- a/src/ServiceControl.Audit.AcceptanceTests/TestsFilter.cs +++ b/src/ServiceControl.Audit.AcceptanceTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultAudit")] \ No newline at end of file diff --git a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj index 7b0f2fbb6a..3dd94be5ea 100644 --- a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj +++ b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/ServiceControl.Audit.Persistence.Tests.RavenDB.csproj @@ -2,6 +2,7 @@ net10.0 + DefaultAudit diff --git a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/TestsFilter.cs b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/TestsFilter.cs index cbb64ab1fc..653413cf8e 100644 --- a/src/ServiceControl.Audit.Persistence.Tests.RavenDB/TestsFilter.cs +++ b/src/ServiceControl.Audit.Persistence.Tests.RavenDB/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultAudit")] \ No newline at end of file diff --git a/src/ServiceControl.Audit.Persistence.Tests/ServiceControl.Audit.Persistence.Tests.csproj b/src/ServiceControl.Audit.Persistence.Tests/ServiceControl.Audit.Persistence.Tests.csproj index b7f6909c1f..ece853badc 100644 --- a/src/ServiceControl.Audit.Persistence.Tests/ServiceControl.Audit.Persistence.Tests.csproj +++ b/src/ServiceControl.Audit.Persistence.Tests/ServiceControl.Audit.Persistence.Tests.csproj @@ -2,6 +2,7 @@ net10.0 + DefaultAudit diff --git a/src/ServiceControl.Audit.Persistence.Tests/TestsFilter.cs b/src/ServiceControl.Audit.Persistence.Tests/TestsFilter.cs index cbb64ab1fc..653413cf8e 100644 --- a/src/ServiceControl.Audit.Persistence.Tests/TestsFilter.cs +++ b/src/ServiceControl.Audit.Persistence.Tests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultAudit")] \ No newline at end of file diff --git a/src/ServiceControl.Audit.UnitTests/ServiceControl.Audit.UnitTests.csproj b/src/ServiceControl.Audit.UnitTests/ServiceControl.Audit.UnitTests.csproj index ec6be43716..44a4d9efa0 100644 --- a/src/ServiceControl.Audit.UnitTests/ServiceControl.Audit.UnitTests.csproj +++ b/src/ServiceControl.Audit.UnitTests/ServiceControl.Audit.UnitTests.csproj @@ -2,6 +2,7 @@ net10.0 + DefaultAudit diff --git a/src/ServiceControl.Audit.UnitTests/TestsFilter.cs b/src/ServiceControl.Audit.UnitTests/TestsFilter.cs index cbb64ab1fc..653413cf8e 100644 --- a/src/ServiceControl.Audit.UnitTests/TestsFilter.cs +++ b/src/ServiceControl.Audit.UnitTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultAudit")] \ No newline at end of file diff --git a/src/ServiceControl.Audit/ServiceControl.Audit.csproj b/src/ServiceControl.Audit/ServiceControl.Audit.csproj index f88c753d1b..a16a75c832 100644 --- a/src/ServiceControl.Audit/ServiceControl.Audit.csproj +++ b/src/ServiceControl.Audit/ServiceControl.Audit.csproj @@ -59,6 +59,7 @@ + diff --git a/src/ServiceControl.Config.Tests/ServiceControl.Config.Tests.csproj b/src/ServiceControl.Config.Tests/ServiceControl.Config.Tests.csproj index 8bafc4ae31..6c0a7c644b 100644 --- a/src/ServiceControl.Config.Tests/ServiceControl.Config.Tests.csproj +++ b/src/ServiceControl.Config.Tests/ServiceControl.Config.Tests.csproj @@ -2,6 +2,7 @@ net10.0-windows10.0.19041.0 + DefaultCore x64 true diff --git a/src/ServiceControl.Config.Tests/TestsFilter.cs b/src/ServiceControl.Config.Tests/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/ServiceControl.Config.Tests/TestsFilter.cs +++ b/src/ServiceControl.Config.Tests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.Infrastructure.Tests/ServiceControl.Infrastructure.Tests.csproj b/src/ServiceControl.Infrastructure.Tests/ServiceControl.Infrastructure.Tests.csproj index 7664d8aa63..5c53d4918f 100644 --- a/src/ServiceControl.Infrastructure.Tests/ServiceControl.Infrastructure.Tests.csproj +++ b/src/ServiceControl.Infrastructure.Tests/ServiceControl.Infrastructure.Tests.csproj @@ -2,6 +2,7 @@ net10.0 + DefaultCore diff --git a/src/ServiceControl.Infrastructure.Tests/TestsFilter.cs b/src/ServiceControl.Infrastructure.Tests/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/ServiceControl.Infrastructure.Tests/TestsFilter.cs +++ b/src/ServiceControl.Infrastructure.Tests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.Monitoring.AcceptanceTests/ServiceControl.Monitoring.AcceptanceTests.csproj b/src/ServiceControl.Monitoring.AcceptanceTests/ServiceControl.Monitoring.AcceptanceTests.csproj index 19e62962ab..dd95e8cfad 100644 --- a/src/ServiceControl.Monitoring.AcceptanceTests/ServiceControl.Monitoring.AcceptanceTests.csproj +++ b/src/ServiceControl.Monitoring.AcceptanceTests/ServiceControl.Monitoring.AcceptanceTests.csproj @@ -2,6 +2,7 @@ net10.0 + DefaultMonitoring diff --git a/src/ServiceControl.Monitoring.AcceptanceTests/TestsFilter.cs b/src/ServiceControl.Monitoring.AcceptanceTests/TestsFilter.cs index cbb64ab1fc..35e9af11b9 100644 --- a/src/ServiceControl.Monitoring.AcceptanceTests/TestsFilter.cs +++ b/src/ServiceControl.Monitoring.AcceptanceTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultMonitoring")] \ No newline at end of file diff --git a/src/ServiceControl.Monitoring.UnitTests/ServiceControl.Monitoring.UnitTests.csproj b/src/ServiceControl.Monitoring.UnitTests/ServiceControl.Monitoring.UnitTests.csproj index 14236ddac2..9261529433 100644 --- a/src/ServiceControl.Monitoring.UnitTests/ServiceControl.Monitoring.UnitTests.csproj +++ b/src/ServiceControl.Monitoring.UnitTests/ServiceControl.Monitoring.UnitTests.csproj @@ -2,6 +2,7 @@ net10.0 + DefaultMonitoring diff --git a/src/ServiceControl.Monitoring.UnitTests/TestsFilter.cs b/src/ServiceControl.Monitoring.UnitTests/TestsFilter.cs index cbb64ab1fc..35e9af11b9 100644 --- a/src/ServiceControl.Monitoring.UnitTests/TestsFilter.cs +++ b/src/ServiceControl.Monitoring.UnitTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultMonitoring")] \ No newline at end of file diff --git a/src/ServiceControl.MultiInstance.AcceptanceTests/ServiceControl.MultiInstance.AcceptanceTests.csproj b/src/ServiceControl.MultiInstance.AcceptanceTests/ServiceControl.MultiInstance.AcceptanceTests.csproj index 10b78c9ba4..cbaba829ae 100644 --- a/src/ServiceControl.MultiInstance.AcceptanceTests/ServiceControl.MultiInstance.AcceptanceTests.csproj +++ b/src/ServiceControl.MultiInstance.AcceptanceTests/ServiceControl.MultiInstance.AcceptanceTests.csproj @@ -2,6 +2,7 @@ net10.0 + DefaultCore diff --git a/src/ServiceControl.MultiInstance.AcceptanceTests/SetupFixture.cs b/src/ServiceControl.MultiInstance.AcceptanceTests/SetupFixture.cs index 4c74e8dcc9..a2b3d0c746 100644 --- a/src/ServiceControl.MultiInstance.AcceptanceTests/SetupFixture.cs +++ b/src/ServiceControl.MultiInstance.AcceptanceTests/SetupFixture.cs @@ -3,9 +3,12 @@ namespace ServiceControl.MultiInstance.AcceptanceTests; using System; using System.Diagnostics; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; using NUnit.Framework; +using AuditEventSourceCreator = global::ServiceControl.Audit.Infrastructure.EventSourceCreator; +using PrimaryEventSourceCreator = ServiceBus.Management.Infrastructure.Installers.EventSourceCreator; [SetUpFixture] public class SetupFixture @@ -13,17 +16,30 @@ public class SetupFixture [OneTimeSetUp] public async Task Setup() { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - ServiceBus.Management.Infrastructure.Installers.EventSourceCreator.Create(); + return; + } + + // These tests host a primary and an audit instance, so both event sources have to exist before + // either instance runs its setup. The audit source used to be created as a side effect of the + // audit acceptance tests running earlier in the same CI job, which is no longer the case. + PrimaryEventSourceCreator.Create(); + AuditEventSourceCreator.Create(); + + await WaitForSource(PrimaryEventSourceCreator.SourceName); + await WaitForSource(AuditEventSourceCreator.SourceName); + } - //There is a delay for this becoming true, tests will fall over if they interleave in the wrong way. - var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); - while (!EventLog.SourceExists(ServiceBus.Management.Infrastructure.Installers.EventSourceCreator.SourceName)) - { - cts.Token.ThrowIfCancellationRequested(); - await Task.Delay(500, CancellationToken.None); - } + //There is a delay for this becoming true, tests will fall over if they interleave in the wrong way. + [SupportedOSPlatform("windows")] + static async Task WaitForSource(string sourceName) + { + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); + while (!EventLog.SourceExists(sourceName)) + { + cts.Token.ThrowIfCancellationRequested(); + await Task.Delay(500, CancellationToken.None); } } -} \ No newline at end of file +} diff --git a/src/ServiceControl.MultiInstance.AcceptanceTests/TestsFilter.cs b/src/ServiceControl.MultiInstance.AcceptanceTests/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/ServiceControl.MultiInstance.AcceptanceTests/TestsFilter.cs +++ b/src/ServiceControl.MultiInstance.AcceptanceTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.Persistence.RavenDB/Editing/NotificationsManager.cs b/src/ServiceControl.Persistence.RavenDB/Editing/NotificationsManager.cs index 57e8d2b454..6531b6ed7b 100644 --- a/src/ServiceControl.Persistence.RavenDB/Editing/NotificationsManager.cs +++ b/src/ServiceControl.Persistence.RavenDB/Editing/NotificationsManager.cs @@ -1,6 +1,5 @@ namespace ServiceControl.Persistence.RavenDB.Editing { - using System; using System.Threading.Tasks; using Notifications; using Raven.Client.Documents.Session; @@ -8,11 +7,12 @@ class NotificationsManager(IAsyncDocumentSession session) : AbstractSessionManager(session), INotificationsManager { const string SingleDocumentId = "NotificationsSettings/All"; - static readonly TimeSpan CacheTimeout = TimeSpan.FromMinutes(5); // Raven requires this to be at least 1 second public async Task LoadSettings() { - using var aggressivelyCacheFor = await Session.Advanced.DocumentStore.AggressivelyCacheForAsync(CacheTimeout); + // Deliberately not aggressively cached. These settings are read rarely and edited by hand, + // and aggressive caching invalidates asynchronously via the Changes API, so a read straight + // after a save can return the pre-save document. var settings = await Session .LoadAsync(SingleDocumentId); diff --git a/src/ServiceControl.Persistence.Tests.InMemory/ServiceControl.Persistence.Tests.InMemory.csproj b/src/ServiceControl.Persistence.Tests.InMemory/ServiceControl.Persistence.Tests.InMemory.csproj index 5854e9fa05..7bf877fa51 100644 --- a/src/ServiceControl.Persistence.Tests.InMemory/ServiceControl.Persistence.Tests.InMemory.csproj +++ b/src/ServiceControl.Persistence.Tests.InMemory/ServiceControl.Persistence.Tests.InMemory.csproj @@ -2,6 +2,7 @@ net10.0 + DefaultCore diff --git a/src/ServiceControl.Persistence.Tests.InMemory/TestsFilter.cs b/src/ServiceControl.Persistence.Tests.InMemory/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/ServiceControl.Persistence.Tests.InMemory/TestsFilter.cs +++ b/src/ServiceControl.Persistence.Tests.InMemory/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.Persistence.Tests.PostgreSql/ServiceControl.Persistence.Tests.PostgreSql.csproj b/src/ServiceControl.Persistence.Tests.PostgreSql/ServiceControl.Persistence.Tests.PostgreSql.csproj index 492db2ec1c..1e0bf2cebb 100644 --- a/src/ServiceControl.Persistence.Tests.PostgreSql/ServiceControl.Persistence.Tests.PostgreSql.csproj +++ b/src/ServiceControl.Persistence.Tests.PostgreSql/ServiceControl.Persistence.Tests.PostgreSql.csproj @@ -2,6 +2,7 @@ net10.0 + PostgreSqlPersistence diff --git a/src/ServiceControl.Persistence.Tests.RavenDB/ServiceControl.Persistence.Tests.RavenDB.csproj b/src/ServiceControl.Persistence.Tests.RavenDB/ServiceControl.Persistence.Tests.RavenDB.csproj index 85d3b51c65..4b8c1de262 100644 --- a/src/ServiceControl.Persistence.Tests.RavenDB/ServiceControl.Persistence.Tests.RavenDB.csproj +++ b/src/ServiceControl.Persistence.Tests.RavenDB/ServiceControl.Persistence.Tests.RavenDB.csproj @@ -2,6 +2,7 @@ net10.0 + PrimaryRavenPersistence diff --git a/src/ServiceControl.Persistence.Tests.SqlServer/ServiceControl.Persistence.Tests.SqlServer.csproj b/src/ServiceControl.Persistence.Tests.SqlServer/ServiceControl.Persistence.Tests.SqlServer.csproj index 2e762eb089..73942c6501 100644 --- a/src/ServiceControl.Persistence.Tests.SqlServer/ServiceControl.Persistence.Tests.SqlServer.csproj +++ b/src/ServiceControl.Persistence.Tests.SqlServer/ServiceControl.Persistence.Tests.SqlServer.csproj @@ -2,6 +2,7 @@ net10.0 + SqlServerPersistence diff --git a/src/ServiceControl.Transports.ASBS.Tests/ServiceControl.Transports.ASBS.Tests.csproj b/src/ServiceControl.Transports.ASBS.Tests/ServiceControl.Transports.ASBS.Tests.csproj index a7d3313ad3..fb053d09be 100644 --- a/src/ServiceControl.Transports.ASBS.Tests/ServiceControl.Transports.ASBS.Tests.csproj +++ b/src/ServiceControl.Transports.ASBS.Tests/ServiceControl.Transports.ASBS.Tests.csproj @@ -2,6 +2,7 @@ net10.0 + AzureServiceBus diff --git a/src/ServiceControl.Transports.ASQ.Tests/ServiceControl.Transports.ASQ.Tests.csproj b/src/ServiceControl.Transports.ASQ.Tests/ServiceControl.Transports.ASQ.Tests.csproj index 6a9f7fb7e9..dc1f08141d 100644 --- a/src/ServiceControl.Transports.ASQ.Tests/ServiceControl.Transports.ASQ.Tests.csproj +++ b/src/ServiceControl.Transports.ASQ.Tests/ServiceControl.Transports.ASQ.Tests.csproj @@ -1,7 +1,8 @@ - + net10.0 + AzureStorageQueues diff --git a/src/ServiceControl.Transports.IBMMQ.Tests/ServiceControl.Transports.IBMMQ.Tests.csproj b/src/ServiceControl.Transports.IBMMQ.Tests/ServiceControl.Transports.IBMMQ.Tests.csproj index f441f9cf89..c22367753e 100644 --- a/src/ServiceControl.Transports.IBMMQ.Tests/ServiceControl.Transports.IBMMQ.Tests.csproj +++ b/src/ServiceControl.Transports.IBMMQ.Tests/ServiceControl.Transports.IBMMQ.Tests.csproj @@ -2,6 +2,7 @@ net10.0 + IBMMQ diff --git a/src/ServiceControl.Transports.Msmq.Tests/ServiceControl.Transports.Msmq.Tests.csproj b/src/ServiceControl.Transports.Msmq.Tests/ServiceControl.Transports.Msmq.Tests.csproj index e94380b92c..356a3c96fc 100644 --- a/src/ServiceControl.Transports.Msmq.Tests/ServiceControl.Transports.Msmq.Tests.csproj +++ b/src/ServiceControl.Transports.Msmq.Tests/ServiceControl.Transports.Msmq.Tests.csproj @@ -2,6 +2,7 @@ net10.0-windows + MSMQ diff --git a/src/ServiceControl.Transports.PostgreSql.Tests/ServiceControl.Transports.PostgreSql.Tests.csproj b/src/ServiceControl.Transports.PostgreSql.Tests/ServiceControl.Transports.PostgreSql.Tests.csproj index 3071023ee0..2e16270353 100644 --- a/src/ServiceControl.Transports.PostgreSql.Tests/ServiceControl.Transports.PostgreSql.Tests.csproj +++ b/src/ServiceControl.Transports.PostgreSql.Tests/ServiceControl.Transports.PostgreSql.Tests.csproj @@ -2,6 +2,7 @@ net10.0 + PostgreSql diff --git a/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/ServiceControl.Transports.RabbitMQClassicConventionalRoutingTests.csproj b/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/ServiceControl.Transports.RabbitMQClassicConventionalRoutingTests.csproj index 3bd02418c1..ae91141a0e 100644 --- a/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/ServiceControl.Transports.RabbitMQClassicConventionalRoutingTests.csproj +++ b/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/ServiceControl.Transports.RabbitMQClassicConventionalRoutingTests.csproj @@ -1,7 +1,8 @@ - + net10.0 + RabbitMQClassicConventional diff --git a/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/TestsFilter.cs b/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/TestsFilter.cs index 3d1ec7f59c..b8b0d9f08b 100644 --- a/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/TestsFilter.cs +++ b/src/ServiceControl.Transports.RabbitMQClassicConventionalRouting.Tests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInRabbitMQTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("RabbitMQClassicConventional")] \ No newline at end of file diff --git a/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests.csproj b/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests.csproj index d5bf028a66..c8b65116c8 100644 --- a/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests.csproj +++ b/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests.csproj @@ -1,7 +1,8 @@ - + net10.0 + RabbitMQClassicDirect diff --git a/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/TestsFilter.cs b/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/TestsFilter.cs index 3d1ec7f59c..605a7b298b 100644 --- a/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/TestsFilter.cs +++ b/src/ServiceControl.Transports.RabbitMQClassicDirectRouting.Tests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInRabbitMQTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("RabbitMQClassicDirect")] \ No newline at end of file diff --git a/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests.csproj b/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests.csproj index d5bf028a66..0e5b9c9b3a 100644 --- a/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests.csproj +++ b/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests.csproj @@ -1,7 +1,8 @@ - + net10.0 + RabbitMQQuorumConventional diff --git a/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/TestsFilter.cs b/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/TestsFilter.cs index 3d1ec7f59c..4587c56b95 100644 --- a/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/TestsFilter.cs +++ b/src/ServiceControl.Transports.RabbitMQQuorumConventionalRouting.Tests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInRabbitMQTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("RabbitMQQuorumConventional")] \ No newline at end of file diff --git a/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests.csproj b/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests.csproj index 3bd02418c1..c9cf087156 100644 --- a/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests.csproj +++ b/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests.csproj @@ -1,7 +1,8 @@ - + net10.0 + RabbitMQQuorumDirect diff --git a/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/TestsFilter.cs b/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/TestsFilter.cs index 3d1ec7f59c..9977bd7ab6 100644 --- a/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/TestsFilter.cs +++ b/src/ServiceControl.Transports.RabbitMQQuorumDirectRouting.Tests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInRabbitMQTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("RabbitMQQuorumDirect")] \ No newline at end of file diff --git a/src/ServiceControl.Transports.SQS.Tests/ServiceControl.Transports.SQS.Tests.csproj b/src/ServiceControl.Transports.SQS.Tests/ServiceControl.Transports.SQS.Tests.csproj index cee5fd3419..8057265d47 100644 --- a/src/ServiceControl.Transports.SQS.Tests/ServiceControl.Transports.SQS.Tests.csproj +++ b/src/ServiceControl.Transports.SQS.Tests/ServiceControl.Transports.SQS.Tests.csproj @@ -2,6 +2,7 @@ net10.0 + SQS diff --git a/src/ServiceControl.Transports.SqlServer.Tests/ServiceControl.Transports.SqlServer.Tests.csproj b/src/ServiceControl.Transports.SqlServer.Tests/ServiceControl.Transports.SqlServer.Tests.csproj index 62fe2403f2..a19182e7f4 100644 --- a/src/ServiceControl.Transports.SqlServer.Tests/ServiceControl.Transports.SqlServer.Tests.csproj +++ b/src/ServiceControl.Transports.SqlServer.Tests/ServiceControl.Transports.SqlServer.Tests.csproj @@ -2,6 +2,7 @@ net10.0 + SqlServer diff --git a/src/ServiceControl.Transports.Tests/ServiceControl.Transports.Tests.csproj b/src/ServiceControl.Transports.Tests/ServiceControl.Transports.Tests.csproj index 261cccf063..fd649e1b60 100644 --- a/src/ServiceControl.Transports.Tests/ServiceControl.Transports.Tests.csproj +++ b/src/ServiceControl.Transports.Tests/ServiceControl.Transports.Tests.csproj @@ -2,6 +2,7 @@ net10.0 + DefaultCore diff --git a/src/ServiceControl.Transports.Tests/TestsFilter.cs b/src/ServiceControl.Transports.Tests/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/ServiceControl.Transports.Tests/TestsFilter.cs +++ b/src/ServiceControl.Transports.Tests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControl.UnitTests/ServiceControl.UnitTests.csproj b/src/ServiceControl.UnitTests/ServiceControl.UnitTests.csproj index 5c3a965155..280ef9eced 100644 --- a/src/ServiceControl.UnitTests/ServiceControl.UnitTests.csproj +++ b/src/ServiceControl.UnitTests/ServiceControl.UnitTests.csproj @@ -2,6 +2,7 @@ net10.0 + DefaultCore diff --git a/src/ServiceControl.UnitTests/TestsFilter.cs b/src/ServiceControl.UnitTests/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/ServiceControl.UnitTests/TestsFilter.cs +++ b/src/ServiceControl.UnitTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControlInstaller.Engine.UnitTests/ServiceControlInstaller.Engine.UnitTests.csproj b/src/ServiceControlInstaller.Engine.UnitTests/ServiceControlInstaller.Engine.UnitTests.csproj index 3247d3f367..3908dc1ba1 100644 --- a/src/ServiceControlInstaller.Engine.UnitTests/ServiceControlInstaller.Engine.UnitTests.csproj +++ b/src/ServiceControlInstaller.Engine.UnitTests/ServiceControlInstaller.Engine.UnitTests.csproj @@ -2,6 +2,7 @@ net10.0-windows + DefaultCore diff --git a/src/ServiceControlInstaller.Engine.UnitTests/TestsFilter.cs b/src/ServiceControlInstaller.Engine.UnitTests/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/ServiceControlInstaller.Engine.UnitTests/TestsFilter.cs +++ b/src/ServiceControlInstaller.Engine.UnitTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/ServiceControlInstaller.Packaging.UnitTests/ServiceControlInstaller.Packaging.UnitTests.csproj b/src/ServiceControlInstaller.Packaging.UnitTests/ServiceControlInstaller.Packaging.UnitTests.csproj index be95909ae0..38dd4fbc7d 100644 --- a/src/ServiceControlInstaller.Packaging.UnitTests/ServiceControlInstaller.Packaging.UnitTests.csproj +++ b/src/ServiceControlInstaller.Packaging.UnitTests/ServiceControlInstaller.Packaging.UnitTests.csproj @@ -2,6 +2,7 @@ net10.0 + DefaultCore diff --git a/src/ServiceControlInstaller.Packaging.UnitTests/TestsFilter.cs b/src/ServiceControlInstaller.Packaging.UnitTests/TestsFilter.cs index cbb64ab1fc..aab6eb2319 100644 --- a/src/ServiceControlInstaller.Packaging.UnitTests/TestsFilter.cs +++ b/src/ServiceControlInstaller.Packaging.UnitTests/TestsFilter.cs @@ -1 +1 @@ -[assembly: IncludeInDefaultTests()] \ No newline at end of file +[assembly: IncludeInTestCategory("DefaultCore")] \ No newline at end of file diff --git a/src/TestHelper/IncludeInDefaultTestsAttribute.cs b/src/TestHelper/IncludeInDefaultTestsAttribute.cs deleted file mode 100644 index 83442d9b40..0000000000 --- a/src/TestHelper/IncludeInDefaultTestsAttribute.cs +++ /dev/null @@ -1,4 +0,0 @@ -public class IncludeInDefaultTestsAttribute : IncludeInTestsAttribute -{ - protected override string Filter => "Default"; -} \ No newline at end of file diff --git a/src/TestHelper/IncludeInRabbitMQTestsAttribute.cs b/src/TestHelper/IncludeInRabbitMQTestsAttribute.cs deleted file mode 100644 index 23002f4336..0000000000 --- a/src/TestHelper/IncludeInRabbitMQTestsAttribute.cs +++ /dev/null @@ -1,4 +0,0 @@ -public class IncludeInRabbitMQTestsAttribute : IncludeInTestsAttribute -{ - protected override string Filter => "RabbitMQ"; -} diff --git a/tools/run-tests.ps1 b/tools/run-tests.ps1 new file mode 100644 index 0000000000..5927f0d073 --- /dev/null +++ b/tools/run-tests.ps1 @@ -0,0 +1,60 @@ +# Runs dotnet test against an explicit list of test projects, rather than discovering every test +# project under src. The list comes from tools/select-test-projects.ps1, so a job only pays for the +# assemblies belonging to its test category. +# +# This is a scoped replacement for Particular/run-tests-action, which has no way to be told which +# projects to run. It should fold back into that action once it grows a 'projects' input. + +param( + [Parameter(Mandatory)] + [string]$Projects, + + [string]$TargetPlatform = 'x64', + + [switch]$ReportWarnings +) + +$ErrorActionPreference = 'Stop' + +$projectPaths = $Projects -split "`n" | ForEach-Object { $_.Trim() } | Where-Object { $_ } + +if ($projectPaths.Count -eq 0) { + throw 'No test projects were supplied.' +} + +Write-Output "Target Platform = $TargetPlatform" + +$reportWarningsValue = if ($ReportWarnings) { 'true' } else { 'false' } +$isUnix = $PSVersionTable.Platform -eq 'Unix' +$exitCode = 0 + +foreach ($project in $projectPaths) { + $frameworks = @( + (Select-Xml -Path $project -XPath "/Project/PropertyGroup/TargetFramework").Node.InnerText + (Select-Xml -Path $project -XPath "/Project/PropertyGroup/TargetFrameworks").Node.InnerText -split ';' + ) | Where-Object { $_ } + + if ($frameworks.Count -eq 0) { + throw "Could not determine a target framework for $project." + } + + foreach ($framework in $frameworks) { + if ($isUnix -and ($framework.StartsWith('net4') -or $framework.Contains('-windows'))) { + Write-Output "Skipping $(Split-Path $project -Leaf) ($framework) because it cannot run on this platform." + continue + } + + Write-Output "::group::Running $(Split-Path $project -Leaf) ($framework)" + + dotnet test $project --configuration Release --no-build --framework $framework --logger "GitHubActions;report-warnings=$reportWarningsValue" -- RunConfiguration.TreatNoTestsAsError=true "RunConfiguration.TargetPlatform=$TargetPlatform" + + Write-Output '::endgroup::' + + if ($LASTEXITCODE -ne 0) { + Write-Output "::error::Exit code = $LASTEXITCODE" + $exitCode = 1 + } + } +} + +exit $exitCode diff --git a/tools/select-test-projects.ps1 b/tools/select-test-projects.ps1 new file mode 100644 index 0000000000..f6c50c099a --- /dev/null +++ b/tools/select-test-projects.ps1 @@ -0,0 +1,89 @@ +# Selects the test projects belonging to a test category, so that CI can build and run only those +# rather than the whole solution. The category is declared by the property in each test +# project, which must match the Filter of the assembly-level IncludeIn*Tests attribute in that project. +# +# Writes the selected project paths to $GITHUB_OUTPUT as 'test-projects', and generates an MSBuild +# traversal project so that `dotnet build` can build the whole selection in one graph. +# +# Use -List to print every category and its projects without writing any files. + +[CmdletBinding(DefaultParameterSetName = 'Select')] +param( + [Parameter(Mandatory, ParameterSetName = 'Select')] + [string]$Category, + + [Parameter(ParameterSetName = 'Select')] + [string]$TraversalProjectPath = 'tests.proj', + + [Parameter(Mandatory, ParameterSetName = 'List')] + [switch]$List +) + +$ErrorActionPreference = 'Stop' + +$projectsByCategory = @{} + +# -Filter rather than -Include: the provider applies it during traversal, which is dramatically faster +# on a tree that already contains bin/obj output. +Get-ChildItem -Path src -Filter '*.csproj' -Recurse -File | ForEach-Object { + $testCategory = (Select-Xml -Path $_.FullName -XPath "/Project/PropertyGroup/TestCategory").Node.InnerText + + if ([string]::IsNullOrWhiteSpace($testCategory)) { + return + } + + if (-not $projectsByCategory.ContainsKey($testCategory)) { + $projectsByCategory[$testCategory] = New-Object Collections.Generic.List[String] + } + + $projectsByCategory[$testCategory].Add((Resolve-Path -Relative $_.FullName)) +} + +if ($List) { + foreach ($key in $projectsByCategory.Keys | Sort-Object) { + Write-Output "$key" + $projectsByCategory[$key] | Sort-Object | ForEach-Object { Write-Output " $_" } + } + return +} + +if (-not $projectsByCategory.ContainsKey($Category)) { + $known = ($projectsByCategory.Keys | Sort-Object) -join ', ' + throw "No test project declares $Category. Known categories: $known" +} + +$projects = $projectsByCategory[$Category] | Sort-Object + +Write-Output "Test category '$Category' selects $($projects.Count) project(s):" +$projects | ForEach-Object { Write-Output " $_" } + +# ReferenceOutputAssembly is irrelevant here because the traversal project produces nothing itself. +# TargetFramework is undefined per reference so that projects targeting net10.0-windows are not forced +# onto the traversal project's own framework, matching the convention in ProjectReferences.Transports.props. +$references = $projects | ForEach-Object { + " " +} + +$traversal = @" + + + + + net10.0 + + + +$($references -join [Environment]::NewLine) + + + +"@ + +Set-Content -Path $TraversalProjectPath -Value $traversal -Encoding utf8 +Write-Output "Wrote traversal project to $TraversalProjectPath" + +if ($Env:GITHUB_OUTPUT) { + "test-projects<