Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions build/scripts/ParallelTestExecution.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,52 @@ function Get-CachedTestRunResult {
return $null
}

<#
.SYNOPSIS
Resolves the authoritative app name for a project from its app.json "name" field.
.DESCRIPTION
Get-ApplicationGroup exposes the projects.json key as ApplicationName. That key is required to
match the app.json "name", but the two can drift (typos, "-Tests" vs " Test", trailing dots).
When they drift, the container only ever knows the app.json "name", so any match keyed off
ApplicationName silently drops the app from the test dispatch set and skips ALL of its tests
while the build stays green. Reading the name straight from app.json (via AppJsonPath) makes
the dispatch robust against that drift. Falls back to ApplicationName if app.json is missing
or unreadable.
.OUTPUTS
[string] The app.json "name", or ApplicationName as a fallback.
#>
function Get-AppNameFromMetadata {
param(
[Parameter(Mandatory=$true)]
$BuildMetadata
)

$appJsonPath = $BuildMetadata.AppJsonPath
if (-not [string]::IsNullOrWhiteSpace($appJsonPath) -and (Test-Path $appJsonPath -PathType Leaf)) {
try {
$appName = (Get-Content $appJsonPath -Raw | ConvertFrom-Json).name
if (-not [string]::IsNullOrWhiteSpace($appName)) {
if ($appName -ne $BuildMetadata.ApplicationName) {
Write-Host "::warning::Test app projects.json key '$($BuildMetadata.ApplicationName)' does not match its app.json name '$appName'. Using the app.json name for test dispatch. Align the projects.json key (and build/groups.json name) with the app.json name to avoid confusion."
Comment thread
aholstrup1 marked this conversation as resolved.
}
return $appName
}
} catch {
Write-Host "WARNING: Could not read app name from '$appJsonPath' ($($_.Exception.Message)); falling back to projects.json key '$($BuildMetadata.ApplicationName)'."
}
}
return $BuildMetadata.ApplicationName
}

