diff --git a/run-ui-tests.ps1 b/run-ui-tests.ps1 new file mode 100644 index 00000000..b1e52edc --- /dev/null +++ b/run-ui-tests.ps1 @@ -0,0 +1,131 @@ +[CmdletBinding()] +param( + [string]$ExecutablePath, + [string]$Filter = 'TestCategory=UI', + [string]$Logger = 'console;verbosity=normal', + [switch]$NoBuild, + [switch]$ListTests, + [switch]$ConfirmIsolatedProfile, + [string[]]$AdditionalDotNetArguments = @() +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +$repositoryRoot = $PSScriptRoot +$testProject = Join-Path $repositoryRoot 'tests\FileManager.UiTests\FileManager.UiTests.csproj' + +function Resolve-FileManagerExecutable { + param([string]$RequestedPath) + + $candidates = [System.Collections.Generic.List[string]]::new() + if (-not [string]::IsNullOrWhiteSpace($RequestedPath)) { + # An explicit path is authoritative so a typo cannot silently test a different checkout build. + if (-not (Test-Path -LiteralPath $RequestedPath -PathType Leaf)) { + throw "The requested FileManager executable was not found: $RequestedPath" + } + return (Resolve-Path -LiteralPath $RequestedPath).Path + } + if (-not [string]::IsNullOrWhiteSpace($env:FILEMANAGER_UI_EXE)) { + # Preserve an explicit caller selection before trying checkout-relative or installed locations. + $candidates.Add($env:FILEMANAGER_UI_EXE) + } + + # Repository-relative candidates make the runner portable across drive letters and checkout directories. + foreach ($relativePath in @( + 'src\vcxproj\salamander\Debug_x64\salamand.exe', + 'src\vcxproj\salamander\Release_x64\salamand.exe', + 'src\vcxproj\salamander\Debug_x86\salamand.exe', + 'src\vcxproj\salamander\Release_x86\salamand.exe' + )) { + $candidates.Add((Join-Path $repositoryRoot $relativePath)) + } + + foreach ($appPathKey in @( + 'HKCU:\Software\Microsoft\Windows\CurrentVersion\App Paths\salamand.exe', + 'HKLM:\Software\Microsoft\Windows\CurrentVersion\App Paths\salamand.exe', + 'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\App Paths\salamand.exe' + )) { + if (Test-Path -LiteralPath $appPathKey) { + # App Paths is the authoritative Windows registration when Salamander was installed outside this checkout. + $registeredPath = (Get-Item -LiteralPath $appPathKey).GetValue('') + if (-not [string]::IsNullOrWhiteSpace($registeredPath)) { + $candidates.Add($registeredPath) + } + } + } + + foreach ($candidate in $candidates) { + if (-not [string]::IsNullOrWhiteSpace($candidate) -and + (Test-Path -LiteralPath $candidate -PathType Leaf)) { + return (Resolve-Path -LiteralPath $candidate).Path + } + } + + throw 'salamand.exe was not found. Build Debug x64 or pass -ExecutablePath with the installed application path.' +} + +if (-not $ConfirmIsolatedProfile) { + # The application writes HKCU configuration, so require an explicit acknowledgement before enabling the test gate. + throw 'UI tests modify the current Windows profile. Re-run with -ConfirmIsolatedProfile under a dedicated disposable test account.' +} +if (-not (Test-Path -LiteralPath $testProject -PathType Leaf)) { + throw "The UI test project was not found: $testProject" +} +if ($null -eq (Get-Command dotnet -ErrorAction SilentlyContinue)) { + throw 'dotnet was not found on PATH.' +} + +$resolvedExecutable = Resolve-FileManagerExecutable $ExecutablePath +$previousIsolated = $env:FILEMANAGER_UI_ISOLATED +$previousExecutable = $env:FILEMANAGER_UI_EXE +$testExitCode = 1 + +try { + $env:FILEMANAGER_UI_ISOLATED = '1' + $env:FILEMANAGER_UI_EXE = $resolvedExecutable + + $arguments = [System.Collections.Generic.List[string]]::new() + $arguments.Add('test') + $arguments.Add($testProject) + if ($NoBuild) { + # A no-build rerun must also avoid restore, which would regenerate ignored NuGet obj metadata unnecessarily. + $arguments.Add('--no-build') + $arguments.Add('--no-restore') + } + if ($ListTests) { + $arguments.Add('--list-tests') + } + if (-not [string]::IsNullOrWhiteSpace($Filter)) { + $arguments.Add('--filter') + $arguments.Add($Filter) + } + if (-not [string]::IsNullOrWhiteSpace($Logger)) { + $arguments.Add('--logger') + $arguments.Add($Logger) + } + foreach ($argument in $AdditionalDotNetArguments) { + $arguments.Add($argument) + } + + Write-Host "Running FileManager UI tests" + Write-Host " Project: $testProject" + Write-Host " Executable: $resolvedExecutable" + Write-Host " Filter: $Filter" + + Push-Location $repositoryRoot + try { + & dotnet @($arguments.ToArray()) + $testExitCode = $LASTEXITCODE + } + finally { + Pop-Location + } +} +finally { + # Restore the caller's process environment so focused reruns do not affect later shell commands. + $env:FILEMANAGER_UI_ISOLATED = $previousIsolated + $env:FILEMANAGER_UI_EXE = $previousExecutable +} + +exit $testExitCode diff --git a/src/async_copy.cpp b/src/async_copy.cpp index 5e5595b6..428c1b4f 100644 --- a/src/async_copy.cpp +++ b/src/async_copy.cpp @@ -6273,6 +6273,7 @@ BOOL DoDeleteFile(HWND hProgressDlg, COperation* operation, const CQuadWord& siz DeleteFileWithVerifiedIdentity(name, operation->SourceIdentity, &err); } DELETE_READY: + ; // A label must own a statement even when identity rejection only skips deletion. } else { diff --git a/tests/FileManager.UiTests/README.md b/tests/FileManager.UiTests/README.md index 7debcd2e..daa35dfd 100644 --- a/tests/FileManager.UiTests/README.md +++ b/tests/FileManager.UiTests/README.md @@ -1,11 +1,13 @@ # FileManager UI tests -This project contains 100 basic, parameterized FlaUI/UIA3 NUnit cases plus focused file-operation characterization cases for the native FileManager UI. The cases cover application launch, accessibility-tree discovery, Configuration dialog cancel/commit/restart flows, a committed setting verified after restart, FTP bookmark creation plus edit verified after restart, and native disk create/copy/rename/move/delete/find/view/edit commands. +This project contains a seven-case parameterized FlaUI/UIA3 lifecycle group, together with focused file-operation, recovery, plug-in, toolbar, TLS, and native-safety characterization tests. The cases cover application launch, accessibility-tree discovery, Configuration dialog cancel/commit/restart flows, a committed setting verified after restart, FTP bookmark creation plus edit verified after restart, and native disk create/copy/rename/move/delete/find/view/edit commands. The tests intentionally refuse to run unless `FILEMANAGER_UI_ISOLATED=1` is set. The application persists configuration under the current user registry hive, so run them under a dedicated Windows test account or another isolated user profile. Set these environment variables before running: +The `run-ui-tests.ps1` entry point described below sets `FILEMANAGER_UI_ISOLATED` and `FILEMANAGER_UI_EXE` for its child test process. Configure the remaining variables only when their optional test lanes are required. When calling `dotnet test` directly, set the first two variables manually. + - `FILEMANAGER_UI_ISOLATED=1` — confirms that the current Windows profile is disposable. - `FILEMANAGER_UI_EXE` — absolute path to `salamand.exe` or a debug build of the executable. - `FILEMANAGER_UI_ARGUMENTS` — optional command-line arguments, for example a test-only `-c` configuration file. @@ -15,12 +17,22 @@ Set these environment variables before running: - `FILEMANAGER_UI_ADS_UNSUPPORTED_TARGET_ROOT` — an existing dedicated directory on a different volume that does not support alternate data streams (for example FAT/FAT32/exFAT). This enables the ADS metadata-loss decision scenario and the fixture creates and removes only a GUID-named child below this directory. - `FILEMANAGER_UI_RECYCLE_BIN=1` — explicitly enables the recycle-bin characterization test. It requires the default recycle-bin delete setting in the isolated profile and adds one disposable file to that profile's recycle bin. -Run the suite on an interactive Windows desktop session: +Run the interactive UI category on an interactive Windows desktop session. This is the runner default and currently selects approximately 60 cases: ```powershell -dotnet test tests/FileManager.UiTests/FileManager.UiTests.csproj --filter TestCategory=UI +.\run-ui-tests.ps1 -ConfirmIsolatedProfile ``` +Run the entire test project, currently 125 discovered cases, by explicitly clearing the default category filter: + +```powershell +.\run-ui-tests.ps1 -ConfirmIsolatedProfile -Filter '' +``` + +The complete run includes UI, native-safety, TLS, toolbar-contract, and other non-UI tests. Tests whose optional prerequisites are unavailable—such as the ZIP plug-in, deployed Help content, a second volume, fault injection, or Recycle Bin configuration—are reported as skipped rather than omitted from discovery. + +The runner derives paths from its repository location, prefers an existing checkout build, and otherwise checks the Windows `App Paths` registration for an installed `salamand.exe`. Use `-ExecutablePath 'C:\Program Files\Open Salamander\salamand.exe'` to select another installation, `-Filter 'Name~Copy_file'` for a focused run, or `-NoBuild` after the managed test project is already built. The confirmation switch is intentionally required because the application writes configuration under the current user profile. File-operation tests create their own disposable source and target directories; callers do not need to configure shared test folders. + The configuration dialog is opened through its stable native command ID only to avoid locale-dependent menu text. All window discovery, control inspection, focus, dialog lifecycle, and restart assertions use FlaUI/UIA3. File-operation cases create a fresh disposable directory tree under the system temporary directory for every test, start the left and right panels with `-l`/`-r`, and use the host's stable native command IDs. They verify files and nested directory trees after normal operations, copy and move overwrite/skip conflict choices, rename overwrite-decline and case-only behavior, mixed-selection deletion, continuation after a skipped delete error, file search, internal viewing, configured-editor launch, metadata preservation, cancellation after the worker has started, and destination/name failures. ADS cases create named, empty, large, and edge-named streams; exercise overwrite and retry after a temporarily denied stream; and verify cross-volume preservation or explicit source retention when an ADS-unsupported target reports metadata loss. The recovery fixture seeds an incomplete durable journal with a ready transactional sibling file before launch, then verifies the real startup reconciliation flow. The tests never use a caller-supplied directory as their mutation target; cross-volume cases use only a GUID child under the explicitly configured disposable root. diff --git a/tools/test-bzip2-compatibility.ps1 b/tools/test-bzip2-compatibility.ps1 index 2614b70f..6a5b3ae9 100644 --- a/tools/test-bzip2-compatibility.ps1 +++ b/tools/test-bzip2-compatibility.ps1 @@ -36,8 +36,9 @@ try { $probeExecutable = Join-Path $temporaryDirectory 'bzip2_compatibility_probe.exe' $compilerCommand = ( 'call "' + $vsDevCmd + '" -arch=' + $Architecture + ' -host_arch=x64 && cd /d "' + $temporaryDirectory + '" && ' + - # Upstream's portable integer aliases intentionally trigger these MSVC-only conversion diagnostics. - 'cl /nologo /TC /W4 /WX /wd4100 /wd4244 /wd4245 /DBZ_NO_STDIO /I"' + $bzip2Directory + '" ' + + # Upstream's portability and assertion macros intentionally trigger these + # MSVC-only constant-condition and integer-conversion diagnostics. + 'cl /nologo /TC /W4 /WX /wd4100 /wd4127 /wd4244 /wd4245 /DBZ_NO_STDIO /I"' + $bzip2Directory + '" ' + '/Fe"' + $probeExecutable + '" "' + $probeSource + '" ' + ($sourceFiles -join ' ') )