param ( [string] $BuildFolder = 'Release' )
if ( $PSVersionTable . PSEdition -ne 'Desktop' ) {
& " $ env:SystemRoot/System32/WindowsPowerShell/v1.0/powershell.exe " -NoProfile -STA -ExecutionPolicy Bypass -File $PSCommandPath -BuildFolder $BuildFolder
exit $LASTEXITCODE
}
$ErrorActionPreference = 'Stop'
function Assert-Ap ( $condition , $message ) { if ( -not $condition ) { throw $message } }
Add-Type -AssemblyName PresentationFramework
$apAssemblyPath = Join-Path $PSScriptRoot " DCT_test/bin/ $ BuildFolder/DCT_test.exe "
[void][Reflection.Assembly] :: LoadFrom ( $apAssemblyPath )
$apWindow = New-Object DCT_test . MainWindow
try {
$mode = $apWindow . FindName ( 'ComboNetworkMode' )
$port = $apWindow . FindName ( 'TextPort' )
$controls = $apWindow . FindName ( 'ApControls' )
$externalGuide = $apWindow . FindName ( 'ExternalApGuide' )
Assert-Ap ( $mode . SelectedIndex -eq 1 -and $port . Text -eq '6000' -and $port . IsEnabled ) 'Current app default must be external AP with editable port'
$mode . SelectedIndex = 0
Assert-Ap ( $port . Text -eq '6000' -and $port . IsEnabled -and $controls . Visibility -eq 'Collapsed' ) 'Legacy Direct must remain selectable with editable 6000'
$mode . SelectedIndex = 1
$ips = $apWindow . FindName ( 'ComboIp' )
Assert-Ap ( $port . Text -eq '6000' -and $port . IsEnabled -and $ips . IsEnabled -and $controls . Visibility -eq 'Collapsed' -and $externalGuide . Visibility -eq 'Visible' ) 'External AP current IP and editable port, no hotspot controls'
$freePortListener = [Net.Sockets.TcpListener] :: new ( [Net.IPAddress] :: Loopback , 0 )
$freePortListener . Start ( )
$freePort = $freePortListener . LocalEndpoint . Port
$freePortListener . Stop ( )
$ips . Items . Add ( '127.0.0.1' ) | Out-Null
$ips . SelectedItem = '127.0.0.1'
$port . Text = $freePort . ToString ( )
$toggle = $apWindow . GetType ( ) . GetMethod ( 'BtnToggleServer_Click' , [Reflection.BindingFlags] 'Instance,NonPublic' )
$toggle . Invoke ( $apWindow , @ ( $null , [Windows.RoutedEventArgs] :: new ( ) ) ) | Out-Null
Assert-Ap ( $apWindow . FindName ( 'TextServerStatus' ) . Text -eq " AP 대기 (127.0.0.1: $ freePort) " ) 'AP must use the selected current IP and entered port'
Assert-Ap ( -not $mode . IsEnabled -and -not $ips . IsEnabled -and -not $port . IsEnabled ) 'Running server must lock mode/address/port'
$toggle . Invoke ( $apWindow , @ ( $null , [Windows.RoutedEventArgs] :: new ( ) ) ) | Out-Null
Assert-Ap ( $mode . IsEnabled -and $ips . IsEnabled -and $port . IsEnabled ) 'Stopped external AP must restore endpoint editing'
$mode . SelectedIndex = 2
Assert-Ap ( $externalGuide . Visibility -eq 'Collapsed' ) 'Hotspot must hide external router guide'
Assert-Ap ( $port . Text -eq '5000' -and -not $port . IsEnabled -and $controls . Visibility -eq 'Visible' ) 'AP controls/fixed port'
Assert-Ap ( $apWindow . FindName ( 'ComboIp' ) . SelectedItem -eq '192.168.4.1' ) 'AP fixed IP'
$mode . SelectedIndex = 0
Assert-Ap ( $port . Text -eq '6000' -and $port . IsEnabled -and $controls . Visibility -eq 'Collapsed' -and $externalGuide . Visibility -eq 'Collapsed' ) 'Direct restore'
} finally { $apWindow . Close ( ) }
Write-Output 'PASS: WPF external AP current IP/custom port/start/stop locks, fixed hotspot and Direct restore'
# Validate the actual helper with mocked network inventory: no UAC, NIC or
# firewall mutations are made by these checks.
$global:apVerificationAdapters = @ ( [pscustomobject] @ { Status = 'Up' ; InterfaceDescription = 'Microsoft Wi-Fi Direct Virtual Adapter' ; ifIndex = 10 } )
$global:apVerificationAddresses = @ ( [pscustomobject] @ { IPAddress = '192.168.137.1' ; InterfaceIndex = 10 ; PrefixLength = 24 } )
$global:apVerificationDhcp = 'Disabled'
function Get-NetAdapter { param ( [switch] $IncludeHidden ) $global:apVerificationAdapters }
function Get-NetIPAddress {
param ( $InterfaceIndex , $AddressFamily )
$global:apVerificationAddresses | Where-Object { -not $InterfaceIndex -or $_ . InterfaceIndex -eq $InterfaceIndex }
}
function Get-NetIPInterface { param ( $InterfaceIndex , $AddressFamily ) [pscustomobject] @ { Dhcp = $global:apVerificationDhcp } }
$apHelper = Join-Path $PSScriptRoot 'DCT_test/Configure-HotspotAp.ps1'
& $apHelper -ValidateOnly
function Assert-Rejected ( $message ) {
$rejected = $false
try { & $apHelper -ValidateOnly } catch { $rejected = $true }
Assert-Ap $rejected $message
}
$global:apVerificationAdapters [ 0 ] . InterfaceDescription = 'Intel Wi-Fi physical adapter'
Assert-Rejected 'Physical adapter must be rejected'
$global:apVerificationAdapters [ 0 ] . InterfaceDescription = 'Microsoft Wi-Fi Direct Virtual Adapter'
$global:apVerificationAdapters + = [pscustomobject] @ { Status = 'Up' ; InterfaceDescription = 'Microsoft Wi-Fi Direct Virtual Adapter #2' ; ifIndex = 11 }
$global:apVerificationAddresses + = [pscustomobject] @ { IPAddress = '192.168.137.1' ; InterfaceIndex = 11 ; PrefixLength = 24 }
Assert-Rejected 'Ambiguous hotspot adapters must be rejected'
$global:apVerificationAdapters = @ ( $global:apVerificationAdapters [ 0 ] )
$global:apVerificationAddresses = @ ( $global:apVerificationAddresses [ 0 ] , [pscustomobject] @ { IPAddress = '192.168.4.1' ; InterfaceIndex = 12 ; PrefixLength = 24 } )
Assert-Rejected 'Address owned by another adapter must be rejected'
$global:apVerificationAddresses = @ ( $global:apVerificationAddresses [ 0 ] )
$global:apVerificationDhcp = 'Enabled'
Assert-Rejected 'DHCP client adapter must be rejected'
$global:apVerificationDhcp = 'Disabled'
$global:apVerificationAddresses + = [pscustomobject] @ { IPAddress = '192.168.4.1' ; InterfaceIndex = 10 ; PrefixLength = 16 }
Assert-Rejected 'Wrong existing subnet prefix must be rejected'
$global:apVerificationAddresses [ 1 ] . PrefixLength = 24
& $apHelper -ValidateOnly
Write-Output 'PASS: hotspot helper valid/repeated configuration, physical/ambiguous adapters, conflicting address, DHCP and subnet rejection (no system changes)'