<#
.SYNOPSIS
Returns the names of test apps that are both expected for a country and installed in the container.
.DESCRIPTION
Combines the project metadata in build/projects.json (via Get-ApplicationGroup) with
Get-BcContainerAppInfo so we only ever try to dispatch apps that actually exist in the
container. The country defaults to "w1" when unset or set to the repo-level "base" sentinel.
App names are resolved from each project's app.json "name" (via Get-AppNameFromMetadata) so a
projects.json-key vs app.json-name mismatch cannot silently exclude a test app from dispatch.
#>
function Get-InstalledTestAppNames {
param(
Expand All @@ -68,7 +107,7 @@ function Get-InstalledTestAppNames {
$allTestAppNames = @(
Get-ApplicationGroup -GroupName "All" -CountryCode $Country -SkipLanguagePacks |
Where-Object { $_.IsTest } |
Select-Object -ExpandProperty ApplicationName
ForEach-Object { Get-AppNameFromMetadata -BuildMetadata $_ }
)

$installedAppNames = @(
Expand Down Expand Up @@ -689,4 +728,4 @@ function Invoke-PerProjectTestRun {
return (. $script -parameters $parameters -TestType $testType -AppNamesToTest $appNamesToTest)
}

Export-ModuleMember -Function Invoke-ParallelTestExecution, Get-AvailableBcTenants, Get-CachedTestRunResult, Get-InstalledTestAppNames, Get-AppNamesForBucket, Invoke-PerProjectTestRun
Export-ModuleMember -Function Invoke-ParallelTestExecution, Get-AvailableBcTenants, Get-CachedTestRunResult, Get-InstalledTestAppNames, Get-AppNamesForBucket, Invoke-PerProjectTestRun, Get-AppNameFromMetadata
75 changes: 75 additions & 0 deletions build/scripts/tests/ParallelTestExecution.Test.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
$errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0

Import-Module (Join-Path $PSScriptRoot '../ParallelTestExecution.psm1') -Force

Describe "ParallelTestExecution app-name resolution" {
BeforeAll {
# Create real app.json files on disk so Get-AppNameFromMetadata can read them.
$script:tempRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("patest_" + [System.Guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $script:tempRoot -Force | Out-Null

function New-AppJson {
param([string]$Folder, [string]$Name)
$dir = Join-Path $script:tempRoot $Folder
New-Item -ItemType Directory -Path $dir -Force | Out-Null
$path = Join-Path $dir 'app.json'
@{ name = $Name; id = [System.Guid]::NewGuid().ToString() } | ConvertTo-Json | Set-Content -Path $path -Encoding utf8
return $path
}

# A metadata record whose projects.json key (ApplicationName) differs from its app.json name.
$script:mismatchAppJson = New-AppJson -Folder 'Mismatch' -Name 'Real App Name'
# A metadata record whose key and app.json name agree.
$script:matchAppJson = New-AppJson -Folder 'Match' -Name 'Aligned Tests'
}

AfterAll {
if (Test-Path $script:tempRoot) { Remove-Item $script:tempRoot -Recurse -Force -ErrorAction SilentlyContinue }
}

Context "Get-AppNameFromMetadata" {
It "returns the app.json name when it differs from the projects.json key" {
$md = [PSCustomObject]@{ ApplicationName = 'Projects-Json-Key'; AppJsonPath = $script:mismatchAppJson }
Get-AppNameFromMetadata -BuildMetadata $md | Should -Be 'Real App Name'
}

It "returns the app.json name when it matches the projects.json key" {
$md = [PSCustomObject]@{ ApplicationName = 'Aligned Tests'; AppJsonPath = $script:matchAppJson }
Get-AppNameFromMetadata -BuildMetadata $md | Should -Be 'Aligned Tests'
}

It "falls back to ApplicationName when the app.json path is missing" {
$md = [PSCustomObject]@{ ApplicationName = 'Fallback Name'; AppJsonPath = (Join-Path $script:tempRoot 'does-not-exist\app.json') }
Get-AppNameFromMetadata -BuildMetadata $md | Should -Be 'Fallback Name'
}

It "falls back to ApplicationName when AppJsonPath is empty" {
$md = [PSCustomObject]@{ ApplicationName = 'Fallback Name'; AppJsonPath = '' }
Get-AppNameFromMetadata -BuildMetadata $md | Should -Be 'Fallback Name'
}
}

Context "Get-InstalledTestAppNames resilience to key/name drift" {
It "dispatches a test app whose projects.json key differs from its installed (app.json) name" {
Mock -ModuleName ParallelTestExecution -CommandName Get-ApplicationGroup -MockWith {
@(
[PSCustomObject]@{ IsTest = $true; ApplicationName = 'Projects-Json-Key'; AppJsonPath = $script:mismatchAppJson }
[PSCustomObject]@{ IsTest = $true; ApplicationName = 'Aligned Tests'; AppJsonPath = $script:matchAppJson }
[PSCustomObject]@{ IsTest = $false; ApplicationName = 'Some Prod App'; AppJsonPath = $null }
)
}
Mock -ModuleName ParallelTestExecution -CommandName Get-BcContainerAppInfo -MockWith {
@(
[PSCustomObject]@{ IsInstalled = $true; Name = 'Real App Name' } # installed under the app.json name
[PSCustomObject]@{ IsInstalled = $true; Name = 'Aligned Tests' }
)
}

$result = Get-InstalledTestAppNames -ContainerName 'c' -Tenant 'default' -Country 'w1'

$result | Should -Contain 'Real App Name'
$result | Should -Contain 'Aligned Tests'
$result | Should -Not -Contain 'Projects-Json-Key'
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,11 @@
"codeunitId": 139989,
"codeunitName": "Subc. Subcontracting Test",
"method": "CancelInvoiceWithSubcontractingItemChargeIsBlocked"
},
{
"bug": "641388",
"codeunitId": 149911,
"codeunitName": "Subc. WIP Trans. Create Test",
"method": "WIPTransferRemainderCreatedWhenOpenLineQuantityReduced"
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -328,5 +328,11 @@
"codeunitId": 139913,
"codeunitName": "Vendor Deferrals Test",
"method": "VerifyVendSubContrDefAccountBalancedForCreditMemoWithDiscount"
},
{
"bug": "641478",
"codeunitId": 139913,
"codeunitName": "Vendor Deferrals Test",
"method": "DeferralsReleaseSucceedsWhenGLAccountHasDefaultDeferralTemplateAndJournalTemplNotMandatory"
}
]
Loading