From e202923a69d7bd0771ba67aa284944c76d18232c Mon Sep 17 00:00:00 2001 From: "mustafa.ahmed" Date: Sat, 15 Aug 2026 15:58:56 +0500 Subject: [PATCH] test: add manual harnesses for employee photo preprocessing Add PowerShell harnesses to exercise EmployeePhotoFaceProcessor against real HRMS portal photos without touching a device: run_photo_processor processes given employee ids and writes source/processed JPEGs, and run_photo_soak repeatedly processes real and synthetic images in one process to check stability and memory. Both target 32-bit PowerShell to match the service's x86 build. --- Tests/run_photo_processor.ps1 | 49 +++++++++++++++++++++++++++++++ Tests/run_photo_soak.ps1 | 55 +++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 Tests/run_photo_processor.ps1 create mode 100644 Tests/run_photo_soak.ps1 diff --git a/Tests/run_photo_processor.ps1 b/Tests/run_photo_processor.ps1 new file mode 100644 index 0000000..a865fb4 --- /dev/null +++ b/Tests/run_photo_processor.ps1 @@ -0,0 +1,49 @@ +# Manual harness: runs EmployeePhotoFaceProcessor against real HRMS portal photos. +# Run with 32-bit PowerShell (the service targets x86): +# C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -File Tests\run_photo_processor.ps1 142554 144154 +param([string[]]$EmployeeIds = @('142554')) + +$ErrorActionPreference = 'Stop' +$dir = Join-Path $PSScriptRoot '..\bin\Debug\net48' | Resolve-Path +$outDir = Join-Path $env:TEMP 'hik_photo_test' +New-Item -ItemType Directory -Force -Path $outDir | Out-Null + +[System.AppDomain]::CurrentDomain.add_AssemblyResolve({ + param($sender, $e) + $name = ($e.Name -split ',')[0] + $path = Join-Path $dir "$name.dll" + if (Test-Path $path) { return [System.Reflection.Assembly]::LoadFrom($path) } + return $null + }) + +$asm = [System.Reflection.Assembly]::LoadFrom((Join-Path $dir 'HikvisionAttendanceService.exe')) +$type = $asm.GetType('HikvisionAttendanceService.EmployeePhotoFaceProcessor') +$method = $type.GetMethod('Process', [System.Reflection.BindingFlags]'Static,Public,NonPublic') + +[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 +$client = New-Object System.Net.WebClient + +foreach ($id in $EmployeeIds) { + $url = "https://portal.utopiaindustries.pk/uind/employee-photo/$id.jpeg" + try { $bytes = $client.DownloadData($url) } + catch { Write-Host "$id download failed: $($_.Exception.Message)"; continue } + + $result = $method.Invoke($null, @(, $bytes)) + $srcPath = Join-Path $outDir "$id.source.jpeg" + [System.IO.File]::WriteAllBytes($srcPath, $bytes) + + $line = "id=$id status=$($result.Status) downloaded=$($bytes.Length) " + + "original=$($result.Original.Width)x$($result.Original.Height) " + + "face=$($result.Face.Width)x$($result.Face.Height) " + + "processed=$($result.Processed.Width)x$($result.Processed.Height) " + + "jpegBytes=$($result.JpegBytes.Length) quality=$($result.JpegQuality) " + + "enhance=[$($result.Enhancement)] err=$($result.Error)" + Write-Host $line + + if ($result.JpegBytes.Length -gt 0) { + $outPath = Join-Path $outDir "$id.processed.jpeg" + [System.IO.File]::WriteAllBytes($outPath, $result.JpegBytes) + Write-Host " source=$srcPath" + Write-Host " processed=$outPath" + } +} diff --git a/Tests/run_photo_soak.ps1 b/Tests/run_photo_soak.ps1 new file mode 100644 index 0000000..b56223e --- /dev/null +++ b/Tests/run_photo_soak.ps1 @@ -0,0 +1,55 @@ +# Manual harness: repeatedly processes real + synthetic photos in a single process, +# mirroring how the service handles many employees per sync cycle. +# C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -File Tests\run_photo_soak.ps1 -Rounds 15 +param([int]$Rounds = 15) + +$dir = Join-Path $PSScriptRoot '..\bin\Debug\net48' | Resolve-Path +$log = Join-Path $env:TEMP 'hik_soak.txt' +Set-Content -Path $log -Value "soak start rounds=$Rounds" + +[System.AppDomain]::CurrentDomain.add_AssemblyResolve({ + param($sender, $e) + $name = ($e.Name -split ',')[0] + $path = Join-Path $dir "$name.dll" + if (Test-Path $path) { return [System.Reflection.Assembly]::LoadFrom($path) } + return $null + }) + +$asm = [System.Reflection.Assembly]::LoadFrom((Join-Path $dir 'HikvisionAttendanceService.exe')) +$type = $asm.GetType('HikvisionAttendanceService.EmployeePhotoFaceProcessor') +$method = $type.GetMethod('Process', [System.Reflection.BindingFlags]'Static,Public,NonPublic') +Add-Type -AssemblyName System.Drawing + +$samples = @{} +foreach ($id in '142554', '144154', '149903') { + $p = Join-Path $env:TEMP "hik_photo_test\$id.source.jpeg" + if (Test-Path $p) { $samples[$id] = [System.IO.File]::ReadAllBytes($p) } +} + +$bmp = New-Object System.Drawing.Bitmap 400, 400, ([System.Drawing.Imaging.PixelFormat]::Format24bppRgb) +$g = [System.Drawing.Graphics]::FromImage($bmp); $g.Clear([System.Drawing.Color]::SlateGray); $g.Dispose() +$ms = New-Object System.IO.MemoryStream +$bmp.Save($ms, [System.Drawing.Imaging.ImageFormat]::Jpeg) +$samples['gray-noface'] = $ms.ToArray() +$samples['garbage'] = [byte[]]@(1, 2, 3, 4, 5) +$samples['empty'] = [byte[]]@() + +$proc = [System.Diagnostics.Process]::GetCurrentProcess() +$sw = [System.Diagnostics.Stopwatch]::StartNew() +$counts = @{} +for ($i = 1; $i -le $Rounds; $i++) { + foreach ($key in $samples.Keys) { + $bytes = [byte[]]$samples[$key] + $r = $method.Invoke($null, @(, $bytes)) + $k = "$key=$($r.Status)" + if (-not $counts.ContainsKey($k)) { $counts[$k] = 0 } + $counts[$k]++ + } +} +$sw.Stop() +$proc.Refresh() + +foreach ($k in ($counts.Keys | Sort-Object)) { Add-Content -Path $log -Value "$k count=$($counts[$k])"; Write-Host "$k count=$($counts[$k])" } +$line = "totalMs=$($sw.ElapsedMilliseconds) perCallMs=$([math]::Round($sw.ElapsedMilliseconds / ($Rounds * $samples.Count), 1)) workingSetMB=$([math]::Round($proc.WorkingSet64 / 1MB, 1)) handles=$($proc.HandleCount)" +Add-Content -Path $log -Value $line +Write-Host $line