refactor: 公共面补 BakNRet 前缀,产品名大小写全仓统一
16 个没有前缀的公共函数补上 BakNRet(Write-Log → Write-BakNRetLog、Resolve-BackupEntry → Resolve-BakNRetBackupEntry、Find-ChildDirectoryByName → Find-BakNRetChildDirectoryByName 等), 另外把全仓的 Baknret 统一成 BakNRet(47 个文件、940 处、65 个定义文件重命名)。 这不是审美问题:静态分析直接拓出一条实据 —— Write-Log 与本机某个已装模块导出的命令 **重名**(PSAvoidOverwritingBuiltInCmdlets),而重名的后果是导入两个模块时有一方的命令被 静默遮蔽。补前缀正是这条规则的解法,改名后它归零。 为什么敢做这个规模:PowerShell 的函数名解析大小写不敏感,所以 Baknret → BakNRet 在功能 上是零风险;真正要验证的是 16 个补前缀的调用点,而 276 个断言几乎覆盖了每个函数。另外 "名字与文件名一致"这条不变式有断言盯着(加载器点源的文件集合 vs 磁盘)。 踩到并记下的坑:Windows 文件系统大小写不敏感,所以**只改大小写**的重命名会被 Move-Item 当成同一个文件而静默跳过 —— 同一批里同时改了名字的那 16 个文件却成功了,于是"看起来能跑"。 最后用"先移到临时名、再移到目标名"的两步走解决,判断与替换全部改用显式大小写敏感的形式 (-creplace / -cmatch)。 顺带把名录指纹缓存从 MD5 换成 SHA256(PSAvoidUsingBrokenHashAlgorithms):它只是缓存键, 没有兼容负担。 验收:test.ps1 9/9 全绿(7 与 5.1)、100 个文件两版解析零错、276 个断言全过、 构建工具仍能合回单文件(3300 行)。
This commit is contained in:
1 parent
187d2759fd
commit
42f02d0eca
84 files changed
+1043
-1043
No files matched your search
+166
-166
@@ -13,7 +13,7 @@
|
||||
* 软件名录新结构(`软件名 -> Slot -> @{ Path = ... }`):Slot 排序、前缀补全、
|
||||
多候选报错、`%VAR%` / `$( ... )` 展开、缓存与 -NoCache
|
||||
* 归档命名与逆向解析(软件名 / 字面路径 / @pathname)
|
||||
* 归档项与归档内布局(New-BaknretArchiveItem / 暂存 junction / 硬链接)
|
||||
* 归档项与归档内布局(New-BakNRetArchiveItem / 暂存 junction / 硬链接)
|
||||
* 7z 排除参数翻译(!前缀、!re: 正则展开、Slot 前缀分配、去重)
|
||||
* Windows 命令行拼接
|
||||
* manifest 读写往返与 layouts
|
||||
@@ -271,18 +271,18 @@ Test-Case '标记必须是独立记号:C:\a:-b 仍然是一个路径' {
|
||||
$r = ConvertFrom-BackupListLine -Line 'C:\a:-b'
|
||||
Assert-Equal 'C:\a:-b' $r.Path
|
||||
Assert-Equal 0 $r.Overrides.Keys.Count
|
||||
Assert-Null (Test-BaknretMarker -Token 'C:\a:-b')
|
||||
Assert-Null (Test-BaknretMarker -Token ':+X')
|
||||
Assert-Null (Test-BakNRetMarker -Token 'C:\a:-b')
|
||||
Assert-Null (Test-BakNRetMarker -Token ':+X')
|
||||
}
|
||||
|
||||
Test-Case 'Test-BaknretMarker 识别全部标记(大小写敏感)' {
|
||||
Assert-Equal 'path' (Test-BaknretMarker -Token '::')
|
||||
Assert-Equal 'exclude' (Test-BaknretMarker -Token ':-')
|
||||
Assert-Equal 'include' (Test-BaknretMarker -Token ':+')
|
||||
Assert-Equal 'encrypt' (Test-BaknretMarker -Token ':encrypt')
|
||||
Assert-Equal 'noencrypt' (Test-BaknretMarker -Token ':!encrypt')
|
||||
Assert-Equal 'at' (Test-BaknretMarker -Token '@x')
|
||||
Assert-Equal 'path' (Test-BaknretMarker -Token ' :: ')
|
||||
Test-Case 'Test-BakNRetMarker 识别全部标记(大小写敏感)' {
|
||||
Assert-Equal 'path' (Test-BakNRetMarker -Token '::')
|
||||
Assert-Equal 'exclude' (Test-BakNRetMarker -Token ':-')
|
||||
Assert-Equal 'include' (Test-BakNRetMarker -Token ':+')
|
||||
Assert-Equal 'encrypt' (Test-BakNRetMarker -Token ':encrypt')
|
||||
Assert-Equal 'noencrypt' (Test-BakNRetMarker -Token ':!encrypt')
|
||||
Assert-Equal 'at' (Test-BakNRetMarker -Token '@x')
|
||||
Assert-Equal 'path' (Test-BakNRetMarker -Token ' :: ')
|
||||
}
|
||||
|
||||
Test-Case '记号里有空格时目标可以带空格(第一个修饰符之前全是目标)' {
|
||||
@@ -308,8 +308,8 @@ Test-Case '没有说明时 Comment 为 $null' {
|
||||
Assert-Null (ConvertFrom-BackupListLine -Line 'App').Comment
|
||||
}
|
||||
|
||||
Test-Case 'Split-BaknretToken:引号内的空白不切分,引号保留' {
|
||||
$tokens = @(Split-BaknretToken -Text 'a "b c" d :- ''e,f''')
|
||||
Test-Case 'Split-BakNRetToken:引号内的空白不切分,引号保留' {
|
||||
$tokens = @(Split-BakNRetToken -Text 'a "b c" d :- ''e,f''')
|
||||
Assert-Equal 5 $tokens.Count
|
||||
Assert-Equal 'a' $tokens[0]
|
||||
Assert-Equal '"b c"' $tokens[1]
|
||||
@@ -318,19 +318,19 @@ Test-Case 'Split-BaknretToken:引号内的空白不切分,引号保留' {
|
||||
Assert-Equal "'e,f'" $tokens[4]
|
||||
}
|
||||
|
||||
Test-Case 'Remove-BaknretQuote:成对引号去掉,不成对原样返回' {
|
||||
Assert-Equal 'a' (Remove-BaknretQuote -Text "'a'")
|
||||
Assert-Equal 'a' (Remove-BaknretQuote -Text '"a"')
|
||||
Assert-Equal '"a' (Remove-BaknretQuote -Text '"a')
|
||||
Assert-Equal 'plain' (Remove-BaknretQuote -Text 'plain')
|
||||
Test-Case 'Remove-BakNRetQuote:成对引号去掉,不成对原样返回' {
|
||||
Assert-Equal 'a' (Remove-BakNRetQuote -Text "'a'")
|
||||
Assert-Equal 'a' (Remove-BakNRetQuote -Text '"a"')
|
||||
Assert-Equal '"a' (Remove-BakNRetQuote -Text '"a')
|
||||
Assert-Equal 'plain' (Remove-BakNRetQuote -Text 'plain')
|
||||
}
|
||||
|
||||
Test-Case 'Test-LiteralPath:带分隔符或 % 的按路径,其余按软件名' {
|
||||
Assert-True (Test-LiteralPath -Path 'C:\a')
|
||||
Assert-True (Test-LiteralPath -Path 'C:/a')
|
||||
Assert-True (Test-LiteralPath -Path '%UserProfile%\.ssh')
|
||||
Assert-False (Test-LiteralPath -Path 'MyApp')
|
||||
Assert-True (Test-LiteralPath -Path '')
|
||||
Test-Case 'Test-BakNRetLiteralPath:带分隔符或 % 的按路径,其余按软件名' {
|
||||
Assert-True (Test-BakNRetLiteralPath -Path 'C:\a')
|
||||
Assert-True (Test-BakNRetLiteralPath -Path 'C:/a')
|
||||
Assert-True (Test-BakNRetLiteralPath -Path '%UserProfile%\.ssh')
|
||||
Assert-False (Test-BakNRetLiteralPath -Path 'MyApp')
|
||||
Assert-True (Test-BakNRetLiteralPath -Path '')
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
@@ -338,19 +338,19 @@ Write-Host "`n== 归档命名 ==" -ForegroundColor Cyan
|
||||
# ============================================================================
|
||||
|
||||
Test-Case '基础命名规则' {
|
||||
Assert-Equal 'legendary_from_%UserProfile%+.config' (Get-BackupBaseName -RawPath '%UserProfile%/.config/legendary')
|
||||
Assert-Equal 'FooClolor_from_C_+Programs' (Get-BackupBaseName -RawPath 'C:\Programs\FooClolor')
|
||||
Assert-Equal 'settings.json_from_C_+Programs' (Get-BackupBaseName -RawPath 'C:\Programs\settings.json')
|
||||
Assert-Equal 'legendary_from_%UserProfile%+.config' (Get-BakNRetBackupBaseName -RawPath '%UserProfile%/.config/legendary')
|
||||
Assert-Equal 'FooClolor_from_C_+Programs' (Get-BakNRetBackupBaseName -RawPath 'C:\Programs\FooClolor')
|
||||
Assert-Equal 'settings.json_from_C_+Programs' (Get-BakNRetBackupBaseName -RawPath 'C:\Programs\settings.json')
|
||||
}
|
||||
|
||||
Test-Case '盘符冒号被归一化成下划线' {
|
||||
Assert-Equal 'a_from_C_' (Get-BackupBaseName -RawPath 'C:\a')
|
||||
Assert-Equal 'a_from_C_' (Get-BakNRetBackupBaseName -RawPath 'C:\a')
|
||||
}
|
||||
|
||||
Test-Case '/ 与 \ 以及重复分隔符结果一致' {
|
||||
$a = Get-BackupBaseName -RawPath '%UserProfile%/.config/scoop'
|
||||
$b = Get-BackupBaseName -RawPath '%UserProfile%\.config\scoop'
|
||||
$c = Get-BackupBaseName -RawPath '%UserProfile%\\Documents\\WindowsPowerShell'
|
||||
$a = Get-BakNRetBackupBaseName -RawPath '%UserProfile%/.config/scoop'
|
||||
$b = Get-BakNRetBackupBaseName -RawPath '%UserProfile%\.config\scoop'
|
||||
$c = Get-BakNRetBackupBaseName -RawPath '%UserProfile%\\Documents\\WindowsPowerShell'
|
||||
Assert-Equal $a $b
|
||||
Assert-Equal 'WindowsPowerShell_from_%UserProfile%+Documents' $c
|
||||
}
|
||||
@@ -362,26 +362,26 @@ Test-Case '命名 <-> 路径往返' {
|
||||
'%LocalAppData%\Microsoft\Edge\User Data'
|
||||
)
|
||||
foreach ($case in $cases) {
|
||||
$base = Get-BackupBaseName -RawPath $case
|
||||
$back = Convert-BackupFileNameToPath -FileName "$base.7z"
|
||||
$base = Get-BakNRetBackupBaseName -RawPath $case
|
||||
$back = Convert-BakNRetBackupFileNameToPath -FileName "$base.7z"
|
||||
Assert-Equal ($case -replace '[/\\]+', '\') $back "往返 $case"
|
||||
}
|
||||
}
|
||||
|
||||
Test-Case '归档名里不含非法文件名字符(保留 & % +)' {
|
||||
$base = Get-BackupBaseName -RawPath 'C:\a\b?c*d|e'
|
||||
$base = Get-BakNRetBackupBaseName -RawPath 'C:\a\b?c*d|e'
|
||||
$invalid = [System.IO.Path]::GetInvalidFileNameChars() | Where-Object { $_ -notin @('&', '%', '+') }
|
||||
foreach ($ch in $invalid) {
|
||||
Assert-False ($base.Contains([string]$ch)) "归档名里出现了非法字符 $ch"
|
||||
}
|
||||
Assert-Equal 'keep%and&and+plus' (Format-CatalogName -Name 'keep%and&and+plus')
|
||||
Assert-Equal 'keep%and&and+plus' (Format-BakNRetCatalogName -Name 'keep%and&and+plus')
|
||||
}
|
||||
|
||||
Test-Case 'Get-BaknretArchiveTopName:取归档内相对路径的第一段' {
|
||||
Assert-Equal 'Slot' (Get-BaknretArchiveTopName -ArchivePath 'Slot\a\b')
|
||||
Assert-Equal 'Slot' (Get-BaknretArchiveTopName -ArchivePath '\Slot\')
|
||||
Assert-Equal 'leaf' (Get-BaknretArchiveTopName -ArchivePath 'leaf')
|
||||
Assert-Equal '' (Get-BaknretArchiveTopName -ArchivePath ' ')
|
||||
Test-Case 'Get-BakNRetArchiveTopName:取归档内相对路径的第一段' {
|
||||
Assert-Equal 'Slot' (Get-BakNRetArchiveTopName -ArchivePath 'Slot\a\b')
|
||||
Assert-Equal 'Slot' (Get-BakNRetArchiveTopName -ArchivePath '\Slot\')
|
||||
Assert-Equal 'leaf' (Get-BakNRetArchiveTopName -ArchivePath 'leaf')
|
||||
Assert-Equal '' (Get-BakNRetArchiveTopName -ArchivePath ' ')
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
@@ -390,8 +390,8 @@ Write-Host "`n== 归档项构造与暂存 ==" -ForegroundColor Cyan
|
||||
|
||||
$itemSandbox = Join-Path $env:TEMP ("baknret-item-" + [guid]::NewGuid().ToString('N').Substring(0, 8))
|
||||
|
||||
Test-Case 'New-BaknretArchiveItem:归档内路径被清理,TopName 是第一段' {
|
||||
$item = New-BaknretArchiveItem -ArchivePath '\AlphaData\' -RealPath 'C:\x'
|
||||
Test-Case 'New-BakNRetArchiveItem:归档内路径被清理,TopName 是第一段' {
|
||||
$item = New-BakNRetArchiveItem -ArchivePath '\AlphaData\' -RealPath 'C:\x'
|
||||
Assert-Equal 'AlphaData' $item.ArchivePath
|
||||
Assert-Equal 'AlphaData' $item.TopName
|
||||
Assert-Equal 'slot' $item.Kind
|
||||
@@ -400,13 +400,13 @@ Test-Case 'New-BaknretArchiveItem:归档内路径被清理,TopName 是第一
|
||||
Assert-False $item.IsFile
|
||||
Assert-Equal 0 $item.Exclude.Count
|
||||
|
||||
$nested = New-BaknretArchiveItem -ArchivePath 'Alpha\sub' -RealPath 'C:\y' -Kind 'include' -Origin 'include'
|
||||
$nested = New-BakNRetArchiveItem -ArchivePath 'Alpha\sub' -RealPath 'C:\y' -Kind 'include' -Origin 'include'
|
||||
Assert-Equal 'Alpha' $nested.TopName
|
||||
Assert-Equal 'include' $nested.Kind
|
||||
}
|
||||
|
||||
Test-Case '归档项的属性集与契约一致(没有旧的 Sources / Dirs / Variants)' {
|
||||
$item = New-BaknretArchiveItem -ArchivePath 'A' -RealPath 'C:\a'
|
||||
$item = New-BakNRetArchiveItem -ArchivePath 'A' -RealPath 'C:\a'
|
||||
foreach ($expected in 'ArchivePath', 'TopName', 'RealPath', 'Kind', 'Slot', 'Description', 'Origin', 'Exists', 'IsFile', 'Exclude') {
|
||||
Assert-True ($item.PSObject.Properties.Name -contains $expected) "缺少属性 $expected"
|
||||
}
|
||||
@@ -428,13 +428,13 @@ Test-Case '暂存半途失败会自己清干净(不留指向真实数据的 ju
|
||||
$items = @(
|
||||
# 第一项会成功挂上,而且是**目录走 junction** —— 这正是危险的那个物件:它指向
|
||||
# 真实数据,而临时目录迟早会被某次 Remove-Item -Recurse 扫到。
|
||||
(New-BaknretArchiveItem -ArchivePath 'Good' -RealPath $realDir -Kind 'slot' -Slot 'Good' -Exists $true -IsFile $false)
|
||||
(New-BakNRetArchiveItem -ArchivePath 'Good' -RealPath $realDir -Kind 'slot' -Slot 'Good' -Exists $true -IsFile $false)
|
||||
# 第二项必然失败:源文件不存在,硬链接与复制都会失败
|
||||
(New-BaknretArchiveItem -ArchivePath 'Missing.txt' -RealPath (Join-Path $base 'does-not-exist.txt') -Kind 'slot' -Slot 'Missing' -Exists $false -IsFile $true)
|
||||
(New-BakNRetArchiveItem -ArchivePath 'Missing.txt' -RealPath (Join-Path $base 'does-not-exist.txt') -Kind 'slot' -Slot 'Missing' -Exists $false -IsFile $true)
|
||||
)
|
||||
|
||||
$threw = $false
|
||||
try { New-BaknretArchiveStaging -Items $items -Root (Join-Path $sandbox 'stage') | Out-Null } catch { $threw = $true }
|
||||
try { New-BakNRetArchiveStaging -Items $items -Root (Join-Path $sandbox 'stage') | Out-Null } catch { $threw = $true }
|
||||
Assert-True $threw '第二项应当抛错(源文件不存在,硬链接与复制都会失败)'
|
||||
|
||||
$left = @(Get-ChildItem -LiteralPath $sandbox -Recurse -Force -ErrorAction SilentlyContinue)
|
||||
@@ -445,18 +445,18 @@ Test-Case '暂存半途失败会自己清干净(不留指向真实数据的 ju
|
||||
Remove-Item -LiteralPath $base -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Test-Case 'New-BaknretArchiveStaging:目录走 junction、文件走硬链接,按归档内名字挂载' {
|
||||
Test-Case 'New-BakNRetArchiveStaging:目录走 junction、文件走硬链接,按归档内名字挂载' {
|
||||
$root = Join-Path $itemSandbox 'stage-src'
|
||||
New-Item -ItemType Directory -Path (Join-Path $root 'App') -Force | Out-Null
|
||||
Set-Content -Encoding UTF8 -LiteralPath (Join-Path $root 'App\f.txt') -Value 'x'
|
||||
Set-Content -Encoding UTF8 -LiteralPath (Join-Path $root 'settings.json') -Value '{}'
|
||||
|
||||
$items = @(
|
||||
(New-BaknretArchiveItem -ArchivePath 'AlphaData' -RealPath (Join-Path $root 'App') -Exists $true),
|
||||
(New-BaknretArchiveItem -ArchivePath 'BetaFile' -RealPath (Join-Path $root 'settings.json') -Exists $true -IsFile $true)
|
||||
(New-BakNRetArchiveItem -ArchivePath 'AlphaData' -RealPath (Join-Path $root 'App') -Exists $true),
|
||||
(New-BakNRetArchiveItem -ArchivePath 'BetaFile' -RealPath (Join-Path $root 'settings.json') -Exists $true -IsFile $true)
|
||||
)
|
||||
|
||||
$staging = New-BaknretArchiveStaging -Items $items -Root (Join-Path $itemSandbox 'stage')
|
||||
$staging = New-BakNRetArchiveStaging -Items $items -Root (Join-Path $itemSandbox 'stage')
|
||||
try {
|
||||
$dirLink = Get-Item -LiteralPath (Join-Path $staging 'AlphaData') -Force
|
||||
$fileLink = Get-Item -LiteralPath (Join-Path $staging 'BetaFile') -Force
|
||||
@@ -465,44 +465,44 @@ Test-Case 'New-BaknretArchiveStaging:目录走 junction、文件走硬链接
|
||||
Assert-True (Test-Path -LiteralPath (Join-Path $staging 'AlphaData\f.txt')) '应能穿过 junction 看到内容'
|
||||
}
|
||||
finally {
|
||||
Remove-BaknretArchiveStaging -Root $staging
|
||||
Remove-BakNRetArchiveStaging -Root $staging
|
||||
}
|
||||
|
||||
Assert-False (Test-Path -LiteralPath $staging) '暂存目录应被清理'
|
||||
Assert-True (Test-Path -LiteralPath (Join-Path $root 'App\f.txt')) '清理暂存绝不能删到真实数据'
|
||||
}
|
||||
|
||||
Test-Case 'New-BaknretJunction / Remove-BaknretJunction:只删连接点,不动目标' {
|
||||
Test-Case 'New-BakNRetJunction / Remove-BakNRetJunction:只删连接点,不动目标' {
|
||||
$target = Join-Path $itemSandbox 'j-target'
|
||||
$link = Join-Path $itemSandbox 'j-link'
|
||||
New-Item -ItemType Directory -Path $target -Force | Out-Null
|
||||
Set-Content -Encoding UTF8 -LiteralPath (Join-Path $target 'keep.txt') -Value 'keep'
|
||||
|
||||
New-BaknretJunction -Path $link -Target $target | Out-Null
|
||||
New-BakNRetJunction -Path $link -Target $target | Out-Null
|
||||
Assert-Equal 'Junction' (Get-Item -LiteralPath $link -Force).LinkType
|
||||
Remove-BaknretJunction -Path $link
|
||||
Remove-BakNRetJunction -Path $link
|
||||
Assert-False (Test-Path -LiteralPath $link)
|
||||
Assert-True (Test-Path -LiteralPath (Join-Path $target 'keep.txt')) '目标内容必须留着'
|
||||
|
||||
try {
|
||||
New-BaknretJunction -Path $link -Target $target | Out-Null
|
||||
Remove-BaknretJunction -Path $link
|
||||
New-BaknretJunction -Path $link -Target $target | Out-Null
|
||||
New-BakNRetJunction -Path $link -Target $target | Out-Null
|
||||
Remove-BakNRetJunction -Path $link
|
||||
New-BakNRetJunction -Path $link -Target $target | Out-Null
|
||||
Assert-True $true
|
||||
}
|
||||
finally {
|
||||
Remove-BaknretJunction -Path $link
|
||||
Remove-BakNRetJunction -Path $link
|
||||
}
|
||||
}
|
||||
|
||||
Test-Case 'New-BaknretJunction:目标已存在时明确抛错' {
|
||||
Test-Case 'New-BakNRetJunction:目标已存在时明确抛错' {
|
||||
$target = Join-Path $itemSandbox 'j2-target'
|
||||
$link = Join-Path $itemSandbox 'j2-link'
|
||||
New-Item -ItemType Directory -Path $target -Force | Out-Null
|
||||
New-Item -ItemType Directory -Path $link -Force | Out-Null
|
||||
|
||||
$threw = $false
|
||||
try { New-BaknretJunction -Path $link -Target $target | Out-Null } catch { $threw = $true }
|
||||
try { New-BakNRetJunction -Path $link -Target $target | Out-Null } catch { $threw = $true }
|
||||
Assert-True $threw '同名实体存在时必须抛错,不能静默替换'
|
||||
}
|
||||
|
||||
@@ -515,35 +515,35 @@ Test-Case '旧的 Get-ArchiveExcludeArgument 已经不存在(契约破坏点
|
||||
}
|
||||
|
||||
Test-Case '相对模式自动补上归档项名(锚定在归档根)' {
|
||||
$item = New-BaknretArchiveItem -ArchivePath 'March7thAssistant' -RealPath 'C:\x'
|
||||
$exclude = Get-BaknretExcludeArgument -Item $item -Patterns @('logs\')
|
||||
$item = New-BakNRetArchiveItem -ArchivePath 'March7thAssistant' -RealPath 'C:\x'
|
||||
$exclude = Get-BakNRetExcludeArgument -Item $item -Patterns @('logs\')
|
||||
Assert-Equal 1 $exclude.Arguments.Count
|
||||
Assert-Equal '-x!March7thAssistant\logs' $exclude.Arguments[0]
|
||||
Assert-Null $exclude.Error
|
||||
}
|
||||
|
||||
Test-Case '模式里再写一遍归档项名不会被当成前缀剥掉(新语义:总是拼在项名之后)' {
|
||||
$item = New-BaknretArchiveItem -ArchivePath 'March7thAssistant' -RealPath 'C:\x'
|
||||
$exclude = Get-BaknretExcludeArgument -Item $item -Patterns @('March7thAssistant\logs\')
|
||||
$item = New-BakNRetArchiveItem -ArchivePath 'March7thAssistant' -RealPath 'C:\x'
|
||||
$exclude = Get-BakNRetExcludeArgument -Item $item -Patterns @('March7thAssistant\logs\')
|
||||
Assert-Equal '-x!March7thAssistant\March7thAssistant\logs' $exclude.Arguments[0]
|
||||
}
|
||||
|
||||
Test-Case '! 前缀翻译成任意层级匹配' {
|
||||
$item = New-BaknretArchiveItem -ArchivePath 'UserData' -RealPath 'C:\x'
|
||||
$exclude = Get-BaknretExcludeArgument -Item $item -Patterns @('!*Cache')
|
||||
$item = New-BakNRetArchiveItem -ArchivePath 'UserData' -RealPath 'C:\x'
|
||||
$exclude = Get-BakNRetExcludeArgument -Item $item -Patterns @('!*Cache')
|
||||
Assert-Equal '-xr!*Cache' $exclude.Arguments[0]
|
||||
}
|
||||
|
||||
Test-Case '模式里的空格转成 ? (7z 的模式不支持空格)' {
|
||||
$item = New-BaknretArchiveItem -ArchivePath 'UserData' -RealPath 'C:\x'
|
||||
$exclude = Get-BaknretExcludeArgument -Item $item -Patterns @('Default\Code Cache')
|
||||
$item = New-BakNRetArchiveItem -ArchivePath 'UserData' -RealPath 'C:\x'
|
||||
$exclude = Get-BakNRetExcludeArgument -Item $item -Patterns @('Default\Code Cache')
|
||||
Assert-Equal '-x!UserData\Default\Code?Cache' $exclude.Arguments[0]
|
||||
}
|
||||
|
||||
Test-Case '生成的参数里绝不出现引号(旧实现 -x!"路径" 让排除全部失效)' {
|
||||
$item = New-BaknretArchiveItem -ArchivePath 'UserData' -RealPath 'C:\x'
|
||||
$item = New-BakNRetArchiveItem -ArchivePath 'UserData' -RealPath 'C:\x'
|
||||
$patterns = @('Default\Code Cache', '!*Cache', 'logs\', 'UserData\3rdparty\WebBrowser\UserProfile\Integrated')
|
||||
$exclude = Get-BaknretExcludeArgument -Item $item -Patterns $patterns
|
||||
$exclude = Get-BakNRetExcludeArgument -Item $item -Patterns $patterns
|
||||
Assert-Equal 4 $exclude.Arguments.Count
|
||||
foreach ($argument in $exclude.Arguments) {
|
||||
Assert-False ($argument.Contains('"')) "参数里出现了引号: $argument"
|
||||
@@ -551,8 +551,8 @@ Test-Case '生成的参数里绝不出现引号(旧实现 -x!"路径" 让排
|
||||
}
|
||||
|
||||
Test-Case '空模式被忽略' {
|
||||
$item = New-BaknretArchiveItem -ArchivePath 'x' -RealPath 'C:\x'
|
||||
$exclude = Get-BaknretExcludeArgument -Item $item -Patterns @('', ' ', 'real\')
|
||||
$item = New-BakNRetArchiveItem -ArchivePath 'x' -RealPath 'C:\x'
|
||||
$exclude = Get-BakNRetExcludeArgument -Item $item -Patterns @('', ' ', 'real\')
|
||||
Assert-Equal 1 $exclude.Arguments.Count
|
||||
Assert-Equal '-x!x\real' $exclude.Arguments[0]
|
||||
}
|
||||
@@ -562,9 +562,9 @@ Test-Case '!re:<正则> 展开成源目录里命中的精确路径' {
|
||||
foreach ($dir in 'Cache1', 'Cache2', 'Keep', 'sub\Cache3') {
|
||||
New-Item -ItemType Directory -Path (Join-Path $root $dir) -Force | Out-Null
|
||||
}
|
||||
$item = New-BaknretArchiveItem -ArchivePath 'Data' -RealPath $root
|
||||
$item = New-BakNRetArchiveItem -ArchivePath 'Data' -RealPath $root
|
||||
|
||||
$exclude = Get-BaknretExcludeArgument -Item $item -Patterns @('!re:^Cache\d+$')
|
||||
$exclude = Get-BakNRetExcludeArgument -Item $item -Patterns @('!re:^Cache\d+$')
|
||||
Assert-Null $exclude.Error
|
||||
$sorted = @($exclude.Arguments | Sort-Object)
|
||||
Assert-Equal 3 $sorted.Count
|
||||
@@ -575,34 +575,34 @@ Test-Case '!re:<正则> 展开成源目录里命中的精确路径' {
|
||||
|
||||
Test-Case '非法的 !re: 正则给出明确的 Error,而不是静默漏排除' {
|
||||
$root = Join-Path $itemSandbox 'regex-src'
|
||||
$item = New-BaknretArchiveItem -ArchivePath 'Data' -RealPath $root
|
||||
$exclude = Get-BaknretExcludeArgument -Item $item -Patterns @('!re:*Cache')
|
||||
$item = New-BakNRetArchiveItem -ArchivePath 'Data' -RealPath $root
|
||||
$exclude = Get-BakNRetExcludeArgument -Item $item -Patterns @('!re:*Cache')
|
||||
Assert-True ($null -ne $exclude.Error)
|
||||
Assert-Equal 0 $exclude.Arguments.Count
|
||||
}
|
||||
|
||||
Test-Case '空的 !re: 被忽略' {
|
||||
$root = Join-Path $itemSandbox 'regex-src'
|
||||
$item = New-BaknretArchiveItem -ArchivePath 'Data' -RealPath $root
|
||||
$exclude = Get-BaknretExcludeArgument -Item $item -Patterns @('!re:')
|
||||
$item = New-BakNRetArchiveItem -ArchivePath 'Data' -RealPath $root
|
||||
$exclude = Get-BakNRetExcludeArgument -Item $item -Patterns @('!re:')
|
||||
Assert-Equal 0 $exclude.Arguments.Count
|
||||
Assert-Null $exclude.Error
|
||||
}
|
||||
|
||||
Test-Case '排除参数超过命令行安全长度时明确报错' {
|
||||
$root = Join-Path $itemSandbox 'regex-src'
|
||||
$item = New-BaknretArchiveItem -ArchivePath 'Data' -RealPath $root
|
||||
$exclude = Get-BaknretExcludeArgument -Item $item -Patterns @('aaaa\bbbb') -MaxCommandLineChars 5
|
||||
$item = New-BakNRetArchiveItem -ArchivePath 'Data' -RealPath $root
|
||||
$exclude = Get-BakNRetExcludeArgument -Item $item -Patterns @('aaaa\bbbb') -MaxCommandLineChars 5
|
||||
Assert-True ($null -ne $exclude.Error)
|
||||
}
|
||||
|
||||
Test-Case 'Split-BaknretPatternScope:`<项名>\` 前缀把模式分配给对应归档项' {
|
||||
Test-Case 'Split-BakNRetPatternScope:`<项名>\` 前缀把模式分配给对应归档项' {
|
||||
$items = @(
|
||||
(New-BaknretArchiveItem -ArchivePath 'DefaultConfig' -RealPath 'C:\c'),
|
||||
(New-BaknretArchiveItem -ArchivePath 'GlobalPersist' -RealPath 'C:\g'),
|
||||
(New-BaknretArchiveItem -ArchivePath 'UserPersist' -RealPath 'C:\u')
|
||||
(New-BakNRetArchiveItem -ArchivePath 'DefaultConfig' -RealPath 'C:\c'),
|
||||
(New-BakNRetArchiveItem -ArchivePath 'GlobalPersist' -RealPath 'C:\g'),
|
||||
(New-BakNRetArchiveItem -ArchivePath 'UserPersist' -RealPath 'C:\u')
|
||||
)
|
||||
$map = Split-BaknretPatternScope -Items $items -Patterns @('GlobalPersist\steam\steamapps', 'plain\one', '!*Cache')
|
||||
$map = Split-BakNRetPatternScope -Items $items -Patterns @('GlobalPersist\steam\steamapps', 'plain\one', '!*Cache')
|
||||
|
||||
Assert-Equal 'steam\steamapps' $map[1][0]
|
||||
Assert-Equal 'plain\one' $map[0][0] '没点名任何项的模式对每一项各展开一份'
|
||||
@@ -612,8 +612,8 @@ Test-Case 'Split-BaknretPatternScope:`<项名>\` 前缀把模式分配给对
|
||||
}
|
||||
}
|
||||
|
||||
Test-Case 'Merge-BaknretExcludeArgument:去重且保序' {
|
||||
$merged = Merge-BaknretExcludeArgument -ArgumentLists @(@('-x!a', '-xr!c'), @('-x!a', '-x!b'))
|
||||
Test-Case 'Merge-BakNRetExcludeArgument:去重且保序' {
|
||||
$merged = Merge-BakNRetExcludeArgument -ArgumentLists @(@('-x!a', '-xr!c'), @('-x!a', '-x!b'))
|
||||
Assert-Equal 3 $merged.Count
|
||||
Assert-Equal '-x!a' $merged[0]
|
||||
Assert-Equal '-xr!c' $merged[1]
|
||||
@@ -625,23 +625,23 @@ Write-Host "`n== 命令行拼接 ==" -ForegroundColor Cyan
|
||||
# ============================================================================
|
||||
|
||||
Test-Case '无空格参数原样输出' {
|
||||
Assert-Equal 'a -t7z' (ConvertTo-NativeArgumentString -ArgumentList @('a', '-t7z'))
|
||||
Assert-Equal 'a -t7z' (ConvertTo-BakNRetNativeArgumentString -ArgumentList @('a', '-t7z'))
|
||||
}
|
||||
|
||||
Test-Case '含空格参数加引号' {
|
||||
Assert-Equal '"C:\Program Files\x"' (ConvertTo-NativeArgumentString -ArgumentList @('C:\Program Files\x'))
|
||||
Assert-Equal '"C:\Program Files\x"' (ConvertTo-BakNRetNativeArgumentString -ArgumentList @('C:\Program Files\x'))
|
||||
}
|
||||
|
||||
Test-Case '引号内的结尾反斜杠翻倍(否则会被当成转义引号)' {
|
||||
Assert-Equal '"C:\Program Files\\"' (ConvertTo-NativeArgumentString -ArgumentList @('C:\Program Files\'))
|
||||
Assert-Equal '"C:\Program Files\\"' (ConvertTo-BakNRetNativeArgumentString -ArgumentList @('C:\Program Files\'))
|
||||
}
|
||||
|
||||
Test-Case '内部引号被转义' {
|
||||
Assert-Equal '"say \"hi\""' (ConvertTo-NativeArgumentString -ArgumentList @('say "hi"'))
|
||||
Assert-Equal '"say \"hi\""' (ConvertTo-BakNRetNativeArgumentString -ArgumentList @('say "hi"'))
|
||||
}
|
||||
|
||||
Test-Case '空参数输出一对空引号' {
|
||||
Assert-Equal '""' (ConvertTo-NativeArgumentString -ArgumentList @(''))
|
||||
Assert-Equal '""' (ConvertTo-BakNRetNativeArgumentString -ArgumentList @(''))
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
@@ -654,7 +654,7 @@ Test-Case 'manifest 读写往返(含 layouts)' {
|
||||
New-Item -ItemType Directory -Path $sandbox -Force | Out-Null
|
||||
$path = Join-Path $sandbox 'manifest.json'
|
||||
|
||||
$m = Read-BaknretManifest -Path $path
|
||||
$m = Read-BakNRetManifest -Path $path
|
||||
Assert-Equal 0 $m.items.Count
|
||||
|
||||
$m.compressor = [pscustomobject]@{ name = '7z'; version = '26.03' }
|
||||
@@ -667,9 +667,9 @@ Test-Case 'manifest 读写往返(含 layouts)' {
|
||||
[ordered]@{ name = 'BetaFile'; kind = 'file' }
|
||||
)
|
||||
}
|
||||
Write-BaknretManifest -Path $path -Manifest $m | Out-Null
|
||||
Write-BakNRetManifest -Path $path -Manifest $m | Out-Null
|
||||
|
||||
$again = Read-BaknretManifest -Path $path
|
||||
$again = Read-BakNRetManifest -Path $path
|
||||
Assert-Equal 1 $again.items.Count
|
||||
Assert-Equal 'backed-up' $again.items['my-app'].action
|
||||
Assert-Equal 2 $again.items['my-app'].layouts.Count
|
||||
@@ -683,12 +683,12 @@ Test-Case 'manifest 读写往返(含 layouts)' {
|
||||
Test-Case 'manifest 损坏时不抛异常' {
|
||||
$path = Join-Path $sandbox 'broken.json'
|
||||
[System.IO.File]::WriteAllText($path, '{ this is not json')
|
||||
$m = Read-BaknretManifest -Path $path
|
||||
$m = Read-BakNRetManifest -Path $path
|
||||
Assert-Equal 0 $m.items.Count
|
||||
}
|
||||
|
||||
Test-Case '配置缺失时返回默认值' {
|
||||
$config = Get-BaknretConfig -Path (Join-Path $sandbox 'nope.psd1')
|
||||
$config = Get-BakNRetConfig -Path (Join-Path $sandbox 'nope.psd1')
|
||||
Assert-Equal 'Backups' $config.BackupDir
|
||||
Assert-True ($config.MinFreeSpaceGB -gt 0)
|
||||
}
|
||||
@@ -696,7 +696,7 @@ Test-Case '配置缺失时返回默认值' {
|
||||
Test-Case '配置嵌套段落合并且不丢默认键' {
|
||||
$path = Join-Path $sandbox 'cfg.psd1'
|
||||
Set-Content -Encoding UTF8 -LiteralPath $path -Value "@{ Encryption = @{ Enabled = `$true }; CompressionLevel = 5 }"
|
||||
$config = Get-BaknretConfig -Path $path
|
||||
$config = Get-BakNRetConfig -Path $path
|
||||
Assert-Equal 5 $config.CompressionLevel
|
||||
Assert-True $config.Encryption.Enabled
|
||||
Assert-True $config.Encryption.EncryptHeaders '未覆盖的嵌套键应保留默认值'
|
||||
@@ -706,9 +706,9 @@ Test-Case '口令:环境变量可读取,取不到返回 $null' {
|
||||
$old = $env:BAKNRET_PASSWORD
|
||||
try {
|
||||
Remove-Item Env:BAKNRET_PASSWORD -ErrorAction SilentlyContinue
|
||||
Assert-Null (Get-BaknretPassword -PasswordFile (Join-Path $sandbox 'nope'))
|
||||
Assert-Null (Get-BakNRetPassword -PasswordFile (Join-Path $sandbox 'nope'))
|
||||
$env:BAKNRET_PASSWORD = 'from-env'
|
||||
Assert-Equal 'from-env' (Get-BaknretPassword)
|
||||
Assert-Equal 'from-env' (Get-BakNRetPassword)
|
||||
}
|
||||
finally {
|
||||
Remove-Item Env:BAKNRET_PASSWORD -ErrorAction SilentlyContinue
|
||||
@@ -731,7 +731,7 @@ Test-Case '名录解析:裸键、引号键、带 - 与 . 的名字' {
|
||||
$content = "@{`n FooClolor = @{ DefaultData = @{ Path = '$appDir' } }`n 'scoop-config' = @{ DefaultConfig = @{ Path = '$configDir' } }`n '.ssh' = @{ DefaultData = @{ Path = '$appDir' } }`n}`n"
|
||||
[System.IO.File]::WriteAllText($catPath, $content, [System.Text.UTF8Encoding]::new($true))
|
||||
|
||||
$catalog = Get-SoftwareCatalog -Path $catPath -NoCache
|
||||
$catalog = Get-BakNRetSoftwareCatalog -Path $catPath -NoCache
|
||||
Assert-Equal 3 $catalog.Count
|
||||
Assert-True $catalog.ContainsKey('FooClolor')
|
||||
Assert-True $catalog.ContainsKey('scoop-config') '带连字符的键必须能解析'
|
||||
@@ -743,7 +743,7 @@ Test-Case '名录:Slot 按名字排序(哈希表顺序不可依赖)' {
|
||||
$catPath = Join-Path $catalogSandbox 'sorted.psd1'
|
||||
[System.IO.File]::WriteAllText($catPath, "@{`n FooClolor = @{ Zeta = @{ Path = '$appDir' }; Alpha = @{ Path = '$configDir' } }`n}`n", [System.Text.UTF8Encoding]::new($true))
|
||||
|
||||
$catalog = Get-SoftwareCatalog -Path $catPath -NoCache
|
||||
$catalog = Get-BakNRetSoftwareCatalog -Path $catPath -NoCache
|
||||
Assert-Equal 'Multi' $catalog['FooClolor'].Kind
|
||||
Assert-Equal 2 $catalog['FooClolor'].Slots.Count
|
||||
Assert-Equal 'Alpha' $catalog['FooClolor'].Slots[0].Name
|
||||
@@ -756,7 +756,7 @@ Test-Case '名录:Slot 对象字段与契约一致' {
|
||||
Set-Content -Encoding UTF8 -LiteralPath $filePath -Value '{}'
|
||||
[System.IO.File]::WriteAllText($catPath, "@{`n Rich = @{`n Beta = @{ Path = '$filePath'; Encrypt = `$true; Description = 'beta 说明' }`n Alpha = @{ Path = '$configDir'; Exclude = '!*Cache,sub\one'; Include = 'Modules:$appDir' }`n }`n}`n", [System.Text.UTF8Encoding]::new($true))
|
||||
|
||||
$catalog = Get-SoftwareCatalog -Path $catPath -NoCache
|
||||
$catalog = Get-BakNRetSoftwareCatalog -Path $catPath -NoCache
|
||||
$entry = $catalog['Rich']
|
||||
foreach ($expected in 'Name', 'Path', 'Description', 'Slots', 'Kind', 'Error', 'Missing', 'Raw') {
|
||||
Assert-True ($entry.PSObject.Properties.Name -contains $expected) "名录条目缺少 $expected"
|
||||
@@ -789,7 +789,7 @@ Test-Case '名录解析:目录带版本后缀时按前缀补全(legendary_2.
|
||||
$catPath = Join-Path $catalogSandbox 'suffix.psd1'
|
||||
[System.IO.File]::WriteAllText($catPath, "@{`n legendary = @{ DefaultConfig = @{ Path = '$root\legendary' } }`n}`n", [System.Text.UTF8Encoding]::new($true))
|
||||
|
||||
$catalog = Get-SoftwareCatalog -Path $catPath -NoCache
|
||||
$catalog = Get-BakNRetSoftwareCatalog -Path $catPath -NoCache
|
||||
Assert-Equal 'Single' $catalog['legendary'].Kind
|
||||
Assert-True $catalog['legendary'].Slots[0].Resolved -like '*legendary_2.0.4'
|
||||
Assert-True $catalog['legendary'].Slots[0].Suffixed '补全出来的路径要标记 Suffixed'
|
||||
@@ -801,20 +801,20 @@ Test-Case '名录解析:不会把 Legendary 误配成 LegendarySomething' {
|
||||
$catPath = Join-Path $catalogSandbox 'strict.psd1'
|
||||
[System.IO.File]::WriteAllText($catPath, "@{`n Legendary = @{ DefaultConfig = @{ Path = '$root\Legendary' } }`n}`n", [System.Text.UTF8Encoding]::new($true))
|
||||
|
||||
$catalog = Get-SoftwareCatalog -Path $catPath -NoCache
|
||||
$catalog = Get-BakNRetSoftwareCatalog -Path $catPath -NoCache
|
||||
Assert-Equal 'Unresolved' $catalog['Legendary'].Kind '必须以下一个字符是 _ 或 - 为界'
|
||||
Assert-False $catalog['Legendary'].Slots[0].Exists
|
||||
Assert-Equal 1 $catalog['Legendary'].Missing.Count
|
||||
}
|
||||
|
||||
Test-Case 'Find-ChildDirectoryByName:_ / - 边界之外不补全' {
|
||||
Test-Case 'Find-BakNRetChildDirectoryByName:_ / - 边界之外不补全' {
|
||||
$root = Join-Path $catalogSandbox 'boundary'
|
||||
New-Item -ItemType Directory -Path (Join-Path $root 'Legendary_1.0') -Force | Out-Null
|
||||
New-Item -ItemType Directory -Path (Join-Path $root 'Legendary-2.0') -Force | Out-Null
|
||||
New-Item -ItemType Directory -Path (Join-Path $root 'LegendarySomething') -Force | Out-Null
|
||||
New-Item -ItemType Directory -Path (Join-Path $root 'Unrelated') -Force | Out-Null
|
||||
|
||||
$found = @(Find-ChildDirectoryByName -Parent $root -Name 'Legendary')
|
||||
$found = @(Find-BakNRetChildDirectoryByName -Parent $root -Name 'Legendary')
|
||||
Assert-Equal 2 $found.Count
|
||||
Assert-True (@($found | Where-Object { $_ -like '*Legendary_1.0' }).Count -eq 1)
|
||||
Assert-True (@($found | Where-Object { $_ -like '*Legendary-2.0' }).Count -eq 1)
|
||||
@@ -827,7 +827,7 @@ Test-Case '名录:一个 Slot 补全出多个目录时明确报错(否则会
|
||||
$catPath = Join-Path $catalogSandbox 'multicand.psd1'
|
||||
[System.IO.File]::WriteAllText($catPath, "@{`n Legendary = @{ DefaultConfig = @{ Path = '$root\Legendary' } }`n}`n", [System.Text.UTF8Encoding]::new($true))
|
||||
|
||||
$catalog = Get-SoftwareCatalog -Path $catPath -NoCache
|
||||
$catalog = Get-BakNRetSoftwareCatalog -Path $catPath -NoCache
|
||||
$entry = $catalog['Legendary']
|
||||
Assert-True ($null -ne $entry.Error)
|
||||
Assert-True ($entry.Error -like '*拆成多个 Slot*') '错误里要给出可操作的指引'
|
||||
@@ -836,7 +836,7 @@ Test-Case '名录:一个 Slot 补全出多个目录时明确报错(否则会
|
||||
Test-Case '名录:Slot 写法不对 / 缺少 Path 都记成 Error,而不是崩掉' {
|
||||
$bad = Join-Path $catalogSandbox 'bad.psd1'
|
||||
[System.IO.File]::WriteAllText($bad, "@{`n A = @{ S = 'C:\x' }`n B = @{ S = @{ Description = 'x' } }`n}`n", [System.Text.UTF8Encoding]::new($true))
|
||||
$catalog = Get-SoftwareCatalog -Path $bad -NoCache
|
||||
$catalog = Get-BakNRetSoftwareCatalog -Path $bad -NoCache
|
||||
Assert-Equal 'Invalid' $catalog['A'].Kind
|
||||
Assert-True ($catalog['A'].Error -like '*Slot S*')
|
||||
Assert-Equal 'Invalid' $catalog['B'].Kind
|
||||
@@ -846,12 +846,12 @@ Test-Case '名录:Slot 写法不对 / 缺少 Path 都记成 Error,而不是
|
||||
Test-Case '名录:裸字符串 / 数组等旧结构会被 ERROR 跳过(不再当目录用)' {
|
||||
$legacy = Join-Path $catalogSandbox 'legacy.psd1'
|
||||
[System.IO.File]::WriteAllText($legacy, "@{`n A = 'C:\Programs\A'`n B = @('C:\1','C:\2')`n}`n", [System.Text.UTF8Encoding]::new($true))
|
||||
$catalog = Get-SoftwareCatalog -Path $legacy -NoCache
|
||||
$catalog = Get-BakNRetSoftwareCatalog -Path $legacy -NoCache
|
||||
Assert-Equal 0 $catalog.Count '旧结构必须被跳过,不能猜成目录'
|
||||
|
||||
$dirs = Join-Path $catalogSandbox 'dirs.psd1'
|
||||
[System.IO.File]::WriteAllText($dirs, "@{`n C = @{ Dirs = @('C:\1','C:\2') }`n}`n", [System.Text.UTF8Encoding]::new($true))
|
||||
$catalog = Get-SoftwareCatalog -Path $dirs -NoCache
|
||||
$catalog = Get-BakNRetSoftwareCatalog -Path $dirs -NoCache
|
||||
Assert-Equal 1 $catalog.Count
|
||||
Assert-Equal 'Invalid' $catalog['C'].Kind
|
||||
Assert-True ($catalog['C'].Error -like '*Dirs*')
|
||||
@@ -869,7 +869,7 @@ Test-Case '名录:Includes 分文件维护会被合并' {
|
||||
[System.IO.File]::WriteAllText($extraPath, "@{`n 'extra-app' = @{ D = @{ Path = '$appDir' } }`n}`n", [System.Text.UTF8Encoding]::new($true))
|
||||
[System.IO.File]::WriteAllText($basePath, "@{`n Includes = @('Extra.psd1')`n 'base-app' = @{ D = @{ Path = '$configDir' } }`n}`n", [System.Text.UTF8Encoding]::new($true))
|
||||
|
||||
$catalog = Get-SoftwareCatalog -Path $basePath -NoCache
|
||||
$catalog = Get-BakNRetSoftwareCatalog -Path $basePath -NoCache
|
||||
Assert-True $catalog.ContainsKey('base-app')
|
||||
Assert-True $catalog.ContainsKey('extra-app') 'Includes 引入的条目也应在'
|
||||
}
|
||||
@@ -878,20 +878,20 @@ Test-Case '名录:结果按文件指纹缓存,-NoCache 强制重读' {
|
||||
$catPath = Join-Path $catalogSandbox 'cache.psd1'
|
||||
[System.IO.File]::WriteAllText($catPath, "@{`n Cached = @{ D = @{ Path = '$appDir' } }`n}`n", [System.Text.UTF8Encoding]::new($true))
|
||||
|
||||
$first = Get-SoftwareCatalog -Path $catPath
|
||||
$second = Get-SoftwareCatalog -Path $catPath
|
||||
$first = Get-BakNRetSoftwareCatalog -Path $catPath
|
||||
$second = Get-BakNRetSoftwareCatalog -Path $catPath
|
||||
Assert-True ([object]::ReferenceEquals($first, $second)) '同一文件同一指纹应命中缓存'
|
||||
|
||||
$fresh = Get-SoftwareCatalog -Path $catPath -NoCache
|
||||
$fresh = Get-BakNRetSoftwareCatalog -Path $catPath -NoCache
|
||||
Assert-False ([object]::ReferenceEquals($first, $fresh)) '-NoCache 必须重新读'
|
||||
|
||||
[System.IO.File]::WriteAllText($catPath, "@{`n Cached = @{ D = @{ Path = '$configDir' } }`n}`n", [System.Text.UTF8Encoding]::new($true))
|
||||
$changed = Get-SoftwareCatalog -Path $catPath
|
||||
$changed = Get-BakNRetSoftwareCatalog -Path $catPath
|
||||
Assert-Equal $configDir $changed['Cached'].Slots[0].Resolved '文件变了缓存必须失效'
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
Write-Host "`n== Resolve-BackupEntry(归档项解析) ==" -ForegroundColor Cyan
|
||||
Write-Host "`n== Resolve-BakNRetBackupEntry(归档项解析) ==" -ForegroundColor Cyan
|
||||
# ============================================================================
|
||||
|
||||
$resolveSandbox = Join-Path $env:TEMP ("baknret-resolve-" + [guid]::NewGuid().ToString('N').Substring(0, 8))
|
||||
@@ -909,7 +909,7 @@ $resolveCatalog = Join-Path $resolveSandbox 'catalog.psd1'
|
||||
|
||||
Test-Case '软件名条目:一个 Slot -> 一个归档项,路径是 Slot 名,归档名是软件名' {
|
||||
$item = ConvertFrom-BackupListLine -Line 'FooClolor'
|
||||
$resolved = Resolve-BackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
$resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
|
||||
Assert-True $resolved.IsName
|
||||
Assert-Equal 'name' $resolved.ArchiveFlavor
|
||||
@@ -925,9 +925,9 @@ Test-Case '软件名条目:一个 Slot -> 一个归档项,路径是 Slot 名
|
||||
Assert-True $resolved.Items[0].Exists
|
||||
}
|
||||
|
||||
Test-Case 'Resolve-BackupEntry 的返回值里没有旧的 Sources / RootName 等属性' {
|
||||
Test-Case 'Resolve-BakNRetBackupEntry 的返回值里没有旧的 Sources / RootName 等属性' {
|
||||
$item = ConvertFrom-BackupListLine -Line 'FooClolor'
|
||||
$resolved = Resolve-BackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
$resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
foreach ($expected in 'IsName', 'CatalogEntry', 'BaseName', 'ArchiveFlavor', 'Direction', 'Items', 'Encrypt', 'ExcludePatterns', 'HasExcludeOverride', 'Includes', 'HasIncludeOverride', 'Source', 'Error', 'Blocking') {
|
||||
Assert-True ($resolved.PSObject.Properties.Name -contains $expected) "缺少属性 $expected"
|
||||
}
|
||||
@@ -938,7 +938,7 @@ Test-Case 'Resolve-BackupEntry 的返回值里没有旧的 Sources / RootName
|
||||
|
||||
Test-Case '字面路径条目:仍用路径命名算法,归档内是历史布局 <末级名>' {
|
||||
$item = ConvertFrom-BackupListLine -Line 'C:\Programs\FooClolor'
|
||||
$resolved = Resolve-BackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
$resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
Assert-False $resolved.IsName
|
||||
Assert-Equal 'path' $resolved.ArchiveFlavor
|
||||
Assert-Equal 'FooClolor_from_C_+Programs' $resolved.BaseName
|
||||
@@ -949,34 +949,34 @@ Test-Case '字面路径条目:仍用路径命名算法,归档内是历史布
|
||||
|
||||
Test-Case '@pathname 用名录里的真实路径跑命名算法' {
|
||||
$item = ConvertFrom-BackupListLine -Line 'FooClolor @pathname'
|
||||
$resolved = Resolve-BackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
Assert-Equal (Get-BackupBaseName -RawPath $dirA) $resolved.BaseName
|
||||
$resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
Assert-Equal (Get-BakNRetBackupBaseName -RawPath $dirA) $resolved.BaseName
|
||||
Assert-Equal 'DefaultData' $resolved.Items[0].ArchivePath '归档内名字仍由 Slot 决定'
|
||||
}
|
||||
|
||||
Test-Case ':: / @ Path= 覆盖单 Slot 条目的真实路径' {
|
||||
$item = ConvertFrom-BackupListLine -Line "FooClolor :: $dirB"
|
||||
$resolved = Resolve-BackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
$resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
Assert-Equal 1 $resolved.Items.Count
|
||||
Assert-Equal $dirB $resolved.Items[0].RealPath
|
||||
Assert-Equal 'DefaultData' $resolved.Items[0].ArchivePath
|
||||
Assert-Null $resolved.Blocking
|
||||
|
||||
$atItem = ConvertFrom-BackupListLine -Line "FooClolor @ Path='$dirB'"
|
||||
$atResolved = Resolve-BackupEntry -Entry $atItem -CatalogPath $resolveCatalog
|
||||
$atResolved = Resolve-BakNRetBackupEntry -Entry $atItem -CatalogPath $resolveCatalog
|
||||
Assert-Equal $dirB $atResolved.Items[0].RealPath
|
||||
}
|
||||
|
||||
Test-Case ':: 覆盖路径时,多 Slot 的条目会 Blocking(说不清是哪个 Slot)' {
|
||||
$item = ConvertFrom-BackupListLine -Line "MultiApp :: $dirB"
|
||||
$resolved = Resolve-BackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
$resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
Assert-True ($null -ne $resolved.Blocking)
|
||||
Assert-True ($resolved.Blocking -like '*2 个 Slot*')
|
||||
}
|
||||
|
||||
Test-Case '名录里没有该名字:BaseName 退回可读目录名,并给出 Error' {
|
||||
$item = ConvertFrom-BackupListLine -Line 'no-such-thing'
|
||||
$resolved = Resolve-BackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
$resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
Assert-Equal 'no-such-thing' $resolved.BaseName
|
||||
Assert-True ($null -ne $resolved.Error) '应给出错误说明'
|
||||
Assert-Equal 0 $resolved.Items.Count
|
||||
@@ -986,7 +986,7 @@ Test-Case '名录里的路径不存在时仍给出归档项(恢复要靠它还
|
||||
$catPath = Join-Path $resolveSandbox 'gone.psd1'
|
||||
[System.IO.File]::WriteAllText($catPath, "@{`n 'gone-app' = @{ DefaultData = @{ Path = 'C:\definitely\not\here' } }`n}`n", [System.Text.UTF8Encoding]::new($true))
|
||||
$item = ConvertFrom-BackupListLine -Line 'gone-app'
|
||||
$resolved = Resolve-BackupEntry -Entry $item -CatalogPath $catPath
|
||||
$resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $catPath
|
||||
Assert-Equal 1 $resolved.Items.Count '恢复端据 Items 还原'
|
||||
Assert-Equal 'C:\definitely\not\here' $resolved.Items[0].RealPath
|
||||
Assert-False $resolved.Items[0].Exists
|
||||
@@ -995,7 +995,7 @@ Test-Case '名录里的路径不存在时仍给出归档项(恢复要靠它还
|
||||
|
||||
Test-Case ':+ 追加项:<归档内相对路径>:<宿主机绝对路径>' {
|
||||
$item = ConvertFrom-BackupListLine -Line "FooClolor :+ Modules:$dirExtra"
|
||||
$resolved = Resolve-BackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
$resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
Assert-Equal 2 $resolved.Items.Count
|
||||
Assert-Equal 'DefaultData' $resolved.Items[0].ArchivePath
|
||||
Assert-Equal 'Modules' $resolved.Items[1].ArchivePath
|
||||
@@ -1011,11 +1011,11 @@ Test-Case '条目级 :+ 覆盖名录里的 Include(不是叠加)' {
|
||||
$catPath = Join-Path $resolveSandbox 'withinc.psd1'
|
||||
[System.IO.File]::WriteAllText($catPath, "@{`n IncApp = @{ DefaultData = @{ Path = '$dirA'; Include = 'CatalogInc:$incDir' } }`n}`n", [System.Text.UTF8Encoding]::new($true))
|
||||
|
||||
$plain = Resolve-BackupEntry -Entry (ConvertFrom-BackupListLine -Line 'IncApp') -CatalogPath $catPath
|
||||
$plain = Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'IncApp') -CatalogPath $catPath
|
||||
Assert-Equal 2 $plain.Items.Count
|
||||
Assert-Equal 'CatalogInc' $plain.Items[1].ArchivePath
|
||||
|
||||
$overridden = Resolve-BackupEntry -Entry (ConvertFrom-BackupListLine -Line "IncApp :+ EntryInc:$dirExtra") -CatalogPath $catPath
|
||||
$overridden = Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line "IncApp :+ EntryInc:$dirExtra") -CatalogPath $catPath
|
||||
Assert-Equal 2 $overridden.Items.Count
|
||||
Assert-Equal 'EntryInc' $overridden.Items[1].ArchivePath
|
||||
Assert-Equal $dirExtra $overridden.Items[1].RealPath
|
||||
@@ -1023,45 +1023,45 @@ Test-Case '条目级 :+ 覆盖名录里的 Include(不是叠加)' {
|
||||
|
||||
Test-Case '归档内路径冲突:同名追加项 -> Blocking' {
|
||||
$item = ConvertFrom-BackupListLine -Line "FooClolor :+ DefaultData:$dirExtra"
|
||||
$resolved = Resolve-BackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
$resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
Assert-True ($null -ne $resolved.Blocking)
|
||||
Assert-True ($resolved.Blocking -like '*冲突*')
|
||||
}
|
||||
|
||||
Test-Case '归档内路径冲突:父子关系 -> Blocking' {
|
||||
$item = ConvertFrom-BackupListLine -Line "FooClolor :+ DefaultData\sub:$dirExtra"
|
||||
$resolved = Resolve-BackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
$resolved = Resolve-BakNRetBackupEntry -Entry $item -CatalogPath $resolveCatalog
|
||||
Assert-True ($null -ne $resolved.Blocking)
|
||||
Assert-True ($resolved.Blocking -like '*父子*')
|
||||
}
|
||||
|
||||
Test-Case '加密:名录 Slot 的 Encrypt 之和,条目级覆盖优先' {
|
||||
$catalogEncrypt = Resolve-BackupEntry -Entry (ConvertFrom-BackupListLine -Line 'FooClolor') -CatalogPath $resolveCatalog
|
||||
$catalogEncrypt = Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'FooClolor') -CatalogPath $resolveCatalog
|
||||
Assert-True $catalogEncrypt.Encrypt
|
||||
|
||||
$off = Resolve-BackupEntry -Entry (ConvertFrom-BackupListLine -Line 'FooClolor :!encrypt') -CatalogPath $resolveCatalog
|
||||
$off = Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'FooClolor :!encrypt') -CatalogPath $resolveCatalog
|
||||
Assert-False $off.Encrypt
|
||||
|
||||
$on = Resolve-BackupEntry -Entry (ConvertFrom-BackupListLine -Line 'MultiApp :encrypt') -CatalogPath $resolveCatalog
|
||||
$on = Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'MultiApp :encrypt') -CatalogPath $resolveCatalog
|
||||
Assert-True $on.Encrypt '名录没开、条目开了 -> 开'
|
||||
Assert-False $on.HasExcludeOverride
|
||||
}
|
||||
|
||||
Test-Case '方向标记随条目一路传到 Resolve-BackupEntry' {
|
||||
Assert-Equal 'backup' (Resolve-BackupEntry -Entry (ConvertFrom-BackupListLine -Line '+ FooClolor') -CatalogPath $resolveCatalog).Direction
|
||||
Assert-Equal 'restore' (Resolve-BackupEntry -Entry (ConvertFrom-BackupListLine -Line '- FooClolor') -CatalogPath $resolveCatalog).Direction
|
||||
Test-Case '方向标记随条目一路传到 Resolve-BakNRetBackupEntry' {
|
||||
Assert-Equal 'backup' (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line '+ FooClolor') -CatalogPath $resolveCatalog).Direction
|
||||
Assert-Equal 'restore' (Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line '- FooClolor') -CatalogPath $resolveCatalog).Direction
|
||||
}
|
||||
|
||||
Test-Case '条目级 :- 覆盖名录里的 Exclude(不是叠加)' {
|
||||
$catPath = Join-Path $resolveSandbox 'withexc.psd1'
|
||||
[System.IO.File]::WriteAllText($catPath, "@{`n ExcApp = @{ DefaultData = @{ Path = '$dirA'; Exclude = '!*CatalogCache' } }`n}`n", [System.Text.UTF8Encoding]::new($true))
|
||||
|
||||
$plain = Resolve-BackupEntry -Entry (ConvertFrom-BackupListLine -Line 'ExcApp') -CatalogPath $catPath
|
||||
$plain = Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'ExcApp') -CatalogPath $catPath
|
||||
Assert-False $plain.HasExcludeOverride
|
||||
Assert-Equal 0 $plain.ExcludePatterns.Count
|
||||
Assert-Equal '!*CatalogCache' $plain.Items[0].Exclude[0]
|
||||
|
||||
$overridden = Resolve-BackupEntry -Entry (ConvertFrom-BackupListLine -Line 'ExcApp :- !*Listed') -CatalogPath $catPath
|
||||
$overridden = Resolve-BakNRetBackupEntry -Entry (ConvertFrom-BackupListLine -Line 'ExcApp :- !*Listed') -CatalogPath $catPath
|
||||
Assert-True $overridden.HasExcludeOverride
|
||||
Assert-Equal '!*Listed' $overridden.ExcludePatterns[0]
|
||||
}
|
||||
@@ -1093,8 +1093,8 @@ else {
|
||||
Set-Content -Encoding UTF8 -LiteralPath (Join-Path $source 'Default\IndexedDB\i.bin') 'i'
|
||||
|
||||
$patterns = @('!*Cache', 'component_crx_cache', 'Default\Code Cache', 'Default\Extensions', 'Default\IndexedDB')
|
||||
$archiveItem = New-BaknretArchiveItem -ArchivePath $itemName -RealPath $source -Kind 'path' -Exists $true
|
||||
$excludeArgs = @((Get-BaknretExcludeArgument -Item $archiveItem -Patterns $patterns).Arguments)
|
||||
$archiveItem = New-BakNRetArchiveItem -ArchivePath $itemName -RealPath $source -Kind 'path' -Exists $true
|
||||
$excludeArgs = @((Get-BakNRetExcludeArgument -Item $archiveItem -Patterns $patterns).Arguments)
|
||||
Assert-True ($excludeArgs.Count -ge 5) '排除参数应被翻译出来'
|
||||
|
||||
$archive = Join-Path $sandbox 'excl.7z'
|
||||
@@ -1124,8 +1124,8 @@ else {
|
||||
Set-Content -Encoding UTF8 -LiteralPath (Join-Path $source "$d\x.bin") 'x'
|
||||
}
|
||||
|
||||
$archiveItem = New-BaknretArchiveItem -ArchivePath $itemName -RealPath $source -Kind 'slot' -Exists $true
|
||||
$excludeArgs = @((Get-BaknretExcludeArgument -Item $archiveItem -Patterns @('!re:^Cache\d+$')).Arguments)
|
||||
$archiveItem = New-BakNRetArchiveItem -ArchivePath $itemName -RealPath $source -Kind 'slot' -Exists $true
|
||||
$excludeArgs = @((Get-BakNRetExcludeArgument -Item $archiveItem -Patterns @('!re:^Cache\d+$')).Arguments)
|
||||
Assert-Equal 3 $excludeArgs.Count '两个顶层 Cache 目录 + 嵌套的 sub\Cache3'
|
||||
|
||||
$archive = Join-Path $sandbox 're.7z'
|
||||
@@ -1159,7 +1159,7 @@ else {
|
||||
Assert-True (Test-Path -LiteralPath (Join-Path $verify "$itemName\Default\Cache\c.bin")) '对照组应当包含被排除的那个文件'
|
||||
}
|
||||
|
||||
Test-Case 'Get-ArchiveTopLevelNames 能列出归档内的顶层条目名' {
|
||||
Test-Case 'Get-BakNRetArchiveTopLevelNames 能列出归档内的顶层条目名' {
|
||||
$root = Join-Path $sandbox 'topnames'
|
||||
New-Item -ItemType Directory -Path (Join-Path $root 'Alpha') -Force | Out-Null
|
||||
New-Item -ItemType Directory -Path (Join-Path $root 'Beta') -Force | Out-Null
|
||||
@@ -1169,7 +1169,7 @@ else {
|
||||
$code = Invoke-ExternalCommand -FilePath $sevenZip -ArgumentList @('a', '-t7z', '-mx=1', '-bso0', '-bsp0', $archive, 'Alpha', 'Beta') -WorkingDirectory $root
|
||||
Assert-Equal 0 $code
|
||||
|
||||
$names = @(Get-ArchiveTopLevelNames -ArchivePath $archive -SevenZip $sevenZip)
|
||||
$names = @(Get-BakNRetArchiveTopLevelNames -ArchivePath $archive -SevenZip $sevenZip)
|
||||
$sorted = @($names | Sort-Object)
|
||||
Assert-Equal 2 $sorted.Count
|
||||
Assert-Equal 'Alpha' $sorted[0]
|
||||
@@ -1203,13 +1203,13 @@ Test-Case '口令不会进日志:DEBUG 下打印的命令行要遮蔽 -p 参
|
||||
$sentinel = 'SENTINEL-PW-9f3a'
|
||||
$logPath = $null
|
||||
try {
|
||||
$logPath = Start-BaknretLog -Directory $sandbox -Prefix 'secret'
|
||||
Set-BaknretDebug
|
||||
$logPath = Start-BakNRetLog -Directory $sandbox -Prefix 'secret'
|
||||
Set-BakNRetDebug
|
||||
$null = Invoke-ExternalCommand -FilePath 'cmd.exe' -ArgumentList @('/c', 'exit 0', "-p$sentinel")
|
||||
}
|
||||
finally {
|
||||
Set-BaknretDebug -Enabled:$false
|
||||
Stop-BaknretLog
|
||||
Set-BakNRetDebug -Enabled:$false
|
||||
Stop-BakNRetLog
|
||||
}
|
||||
|
||||
$logText = Get-Content -Encoding UTF8 -LiteralPath $logPath -Raw
|
||||
@@ -1236,7 +1236,7 @@ Test-Case '空目录的摘要给 0 而不是 $null(否则空间守卫会静默
|
||||
$empty = Join-Path $env:TEMP ("bnr-empty-" + [guid]::NewGuid().ToString('N').Substring(0, 8))
|
||||
New-Item -ItemType Directory -Path $empty -Force | Out-Null
|
||||
try {
|
||||
$summary = Get-FolderSummary -FolderPath $empty
|
||||
$summary = Get-BakNRetFolderSummary -FolderPath $empty
|
||||
Assert-Equal 0 $summary.FileCount
|
||||
Assert-True ($null -ne $summary.TotalSize) 'TotalSize 不能是 $null'
|
||||
Assert-True ($summary.TotalSize -is [long]) 'TotalSize 应当是整数'
|
||||
@@ -1252,11 +1252,11 @@ Test-Case '原子替换:成功时新内容到位且不留 .tmp;失败时旧
|
||||
New-Item -ItemType Directory -Path $base -Force | Out-Null
|
||||
$target = Join-Path $base 'text.json'
|
||||
try {
|
||||
Write-BaknretAtomicText -Path $target -Text 'FIRST' | Out-Null
|
||||
Write-BakNRetAtomicText -Path $target -Text 'FIRST' | Out-Null
|
||||
Assert-Equal 'FIRST' (Get-Content -Encoding UTF8 -LiteralPath $target -Raw)
|
||||
|
||||
# 目标已存在时走 File.Replace
|
||||
Write-BaknretAtomicText -Path $target -Text 'SECOND' | Out-Null
|
||||
Write-BakNRetAtomicText -Path $target -Text 'SECOND' | Out-Null
|
||||
Assert-Equal 'SECOND' (Get-Content -Encoding UTF8 -LiteralPath $target -Raw)
|
||||
Assert-FileMissing "$target.tmp"
|
||||
|
||||
@@ -1264,7 +1264,7 @@ Test-Case '原子替换:成功时新内容到位且不留 .tmp;失败时旧
|
||||
# 后者失败时旧文件已经没了,而 manifest 丢了就只能靠文件名反推。
|
||||
(Get-Item -LiteralPath $target).IsReadOnly = $true
|
||||
$threw = $false
|
||||
try { Write-BaknretAtomicText -Path $target -Text 'SHOULD-NOT-LAND' | Out-Null } catch { $threw = $true }
|
||||
try { Write-BakNRetAtomicText -Path $target -Text 'SHOULD-NOT-LAND' | Out-Null } catch { $threw = $true }
|
||||
Assert-True $threw '目标只读时替换应当抛错'
|
||||
Assert-Equal 'SECOND' (Get-Content -Encoding UTF8 -LiteralPath $target -Raw)
|
||||
}
|
||||
@@ -1286,22 +1286,22 @@ Test-Case '运行锁:同一份备份目录同时只能有一个持有者' {
|
||||
$first = $null
|
||||
$second = $null
|
||||
try {
|
||||
$first = Enter-BaknretRunLock -Directory $dir
|
||||
$first = Enter-BakNRetRunLock -Directory $dir
|
||||
Assert-True ($null -ne $first) '第一次应当拿到锁'
|
||||
|
||||
$second = Enter-BaknretRunLock -Directory $dir
|
||||
$second = Enter-BakNRetRunLock -Directory $dir
|
||||
Assert-True ($null -eq $second) '同一目录的第二次不应当拿到锁'
|
||||
|
||||
Assert-FileExists (Get-BaknretRunLockPath -Directory $dir)
|
||||
Assert-FileExists (Get-BakNRetRunLockPath -Directory $dir)
|
||||
}
|
||||
finally {
|
||||
Exit-BaknretRunLock -Lock $second
|
||||
Exit-BaknretRunLock -Lock $first
|
||||
Exit-BakNRetRunLock -Lock $second
|
||||
Exit-BakNRetRunLock -Lock $first
|
||||
}
|
||||
|
||||
# 锁文件是**独占**打开的(FileShare.None),内容只能在释放之后读 —— 而它本来就该
|
||||
# 留在那里给排查的人看:"到底是谁占着"这个问题不该靠猜。
|
||||
$info = Get-Content -Encoding UTF8 -LiteralPath (Get-BaknretRunLockPath -Directory $dir) -Raw
|
||||
$info = Get-Content -Encoding UTF8 -LiteralPath (Get-BakNRetRunLockPath -Directory $dir) -Raw
|
||||
Assert-True ($info -match "pid=$PID") "锁文件里应当写明持有进程,实际内容是:$info"
|
||||
|
||||
Remove-Item -LiteralPath $dir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
@@ -1310,13 +1310,13 @@ Test-Case '运行锁:同一份备份目录同时只能有一个持有者' {
|
||||
Test-Case '运行锁:释放之后可以重新取得' {
|
||||
$dir = Join-Path $env:TEMP ("bnr-lock-" + [guid]::NewGuid().ToString('N').Substring(0, 8))
|
||||
try {
|
||||
$first = Enter-BaknretRunLock -Directory $dir
|
||||
$first = Enter-BakNRetRunLock -Directory $dir
|
||||
Assert-True ($null -ne $first) '第一次应当拿到锁'
|
||||
Exit-BaknretRunLock -Lock $first
|
||||
Exit-BakNRetRunLock -Lock $first
|
||||
|
||||
$second = Enter-BaknretRunLock -Directory $dir
|
||||
$second = Enter-BakNRetRunLock -Directory $dir
|
||||
Assert-True ($null -ne $second) '释放之后应当能重新拿到锁'
|
||||
Exit-BaknretRunLock -Lock $second
|
||||
Exit-BakNRetRunLock -Lock $second
|
||||
}
|
||||
finally {
|
||||
Remove-Item -LiteralPath $dir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
|
||||
Reference in new issue
Block a user