Files
lmxopcua/scripts/integration/run-focas.ps1
Joseph Doherty a25593a9c6 chore: organize solution into module folders (Core/Server/Drivers/Client/Tooling)
Group all 69 projects into category subfolders under src/ and tests/ so the
Rider Solution Explorer mirrors the module structure. Folders: Core, Server,
Drivers (with a nested Driver CLIs subfolder), Client, Tooling.

- Move every project folder on disk with git mv (history preserved as renames).
- Recompute relative paths in 57 .csproj files: cross-category ProjectReferences,
  the lib/ HintPath+None refs in Driver.Historian.Wonderware, and the external
  mxaccessgw refs in Driver.Galaxy and its test project.
- Rebuild ZB.MOM.WW.OtOpcUa.slnx with nested solution folders.
- Re-prefix project paths in functional scripts (e2e, compliance, smoke SQL,
  integration, install).

Build green (0 errors); unit tests pass. Docs left for a separate pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 01:55:28 -04:00

124 lines
4.3 KiB
PowerShell

#Requires -Version 7.0
<#
.SYNOPSIS
Orchestrates the FOCAS driver integration-test loop: bring up the
focas-mock Docker container, run the managed wire-client integration
tests, tear down Docker.
.DESCRIPTION
The FOCAS integration fixture now needs just two things running
together:
1. A single focas-mock container listening on :8193 (one service,
no per-series compose profile ceremony — the mock's native
FOCAS Ethernet responder handles every call the managed driver
issues).
2. The integration-test assembly built. The managed
`WireFocasClient` dials the mock directly; there is no shim
DLL, no P/Invoke, no test-bin DLL copy step.
This script handles both and cleans up on exit.
Designed to run unattended on a build agent or on a developer box.
Exit code matches the test suite (0 = all pass or skip-clean,
non-zero when any integration test failed).
.PARAMETER Profile
focas-mock profile name to seed at startup (e.g. `ThirtyOne_i`,
`Sixteen_i`, `fwlib30i64`). Defaults to `ThirtyOne_i`. The fixture
resolves aliases via `FocasCncSeries`, and tests that need per-series
state can call `fixture.LoadProfileAsync` directly at test start to
override the default.
.PARAMETER SkipDocker
Skip docker up/down. Use when the mock is already running from
another shell.
.PARAMETER Configuration
Build configuration — Debug or Release. Default: Debug.
.PARAMETER KeepDocker
Don't tear down the docker stack on exit. Useful for iterating on
tests.
#>
param(
[string]$Profile = "ThirtyOne_i",
[switch]$SkipDocker,
[ValidateSet("Debug", "Release")]
[string]$Configuration = "Debug",
[switch]$KeepDocker
)
Set-StrictMode -Version 3.0
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path
$integTests = Join-Path $repoRoot "tests\Drivers\ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests"
$dockerYml = Join-Path $integTests "Docker\docker-compose.yml"
function Write-Step { param([string]$Msg) Write-Host ""; Write-Host "=== $Msg ===" -ForegroundColor Cyan }
function Write-Info { param([string]$Msg) Write-Host "[INFO] $Msg" -ForegroundColor Gray }
function Write-Fail { param([string]$Msg) Write-Host "[FAIL] $Msg" -ForegroundColor Red }
$cleanupScripts = @()
trap {
Write-Host ""
Write-Fail "run-focas.ps1 crashed: $_"
foreach ($c in $cleanupScripts) { try { & $c } catch { Write-Host "cleanup failed: $_" -ForegroundColor DarkYellow } }
exit 2
}
Write-Step "Build FOCAS IntegrationTests ($Configuration)"
dotnet build (Join-Path $integTests "ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests.csproj") `
--configuration $Configuration --nologo --verbosity minimal
if ($LASTEXITCODE -ne 0) { throw "dotnet build failed (exit $LASTEXITCODE)" }
if (-not $SkipDocker) {
Write-Step "docker compose up"
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
Write-Fail "docker CLI not on PATH. Install Docker Desktop or pass -SkipDocker + run the mock externally."
exit 4
}
docker compose -f $dockerYml up -d --build --wait 2>&1 | Write-Host
if ($LASTEXITCODE -ne 0) { throw "docker compose up failed (exit $LASTEXITCODE)" }
if (-not $KeepDocker) {
$cleanupScripts += {
Write-Step "docker compose down"
docker compose -f $dockerYml down --remove-orphans 2>&1 | Write-Host
}
}
Write-Info "probing localhost:8193..."
$tcp = [System.Net.Sockets.TcpClient]::new()
try {
$ok = $tcp.ConnectAsync("127.0.0.1", 8193).Wait([TimeSpan]::FromSeconds(5))
if (-not $ok -or -not $tcp.Connected) {
throw "TCP probe to localhost:8193 failed after docker compose --wait succeeded"
}
}
finally { $tcp.Dispose() }
Write-Info "mock is accepting connections"
}
else {
Write-Step "Docker (skipped)"
}
Write-Step "dotnet test (wire-backend integration)"
$env:OTOPCUA_FOCAS_SIM_PROFILE = $Profile
dotnet test (Join-Path $integTests "ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests.csproj") `
--configuration $Configuration --no-build --nologo --verbosity minimal
$testExit = $LASTEXITCODE
foreach ($c in $cleanupScripts) { & $c }
if ($testExit -ne 0) {
Write-Fail "integration tests failed with exit $testExit"
exit $testExit
}
Write-Host ""
Write-Host "run-focas.ps1 completed successfully." -ForegroundColor Green
exit 0