Files
UA-.NETStandard/.github/scripts/get-test-verdict.ps1
romanett d623007a3f Remove the Azure Pipelines CI and publishing definitions from master (#4575)
# Description

Removes the Azure Pipelines CI and package-publishing definitions from
`master`. GitHub Actions already owns all CI and package publishing for
`master`, but pushes to `master` still queued the Azure definitions.
They duplicated the Actions work and posted failing checks, for example
[`Nuget Preview (Push Nuget Preview Pack Nugets
Debug)`](https://github.com/OPCFoundation/UA-.NETStandard/runs/109065914524)
on `19dc504c5`.

## Removed

- `azure-pipelines.yml`, `azure-pipelines-preview.yml`
- `.azurepipelines/` templates: `ci.yml`, `coverage.yml`, `preview.yml`,
`sample-ha-longhaul.yml`, `sln.yml`, `test.yml`, `test-aot.yml`
- Helpers only those templates used: `get-matrix.ps1`, `get-root.ps1`,
`evaluate-test-results.ps1`
- `TrxGateScriptTests`: it tested only `evaluate-test-results.ps1`. The
Actions equivalent, `.github/scripts/get-test-verdict.ps1`, keeps its
own `CiTestVerdictTests`.

## Kept

**OneFuzz:** `.azurepipelines/onefuzz.yml` stays. Submitting to the
internal OneFuzz service needs the Azure DevOps-only `onefuzz-task`,
which has no GitHub Actions equivalent. The pipeline has no push, PR or
schedule trigger, so it never runs on `master` pushes. The public drop
build keeps running in the GitHub `fuzz-public-drop` job.

**Scripts GitHub Actions still invokes:** these stay in
`.azurepipelines/`, so every workflow path and `release.yml`'s
candidate-bound checkout keep working. Only `generate-slnx.ps1` changes,
and only its comment.

| Script | Used by |
| --- | --- |
| `set-version.ps1` | buildandtest, nightly, nuget-publish,
docker-image, stress / stability / long-haul, `run-dotnet-tests` action
|
| `generate-slnx.ps1`, `signlist{Debug,Release}.txt`,
`validate-source-generator-packages.ps1` (+ `expected-packages.txt`) |
nuget-publish |
| `package-version-policy.ps1`, `validate-nuget-package-set.ps1`,
`validate-preview-package-ordering.ps1`,
`assert-published-packages-match.ps1` | nuget-publish, release |
| `check-coverage.ps1` | buildandtest, nightly |
| `validate-migration-plugin.ps1` | buildandtest |

I didn't rename the directory on purpose. `release.yml` checks out the
candidate commit and runs `./.azurepipelines/package-version-policy.ps1`
from it, so moving the scripts would break promotion of any candidate
built before the move.

## Docs and comments

- `UA.slnx` drops the deleted files.
- `codecov.yml`, `coverage-thresholds.json`, `targets.props`, the
Redundancy sample test comments, the `.github` scripts and workflows,
`CONTRIBUTING.md`, `docs/` and `fuzzing/` no longer describe Azure as
running on `master` (`/azp run`, the `azure` Codecov flag, "the Azure
gate", the Azure long-haul and AoT legs).
- In the Developer Guide, the "Triggering a pipeline run" and "Migration
status" sections are replaced by "Pull requests from outside
contributors" and "Azure Pipelines on other branches".
- The new section names `onefuzz.yml` as the one remaining Azure
definition.
- It keeps the warning that the `Release` ruleset's Azure required check
must be lifted before cutting a new `release/2.<minor>` branch.
- `master` is merged in, including the #4555 docs rewrite. Its new
`docs/RedundancySampleTests.md` needed the Azure references removed
again. `docs/README.md` already had none.

## Reviewer notes

- **Azure DevOps side (admin action, not in this PR):** the definitions
are configured in the Azure DevOps portal. With the YAML gone, `master`
pushes give the CI and preview definitions nothing to run. Disabling
definition 14's push trigger/schedule, definition 16's build-completion
trigger, and the stale definition 13 stops any residual "file not found"
runs. Leave the OneFuzz definition enabled.
- `master378`, the 1.x `release/*` lines and the frozen `release/2.0.0`
keep their own `azure-pipelines.yml`. This PR only touches `master`.

## Related Issues

- Follow-up to #4513 (GitHub Actions for CI and nightly).

## Checklist

- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [ ] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [ ] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [ ] I have addressed **all** PR feedback received.

This PR removes a test and adds none. Locally, at `9b00da817`, the full
`Opc.Ua.Tools.Tests` suite passed on net10.0: 884 passed, 1 skipped. The
later commits (OneFuzz restore, merge with `master`) touch only YAML and
docs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 07:44:36 +02:00

132 lines
5.1 KiB
PowerShell

<#
.SYNOPSIS
Decides whether one test project's run passed, and why.
.DESCRIPTION
Dot-sourced by .github/scripts/run-dotnet-tests.ps1 and exercised directly by
tests/Opc.Ua.Tools.Tests/CiTestVerdictTests.cs. It is separated from the
executor because it is the single decision that determines whether continuous
integration reports green: a rule that is too strict reports a false red, and
one that is too lax lets a genuinely broken suite merge. Keeping it in its own
file lets it be tested without building or running anything.
The emitted TRX decides the verdict, not the 'dotnet test' exit code. A
non-zero exit is tolerated when - and only when - the results record at least
one *passing* test and no failure of any kind, which means the host died
during process exit after the last test and every teardown had already run.
A host that dies mid-run leaves a non-zero failed/aborted/passedButRunAborted counter and is
rejected, and a suite whose tests were all skipped is rejected as well.
#>
<#
.SYNOPSIS
Returns the verdict for a single project's run.
.PARAMETER TrxFileCount
Number of TRX files the run produced.
.PARAMETER Total
Total number of tests recorded across those files.
.PARAMETER Passed
Recorded tests that actually ran and passed. A run whose tests were all
skipped reports Total > 0 with Passed = 0 and must not be treated as green.
.PARAMETER Failed
Sum of every TRX counter that represents a failed, non-passing, or
unfinished test: failed, error, timeout, aborted, passedButRunAborted,
inconclusive, notRunnable, disconnected, warning, completed, inProgress,
and pending - a fail-closed set.
.PARAMETER ExitCode
Exit code of the 'dotnet test' process.
.PARAMETER TimedOut
Whether the executor killed the process at its per-project ceiling.
.PARAMETER TimeoutMinutes
The ceiling that was applied, used only to phrase the reason.
.OUTPUTS
An object with Passed (bool), Tolerated (bool) and Reason (string). Tolerated
is only ever true alongside Passed, and always carries a reason so the
condition stays visible in the job summary.
#>
function Get-TestRunVerdict
{
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)] [int] $TrxFileCount,
[Parameter(Mandatory = $true)] [int] $Total,
[Parameter(Mandatory = $true)] [int] $Passed,
[Parameter(Mandatory = $true)] [int] $Failed,
[Parameter(Mandatory = $true)] [int] $ExitCode,
[Parameter(Mandatory = $true)] [bool] $TimedOut,
[Parameter(Mandatory = $false)] [int] $TimeoutMinutes = 0
)
# A timeout is never benign: the executor killed a process that was still
# running, so whatever the partial results say, the run did not complete.
if ($TimedOut) {
return [pscustomobject]@{
Passed = $false
Tolerated = $false
Reason = "The test run exceeded the $TimeoutMinutes-minute per-project ceiling."
}
}
# Every mainline project runs on VSTest, so silence here is a broken run
# rather than an empty one - reporting it as a pass would hide a suite that
# stopped executing entirely.
if ($TrxFileCount -le 0) {
return [pscustomobject]@{
Passed = $false
Tolerated = $false
Reason = ('No TRX was produced. Every mainline test project runs on VSTest and must emit one; ' +
'a Microsoft.Testing.Platform project belongs in a dedicated job instead of this matrix.')
}
}
# Evaluated before the exit code so a host that died mid-run is rejected
# rather than tolerated below.
if ($Failed -gt 0) {
return [pscustomobject]@{
Passed = $false
Tolerated = $false
Reason = "$Failed test(s) failed, were non-passing, or did not finish."
}
}
if ($Total -le 0) {
return [pscustomobject]@{
Passed = $false
Tolerated = $false
Reason = 'No tests were recorded. The project is applicable to this profile, so discovery is broken.'
}
}
# Total counts every recorded test including skipped ones, so a suite that
# was entirely skipped still reports Total > 0 with nothing verified. The
# executor's contract is that an applicable project executes at least one
# test, so require a passing test rather than merely a recorded one.
if ($Passed -le 0) {
return [pscustomobject]@{
Passed = $false
Tolerated = $false
Reason = ("None of the $Total recorded test(s) passed - every one was skipped or not executed. " +
'The project is applicable to this profile, so it must run at least one test.')
}
}
if ($ExitCode -ne 0) {
return [pscustomobject]@{
Passed = $true
Tolerated = $true
Reason = ("Tolerated: $Passed of $Total test(s) passed with no failure, but the host exited " +
"with code $ExitCode (at-exit stall).")
}
}
return [pscustomobject]@{ Passed = $true; Tolerated = $false; Reason = '' }
}