test harness: extend matrix to all 9 PC types
Adds rows for Standard-Timeclock, CMM, Keyence, Lab, WaxAndTrace, Genspect, Display, Shopfloor alongside the existing Standard-Machine. Per-type apps verified against the corresponding v2 manifest's detection methods (PC-DMIS 2016/2019R2/Protect Viewer/CLM/goCMM for CMM; VR-6000/USB driver for Keyence; kiosk shortcut for Display). Common app list deduped via "$ref": "common.<key>" pattern. Verifier resolves refs into the per-type apps array at runtime so each row stays short and PCTypes-filter-aware (Lab + Display + Shopfloor get fewer common apps because the manifest's PCTypes filter excludes them from FMS hosts pin / Oracle / OpenText respectively). verify-state.ps1 changes: - $ref resolution against the matrix.common namespace - Registry method now permits no DetectionName (key-existence only, e.g. Protect Viewer) - New PnpUtilGrep method for INF-driver checks (Keyence USB driver) Smoke-verified end-to-end on the win11 VM as SYSTEM via qga - 60 checks across 9 PC types. Type-specific failures (5 CMM, 2 Keyence, 1 Display) correctly surface "no payload staged" rather than masking it as pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,12 +31,41 @@ if (-not $entry) {
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Resolve `{ "$ref": "common.<key>" }` entries: matrix.json uses $ref to dedup
|
||||||
|
# common app lists (e.g. "common.all", "common.fmsResolver") so per-PC-type
|
||||||
|
# rows can compose without copy/paste. Walk the apps list, expand each ref
|
||||||
|
# inline, drop the ref placeholder.
|
||||||
|
$resolvedApps = @()
|
||||||
|
foreach ($a in $entry.apps) {
|
||||||
|
$refVal = $a.PSObject.Properties['$ref']
|
||||||
|
if ($refVal) {
|
||||||
|
$parts = $refVal.Value -split '\.'
|
||||||
|
if ($parts.Count -eq 2 -and $parts[0] -eq 'common') {
|
||||||
|
$key = $parts[1]
|
||||||
|
$list = $matrix.common.$key
|
||||||
|
if ($list) {
|
||||||
|
foreach ($x in $list) { $resolvedApps += $x }
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
Write-Host "[WARN] unresolved `$ref: $($refVal.Value)"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
Write-Host "[WARN] unsupported `$ref form: $($refVal.Value)"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
$resolvedApps += $a
|
||||||
|
}
|
||||||
|
|
||||||
function Test-AppState {
|
function Test-AppState {
|
||||||
param($app)
|
param($app)
|
||||||
$v = $app.verify
|
$v = $app.verify
|
||||||
switch ($v.method) {
|
switch ($v.method) {
|
||||||
'Registry' {
|
'Registry' {
|
||||||
if (-not (Test-Path -LiteralPath $v.path)) { return @{ pass=$false; detail="reg path missing: $($v.path)" } }
|
if (-not (Test-Path -LiteralPath $v.path)) { return @{ pass=$false; detail="reg path missing: $($v.path)" } }
|
||||||
|
# No DetectionName/value -> key existence is enough (e.g. Protect Viewer entry)
|
||||||
|
if (-not $v.name) {
|
||||||
|
return @{ pass=$true; detail="reg key exists: $($v.path)" }
|
||||||
|
}
|
||||||
$val = (Get-ItemProperty -LiteralPath $v.path -Name $v.name -ErrorAction SilentlyContinue).$($v.name)
|
$val = (Get-ItemProperty -LiteralPath $v.path -Name $v.name -ErrorAction SilentlyContinue).$($v.name)
|
||||||
if ($null -eq $val) { return @{ pass=$false; detail="reg name $($v.name) not present" } }
|
if ($null -eq $val) { return @{ pass=$false; detail="reg name $($v.name) not present" } }
|
||||||
if ($v.value -and $val -ne $v.value) {
|
if ($v.value -and $val -ne $v.value) {
|
||||||
@@ -70,12 +99,19 @@ function Test-AppState {
|
|||||||
if ($hit) { return @{ pass=$true; detail="pattern matched: $($v.pattern)" } }
|
if ($hit) { return @{ pass=$true; detail="pattern matched: $($v.pattern)" } }
|
||||||
return @{ pass=$false; detail="pattern not found: $($v.pattern)" }
|
return @{ pass=$false; detail="pattern not found: $($v.pattern)" }
|
||||||
}
|
}
|
||||||
|
'PnpUtilGrep' {
|
||||||
|
$drivers = & pnputil /enum-drivers 2>&1 | Out-String
|
||||||
|
if ($drivers -match $v.pattern) {
|
||||||
|
return @{ pass=$true; detail="pnputil match: $($v.pattern)" }
|
||||||
|
}
|
||||||
|
return @{ pass=$false; detail="pnputil no match for: $($v.pattern)" }
|
||||||
|
}
|
||||||
default { return @{ pass=$false; detail="unknown method: $($v.method)" } }
|
default { return @{ pass=$false; detail="unknown method: $($v.method)" } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$total = 0; $passed = 0; $failed = @()
|
$total = 0; $passed = 0; $failed = @()
|
||||||
foreach ($app in $entry.apps) {
|
foreach ($app in $resolvedApps) {
|
||||||
$total++
|
$total++
|
||||||
$r = Test-AppState -app $app
|
$r = Test-AppState -app $app
|
||||||
if ($r.pass) {
|
if ($r.pass) {
|
||||||
|
|||||||
@@ -1,18 +1,34 @@
|
|||||||
{
|
{
|
||||||
"_comment": "Test matrix for shopfloor harness. Each PC-type entry lists apps to verify + drift scenarios for Path B's tamper+heal phase. Verify methods mirror the v2 manifest's DetectionMethod so harness verification == GE-Enforce detection.",
|
"_comment": "Test matrix for shopfloor harness. Each PC-type entry lists apps to verify + drift scenarios for Path B's tamper+heal phase. Verify methods mirror the v2 manifest's DetectionMethod so harness == GE-Enforce detection. Apps requiring binary installers staged on share but not in repo: see paths in apps/ entries. Without staged payload, verify will FAIL for those - that surfaces 'payload missing' as actionable signal.",
|
||||||
|
|
||||||
"pcTypes": [
|
"common": {
|
||||||
{
|
"_comment": "Shared app list referenced by per-PC-type entries via 'includeCommon: <key>'. Filter mimics the manifest's PCTypes gating so each PC-type test only verifies apps that SHOULD be installed for that type.",
|
||||||
"PCType": "Standard",
|
"all": [
|
||||||
"PCSubType": "Machine",
|
|
||||||
"scopes": ["common", "standard-machine"],
|
|
||||||
"apps": [
|
|
||||||
{ "name": "Adobe Acrobat Reader DC", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{AC76BA86-7AD7-1033-7B44-AC0F074E4100}", "name": "DisplayVersion", "value": "25.001.20531" } },
|
{ "name": "Adobe Acrobat Reader DC", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{AC76BA86-7AD7-1033-7B44-AC0F074E4100}", "name": "DisplayVersion", "value": "25.001.20531" } },
|
||||||
{ "name": "WJF Defect Tracker", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{CC1B4D32-1606-4A3F-8F24-31312F723D5C}", "name": "DisplayVersion", "value": "01.00.0102" } },
|
{ "name": "WJF Defect Tracker", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{CC1B4D32-1606-4A3F-8F24-31312F723D5C}", "name": "DisplayVersion", "value": "01.00.0102" } },
|
||||||
{ "name": "3OF9 barcode font", "verify": { "method": "File", "path": "C:\\Windows\\Fonts\\3OF9.ttf" } },
|
{ "name": "3OF9 barcode font", "verify": { "method": "File", "path": "C:\\Windows\\Fonts\\3OF9.ttf" } },
|
||||||
{ "name": "Edge IE-Mode site list", "verify": { "method": "Hash", "path": "C:\\ProgramData\\Edge\\enterprise-mode-site-list.xml", "value": "16F2A6E45EFA19ED7B1C54B264D6B33597678D3A5303255BC7CEB7E8510C60FC" } },
|
{ "name": "Edge IE-Mode site list", "verify": { "method": "Hash", "path": "C:\\ProgramData\\Edge\\enterprise-mode-site-list.xml", "value": "16F2A6E45EFA19ED7B1C54B264D6B33597678D3A5303255BC7CEB7E8510C60FC" } }
|
||||||
{ "name": "OpenText HostExplorer", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\GE\\OpenText", "name": "Installed", "value": "15.0.SP1.2" } },
|
],
|
||||||
{ "name": "FMS hosts pin", "verify": { "method": "FileGrep", "path": "C:\\Windows\\System32\\drivers\\etc\\hosts", "pattern": "10\\.233\\.112\\.158\\s+wjfms3\\.ae\\.ge\\.com" } },
|
"fmsResolver": [
|
||||||
|
{ "name": "FMS hosts pin", "verify": { "method": "FileGrep", "path": "C:\\Windows\\System32\\drivers\\etc\\hosts", "pattern": "10\\.233\\.112\\.158\\s+wjfms3\\.ae\\.ge\\.com" } }
|
||||||
|
],
|
||||||
|
"oracle": [
|
||||||
|
{ "name": "Oracle Client 11.2", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\WOW6432Node\\Oracle\\KEY_OraClient11g_home1", "name": "ORACLE_HOME_NAME", "value": "OraClient11g_home1" } }
|
||||||
|
],
|
||||||
|
"openText": [
|
||||||
|
{ "name": "OpenText HostExplorer", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\GE\\OpenText", "name": "Installed", "value": "15.0.SP1.2" } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"pcTypes": [
|
||||||
|
{
|
||||||
|
"PCType": "Standard", "PCSubType": "Machine",
|
||||||
|
"scopes": ["common", "standard-machine"],
|
||||||
|
"apps": [
|
||||||
|
{ "$ref": "common.all" },
|
||||||
|
{ "$ref": "common.fmsResolver" },
|
||||||
|
{ "$ref": "common.oracle" },
|
||||||
|
{ "$ref": "common.openText" },
|
||||||
{ "name": "FMS Primary host", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\WOW6432Node\\GE Aircraft Engines\\Dnc\\FMS", "name": "FMSHostPrimary", "value": "wjfms3.ae.ge.com" } },
|
{ "name": "FMS Primary host", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\WOW6432Node\\GE Aircraft Engines\\Dnc\\FMS", "name": "FMSHostPrimary", "value": "wjfms3.ae.ge.com" } },
|
||||||
{ "name": "FMS Secondary host", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\WOW6432Node\\GE Aircraft Engines\\Dnc\\FMS", "name": "FMSHostSecondary", "value": "10.233.112.158" } },
|
{ "name": "FMS Secondary host", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\WOW6432Node\\GE Aircraft Engines\\Dnc\\FMS", "name": "FMSHostSecondary", "value": "10.233.112.158" } },
|
||||||
{ "name": "eDNC bundles NTLARS", "verify": { "method": "FileVersion", "path": "C:\\Program Files (x86)\\Dnc\\bin\\DncMain.exe", "value": "6.4.5.0" } }
|
{ "name": "eDNC bundles NTLARS", "verify": { "method": "FileVersion", "path": "C:\\Program Files (x86)\\Dnc\\bin\\DncMain.exe", "value": "6.4.5.0" } }
|
||||||
@@ -25,6 +41,108 @@
|
|||||||
{ "name": "Edge IE site list overwrite", "tamper": { "method": "FileOverwrite", "path": "C:\\ProgramData\\Edge\\enterprise-mode-site-list.xml", "content": "<!--tampered-->" }, "expectedHeal": "Edge IE-Mode site list" },
|
{ "name": "Edge IE site list overwrite", "tamper": { "method": "FileOverwrite", "path": "C:\\ProgramData\\Edge\\enterprise-mode-site-list.xml", "content": "<!--tampered-->" }, "expectedHeal": "Edge IE-Mode site list" },
|
||||||
{ "name": "3OF9 font deleted", "tamper": { "method": "FileDelete", "path": "C:\\Windows\\Fonts\\3OF9.ttf" }, "expectedHeal": "3OF9 barcode font" }
|
{ "name": "3OF9 font deleted", "tamper": { "method": "FileDelete", "path": "C:\\Windows\\Fonts\\3OF9.ttf" }, "expectedHeal": "3OF9 barcode font" }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"PCType": "Standard", "PCSubType": "Timeclock",
|
||||||
|
"scopes": ["common"],
|
||||||
|
"_comment": "Standard-Timeclock has NO standard-timeclock/manifest.json on the v2 share. Common-scope FMS hosts pin still applies (PCTypes filter includes Standard for either subtype). Standard-machine scope (FMS reg, eDNC, UDC, eMxInfo) is gated to subtype=Machine and is NOT enforced on Timeclock - those apps are correctly absent here.",
|
||||||
|
"apps": [
|
||||||
|
{ "$ref": "common.all" },
|
||||||
|
{ "$ref": "common.fmsResolver" },
|
||||||
|
{ "$ref": "common.oracle" },
|
||||||
|
{ "$ref": "common.openText" }
|
||||||
|
],
|
||||||
|
"driftScenarios": []
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"PCType": "CMM", "PCSubType": "",
|
||||||
|
"scopes": ["common", "cmm"],
|
||||||
|
"apps": [
|
||||||
|
{ "$ref": "common.all" },
|
||||||
|
{ "$ref": "common.fmsResolver" },
|
||||||
|
{ "$ref": "common.oracle" },
|
||||||
|
{ "$ref": "common.openText" },
|
||||||
|
{ "name": "PC-DMIS 2016", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{5389B196-81F0-44A9-A073-4C1D72041F09}", "name": "DisplayVersion", "value": "11.0.1179.0" } },
|
||||||
|
{ "name": "PC-DMIS 2019 R2", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{49DBE7F9-228A-4E66-8BB5-DB5A446DCAE7}", "name": "DisplayVersion", "value": "14.2.728.0" } },
|
||||||
|
{ "name": "Protect Viewer", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{7DE6B8AF-F580-4CDE-845F-FBE46C1FCF69}" } },
|
||||||
|
{ "name": "CLM 1.8.73", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{a55fecde-0776-474e-a5b3-d57ea93d6a9f}", "name": "DisplayVersion", "value": "1.8.73.0" } },
|
||||||
|
{ "name": "goCMM", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{94f02b85-bbca-422e-9b8b-0c16a769eced}", "name": "DisplayVersion", "value": "1.1.6710.18601" } }
|
||||||
|
],
|
||||||
|
"driftScenarios": []
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"PCType": "Keyence", "PCSubType": "",
|
||||||
|
"scopes": ["common", "keyence"],
|
||||||
|
"apps": [
|
||||||
|
{ "$ref": "common.all" },
|
||||||
|
{ "$ref": "common.fmsResolver" },
|
||||||
|
{ "$ref": "common.oracle" },
|
||||||
|
{ "$ref": "common.openText" },
|
||||||
|
{ "name": "VR-6000 Series Software", "verify": { "method": "Registry", "path": "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{058E7194-BDF8-4FA2-9D69-978BB0F25214}", "name": "DisplayVersion", "value": "4.3.7" } },
|
||||||
|
{ "name": "KEYENCE VR Series USB Driver", "verify": { "method": "PnpUtilGrep", "pattern": "keyence_vr_series\\.inf" } }
|
||||||
|
],
|
||||||
|
"driftScenarios": []
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"PCType": "Lab", "PCSubType": "",
|
||||||
|
"scopes": ["common"],
|
||||||
|
"_comment": "lab/manifest.json on v2 share has empty Applications array - Lab gets only common apps. OpenText applies (PCTypes=*). FMS hosts pin and Oracle do NOT (Lab is excluded from those PCTypes filters).",
|
||||||
|
"apps": [
|
||||||
|
{ "$ref": "common.all" },
|
||||||
|
{ "$ref": "common.openText" }
|
||||||
|
],
|
||||||
|
"driftScenarios": []
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"PCType": "WaxAndTrace", "PCSubType": "",
|
||||||
|
"scopes": ["common", "waxandtrace"],
|
||||||
|
"apps": [
|
||||||
|
{ "$ref": "common.all" },
|
||||||
|
{ "$ref": "common.fmsResolver" },
|
||||||
|
{ "$ref": "common.oracle" },
|
||||||
|
{ "$ref": "common.openText" }
|
||||||
|
],
|
||||||
|
"driftScenarios": []
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"PCType": "Genspect", "PCSubType": "",
|
||||||
|
"scopes": ["common", "genspect"],
|
||||||
|
"apps": [
|
||||||
|
{ "$ref": "common.all" },
|
||||||
|
{ "$ref": "common.fmsResolver" },
|
||||||
|
{ "$ref": "common.oracle" },
|
||||||
|
{ "$ref": "common.openText" }
|
||||||
|
],
|
||||||
|
"driftScenarios": []
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"PCType": "Display", "PCSubType": "",
|
||||||
|
"scopes": ["common", "display"],
|
||||||
|
"_comment": "Display gets Oracle (PCTypes filter includes it) but NOT FMS hosts pin. Display kiosk shortcut at the well-known Start Menu path is the marker for the kiosk setup CMD.",
|
||||||
|
"apps": [
|
||||||
|
{ "$ref": "common.all" },
|
||||||
|
{ "$ref": "common.oracle" },
|
||||||
|
{ "$ref": "common.openText" },
|
||||||
|
{ "name": "GE Aerospace Display kiosk setup", "verify": { "method": "File", "path": "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\GE Aerospace\\GE Aerospace Dashboard.lnk" } }
|
||||||
|
],
|
||||||
|
"driftScenarios": []
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"PCType": "Shopfloor", "PCSubType": "",
|
||||||
|
"scopes": ["common"],
|
||||||
|
"_comment": "Shopfloor is the bare baseline image (no per-type apps). Most common apps with PCTypes=* still install. FMS hosts pin and Oracle are gated by PCTypes filters that exclude Shopfloor. OpenText also gated (PCTypes excludes Shopfloor).",
|
||||||
|
"apps": [
|
||||||
|
{ "$ref": "common.all" }
|
||||||
|
],
|
||||||
|
"driftScenarios": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user