test: add path smoothing scenarios and fixtures
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
param(
|
||||
[string]$AssemblyPath = (Join-Path $PSScriptRoot '..\bin\Debug\netstandard2.0\ClumsyPilot.dll'),
|
||||
[string]$FixturePath = (Join-Path $PSScriptRoot '..\ParkrobTrajplanner\PathSmoothing\Test\Fixtures\path-smoothing-fixtures.json'))
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$newtonsoftPath = Join-Path $env:USERPROFILE '.nuget\packages\newtonsoft.json\13.0.4\lib\netstandard2.0\Newtonsoft.Json.dll'
|
||||
if (Test-Path -LiteralPath $newtonsoftPath) { [Reflection.Assembly]::LoadFrom($newtonsoftPath) | Out-Null }
|
||||
$assembly = [Reflection.Assembly]::LoadFrom((Resolve-Path $AssemblyPath))
|
||||
|
||||
function Assert-True($Actual, [string]$Message) { if (-not $Actual) { throw $Message } }
|
||||
function Assert-Equal($Expected, $Actual, [string]$Message) {
|
||||
if ($Expected -ne $Actual) { throw "$Message Expected=$Expected Actual=$Actual" }
|
||||
}
|
||||
function Assert-ThrowsMatching([scriptblock]$Action, [string]$ExpectedPattern, [string]$Message) {
|
||||
try {
|
||||
& $Action
|
||||
}
|
||||
catch {
|
||||
$exception = $_.Exception
|
||||
while ($null -ne $exception.InnerException) { $exception = $exception.InnerException }
|
||||
if ($exception.Message -match $ExpectedPattern) { return }
|
||||
throw "$Message ExpectedPattern=$ExpectedPattern Actual=$($exception.Message)"
|
||||
}
|
||||
throw "$Message Expected an exception."
|
||||
}
|
||||
function Get-RequiredType([string]$Name) { return $assembly.GetType($Name, $true) }
|
||||
|
||||
$root = 'MultiWheelC.TrajectoryPlanning.PathSmoothing.Test.'
|
||||
$comparison = 'MultiWheelC.TrajectoryPlanning.PathSmoothing.Comparison.'
|
||||
$loaderType = Get-RequiredType ($root + 'SmoothingScenarioFixtureLoader')
|
||||
$factoryType = Get-RequiredType ($root + 'SmoothingScenarioFactory')
|
||||
$fixtureType = Get-RequiredType ($root + 'SmoothingScenarioFixture')
|
||||
$comparisonRequestType = Get-RequiredType ($comparison + 'PathSmoothingComparisonRequest')
|
||||
$preprocessorType = Get-RequiredType 'MultiWheelC.TrajectoryPlanning.PathSmoothing.Processing.PathSmoothingPreprocessor'
|
||||
|
||||
$loadMethod = $loaderType.GetMethod('LoadAndVerify', [Type[]]@([string]))
|
||||
Assert-True ($null -ne $loadMethod) 'Fixture loader must expose LoadAndVerify(string).'
|
||||
$fixtureRequestsMethod = $factoryType.GetMethod('CreateFixtureRequests', [Type[]]@([string]))
|
||||
Assert-True ($null -ne $fixtureRequestsMethod) 'Scenario factory must expose CreateFixtureRequests(string).'
|
||||
$generateMethod = $assembly.GetType($root + 'SmoothingFixtureGenerator', $true).GetMethod('Generate', [Type[]]@([string], [bool]))
|
||||
Assert-True ($null -ne $generateMethod) 'Fixture generator must expose Generate(string, bool).'
|
||||
$tryPrepareMethod = $preprocessorType.GetMethod('TryPrepare')
|
||||
Assert-True ($null -ne $tryPrepareMethod) 'Fixture paths must be checked through PathSmoothingPreprocessor.TryPrepare.'
|
||||
|
||||
$fixtures = $loadMethod.Invoke($null, @((Resolve-Path $FixturePath).Path))
|
||||
$fixtureDocument = Get-Content -Raw -Encoding UTF8 $FixturePath | ConvertFrom-Json
|
||||
$expected = @(
|
||||
'straight', 'single-turn', 's-bend', 'large-heading-change',
|
||||
'rectangle-detour', 'multi-obstacle-detour',
|
||||
'narrow-corridor', 'forward-reverse-switch')
|
||||
Assert-Equal $expected.Count $fixtures.Count 'Fixture loader must return exactly eight fast fixtures.'
|
||||
|
||||
$actualIds = @($fixtures | ForEach-Object { $_.Id })
|
||||
Assert-Equal ($expected -join ',') ($actualIds -join ',') 'Fixture IDs must be stable and in documented order.'
|
||||
Assert-Equal $actualIds.Count (@($actualIds | Select-Object -Unique).Count) 'Fixture IDs must be unique.'
|
||||
foreach ($fixture in $fixtures) {
|
||||
Assert-True ($fixture -is $fixtureType) 'Loader must return immutable smoothing fixture values.'
|
||||
Assert-True ($fixture.FixtureVersion -gt 0) "Fixture $($fixture.Id) must carry a positive version."
|
||||
Assert-True $fixture.IsConfigurationFingerprintCurrent "Fixture $($fixture.Id) must match its stored configuration fingerprint."
|
||||
Assert-True ($fixture.ConfigurationFingerprint -match '^sha256:[0-9a-f]{64}$') "Fixture $($fixture.Id) must expose a lowercase SHA-256 fingerprint."
|
||||
Assert-True ($fixture.Path.Count -gt 1) "Fixture $($fixture.Id) must include a coarse path."
|
||||
Assert-True ($fixture.Segments.Count -gt 0) "Fixture $($fixture.Id) must include direction segments."
|
||||
foreach ($segment in $fixture.Segments) {
|
||||
Assert-True ($segment.StartIndex -ge 0 -and $segment.EndIndex -lt $fixture.Path.Count -and $segment.EndIndex -ge $segment.StartIndex) "Fixture $($fixture.Id) must retain valid segment coverage."
|
||||
}
|
||||
}
|
||||
foreach ($record in $fixtureDocument.scenarios) {
|
||||
Assert-True ($null -ne $record.planningConfiguration) "Fixture $($record.id) must retain the planning configuration that produced its coarse path."
|
||||
}
|
||||
|
||||
$requests = $fixtureRequestsMethod.Invoke($null, @((Resolve-Path $FixturePath).Path))
|
||||
Assert-Equal 8 $requests.Count 'Fast fixtures must create exactly eight comparison requests without Hybrid A*.'
|
||||
foreach ($request in $requests) {
|
||||
Assert-True ($request -is $comparisonRequestType) 'Fast fixture factory must create comparison requests.'
|
||||
$prepareArguments = [object[]]@($request.SmoothingRequest, $null, $null)
|
||||
$prepared = $tryPrepareMethod.Invoke([Activator]::CreateInstance($preprocessorType), $prepareArguments)
|
||||
Assert-True $prepared "Fixture path must satisfy the PathSmoothingPreprocessor input contract. Reason=$($prepareArguments[2])"
|
||||
}
|
||||
|
||||
$loaderSource = Get-Content -Raw -Encoding UTF8 (Join-Path $PSScriptRoot '..\ParkrobTrajplanner\PathSmoothing\Test\SmoothingScenarioFixtureLoader.cs')
|
||||
Assert-True (-not $loaderSource.Contains('HybridAStarPlanner')) 'Fixture-only loading must not reference HybridAStarPlanner.'
|
||||
$generatorSource = Get-Content -Raw -Encoding UTF8 (Join-Path $PSScriptRoot '..\ParkrobTrajplanner\PathSmoothing\Test\SmoothingFixtureGenerator.cs')
|
||||
Assert-True $generatorSource.Contains('CoarsePathPlanningService') 'Fixture generation must snapshot actual successful coarse-path planning outputs.'
|
||||
Assert-True $generatorSource.Contains('CoarsePathScenarioFactory') 'Fixture generation must use the established coarse-path scenarios where available.'
|
||||
|
||||
function Write-CorruptedFixture([scriptblock]$Mutate) {
|
||||
$temporaryPath = [IO.Path]::GetTempFileName()
|
||||
$document = Get-Content -Raw -Encoding UTF8 $FixturePath | ConvertFrom-Json
|
||||
& $Mutate $document
|
||||
[IO.File]::WriteAllText($temporaryPath, ($document | ConvertTo-Json -Depth 16), [Text.UTF8Encoding]::new($false))
|
||||
return $temporaryPath
|
||||
}
|
||||
|
||||
$coverageFixture = Write-CorruptedFixture { param($document) $document.scenarios[0].segments[0].endIndex = 1 }
|
||||
$directionFixture = Write-CorruptedFixture { param($document) $document.scenarios[0].segments[0].direction = 1 }
|
||||
$switchFixture = Write-CorruptedFixture { param($document) $document.scenarios[0].segments[0].endsAtGearSwitch = $true }
|
||||
try {
|
||||
Assert-ThrowsMatching { $loadMethod.Invoke($null, @($coverageFixture)) } 'Fixture path contract' 'Loader must reject segment gaps before accepting the fingerprint.'
|
||||
Assert-ThrowsMatching { $loadMethod.Invoke($null, @($directionFixture)) } 'Fixture path contract' 'Loader must reject segment direction mismatches before accepting the fingerprint.'
|
||||
Assert-ThrowsMatching { $loadMethod.Invoke($null, @($switchFixture)) } 'Fixture path contract' 'Loader must reject illegal gear-switch topology before accepting the fingerprint.'
|
||||
}
|
||||
finally {
|
||||
foreach ($temporaryPath in @($coverageFixture, $directionFixture, $switchFixture)) {
|
||||
if ($temporaryPath -and [IO.File]::Exists($temporaryPath)) { [IO.File]::Delete($temporaryPath) }
|
||||
}
|
||||
}
|
||||
|
||||
$generationPath = [IO.Path]::GetTempFileName()
|
||||
try {
|
||||
Assert-ThrowsMatching { $generateMethod.Invoke($null, @($generationPath, $false)) } '.+' 'Fixture generation must refuse to overwrite an existing target.'
|
||||
$generateMethod.Invoke($null, @($generationPath, $true))
|
||||
$generatedFixtures = $loadMethod.Invoke($null, @($generationPath))
|
||||
Assert-Equal 8 $generatedFixtures.Count 'Generated fixture data must remain loadable and retain all scenarios.'
|
||||
}
|
||||
finally {
|
||||
if ([IO.File]::Exists($generationPath)) { [IO.File]::Delete($generationPath) }
|
||||
}
|
||||
|
||||
Write-Output 'Path smoothing fixture checks passed.'
|
||||
Reference in New Issue
Block a user