diff --git a/.gitignore b/.gitignore index b10b426374d528986f027a4f06c106c8df415b02..a394d270c02acc5b4765500f48d5bac581bdab39 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ /corpus/private/ /corpus/recovered/ /evidence/ -/.DS_Store __pycache__/ /fuzz/target/ /fuzz/corpus/ diff --git a/corpus/page-lifecycle/removal/README.md b/corpus/page-lifecycle/removal/README.md index 2d227802946bcfc09ffb98f479ff08fe9fd28edf..05edb05202ecc5222108986c5e689f8eda4eef92 100644 --- a/corpus/page-lifecycle/removal/README.md +++ b/corpus/page-lifecycle/removal/README.md @@ -50,3 +50,15 @@ failure. Decode the input hex into a new writable corpus and set the platform path separator, then use `cargo +nightly fuzz run document CORPUS -- -seed=1993 -max_total_time=120 -timeout=30 -runs=20000`. The target also retains its built-in synthetic sources. This is bounded reader testing of these inputs. + +`rust-followup` takes four Rust `delete_pages_permanently` outputs (`parent`, `all`, +`features`, `leading-parent`) and lets OneNote 2010 create a titled page with a bold +body in each section through COM (`tools/native/page-removal-followup.ps1`), then +reopens the natively saved section through a fresh cache. The Rust test +`native_pages_created_after_rust_removal_reopen_cold` requires every surviving page +to keep its exact active revision through both native phases, the new page to carry +the native title and body, and the cold reopen to change no object space. +`tools/test_page_removal.py` compares both captures' native XML with the Rust model +and finds the native title exactly once in each hierarchy. Regenerate the candidates +with `ONESTORE_PAGE_REMOVAL_OUTPUT` on the removal reference test, then run the +runner with `--author` into `followup` and cold into `followup-cold`. diff --git a/corpus/page-lifecycle/removal/rust-followup/all/candidate/notebook/Lifecycle.one b/corpus/page-lifecycle/removal/rust-followup/all/candidate/notebook/Lifecycle.one new file mode 100644 index 0000000000000000000000000000000000000000..c62907c99edfd451551a13ca4a9765a6863b4ecb Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/all/candidate/notebook/Lifecycle.one differ diff --git a/corpus/page-lifecycle/removal/rust-followup/all/candidate/notebook/Open Notebook.onetoc2 b/corpus/page-lifecycle/removal/rust-followup/all/candidate/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..dd1d819eba785a38d9e40b7e5756d54aabd53423 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/all/candidate/notebook/Open Notebook.onetoc2 differ diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/commands.jsonl b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..272e436dc9993dbab4ce33e98a66201cf9087ad9 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/commands.jsonl @@ -0,0 +1,3 @@ +{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-9D41F146 -ExpectedPages 1", "exit": 0, "stdout": "Read 1 sections and 1 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/format-differences.json b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/format-differences.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/format-differences.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/geometry-differences.json b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/geometry-differences.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/geometry-differences.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/machine.json b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..6dd00423f2118997f76bdfed137f38a6dc89d92f --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/machine.json @@ -0,0 +1 @@ +{"name": "m6-9d41f146", "hostname": "ONE-M6-9D41F146"} diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/notebook/Lifecycle.one b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/notebook/Lifecycle.one new file mode 100644 index 0000000000000000000000000000000000000000..109ebb490bd6bfa8b8c8355a1d156ccb7af0c005 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/notebook/Lifecycle.one differ diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/notebook/Open Notebook.onetoc2 b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..dd1d819eba785a38d9e40b7e5756d54aabd53423 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/notebook/Open Notebook.onetoc2 differ diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/pdf-format-checks.json b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/pdf-format-checks.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/pdf-format-checks.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/read/environment.json b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..78ee0f9728a367e6c834591068a71a285d72b4ff --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-9D41F146", + "cold": true, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/read/hierarchy.xml b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..aba3be8c57f9508052f551d5e9dd9b2e1dc1b25f --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/read/page-000.xml b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..136e228354ab47498675e05b15e035c6839cae71 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/read/page-000.xml @@ -0,0 +1,3 @@ + +Native body after Rust removal.]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/read/payloads.json b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/read/payloads.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/run.json b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/run.json new file mode 100644 index 0000000000000000000000000000000000000000..471b5efd98a69d8a966cc1c208ed2283f0f0bf1e --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/run.json @@ -0,0 +1,18 @@ +{ + "notebook": "/private/tmp/page-removal/run/all/followup/notebook", + "expected_pages": 1, + "author": null, + "author_timeout_seconds": 600, + "inspect": false, + "collect_notebook": true, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/scripts/cold.ps1 b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/scripts/read.ps1 b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..476094ab1e0c47077988a6461db2080cf208642b --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/scripts/read.ps1 @@ -0,0 +1,142 @@ +param( + [Parameter(Mandatory=$true)][string]$Root, + [int]$ExpectedPages = -1, + [switch]$UseCurrentCache, + [switch]$Pdf, + [switch]$KeepOpen, + [string]$CloneHost = '' +) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +$notebook = Join-Path $root 'notebook' +$output = Join-Path $root 'read' +if (Test-Path $output) { throw 'Choose a new read destination.' } +if ($UseCurrentCache) { + if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') { + throw 'Park the personal OneNote profile before reading test notebooks.' + } +} else { + & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost +} +New-Item -ItemType Directory -Path $output | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +$failure = $null +try { + $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0) + $process = Get-Process ONENOTE + @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion; + powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } | + ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8 + $sections = @() + foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) { + $id = '' + $app.OpenHierarchy($file.FullName, '', [ref]$id, 0) + $sections += $id + } + $deadline = [DateTime]::UtcNow.AddSeconds(300) + $previous = '' + $lastChange = '' + $stableSince = [DateTime]::UtcNow + $settled = $false + do { + $pages = @{} + foreach ($section in $sections) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$xml = $hierarchy + foreach ($node in $xml.SelectNodes('//*[@path]')) { + if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) { + throw 'OneNote opened a section outside the copied notebook.' + } + } + foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) { + $id = $node.GetAttribute('ID') + $content = '' + $app.GetPageContent($id, [ref]$content, 1, 1) + $pages[$id] = $content + } + } + $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] })) + if ($signature -ne $previous) { + $lastChange = $previous + $previous = $signature + $stableSince = [DateTime]::UtcNow + } + if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or + ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and + ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $deadline) + if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) { + [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8) + $index = 0 + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + $index++ + } + $hierarchy = '' + $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8) + throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled." + } + $index = 0 + $payloads = @() + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + if ($Pdf) { + $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index) + $app.NavigateTo($id, '', $false) + $app.Publish($id, $pdfPath, 3, '') + if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' } + } + [xml]$page = $pages[$id] + foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) { + $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache')) + $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant() + [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes) + $payloads += @{ page = $id; object = $file.ParentNode.GetAttribute('objectID'); + kind = $file.LocalName; name = $file.GetAttribute('preferredName'); + sha256 = $hash; bytes = $bytes.Length } + } + $index++ + } + [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8) + $all = '' + $app.GetHierarchy($notebookId, 4, [ref]$all, 1) + [xml]$finalTree = $all + $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique) + if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) { + throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.' + } + [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8) + Write-Output "Read $($sections.Count) sections and $($pages.Count) pages." +} catch { + $failure = $_ + [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + throw +} finally { + try { + try { + if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) } + if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) { + $app.CloseNotebook($notebookId, $false) + } + } catch { + if ($null -eq $failure) { throw } + [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + } + } finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + } + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + if (-not $UseCurrentCache -and -not $CloneHost) { + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/source.json b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/source.json new file mode 100644 index 0000000000000000000000000000000000000000..ff4ba59576491f27c378f85c1eb592fdbf70ff3f --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Lifecycle.one", + "bytes": 71048, + "sha256": "36a33c1d7f8a13d93f6bcb0ea9431d5524a1f43aa8a8e121dc2c735e076cc029", + "mtime_ns": 1789112222791725210 + }, + { + "path": "Open Notebook.onetoc2", + "bytes": 4800, + "sha256": "0f15237414704e35cc85965841e1388fb91d2150e1eb2f41981256649e72cd40", + "mtime_ns": 1789112222791871294 + } +] diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/table-autofit.json b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/table-autofit.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/table-autofit.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/teardown.json b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup-cold/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/commands.jsonl b/corpus/page-lifecycle/removal/rust-followup/all/followup/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..b57045b0e11b4803fa86e12ba75d117d4eddbc2e --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/commands.jsonl @@ -0,0 +1,4 @@ +{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\author.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-4C5FF100", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-4C5FF100 -ExpectedPages 1 -UseCurrentCache", "exit": 0, "stdout": "Read 1 sections and 1 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/format-differences.json b/corpus/page-lifecycle/removal/rust-followup/all/followup/format-differences.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/format-differences.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/geometry-differences.json b/corpus/page-lifecycle/removal/rust-followup/all/followup/geometry-differences.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/geometry-differences.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/machine.json b/corpus/page-lifecycle/removal/rust-followup/all/followup/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..03ed100faa8e91a2d71f82f8d82209e0045873c2 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/machine.json @@ -0,0 +1 @@ +{"name": "m6-4c5ff100", "hostname": "ONE-M6-4C5FF100"} diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/native-title.txt b/corpus/page-lifecycle/removal/rust-followup/all/followup/native-title.txt new file mode 100644 index 0000000000000000000000000000000000000000..1d6cb6d0262b9c45259962e524e8b806eb94dbac --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/native-title.txt @@ -0,0 +1 @@ +Native after Rust removal 🦋 é \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/notebook/Lifecycle.one b/corpus/page-lifecycle/removal/rust-followup/all/followup/notebook/Lifecycle.one new file mode 100644 index 0000000000000000000000000000000000000000..109ebb490bd6bfa8b8c8355a1d156ccb7af0c005 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/all/followup/notebook/Lifecycle.one differ diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/notebook/Open Notebook.onetoc2 b/corpus/page-lifecycle/removal/rust-followup/all/followup/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..dd1d819eba785a38d9e40b7e5756d54aabd53423 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/all/followup/notebook/Open Notebook.onetoc2 differ diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/pdf-format-checks.json b/corpus/page-lifecycle/removal/rust-followup/all/followup/pdf-format-checks.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/pdf-format-checks.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/read/environment.json b/corpus/page-lifecycle/removal/rust-followup/all/followup/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..a2b2b563f3da92fe04b0823e72b0c40257bede24 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-4C5FF100", + "cold": false, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/read/hierarchy.xml b/corpus/page-lifecycle/removal/rust-followup/all/followup/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..24038339d3ff7079016d482c48f9eaa85755cf34 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/read/page-000.xml b/corpus/page-lifecycle/removal/rust-followup/all/followup/read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..e665510f38610111b74fa8aa21c92d5a18366c3d --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/read/page-000.xml @@ -0,0 +1,3 @@ + +Native body after Rust removal.]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/read/payloads.json b/corpus/page-lifecycle/removal/rust-followup/all/followup/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/read/payloads.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/run.json b/corpus/page-lifecycle/removal/rust-followup/all/followup/run.json new file mode 100644 index 0000000000000000000000000000000000000000..f789eb2ef4cee0c78af9a9e0eb2a69b68d3fb20f --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/run.json @@ -0,0 +1,19 @@ +{ + "notebook": "/private/tmp/page-removal/run/all/candidate/notebook", + "expected_pages": 1, + "author": "tools/native/page-removal-followup.ps1", + "author_timeout_seconds": 600, + "inspect": false, + "collect_notebook": true, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "author.ps1": "6e6bd74f0460457118d436345350c38594c52bd9199fb4a1e31efec3b3e954fd", + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/scripts/author.ps1 b/corpus/page-lifecycle/removal/rust-followup/all/followup/scripts/author.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..1393a1ff21e7ae5c383cf8babf1f4f21d5f0c14b --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/scripts/author.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [Parameter(Mandatory=$true)][string]$CloneHost) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +& "$PSScriptRoot\cold-current.ps1" -Root $Root -CloneHost $CloneHost +$app = New-Object -ComObject OneNote.Application +$notebook = '' +try { + $app.OpenHierarchy((Join-Path $Root 'notebook'), '', [ref]$notebook, 0) + $section = '' + $app.OpenHierarchy('Lifecycle.one', $notebook, [ref]$section, 0) + $page = '' + $app.CreateNewPage($section, [ref]$page, 2) + $title = 'Native after Rust removal ' + [char]::ConvertFromUtf32(0x1F98B) + ' e' + [char]0x0301 + $xml = 'Native body after Rust removal.]]>' + $app.UpdatePageContent($xml, [DateTime]::MinValue, 1, $false) + $app.SyncHierarchy($notebook) + [IO.File]::WriteAllText("$Root\native-title.txt", $title, [Text.Encoding]::UTF8) +} finally { + try { if ($notebook) { $app.CloseNotebook($notebook, $false) } } + finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 120 -ErrorAction SilentlyContinue + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/scripts/cold.ps1 b/corpus/page-lifecycle/removal/rust-followup/all/followup/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/scripts/read.ps1 b/corpus/page-lifecycle/removal/rust-followup/all/followup/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..476094ab1e0c47077988a6461db2080cf208642b --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/scripts/read.ps1 @@ -0,0 +1,142 @@ +param( + [Parameter(Mandatory=$true)][string]$Root, + [int]$ExpectedPages = -1, + [switch]$UseCurrentCache, + [switch]$Pdf, + [switch]$KeepOpen, + [string]$CloneHost = '' +) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +$notebook = Join-Path $root 'notebook' +$output = Join-Path $root 'read' +if (Test-Path $output) { throw 'Choose a new read destination.' } +if ($UseCurrentCache) { + if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') { + throw 'Park the personal OneNote profile before reading test notebooks.' + } +} else { + & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost +} +New-Item -ItemType Directory -Path $output | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +$failure = $null +try { + $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0) + $process = Get-Process ONENOTE + @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion; + powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } | + ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8 + $sections = @() + foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) { + $id = '' + $app.OpenHierarchy($file.FullName, '', [ref]$id, 0) + $sections += $id + } + $deadline = [DateTime]::UtcNow.AddSeconds(300) + $previous = '' + $lastChange = '' + $stableSince = [DateTime]::UtcNow + $settled = $false + do { + $pages = @{} + foreach ($section in $sections) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$xml = $hierarchy + foreach ($node in $xml.SelectNodes('//*[@path]')) { + if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) { + throw 'OneNote opened a section outside the copied notebook.' + } + } + foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) { + $id = $node.GetAttribute('ID') + $content = '' + $app.GetPageContent($id, [ref]$content, 1, 1) + $pages[$id] = $content + } + } + $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] })) + if ($signature -ne $previous) { + $lastChange = $previous + $previous = $signature + $stableSince = [DateTime]::UtcNow + } + if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or + ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and + ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $deadline) + if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) { + [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8) + $index = 0 + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + $index++ + } + $hierarchy = '' + $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8) + throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled." + } + $index = 0 + $payloads = @() + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + if ($Pdf) { + $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index) + $app.NavigateTo($id, '', $false) + $app.Publish($id, $pdfPath, 3, '') + if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' } + } + [xml]$page = $pages[$id] + foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) { + $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache')) + $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant() + [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes) + $payloads += @{ page = $id; object = $file.ParentNode.GetAttribute('objectID'); + kind = $file.LocalName; name = $file.GetAttribute('preferredName'); + sha256 = $hash; bytes = $bytes.Length } + } + $index++ + } + [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8) + $all = '' + $app.GetHierarchy($notebookId, 4, [ref]$all, 1) + [xml]$finalTree = $all + $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique) + if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) { + throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.' + } + [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8) + Write-Output "Read $($sections.Count) sections and $($pages.Count) pages." +} catch { + $failure = $_ + [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + throw +} finally { + try { + try { + if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) } + if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) { + $app.CloseNotebook($notebookId, $false) + } + } catch { + if ($null -eq $failure) { throw } + [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + } + } finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + } + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + if (-not $UseCurrentCache -and -not $CloneHost) { + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/source.json b/corpus/page-lifecycle/removal/rust-followup/all/followup/source.json new file mode 100644 index 0000000000000000000000000000000000000000..0ed7dfc21854677822642582c2c64f8d014cc0b0 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Lifecycle.one", + "bytes": 64904, + "sha256": "645169c4f76dedd0deb0c928a08339479763ae607c21b7f604c21d823eee53eb", + "mtime_ns": 1789112028773633383 + }, + { + "path": "Open Notebook.onetoc2", + "bytes": 4800, + "sha256": "0f15237414704e35cc85965841e1388fb91d2150e1eb2f41981256649e72cd40", + "mtime_ns": 1789112028775905222 + } +] diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/table-autofit.json b/corpus/page-lifecycle/removal/rust-followup/all/followup/table-autofit.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/table-autofit.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/all/followup/teardown.json b/corpus/page-lifecycle/removal/rust-followup/all/followup/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/all/followup/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/page-lifecycle/removal/rust-followup/features/candidate/notebook/Lifecycle.one b/corpus/page-lifecycle/removal/rust-followup/features/candidate/notebook/Lifecycle.one new file mode 100644 index 0000000000000000000000000000000000000000..d6167b04713bf4ddbe1f66b0889f651b8ad9d633 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/features/candidate/notebook/Lifecycle.one differ diff --git a/corpus/page-lifecycle/removal/rust-followup/features/candidate/notebook/Open Notebook.onetoc2 b/corpus/page-lifecycle/removal/rust-followup/features/candidate/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..18f015f65a9419ea2a74835decb84d7b76760183 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/features/candidate/notebook/Open Notebook.onetoc2 differ diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/commands.jsonl b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..6a92af073bea2e379f714ff7df6fa5b79e6348e5 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/commands.jsonl @@ -0,0 +1,3 @@ +{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-53651AB4 -ExpectedPages 7", "exit": 0, "stdout": "Read 1 sections and 7 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/format-differences.json b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/format-differences.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/format-differences.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/geometry-differences.json b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/geometry-differences.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/geometry-differences.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/machine.json b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..8e1e3e547bb11369fba508bd8d88ea5b40481e2b --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/machine.json @@ -0,0 +1 @@ +{"name": "m6-53651ab4", "hostname": "ONE-M6-53651AB4"} diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/notebook/Lifecycle.one b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/notebook/Lifecycle.one new file mode 100644 index 0000000000000000000000000000000000000000..2161fccbbe9e8d4960d8a2da837179db6c4b2ca6 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/notebook/Lifecycle.one differ diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/notebook/Open Notebook.onetoc2 b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..18f015f65a9419ea2a74835decb84d7b76760183 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/notebook/Open Notebook.onetoc2 differ diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/pdf-format-checks.json b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/pdf-format-checks.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/pdf-format-checks.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/environment.json b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..b6daba10ca2c77f5587014c0ae5b59a421228499 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-53651AB4", + "cold": true, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/hierarchy.xml b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..673ca207f9911f1bb9008ce68c7556b900ab7651 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-000.xml b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..8b11ce85b2864cff5b5a794ce2172eac2d244105 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-000.xml @@ -0,0 +1,21 @@ + +/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0a +HBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIy +MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABAAGADASIA +AhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQA +AAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3 +ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWm +p6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEA +AwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSEx +BhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElK +U1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3 +uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDwhYfa +plh9qsrD7VMsPtUqRFOqVVh9qmWH2qysPtUyw+1UpHbTqlZYfaplh9qsrD7VMsPtVqR206pVWGpl +h9qsrDUyw+1UpHbTqlZYalWH2q0sNSrD7VSkdtOqVlh9qmWH2qysPtUyw+1UpHdTqnHrD7VMsPtV +lYfaplh9q81SPyqnVKqw+1TLD7VZWH2qZYfaqUjtp1SssPtUyw+1WVh9qmWH2qlI7adUqrDUyw+1 +WVhqZYfarUjtp1SssNSrD7VaWGpVh9qpSO2nVKyw+1TLD7VZWH2qZYfaqUjup1Tj1h9qmWH2qysP +tUyw+1eapH5VTqlVYfaplh9qsrD7VMsPtVKR206pWWH2qZYfarKw+1TLD7VSkdtOqVVhqZYfarKw +1MsPtVqR206pWWGpVh9qtLDUqw+1UpHbTqlZYfaplh9qsrD7VMsPtVKR206px6w+1TLD7VZWH2qZ +YfavNUj8rp1SqsPtUyw+1WVh9qmWH2qlI7adUrLD7VMsPtVlYfaplh9qpSO2nVKqw1MsPtVlYamW +H2q1I7adUrLDUqw+1WlhqVYfaqUjtp1SssPtUyw+1WVh9qmWH2qlI7adU//Z + diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-001.xml b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-001.xml new file mode 100644 index 0000000000000000000000000000000000000000..fd0654815ec2be17f896932b46ad35321185ebd3 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-001.xml @@ -0,0 +1,6 @@ + +iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAIAAABqVuVZAAAAwUlEQVR4nO3ZwQ2DQBDF0Hkk/dec +CuASwXCwtBVY/mYBM3OMznEC4TsfMx1nEALk2o8ACZB/GpJBAiSD3PcgbmICJIM0MVvX/RokQDJI +E1ODvPOrS5EWIBmkialB1ntcpAVIBmli1rvTRVGAZJAmZj1AvawKkAzSxNQg6zEu0gIkgzQx69Hp +oihAMkgT85LTn1UBkkGamBpkvcdFWoBkkCZmvTtdFAVIBmli1gPUy6oAySBNTA2yHuMiLUAyyPMT ++wHXVRp4qDc0HQAAAABJRU5ErkJggg== diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-002.xml b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-002.xml new file mode 100644 index 0000000000000000000000000000000000000000..20ef6c4713ec013609fa2fefad6c2a04c0bb7b7e --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-002.xml @@ -0,0 +1,3 @@ + +Native body after Rust removal.]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-003.xml b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-003.xml new file mode 100644 index 0000000000000000000000000000000000000000..e701da2ba007b444287e936f43fb9a1768622fc3 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-003.xml @@ -0,0 +1,6 @@ + +iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAIAAABqVuVZAAAAwUlEQVR4nO3ZwQ2DQBDF0Hkk/dec +CuASwXCwtBVY/mYBM3OMznEC4TsfMx1nEALk2o8ACZB/GpJBAiSD3PcgbmICJIM0MVvX/RokQDJI +E1ODvPOrS5EWIBmkialB1ntcpAVIBmli1rvTRVGAZJAmZj1AvawKkAzSxNQg6zEu0gIkgzQx69Hp +oihAMkgT85LTn1UBkkGamBpkvcdFWoBkkCZmvTtdFAVIBmli1gPUy6oAySBNTA2yHuMiLUAyyPMT ++wHXVRp4qDc0HQAAAABJRU5ErkJggg== diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-004.xml b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-004.xml new file mode 100644 index 0000000000000000000000000000000000000000..12e1b328e3a710166b1e7580cad6c44bddca1e4d --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-004.xml @@ -0,0 +1,46 @@ + +iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAMAAADS6oI8AAADAFBMVEW4t5mrt5O4qpWrqo6euo2S +uoeZs4ieqoiSqoK4nJCrnIq4kIyrkIaen4SSn36ZmH+ekH+SkHmGuoGGs395t3pounJos3CGqnx5 +qnVtqm9iqmqGnHd5nHGGkHN5kG1on2lomGdokGS4hIirhIK4d4Srd32eh3ySh3aZgHeed3eSd3G4 +a4Cra3m4YHyrYHaea3OSa22eYG+SYGmGh3CGgG55hGloh2FogF+Gd2t5d2Rtd15id1mGa2d5a2CG +YGN5YF1ta1pia1VoYFRWumlWs2dJt2I4ulo4s1hWqmRJql09qlcyqlImulEaukshs0wKukMKs0Em +qkwaqkYPqkAEqjtWnF9JnFlWkFtJkFU4n1E4mE84kEwmn0gan0IhmEMmkEMakD0KnzoKmDgKkDVW +h1hWgFZJhFE4h0k4gEdWd1NJd0w9d0Yyd0FWa09Ja0hWYEtJYEU9a0Iyaz04YDwmh0AahzohgDsK +hzIKgDAmdzsadzUPdy8EdyomazcaazEmYDMaYC0PaysEayYKYCW4VHirVHK4R3SrR22eV2ySV2aZ +UGeeR2eSR2G4O3CrO2m4MGyrMGaeO2OSO12eMF+SMFmGV2CGUF55VFloV1FoUE+GR1t5R1RtR05i +R0mGO1d5O1CGMFN5ME1tO0piO0VoMES4JGirJGK4GGSrGF6eJ1ySJ1aZIFeeGFeSGFG4DmGrDlq4 +A12rA1eeDlSSDk6eA1CSA0qGJE95JEmGGEt5GEVoJ0FoID9oGDyGDkh5DkGGA0R5Az5tDjtiDjZo +AzVWVEdJVEE4Vzk4UDdWR0NJRzw9RzYyRzFWOz9JOzhWMDtJMDU9OzIyOy04MCwmVzAaVyohUCsK +VyIKUCAmRysaRyUPRx8ERxomOycaOyEmMCMaMB0POxsEOxYKMBVWJDdJJDFWGDNJGC04Jyk4ICc4 +GCRWDjBJDilWAyxJAyY9DiMyDh44Ax0mJyAaJxohIBsmGBsaGBUKJxIKIBAKGA0mDhgaDhImAxQa +Aw4PDgwEDgcKAwZYdbPqAAAAAWJLR0QAiAUdSAAAAAxjbVBQSkNtcDA3MTIAAAADSABzvAAABjJJ +REFUWEedkImbTgUcha9ChZl2RUKR0TKTtGiRsZUSEpI0tKm0GoW0WSOjMmNpL82oFG1apJJltNDC +KKRNURRZytZqRr9zzl2+79779Ty+9w943+ccZ7fPLvGv+N1js9jk8r7HfDFPzCRvktfIDOGEA64/ +rYD8oUCFVyhPGuAHtqRYUJq0IHFAOFBh7C4Hu/4B//4NfgO/bwGbN4JNm34FC8D7pWD+XDBv3hww +8w3w5uvgtVfBjBmvAAf+CvrL6f+HfgXo30L/RurdAP2l9M+lfs4c+t+g/3X6X6U+JiB/egH54wJ/ +xQ74bUNkAR9a8F5kQeKAaOAvUr5T7CB20Aay5WexTkBPSt8Rb4uXyUvkBfK8cCqkNxL9O3ZIbyQH +1klvxATkDwVC/p3btcAPrE8RmJ0U0IAX4wJ/+mDBdoLAeg8E1q5da/f8BGZ7IDBr1iy75y0DeqIB +U6dOtXueM5wgsM1QYKvhB0wOaE8MmBzAbkyfPl2BadOmmd2A3Qv8QRBAAXoGfiHRwLskEkDB9OGA +5H5gm/Rbt8oeDcgeDtgAg/qUAQ7YliJQVoZ7lhjJgZIS/FNcHAkUFeGfwsJC50efH8T34guPMgE5 +eNajRBSLSWQiKSImB5GA608rIH8osMZbsDppgB9YmmLBlKQFiQPCgTXGj6vBD6vA99+Bz8EXS0HZ +YrBkyWfgGfDsFFDyNCgungwmTQATx4OicaCw8EHgwL+G/tX0r6JfAfqX0r+YejdA/xT6n6Z+8mT6 +J9A/nv5x1McE5E8vIH/KQGRAzAI+9Ex0QeKAUOBb8g34mnxFPiWfgI/FIvIUeRI8IR4nD5D7yX1i +NHECfzRAvxuQXwH63YD8MQH5owEN0II9DWiAuyAxsHJl8NCKFb5/4cLIAg547LHIAj00dmx8YCXh +QysEBywkuOhDY9GiDwD9ABc9YjwsqCcYMMoYPfpe4HwrvbHckH+ZIb0Bu0G9Ib0BexAoKCiQf8yY +MdCPGkW9BaD+kiCAAvQMfESigUeJH3gIWAAF04cDkvuB5dIvWyZ7NCB7EKBegQLqUwY4YHmKQH4+ +7ulnJAdycxP8iYGcHPyTnZ3tjPQZJoaK2zzyBeSgtUeuaCGakpNIDjE5iARcf1oB+UOBEd6C4UkD +/ED/FAtaJi1IHBAOjDBGDgfDhoCh94BbwW39Qf4toF+/m0Er0LolyD0btGjRHDRtAk46EeScALKz +jwcO/CPoH07/EPoVoL8//bdQ7wbob0n/2dQ3b05/E/pPpP8E6mMC8qcXkD9lIDIgZgEfahVdkDgg +FLib3AXuJHeQm8iN4AZxPTmLnAnOEKeT48ix5BjRkDiBPxqg3w3IrwD9bkD+mID80YAGaMGeBjTA +XZAYGDw4eGjQIN/ft29kAQc0axZZoIcaN44PDCZ8aJDggL4EF11rXCPoB7joVOMUQT3BgAZGw4ZH +A+du6Y2BhvwDDOkN2IPANdIbsAeBrKws+Rs1agR9gwbUWwDq2wkCKEDPwHXED/Qhpj6N+IGTgQVQ +MH04ILkfGCj9gAGyBwHp+/SRPQhQr0AW9SkDHDAwRaBHj/hA3boJ/sRAjRr4p3r16s7VPleIy8Vl +Hj3EJS5HedQVR4oDyQGkBjE5iARcf1oB+VMF3AG9Qwt6plhQP2mBBuwfF7jKB4XeBI2eHgh0797d +HroY1PdAoE6dOnbPEQb0RAOqVatm9+xnOEGgl6FAnuEHTA5oTwyYHMBuZGZmKpCRkWF2A3YvcCVB +AAXoGbiURAP1SCSAgunDAcn9QC/p8/JkjwZkDwdsgEF9ONDN83flgkigffuki2p7/lq1Ei/6v0A3 +48quoFcXkHcRuABc2h6cL84jtUG9WuBwcRjJ3Bdk7AP2FnsRB/5u9Helvwv9CtDvBuRXgH43IP9h +9O9LvxuQPxqQP72A/DGBzp2DAZ06+f527SILOKBmzcgCDahaNT7QmfChToID2hFcdI7RVtAPcNEh +xsGCeoIBlQx7h3ST3uhoyN/BkN6APQi0ld6APQhUqVJF/sqVK0NfqZL0jgP1hQQBFKBn4FziB9oQ +Ux9K/MBBwAIomD4ckNwPdJS+QwfZg4D0bdrIHgSoV6AK9SkDHNAxzQD9sYH/AHAD8Cry1R2MAAAA +AElFTkSuQmCC + diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-005.xml b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-005.xml new file mode 100644 index 0000000000000000000000000000000000000000..e9d9556d56d5adceb3610718267d535cf5b6f34d --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-005.xml @@ -0,0 +1,6 @@ + +iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAIAAABqVuVZAAAAwUlEQVR4nO3ZwQ2DQBDF0Hkk/dec +CuASwXCwtBVY/mYBM3OMznEC4TsfMx1nEALk2o8ACZB/GpJBAiSD3PcgbmICJIM0MVvX/RokQDJI +E1ODvPOrS5EWIBmkialB1ntcpAVIBmli1rvTRVGAZJAmZj1AvawKkAzSxNQg6zEu0gIkgzQx69Hp +oihAMkgT85LTn1UBkkGamBpkvcdFWoBkkCZmvTtdFAVIBmli1gPUy6oAySBNTA2yHuMiLUAyyPMT ++wHXVRp4qDc0HQAAAABJRU5ErkJggg== diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-006.xml b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-006.xml new file mode 100644 index 0000000000000000000000000000000000000000..06085d84fb78cce2f2ca1b9794139e3714ee687f --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/page-006.xml @@ -0,0 +1,9 @@ + +iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAIAAABqVuVZAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAO +xAAADsQBlSsOGwAAARlJREFUeF7t2VFHQ2EcBvD3PWetVqvVam1ERESMiIiIiL7/J5p9gD1dvF51 +8eNcn4uf53n2P1ZLKUOpnkMCtYx0kkAt00F8gkAtsxFQBJpPAEWgxRGgCLScAopAq2NAEWhzAigC +3c0ARaD7U0AR6OEMUAR6nAOKQE/ngCLQ8wWgCLRdAIpAL5eAItDrFaAI9LYEFIHerwFFoI8bQBHo +cwUoAn3dAopA32tAEehnAygKjPu/xjyHBAY0WQDQL+0BBKhtYSVIgiSo6w+xiqmYiqnYX34J2SAb +ZINskA1qa4EESZAEdW1B15e7g9xBbf2VIAmSICPtDmprgQRJkAR1bUHXl7uD3EFt/ZUgCZIgI+0O +amuBBEnQP07QDo1OW207QPoWAAAAAElFTkSuQmCC + diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/payloads.json b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/read/payloads.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/run.json b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/run.json new file mode 100644 index 0000000000000000000000000000000000000000..444c0b4fcd2140ff113f36c4ac5cf6ebfd49c19f --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/run.json @@ -0,0 +1,18 @@ +{ + "notebook": "/private/tmp/page-removal/run/features/followup/notebook", + "expected_pages": 7, + "author": null, + "author_timeout_seconds": 600, + "inspect": false, + "collect_notebook": true, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/scripts/cold.ps1 b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/scripts/read.ps1 b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..476094ab1e0c47077988a6461db2080cf208642b --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/scripts/read.ps1 @@ -0,0 +1,142 @@ +param( + [Parameter(Mandatory=$true)][string]$Root, + [int]$ExpectedPages = -1, + [switch]$UseCurrentCache, + [switch]$Pdf, + [switch]$KeepOpen, + [string]$CloneHost = '' +) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +$notebook = Join-Path $root 'notebook' +$output = Join-Path $root 'read' +if (Test-Path $output) { throw 'Choose a new read destination.' } +if ($UseCurrentCache) { + if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') { + throw 'Park the personal OneNote profile before reading test notebooks.' + } +} else { + & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost +} +New-Item -ItemType Directory -Path $output | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +$failure = $null +try { + $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0) + $process = Get-Process ONENOTE + @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion; + powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } | + ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8 + $sections = @() + foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) { + $id = '' + $app.OpenHierarchy($file.FullName, '', [ref]$id, 0) + $sections += $id + } + $deadline = [DateTime]::UtcNow.AddSeconds(300) + $previous = '' + $lastChange = '' + $stableSince = [DateTime]::UtcNow + $settled = $false + do { + $pages = @{} + foreach ($section in $sections) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$xml = $hierarchy + foreach ($node in $xml.SelectNodes('//*[@path]')) { + if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) { + throw 'OneNote opened a section outside the copied notebook.' + } + } + foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) { + $id = $node.GetAttribute('ID') + $content = '' + $app.GetPageContent($id, [ref]$content, 1, 1) + $pages[$id] = $content + } + } + $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] })) + if ($signature -ne $previous) { + $lastChange = $previous + $previous = $signature + $stableSince = [DateTime]::UtcNow + } + if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or + ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and + ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $deadline) + if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) { + [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8) + $index = 0 + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + $index++ + } + $hierarchy = '' + $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8) + throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled." + } + $index = 0 + $payloads = @() + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + if ($Pdf) { + $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index) + $app.NavigateTo($id, '', $false) + $app.Publish($id, $pdfPath, 3, '') + if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' } + } + [xml]$page = $pages[$id] + foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) { + $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache')) + $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant() + [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes) + $payloads += @{ page = $id; object = $file.ParentNode.GetAttribute('objectID'); + kind = $file.LocalName; name = $file.GetAttribute('preferredName'); + sha256 = $hash; bytes = $bytes.Length } + } + $index++ + } + [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8) + $all = '' + $app.GetHierarchy($notebookId, 4, [ref]$all, 1) + [xml]$finalTree = $all + $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique) + if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) { + throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.' + } + [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8) + Write-Output "Read $($sections.Count) sections and $($pages.Count) pages." +} catch { + $failure = $_ + [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + throw +} finally { + try { + try { + if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) } + if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) { + $app.CloseNotebook($notebookId, $false) + } + } catch { + if ($null -eq $failure) { throw } + [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + } + } finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + } + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + if (-not $UseCurrentCache -and -not $CloneHost) { + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/source.json b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/source.json new file mode 100644 index 0000000000000000000000000000000000000000..5c7d34c2abdbf0c8d7eee89aaa6659484b11fd6e --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Lifecycle.one", + "bytes": 139520, + "sha256": "4c51e1d89b121d74fc15945b3d4e83698956d274bfa092f21a12a817f3acd95a", + "mtime_ns": 1789112618636859068 + }, + { + "path": "Open Notebook.onetoc2", + "bytes": 4800, + "sha256": "f9e4761726171bdc72e946e6742603dd225f2e154298987f36f628b6d8741e78", + "mtime_ns": 1789112618637055068 + } +] diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/table-autofit.json b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/table-autofit.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/table-autofit.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/teardown.json b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup-cold/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/commands.jsonl b/corpus/page-lifecycle/removal/rust-followup/features/followup/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..8890440e62c8df2f58d706068cca42d10f800418 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/commands.jsonl @@ -0,0 +1,4 @@ +{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\author.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-49B2CF78", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-49B2CF78 -ExpectedPages 7 -UseCurrentCache", "exit": 0, "stdout": "Read 1 sections and 7 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/format-differences.json b/corpus/page-lifecycle/removal/rust-followup/features/followup/format-differences.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/format-differences.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/geometry-differences.json b/corpus/page-lifecycle/removal/rust-followup/features/followup/geometry-differences.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/geometry-differences.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/machine.json b/corpus/page-lifecycle/removal/rust-followup/features/followup/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..a418249529b52569a1b8b65cbbf1ad53f97ba9c1 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/machine.json @@ -0,0 +1 @@ +{"name": "m6-49b2cf78", "hostname": "ONE-M6-49B2CF78"} diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/native-title.txt b/corpus/page-lifecycle/removal/rust-followup/features/followup/native-title.txt new file mode 100644 index 0000000000000000000000000000000000000000..1d6cb6d0262b9c45259962e524e8b806eb94dbac --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/native-title.txt @@ -0,0 +1 @@ +Native after Rust removal 🦋 é \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/notebook/Lifecycle.one b/corpus/page-lifecycle/removal/rust-followup/features/followup/notebook/Lifecycle.one new file mode 100644 index 0000000000000000000000000000000000000000..466222a5eec81265694fb371a7f3971a7c738814 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/features/followup/notebook/Lifecycle.one differ diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/notebook/Open Notebook.onetoc2 b/corpus/page-lifecycle/removal/rust-followup/features/followup/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..18f015f65a9419ea2a74835decb84d7b76760183 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/features/followup/notebook/Open Notebook.onetoc2 differ diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/pdf-format-checks.json b/corpus/page-lifecycle/removal/rust-followup/features/followup/pdf-format-checks.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/pdf-format-checks.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/read/environment.json b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..42f3ba3791b3c908136ed484017f14e6b71c78d5 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-49B2CF78", + "cold": false, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/read/hierarchy.xml b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..7ea818ed49dfbad766702b7b502ab2e6e5d87fdd --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-000.xml b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..992e8b870ffa1581d640dcc8baf4b3b9491c0d71 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-000.xml @@ -0,0 +1,3 @@ + +Native body after Rust removal.]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-001.xml b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-001.xml new file mode 100644 index 0000000000000000000000000000000000000000..e589833ec0f679592a096846ed427e76e1b9dc89 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-001.xml @@ -0,0 +1,6 @@ + +iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAIAAABqVuVZAAAAwUlEQVR4nO3ZwQ2DQBDF0Hkk/dec +CuASwXCwtBVY/mYBM3OMznEC4TsfMx1nEALk2o8ACZB/GpJBAiSD3PcgbmICJIM0MVvX/RokQDJI +E1ODvPOrS5EWIBmkialB1ntcpAVIBmli1rvTRVGAZJAmZj1AvawKkAzSxNQg6zEu0gIkgzQx69Hp +oihAMkgT85LTn1UBkkGamBpkvcdFWoBkkCZmvTtdFAVIBmli1gPUy6oAySBNTA2yHuMiLUAyyPMT ++wHXVRp4qDc0HQAAAABJRU5ErkJggg== diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-002.xml b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-002.xml new file mode 100644 index 0000000000000000000000000000000000000000..94285d475fff149e2215da58eb159e42f50edae4 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-002.xml @@ -0,0 +1,21 @@ + +/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0a +HBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIy +MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABAAGADASIA +AhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQA +AAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3 +ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWm +p6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEA +AwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSEx +BhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElK +U1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3 +uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDwhYfa +plh9qsrD7VMsPtUqRFOqVVh9qmWH2qysPtUyw+1UpHbTqlZYfaplh9qsrD7VMsPtVqR206pVWGpl +h9qsrDUyw+1UpHbTqlZYalWH2q0sNSrD7VSkdtOqVlh9qmWH2qysPtUyw+1UpHdTqnHrD7VMsPtV +lYfaplh9q81SPyqnVKqw+1TLD7VZWH2qZYfaqUjtp1SssPtUyw+1WVh9qmWH2qlI7adUqrDUyw+1 +WVhqZYfarUjtp1SssNSrD7VaWGpVh9qpSO2nVKyw+1TLD7VZWH2qZYfaqUjup1Tj1h9qmWH2qysP +tUyw+1eapH5VTqlVYfaplh9qsrD7VMsPtVKR206pWWH2qZYfarKw+1TLD7VSkdtOqVVhqZYfarKw +1MsPtVqR206pWWGpVh9qtLDUqw+1UpHbTqlZYfaplh9qsrD7VMsPtVKR206px6w+1TLD7VZWH2qZ +YfavNUj8rp1SqsPtUyw+1WVh9qmWH2qlI7adUrLD7VMsPtVlYfaplh9qpSO2nVKqw1MsPtVlYamW +H2q1I7adUrLDUqw+1WlhqVYfaqUjtp1SssPtUyw+1WVh9qmWH2qlI7adU//Z + diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-003.xml b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-003.xml new file mode 100644 index 0000000000000000000000000000000000000000..54c0fb93aa867e9698a344396dbd4309290b46af --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-003.xml @@ -0,0 +1,46 @@ + +iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAMAAADS6oI8AAADAFBMVEW4t5mrt5O4qpWrqo6euo2S +uoeZs4ieqoiSqoK4nJCrnIq4kIyrkIaen4SSn36ZmH+ekH+SkHmGuoGGs395t3pounJos3CGqnx5 +qnVtqm9iqmqGnHd5nHGGkHN5kG1on2lomGdokGS4hIirhIK4d4Srd32eh3ySh3aZgHeed3eSd3G4 +a4Cra3m4YHyrYHaea3OSa22eYG+SYGmGh3CGgG55hGloh2FogF+Gd2t5d2Rtd15id1mGa2d5a2CG +YGN5YF1ta1pia1VoYFRWumlWs2dJt2I4ulo4s1hWqmRJql09qlcyqlImulEaukshs0wKukMKs0Em +qkwaqkYPqkAEqjtWnF9JnFlWkFtJkFU4n1E4mE84kEwmn0gan0IhmEMmkEMakD0KnzoKmDgKkDVW +h1hWgFZJhFE4h0k4gEdWd1NJd0w9d0Yyd0FWa09Ja0hWYEtJYEU9a0Iyaz04YDwmh0AahzohgDsK +hzIKgDAmdzsadzUPdy8EdyomazcaazEmYDMaYC0PaysEayYKYCW4VHirVHK4R3SrR22eV2ySV2aZ +UGeeR2eSR2G4O3CrO2m4MGyrMGaeO2OSO12eMF+SMFmGV2CGUF55VFloV1FoUE+GR1t5R1RtR05i +R0mGO1d5O1CGMFN5ME1tO0piO0VoMES4JGirJGK4GGSrGF6eJ1ySJ1aZIFeeGFeSGFG4DmGrDlq4 +A12rA1eeDlSSDk6eA1CSA0qGJE95JEmGGEt5GEVoJ0FoID9oGDyGDkh5DkGGA0R5Az5tDjtiDjZo +AzVWVEdJVEE4Vzk4UDdWR0NJRzw9RzYyRzFWOz9JOzhWMDtJMDU9OzIyOy04MCwmVzAaVyohUCsK +VyIKUCAmRysaRyUPRx8ERxomOycaOyEmMCMaMB0POxsEOxYKMBVWJDdJJDFWGDNJGC04Jyk4ICc4 +GCRWDjBJDilWAyxJAyY9DiMyDh44Ax0mJyAaJxohIBsmGBsaGBUKJxIKIBAKGA0mDhgaDhImAxQa +Aw4PDgwEDgcKAwZYdbPqAAAAAWJLR0QAiAUdSAAAAAxjbVBQSkNtcDA3MTIAAAADSABzvAAABjJJ +REFUWEedkImbTgUcha9ChZl2RUKR0TKTtGiRsZUSEpI0tKm0GoW0WSOjMmNpL82oFG1apJJltNDC +KKRNURRZytZqRr9zzl2+79779Ty+9w943+ccZ7fPLvGv+N1js9jk8r7HfDFPzCRvktfIDOGEA64/ +rYD8oUCFVyhPGuAHtqRYUJq0IHFAOFBh7C4Hu/4B//4NfgO/bwGbN4JNm34FC8D7pWD+XDBv3hww +8w3w5uvgtVfBjBmvAAf+CvrL6f+HfgXo30L/RurdAP2l9M+lfs4c+t+g/3X6X6U+JiB/egH54wJ/ +xQ74bUNkAR9a8F5kQeKAaOAvUr5T7CB20Aay5WexTkBPSt8Rb4uXyUvkBfK8cCqkNxL9O3ZIbyQH +1klvxATkDwVC/p3btcAPrE8RmJ0U0IAX4wJ/+mDBdoLAeg8E1q5da/f8BGZ7IDBr1iy75y0DeqIB +U6dOtXueM5wgsM1QYKvhB0wOaE8MmBzAbkyfPl2BadOmmd2A3Qv8QRBAAXoGfiHRwLskEkDB9OGA +5H5gm/Rbt8oeDcgeDtgAg/qUAQ7YliJQVoZ7lhjJgZIS/FNcHAkUFeGfwsJC50efH8T34guPMgE5 +eNajRBSLSWQiKSImB5GA608rIH8osMZbsDppgB9YmmLBlKQFiQPCgTXGj6vBD6vA99+Bz8EXS0HZ +YrBkyWfgGfDsFFDyNCgungwmTQATx4OicaCw8EHgwL+G/tX0r6JfAfqX0r+YejdA/xT6n6Z+8mT6 +J9A/nv5x1McE5E8vIH/KQGRAzAI+9Ex0QeKAUOBb8g34mnxFPiWfgI/FIvIUeRI8IR4nD5D7yX1i +NHECfzRAvxuQXwH63YD8MQH5owEN0II9DWiAuyAxsHJl8NCKFb5/4cLIAg547LHIAj00dmx8YCXh +QysEBywkuOhDY9GiDwD9ABc9YjwsqCcYMMoYPfpe4HwrvbHckH+ZIb0Bu0G9Ib0BexAoKCiQf8yY +MdCPGkW9BaD+kiCAAvQMfESigUeJH3gIWAAF04cDkvuB5dIvWyZ7NCB7EKBegQLqUwY4YHmKQH4+ +7ulnJAdycxP8iYGcHPyTnZ3tjPQZJoaK2zzyBeSgtUeuaCGakpNIDjE5iARcf1oB+UOBEd6C4UkD +/ED/FAtaJi1IHBAOjDBGDgfDhoCh94BbwW39Qf4toF+/m0Er0LolyD0btGjRHDRtAk46EeScALKz +jwcO/CPoH07/EPoVoL8//bdQ7wbob0n/2dQ3b05/E/pPpP8E6mMC8qcXkD9lIDIgZgEfahVdkDgg +FLib3AXuJHeQm8iN4AZxPTmLnAnOEKeT48ix5BjRkDiBPxqg3w3IrwD9bkD+mID80YAGaMGeBjTA +XZAYGDw4eGjQIN/ft29kAQc0axZZoIcaN44PDCZ8aJDggL4EF11rXCPoB7joVOMUQT3BgAZGw4ZH +A+du6Y2BhvwDDOkN2IPANdIbsAeBrKws+Rs1agR9gwbUWwDq2wkCKEDPwHXED/Qhpj6N+IGTgQVQ +MH04ILkfGCj9gAGyBwHp+/SRPQhQr0AW9SkDHDAwRaBHj/hA3boJ/sRAjRr4p3r16s7VPleIy8Vl +Hj3EJS5HedQVR4oDyQGkBjE5iARcf1oB+VMF3AG9Qwt6plhQP2mBBuwfF7jKB4XeBI2eHgh0797d +HroY1PdAoE6dOnbPEQb0RAOqVatm9+xnOEGgl6FAnuEHTA5oTwyYHMBuZGZmKpCRkWF2A3YvcCVB +AAXoGbiURAP1SCSAgunDAcn9QC/p8/JkjwZkDwdsgEF9ONDN83flgkigffuki2p7/lq1Ei/6v0A3 +48quoFcXkHcRuABc2h6cL84jtUG9WuBwcRjJ3Bdk7AP2FnsRB/5u9Helvwv9CtDvBuRXgH43IP9h +9O9LvxuQPxqQP72A/DGBzp2DAZ06+f527SILOKBmzcgCDahaNT7QmfChToID2hFcdI7RVtAPcNEh +xsGCeoIBlQx7h3ST3uhoyN/BkN6APQi0ld6APQhUqVJF/sqVK0NfqZL0jgP1hQQBFKBn4FziB9oQ +Ux9K/MBBwAIomD4ckNwPdJS+QwfZg4D0bdrIHgSoV6AK9SkDHNAxzQD9sYH/AHAD8Cry1R2MAAAA +AElFTkSuQmCC + diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-004.xml b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-004.xml new file mode 100644 index 0000000000000000000000000000000000000000..dea54bb07c51e60e69758160afd7553f31677024 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-004.xml @@ -0,0 +1,6 @@ + +iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAIAAABqVuVZAAAAwUlEQVR4nO3ZwQ2DQBDF0Hkk/dec +CuASwXCwtBVY/mYBM3OMznEC4TsfMx1nEALk2o8ACZB/GpJBAiSD3PcgbmICJIM0MVvX/RokQDJI +E1ODvPOrS5EWIBmkialB1ntcpAVIBmli1rvTRVGAZJAmZj1AvawKkAzSxNQg6zEu0gIkgzQx69Hp +oihAMkgT85LTn1UBkkGamBpkvcdFWoBkkCZmvTtdFAVIBmli1gPUy6oAySBNTA2yHuMiLUAyyPMT ++wHXVRp4qDc0HQAAAABJRU5ErkJggg== diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-005.xml b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-005.xml new file mode 100644 index 0000000000000000000000000000000000000000..0b86a1b45fe6fcb3f02072ca13dd5fab56963444 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-005.xml @@ -0,0 +1,9 @@ + +iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAIAAABqVuVZAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAO +xAAADsQBlSsOGwAAARlJREFUeF7t2VFHQ2EcBvD3PWetVqvVam1ERESMiIiIiL7/J5p9gD1dvF51 +8eNcn4uf53n2P1ZLKUOpnkMCtYx0kkAt00F8gkAtsxFQBJpPAEWgxRGgCLScAopAq2NAEWhzAigC +3c0ARaD7U0AR6OEMUAR6nAOKQE/ngCLQ8wWgCLRdAIpAL5eAItDrFaAI9LYEFIHerwFFoI8bQBHo +cwUoAn3dAopA32tAEehnAygKjPu/xjyHBAY0WQDQL+0BBKhtYSVIgiSo6w+xiqmYiqnYX34J2SAb +ZINskA1qa4EESZAEdW1B15e7g9xBbf2VIAmSICPtDmprgQRJkAR1bUHXl7uD3EFt/ZUgCZIgI+0O +amuBBEnQP07QDo1OW207QPoWAAAAAElFTkSuQmCC + diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-006.xml b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-006.xml new file mode 100644 index 0000000000000000000000000000000000000000..5d49dbcfd78d74202ce870dc239f35f73a3b3ead --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/page-006.xml @@ -0,0 +1,6 @@ + +iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAIAAABqVuVZAAAAwUlEQVR4nO3ZwQ2DQBDF0Hkk/dec +CuASwXCwtBVY/mYBM3OMznEC4TsfMx1nEALk2o8ACZB/GpJBAiSD3PcgbmICJIM0MVvX/RokQDJI +E1ODvPOrS5EWIBmkialB1ntcpAVIBmli1rvTRVGAZJAmZj1AvawKkAzSxNQg6zEu0gIkgzQx69Hp +oihAMkgT85LTn1UBkkGamBpkvcdFWoBkkCZmvTtdFAVIBmli1gPUy6oAySBNTA2yHuMiLUAyyPMT ++wHXVRp4qDc0HQAAAABJRU5ErkJggg== diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/read/payloads.json b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/read/payloads.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/run.json b/corpus/page-lifecycle/removal/rust-followup/features/followup/run.json new file mode 100644 index 0000000000000000000000000000000000000000..6edbf0a12183a6055fa32fa6ebb441519fbf8730 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/run.json @@ -0,0 +1,19 @@ +{ + "notebook": "/private/tmp/page-removal/run/features/candidate/notebook", + "expected_pages": 7, + "author": "tools/native/page-removal-followup.ps1", + "author_timeout_seconds": 600, + "inspect": false, + "collect_notebook": true, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "author.ps1": "6e6bd74f0460457118d436345350c38594c52bd9199fb4a1e31efec3b3e954fd", + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/scripts/author.ps1 b/corpus/page-lifecycle/removal/rust-followup/features/followup/scripts/author.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..1393a1ff21e7ae5c383cf8babf1f4f21d5f0c14b --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/scripts/author.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [Parameter(Mandatory=$true)][string]$CloneHost) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +& "$PSScriptRoot\cold-current.ps1" -Root $Root -CloneHost $CloneHost +$app = New-Object -ComObject OneNote.Application +$notebook = '' +try { + $app.OpenHierarchy((Join-Path $Root 'notebook'), '', [ref]$notebook, 0) + $section = '' + $app.OpenHierarchy('Lifecycle.one', $notebook, [ref]$section, 0) + $page = '' + $app.CreateNewPage($section, [ref]$page, 2) + $title = 'Native after Rust removal ' + [char]::ConvertFromUtf32(0x1F98B) + ' e' + [char]0x0301 + $xml = 'Native body after Rust removal.]]>' + $app.UpdatePageContent($xml, [DateTime]::MinValue, 1, $false) + $app.SyncHierarchy($notebook) + [IO.File]::WriteAllText("$Root\native-title.txt", $title, [Text.Encoding]::UTF8) +} finally { + try { if ($notebook) { $app.CloseNotebook($notebook, $false) } } + finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 120 -ErrorAction SilentlyContinue + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/scripts/cold.ps1 b/corpus/page-lifecycle/removal/rust-followup/features/followup/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/scripts/read.ps1 b/corpus/page-lifecycle/removal/rust-followup/features/followup/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..476094ab1e0c47077988a6461db2080cf208642b --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/scripts/read.ps1 @@ -0,0 +1,142 @@ +param( + [Parameter(Mandatory=$true)][string]$Root, + [int]$ExpectedPages = -1, + [switch]$UseCurrentCache, + [switch]$Pdf, + [switch]$KeepOpen, + [string]$CloneHost = '' +) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +$notebook = Join-Path $root 'notebook' +$output = Join-Path $root 'read' +if (Test-Path $output) { throw 'Choose a new read destination.' } +if ($UseCurrentCache) { + if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') { + throw 'Park the personal OneNote profile before reading test notebooks.' + } +} else { + & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost +} +New-Item -ItemType Directory -Path $output | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +$failure = $null +try { + $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0) + $process = Get-Process ONENOTE + @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion; + powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } | + ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8 + $sections = @() + foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) { + $id = '' + $app.OpenHierarchy($file.FullName, '', [ref]$id, 0) + $sections += $id + } + $deadline = [DateTime]::UtcNow.AddSeconds(300) + $previous = '' + $lastChange = '' + $stableSince = [DateTime]::UtcNow + $settled = $false + do { + $pages = @{} + foreach ($section in $sections) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$xml = $hierarchy + foreach ($node in $xml.SelectNodes('//*[@path]')) { + if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) { + throw 'OneNote opened a section outside the copied notebook.' + } + } + foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) { + $id = $node.GetAttribute('ID') + $content = '' + $app.GetPageContent($id, [ref]$content, 1, 1) + $pages[$id] = $content + } + } + $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] })) + if ($signature -ne $previous) { + $lastChange = $previous + $previous = $signature + $stableSince = [DateTime]::UtcNow + } + if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or + ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and + ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $deadline) + if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) { + [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8) + $index = 0 + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + $index++ + } + $hierarchy = '' + $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8) + throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled." + } + $index = 0 + $payloads = @() + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + if ($Pdf) { + $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index) + $app.NavigateTo($id, '', $false) + $app.Publish($id, $pdfPath, 3, '') + if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' } + } + [xml]$page = $pages[$id] + foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) { + $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache')) + $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant() + [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes) + $payloads += @{ page = $id; object = $file.ParentNode.GetAttribute('objectID'); + kind = $file.LocalName; name = $file.GetAttribute('preferredName'); + sha256 = $hash; bytes = $bytes.Length } + } + $index++ + } + [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8) + $all = '' + $app.GetHierarchy($notebookId, 4, [ref]$all, 1) + [xml]$finalTree = $all + $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique) + if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) { + throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.' + } + [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8) + Write-Output "Read $($sections.Count) sections and $($pages.Count) pages." +} catch { + $failure = $_ + [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + throw +} finally { + try { + try { + if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) } + if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) { + $app.CloseNotebook($notebookId, $false) + } + } catch { + if ($null -eq $failure) { throw } + [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + } + } finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + } + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + if (-not $UseCurrentCache -and -not $CloneHost) { + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/source.json b/corpus/page-lifecycle/removal/rust-followup/features/followup/source.json new file mode 100644 index 0000000000000000000000000000000000000000..89f2898869d302da880f24f0500622da42562f67 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Lifecycle.one", + "bytes": 132280, + "sha256": "266aaf86a29b942255349dc84ec8a8d9691c965aeafd16122326a97b3acb10ee", + "mtime_ns": 1789112426557088257 + }, + { + "path": "Open Notebook.onetoc2", + "bytes": 4800, + "sha256": "f9e4761726171bdc72e946e6742603dd225f2e154298987f36f628b6d8741e78", + "mtime_ns": 1789112426559091346 + } +] diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/table-autofit.json b/corpus/page-lifecycle/removal/rust-followup/features/followup/table-autofit.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/table-autofit.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/features/followup/teardown.json b/corpus/page-lifecycle/removal/rust-followup/features/followup/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/features/followup/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/candidate/notebook/Lifecycle.one b/corpus/page-lifecycle/removal/rust-followup/leading-parent/candidate/notebook/Lifecycle.one new file mode 100644 index 0000000000000000000000000000000000000000..7fe07784b693ae51a17260afb50bafe4fb3cd360 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/leading-parent/candidate/notebook/Lifecycle.one differ diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/candidate/notebook/Open Notebook.onetoc2 b/corpus/page-lifecycle/removal/rust-followup/leading-parent/candidate/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..1dae1b4365a16cf77afdc426b8d194a6094c4959 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/leading-parent/candidate/notebook/Open Notebook.onetoc2 differ diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/commands.jsonl b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..2c37f99ded86c68753a7e4fd783ddceb880b5b18 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/commands.jsonl @@ -0,0 +1,3 @@ +{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-063C5792 -ExpectedPages 9", "exit": 0, "stdout": "Read 1 sections and 9 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/format-differences.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/format-differences.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/format-differences.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/geometry-differences.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/geometry-differences.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/geometry-differences.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/machine.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..f9216d1602f5fc6bd0625b4fbddb91c00fa88941 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/machine.json @@ -0,0 +1 @@ +{"name": "m6-063c5792", "hostname": "ONE-M6-063C5792"} diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/notebook/Lifecycle.one b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/notebook/Lifecycle.one new file mode 100644 index 0000000000000000000000000000000000000000..3383072968b32d792c5b00be44ef5b8ccf23ad20 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/notebook/Lifecycle.one differ diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/notebook/Open Notebook.onetoc2 b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..1dae1b4365a16cf77afdc426b8d194a6094c4959 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/notebook/Open Notebook.onetoc2 differ diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/pdf-format-checks.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/pdf-format-checks.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/pdf-format-checks.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/environment.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..9e05ec1c8423079d671ca080c52a44995664b654 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-063C5792", + "cold": true, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/hierarchy.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..ca97e331eff8d1687518046c7439a79b14044cdb --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-000.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..f8d107f1f3e620f09f5233ce36544bf3ac0cd5f5 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-000.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-001.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-001.xml new file mode 100644 index 0000000000000000000000000000000000000000..0950640f2a72186a7cba084de16c4612a2f2e9d7 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-001.xml @@ -0,0 +1,4 @@ + +Body grandchild 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-002.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-002.xml new file mode 100644 index 0000000000000000000000000000000000000000..4d3714d0012ec9fac7c078bf3db94d3fb639b8e8 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-002.xml @@ -0,0 +1,4 @@ + +Body parent 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-003.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-003.xml new file mode 100644 index 0000000000000000000000000000000000000000..403bd15be0b0f58a4a0872191af6d4ccaea2a32d --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-003.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-004.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-004.xml new file mode 100644 index 0000000000000000000000000000000000000000..f44adc13639a3e0949b32574a8efe968d37bd0a1 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-004.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-005.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-005.xml new file mode 100644 index 0000000000000000000000000000000000000000..53ce86660fa9b562bbbf7daffd38c9f636e70e4a --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-005.xml @@ -0,0 +1,3 @@ + +Native body after Rust removal.]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-006.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-006.xml new file mode 100644 index 0000000000000000000000000000000000000000..a0542bdf4b035130ff2a99225b955b406646aa14 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-006.xml @@ -0,0 +1,4 @@ + +Body trailing 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-007.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-007.xml new file mode 100644 index 0000000000000000000000000000000000000000..998c43fbf34ac4edb0070c408a5fd2bd6a1c4854 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-007.xml @@ -0,0 +1,4 @@ + +Body automatic 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-008.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-008.xml new file mode 100644 index 0000000000000000000000000000000000000000..abcef66e0197b993a57ffc1724b5635520b07a6e --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/page-008.xml @@ -0,0 +1,4 @@ + +Body duplicate 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/payloads.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/read/payloads.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/run.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/run.json new file mode 100644 index 0000000000000000000000000000000000000000..60af9620b50621dc4b9b5b726ce9de6a5726ab87 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/run.json @@ -0,0 +1,18 @@ +{ + "notebook": "/private/tmp/page-removal/run/leading-parent/followup/notebook", + "expected_pages": 9, + "author": null, + "author_timeout_seconds": 600, + "inspect": false, + "collect_notebook": true, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/scripts/cold.ps1 b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/scripts/read.ps1 b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..476094ab1e0c47077988a6461db2080cf208642b --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/scripts/read.ps1 @@ -0,0 +1,142 @@ +param( + [Parameter(Mandatory=$true)][string]$Root, + [int]$ExpectedPages = -1, + [switch]$UseCurrentCache, + [switch]$Pdf, + [switch]$KeepOpen, + [string]$CloneHost = '' +) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +$notebook = Join-Path $root 'notebook' +$output = Join-Path $root 'read' +if (Test-Path $output) { throw 'Choose a new read destination.' } +if ($UseCurrentCache) { + if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') { + throw 'Park the personal OneNote profile before reading test notebooks.' + } +} else { + & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost +} +New-Item -ItemType Directory -Path $output | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +$failure = $null +try { + $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0) + $process = Get-Process ONENOTE + @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion; + powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } | + ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8 + $sections = @() + foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) { + $id = '' + $app.OpenHierarchy($file.FullName, '', [ref]$id, 0) + $sections += $id + } + $deadline = [DateTime]::UtcNow.AddSeconds(300) + $previous = '' + $lastChange = '' + $stableSince = [DateTime]::UtcNow + $settled = $false + do { + $pages = @{} + foreach ($section in $sections) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$xml = $hierarchy + foreach ($node in $xml.SelectNodes('//*[@path]')) { + if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) { + throw 'OneNote opened a section outside the copied notebook.' + } + } + foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) { + $id = $node.GetAttribute('ID') + $content = '' + $app.GetPageContent($id, [ref]$content, 1, 1) + $pages[$id] = $content + } + } + $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] })) + if ($signature -ne $previous) { + $lastChange = $previous + $previous = $signature + $stableSince = [DateTime]::UtcNow + } + if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or + ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and + ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $deadline) + if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) { + [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8) + $index = 0 + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + $index++ + } + $hierarchy = '' + $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8) + throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled." + } + $index = 0 + $payloads = @() + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + if ($Pdf) { + $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index) + $app.NavigateTo($id, '', $false) + $app.Publish($id, $pdfPath, 3, '') + if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' } + } + [xml]$page = $pages[$id] + foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) { + $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache')) + $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant() + [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes) + $payloads += @{ page = $id; object = $file.ParentNode.GetAttribute('objectID'); + kind = $file.LocalName; name = $file.GetAttribute('preferredName'); + sha256 = $hash; bytes = $bytes.Length } + } + $index++ + } + [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8) + $all = '' + $app.GetHierarchy($notebookId, 4, [ref]$all, 1) + [xml]$finalTree = $all + $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique) + if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) { + throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.' + } + [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8) + Write-Output "Read $($sections.Count) sections and $($pages.Count) pages." +} catch { + $failure = $_ + [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + throw +} finally { + try { + try { + if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) } + if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) { + $app.CloseNotebook($notebookId, $false) + } + } catch { + if ($null -eq $failure) { throw } + [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + } + } finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + } + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + if (-not $UseCurrentCache -and -not $CloneHost) { + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/source.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/source.json new file mode 100644 index 0000000000000000000000000000000000000000..5bd43afecbfca65159103620a696807254fefebb --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Lifecycle.one", + "bytes": 83640, + "sha256": "07d44900cbb3e03c7ccb27fb256afee1601fb2473cfa927b1c3e20aa662c3dd4", + "mtime_ns": 1789113084012096660 + }, + { + "path": "Open Notebook.onetoc2", + "bytes": 4800, + "sha256": "99b99375297e299d0dabe97e4c156a1a2249fd0521edebae64ac3221a5955f2c", + "mtime_ns": 1789113084012236119 + } +] diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/table-autofit.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/table-autofit.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/table-autofit.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/teardown.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup-cold/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/commands.jsonl b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..ff4e17a7646b5df9c9be14fa7db35d795876606d --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/commands.jsonl @@ -0,0 +1,4 @@ +{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\author.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-9A847CAB", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-9A847CAB -ExpectedPages 9 -UseCurrentCache", "exit": 0, "stdout": "Read 1 sections and 9 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/format-differences.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/format-differences.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/format-differences.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/geometry-differences.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/geometry-differences.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/geometry-differences.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/machine.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..322696b28ec0103274fb9099155a6771a0411448 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/machine.json @@ -0,0 +1 @@ +{"name": "m6-9a847cab", "hostname": "ONE-M6-9A847CAB"} diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/native-title.txt b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/native-title.txt new file mode 100644 index 0000000000000000000000000000000000000000..1d6cb6d0262b9c45259962e524e8b806eb94dbac --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/native-title.txt @@ -0,0 +1 @@ +Native after Rust removal 🦋 é \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/notebook/Lifecycle.one b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/notebook/Lifecycle.one new file mode 100644 index 0000000000000000000000000000000000000000..3383072968b32d792c5b00be44ef5b8ccf23ad20 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/notebook/Lifecycle.one differ diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/notebook/Open Notebook.onetoc2 b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..1dae1b4365a16cf77afdc426b8d194a6094c4959 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/notebook/Open Notebook.onetoc2 differ diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/pdf-format-checks.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/pdf-format-checks.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/pdf-format-checks.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/environment.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..9ad51a471da74087fbd8037174d956f3cde710cc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-9A847CAB", + "cold": false, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/hierarchy.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..f2321523859e1724c2f36b7a94dc577fcb1d8aaa --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-000.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..9298dcbbbe758bda0abc748b4e7d56792817039b --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-000.xml @@ -0,0 +1,4 @@ + +Body automatic 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-001.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-001.xml new file mode 100644 index 0000000000000000000000000000000000000000..e81fb3389e0c5c07d2f6df7a31842b4e69c08635 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-001.xml @@ -0,0 +1,4 @@ + +Body duplicate 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-002.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-002.xml new file mode 100644 index 0000000000000000000000000000000000000000..caba52d63cf1396da7822afe5c2c48a67315504f --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-002.xml @@ -0,0 +1,4 @@ + +Body trailing 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-003.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-003.xml new file mode 100644 index 0000000000000000000000000000000000000000..1633d9ae816a1833019e3b2c0374d309d3a6513e --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-003.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-004.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-004.xml new file mode 100644 index 0000000000000000000000000000000000000000..8ed396aba33ba7970da318b21644c30d6e7d46b5 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-004.xml @@ -0,0 +1,3 @@ + +Native body after Rust removal.]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-005.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-005.xml new file mode 100644 index 0000000000000000000000000000000000000000..91f894c8b52afa812c523e188f9860f9706fda70 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-005.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-006.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-006.xml new file mode 100644 index 0000000000000000000000000000000000000000..42c2d85c414b62f40014dd72e418ec1d8011f783 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-006.xml @@ -0,0 +1,4 @@ + +Body grandchild 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-007.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-007.xml new file mode 100644 index 0000000000000000000000000000000000000000..03a4e117fdea11277906ed5c1f3f42f53da98d69 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-007.xml @@ -0,0 +1,4 @@ + +Body parent 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-008.xml b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-008.xml new file mode 100644 index 0000000000000000000000000000000000000000..1657d126f1c92d6106cb941aced314c954b53396 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/page-008.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/payloads.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/read/payloads.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/run.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/run.json new file mode 100644 index 0000000000000000000000000000000000000000..a1fc8f5f18ca882d551ca0ca57f1ce4734ce47bb --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/run.json @@ -0,0 +1,19 @@ +{ + "notebook": "/private/tmp/page-removal/run/leading-parent/candidate/notebook", + "expected_pages": 9, + "author": "tools/native/page-removal-followup.ps1", + "author_timeout_seconds": 600, + "inspect": false, + "collect_notebook": true, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "author.ps1": "6e6bd74f0460457118d436345350c38594c52bd9199fb4a1e31efec3b3e954fd", + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/scripts/author.ps1 b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/scripts/author.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..1393a1ff21e7ae5c383cf8babf1f4f21d5f0c14b --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/scripts/author.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [Parameter(Mandatory=$true)][string]$CloneHost) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +& "$PSScriptRoot\cold-current.ps1" -Root $Root -CloneHost $CloneHost +$app = New-Object -ComObject OneNote.Application +$notebook = '' +try { + $app.OpenHierarchy((Join-Path $Root 'notebook'), '', [ref]$notebook, 0) + $section = '' + $app.OpenHierarchy('Lifecycle.one', $notebook, [ref]$section, 0) + $page = '' + $app.CreateNewPage($section, [ref]$page, 2) + $title = 'Native after Rust removal ' + [char]::ConvertFromUtf32(0x1F98B) + ' e' + [char]0x0301 + $xml = 'Native body after Rust removal.]]>' + $app.UpdatePageContent($xml, [DateTime]::MinValue, 1, $false) + $app.SyncHierarchy($notebook) + [IO.File]::WriteAllText("$Root\native-title.txt", $title, [Text.Encoding]::UTF8) +} finally { + try { if ($notebook) { $app.CloseNotebook($notebook, $false) } } + finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 120 -ErrorAction SilentlyContinue + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/scripts/cold.ps1 b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/scripts/read.ps1 b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..476094ab1e0c47077988a6461db2080cf208642b --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/scripts/read.ps1 @@ -0,0 +1,142 @@ +param( + [Parameter(Mandatory=$true)][string]$Root, + [int]$ExpectedPages = -1, + [switch]$UseCurrentCache, + [switch]$Pdf, + [switch]$KeepOpen, + [string]$CloneHost = '' +) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +$notebook = Join-Path $root 'notebook' +$output = Join-Path $root 'read' +if (Test-Path $output) { throw 'Choose a new read destination.' } +if ($UseCurrentCache) { + if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') { + throw 'Park the personal OneNote profile before reading test notebooks.' + } +} else { + & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost +} +New-Item -ItemType Directory -Path $output | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +$failure = $null +try { + $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0) + $process = Get-Process ONENOTE + @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion; + powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } | + ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8 + $sections = @() + foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) { + $id = '' + $app.OpenHierarchy($file.FullName, '', [ref]$id, 0) + $sections += $id + } + $deadline = [DateTime]::UtcNow.AddSeconds(300) + $previous = '' + $lastChange = '' + $stableSince = [DateTime]::UtcNow + $settled = $false + do { + $pages = @{} + foreach ($section in $sections) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$xml = $hierarchy + foreach ($node in $xml.SelectNodes('//*[@path]')) { + if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) { + throw 'OneNote opened a section outside the copied notebook.' + } + } + foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) { + $id = $node.GetAttribute('ID') + $content = '' + $app.GetPageContent($id, [ref]$content, 1, 1) + $pages[$id] = $content + } + } + $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] })) + if ($signature -ne $previous) { + $lastChange = $previous + $previous = $signature + $stableSince = [DateTime]::UtcNow + } + if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or + ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and + ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $deadline) + if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) { + [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8) + $index = 0 + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + $index++ + } + $hierarchy = '' + $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8) + throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled." + } + $index = 0 + $payloads = @() + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + if ($Pdf) { + $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index) + $app.NavigateTo($id, '', $false) + $app.Publish($id, $pdfPath, 3, '') + if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' } + } + [xml]$page = $pages[$id] + foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) { + $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache')) + $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant() + [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes) + $payloads += @{ page = $id; object = $file.ParentNode.GetAttribute('objectID'); + kind = $file.LocalName; name = $file.GetAttribute('preferredName'); + sha256 = $hash; bytes = $bytes.Length } + } + $index++ + } + [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8) + $all = '' + $app.GetHierarchy($notebookId, 4, [ref]$all, 1) + [xml]$finalTree = $all + $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique) + if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) { + throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.' + } + [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8) + Write-Output "Read $($sections.Count) sections and $($pages.Count) pages." +} catch { + $failure = $_ + [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + throw +} finally { + try { + try { + if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) } + if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) { + $app.CloseNotebook($notebookId, $false) + } + } catch { + if ($null -eq $failure) { throw } + [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + } + } finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + } + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + if (-not $UseCurrentCache -and -not $CloneHost) { + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/source.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/source.json new file mode 100644 index 0000000000000000000000000000000000000000..a0b4ba0e793cce014c04a95e8ce91e8f014cc727 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Lifecycle.one", + "bytes": 76440, + "sha256": "4aabf26fa4960cf6d99d76c1f76906ef1023f04bf01b65ea07464ac87449ca3e", + "mtime_ns": 1789112626756437119 + }, + { + "path": "Open Notebook.onetoc2", + "bytes": 4800, + "sha256": "99b99375297e299d0dabe97e4c156a1a2249fd0521edebae64ac3221a5955f2c", + "mtime_ns": 1789112626758941541 + } +] diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/table-autofit.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/table-autofit.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/table-autofit.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/teardown.json b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/leading-parent/followup/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/candidate/notebook/Lifecycle.one b/corpus/page-lifecycle/removal/rust-followup/parent/candidate/notebook/Lifecycle.one new file mode 100644 index 0000000000000000000000000000000000000000..c24e3d33dbe8b0e1520ab626de0b76d9eb50c593 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/parent/candidate/notebook/Lifecycle.one differ diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/candidate/notebook/Open Notebook.onetoc2 b/corpus/page-lifecycle/removal/rust-followup/parent/candidate/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..4b21b2a4655ba5798579711f620169ba8703d230 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/parent/candidate/notebook/Open Notebook.onetoc2 differ diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/commands.jsonl b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..7f52bf47fb5b15fba4107471fd31e7833e6a7d3d --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/commands.jsonl @@ -0,0 +1,3 @@ +{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-E51EA1B2 -ExpectedPages 9", "exit": 0, "stdout": "Read 1 sections and 9 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/format-differences.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/format-differences.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/format-differences.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/geometry-differences.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/geometry-differences.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/geometry-differences.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/machine.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..570b75fc34d5a631e3c86e82c943e4752ec4989b --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/machine.json @@ -0,0 +1 @@ +{"name": "m6-e51ea1b2", "hostname": "ONE-M6-E51EA1B2"} diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/notebook/Lifecycle.one b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/notebook/Lifecycle.one new file mode 100644 index 0000000000000000000000000000000000000000..77e89bb5d74fc2fae18da69eda267b9907f1c4e4 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/notebook/Lifecycle.one differ diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/notebook/Open Notebook.onetoc2 b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..4b21b2a4655ba5798579711f620169ba8703d230 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/notebook/Open Notebook.onetoc2 differ diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/pdf-format-checks.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/pdf-format-checks.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/pdf-format-checks.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/environment.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..cbb69571638aff46bf12999c3411309d83ae9257 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-E51EA1B2", + "cold": true, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/hierarchy.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..a2ce47acea2d9fc6f9b840f589a0b82d9485c838 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-000.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..182a7c665bbc1f9cbb52306bc7a5606e1a99d9d4 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-000.xml @@ -0,0 +1,4 @@ + +Body trailing 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-001.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-001.xml new file mode 100644 index 0000000000000000000000000000000000000000..33caceac601c6ef1edcf48ede908fd804fd7ea24 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-001.xml @@ -0,0 +1,4 @@ + +Body duplicate 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-002.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-002.xml new file mode 100644 index 0000000000000000000000000000000000000000..153373a906d9ed1d550cf7634aaebc6c1a6755aa --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-002.xml @@ -0,0 +1,4 @@ + +Body automatic 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-003.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-003.xml new file mode 100644 index 0000000000000000000000000000000000000000..68b3002e8923753a7c3deb251aedec8f4eb2efd2 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-003.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-004.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-004.xml new file mode 100644 index 0000000000000000000000000000000000000000..4b1bbbb5232fb2226e633ad50d3005f9e7160b93 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-004.xml @@ -0,0 +1,4 @@ + +Body grandchild 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-005.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-005.xml new file mode 100644 index 0000000000000000000000000000000000000000..17d7e6acffb6e3b7679021840d2f448d1ab30f36 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-005.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-006.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-006.xml new file mode 100644 index 0000000000000000000000000000000000000000..0c942bdf032eef7ec552066297a4470d0aa2641d --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-006.xml @@ -0,0 +1,3 @@ + +Native body after Rust removal.]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-007.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-007.xml new file mode 100644 index 0000000000000000000000000000000000000000..416598cb23bbe1a18ac8d6a3e40d0f8d808af126 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-007.xml @@ -0,0 +1,4 @@ + +Body child 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-008.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-008.xml new file mode 100644 index 0000000000000000000000000000000000000000..c8d7cd9b92cf1a8e409ae6471fd120d35072ea49 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/page-008.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/payloads.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/read/payloads.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/run.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/run.json new file mode 100644 index 0000000000000000000000000000000000000000..9c0f9a413a9bada6bc6e413cb34951453ae421df --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/run.json @@ -0,0 +1,18 @@ +{ + "notebook": "/private/tmp/page-removal/run/parent/followup/notebook", + "expected_pages": 9, + "author": null, + "author_timeout_seconds": 600, + "inspect": false, + "collect_notebook": true, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/scripts/cold.ps1 b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/scripts/read.ps1 b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..476094ab1e0c47077988a6461db2080cf208642b --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/scripts/read.ps1 @@ -0,0 +1,142 @@ +param( + [Parameter(Mandatory=$true)][string]$Root, + [int]$ExpectedPages = -1, + [switch]$UseCurrentCache, + [switch]$Pdf, + [switch]$KeepOpen, + [string]$CloneHost = '' +) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +$notebook = Join-Path $root 'notebook' +$output = Join-Path $root 'read' +if (Test-Path $output) { throw 'Choose a new read destination.' } +if ($UseCurrentCache) { + if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') { + throw 'Park the personal OneNote profile before reading test notebooks.' + } +} else { + & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost +} +New-Item -ItemType Directory -Path $output | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +$failure = $null +try { + $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0) + $process = Get-Process ONENOTE + @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion; + powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } | + ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8 + $sections = @() + foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) { + $id = '' + $app.OpenHierarchy($file.FullName, '', [ref]$id, 0) + $sections += $id + } + $deadline = [DateTime]::UtcNow.AddSeconds(300) + $previous = '' + $lastChange = '' + $stableSince = [DateTime]::UtcNow + $settled = $false + do { + $pages = @{} + foreach ($section in $sections) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$xml = $hierarchy + foreach ($node in $xml.SelectNodes('//*[@path]')) { + if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) { + throw 'OneNote opened a section outside the copied notebook.' + } + } + foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) { + $id = $node.GetAttribute('ID') + $content = '' + $app.GetPageContent($id, [ref]$content, 1, 1) + $pages[$id] = $content + } + } + $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] })) + if ($signature -ne $previous) { + $lastChange = $previous + $previous = $signature + $stableSince = [DateTime]::UtcNow + } + if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or + ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and + ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $deadline) + if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) { + [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8) + $index = 0 + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + $index++ + } + $hierarchy = '' + $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8) + throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled." + } + $index = 0 + $payloads = @() + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + if ($Pdf) { + $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index) + $app.NavigateTo($id, '', $false) + $app.Publish($id, $pdfPath, 3, '') + if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' } + } + [xml]$page = $pages[$id] + foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) { + $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache')) + $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant() + [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes) + $payloads += @{ page = $id; object = $file.ParentNode.GetAttribute('objectID'); + kind = $file.LocalName; name = $file.GetAttribute('preferredName'); + sha256 = $hash; bytes = $bytes.Length } + } + $index++ + } + [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8) + $all = '' + $app.GetHierarchy($notebookId, 4, [ref]$all, 1) + [xml]$finalTree = $all + $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique) + if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) { + throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.' + } + [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8) + Write-Output "Read $($sections.Count) sections and $($pages.Count) pages." +} catch { + $failure = $_ + [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + throw +} finally { + try { + try { + if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) } + if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) { + $app.CloseNotebook($notebookId, $false) + } + } catch { + if ($null -eq $failure) { throw } + [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + } + } finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + } + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + if (-not $UseCurrentCache -and -not $CloneHost) { + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/source.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/source.json new file mode 100644 index 0000000000000000000000000000000000000000..8c4bcdeee919f316a3182ceaec6f30c23a4aaca3 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Lifecycle.one", + "bytes": 62856, + "sha256": "e1c9e25eb5bb28c90ecd2f9bd627497489382e485c47efbc38e719880e7c0f3d", + "mtime_ns": 1789111825549418917 + }, + { + "path": "Open Notebook.onetoc2", + "bytes": 4800, + "sha256": "a0530a11d189eaf6c64273f6997ca3a2e953ab911798993420b872794d1d3cbc", + "mtime_ns": 1789111825549583834 + } +] diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/table-autofit.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/table-autofit.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/table-autofit.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/teardown.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup-cold/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/commands.jsonl b/corpus/page-lifecycle/removal/rust-followup/parent/followup/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..9a38c1d06cd642b4bb6812561346d086e909ce23 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/commands.jsonl @@ -0,0 +1,4 @@ +{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\author.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-E0700600", "exit": 0, "stdout": "", "stderr": "", "error": null} +{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-E0700600 -ExpectedPages 9 -UseCurrentCache", "exit": 0, "stdout": "Read 1 sections and 9 pages.\r\n", "stderr": "", "error": null} +{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null} diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/format-differences.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup/format-differences.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/format-differences.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/geometry-differences.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup/geometry-differences.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/geometry-differences.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/machine.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..58ddf44e426c7ff6d659dc2e09f098b4ea62e98f --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/machine.json @@ -0,0 +1 @@ +{"name": "m6-e0700600", "hostname": "ONE-M6-E0700600"} diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/native-title.txt b/corpus/page-lifecycle/removal/rust-followup/parent/followup/native-title.txt new file mode 100644 index 0000000000000000000000000000000000000000..1d6cb6d0262b9c45259962e524e8b806eb94dbac --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/native-title.txt @@ -0,0 +1 @@ +Native after Rust removal 🦋 é \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/notebook/Lifecycle.one b/corpus/page-lifecycle/removal/rust-followup/parent/followup/notebook/Lifecycle.one new file mode 100644 index 0000000000000000000000000000000000000000..49437dba925e8a3e684cfcccacaf6a6c279bf373 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/parent/followup/notebook/Lifecycle.one differ diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/notebook/Open Notebook.onetoc2 b/corpus/page-lifecycle/removal/rust-followup/parent/followup/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..4b21b2a4655ba5798579711f620169ba8703d230 Binary files /dev/null and b/corpus/page-lifecycle/removal/rust-followup/parent/followup/notebook/Open Notebook.onetoc2 differ diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/pdf-format-checks.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup/pdf-format-checks.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/pdf-format-checks.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/environment.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/environment.json new file mode 100644 index 0000000000000000000000000000000000000000..33936a4a44f62cca5f2695411462577b5aba822c --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/environment.json @@ -0,0 +1,7 @@ +{ + "powershell": "5.1.14409.1005", + "schema": "xs2010", + "hostname": "ONE-M6-E0700600", + "cold": false, + "onenote": "14.0.4763.1000" +} diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/hierarchy.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/hierarchy.xml new file mode 100644 index 0000000000000000000000000000000000000000..052cb1f195ad1e755e5610ccf627aead83a3ea6d --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/hierarchy.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-000.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..c70617f7dc55405d0d76d844475dcd38aed4291e --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-000.xml @@ -0,0 +1,4 @@ + +Body child 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-001.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-001.xml new file mode 100644 index 0000000000000000000000000000000000000000..5ca7645eb6045823acb2ce8b818ac9b4202572d6 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-001.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-002.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-002.xml new file mode 100644 index 0000000000000000000000000000000000000000..dd99c619f8ebe470b08df5eb0e0b06f6f8fba370 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-002.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-003.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-003.xml new file mode 100644 index 0000000000000000000000000000000000000000..42dc5a8822f99a3854c4c0fd1d06912dde0d857e --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-003.xml @@ -0,0 +1,4 @@ + +Body grandchild 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-004.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-004.xml new file mode 100644 index 0000000000000000000000000000000000000000..617f8cadd8c53f8b51f2ee687c5e995ecc2a6582 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-004.xml @@ -0,0 +1,2 @@ + + diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-005.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-005.xml new file mode 100644 index 0000000000000000000000000000000000000000..21034b0c616812c48c64dc059595ab966257011b --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-005.xml @@ -0,0 +1,3 @@ + +Native body after Rust removal.]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-006.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-006.xml new file mode 100644 index 0000000000000000000000000000000000000000..3dc9610328c1dac6527ec48cc49b2e092f7167b9 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-006.xml @@ -0,0 +1,4 @@ + +Body duplicate 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-007.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-007.xml new file mode 100644 index 0000000000000000000000000000000000000000..aaac10673bbd220fb5104ad677b951e826e61500 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-007.xml @@ -0,0 +1,4 @@ + +Body automatic 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-008.xml b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-008.xml new file mode 100644 index 0000000000000000000000000000000000000000..3c3477ba64cc94398c54e05697781bcabef59590 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/page-008.xml @@ -0,0 +1,4 @@ + +Body trailing 🦀 é preserved]]> diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/payloads.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/read/payloads.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/run.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup/run.json new file mode 100644 index 0000000000000000000000000000000000000000..2db66012badd1d3a40daa0a7c952fd271cb98569 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/run.json @@ -0,0 +1,19 @@ +{ + "notebook": "/private/tmp/page-removal/run/parent/candidate/notebook", + "expected_pages": 9, + "author": "tools/native/page-removal-followup.ps1", + "author_timeout_seconds": 600, + "inspect": false, + "collect_notebook": true, + "base": { + "file": "win7-office-base.qcow2", + "format": "qcow2", + "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", + "virtual_size": 68719476736 + }, + "scripts": { + "author.ps1": "6e6bd74f0460457118d436345350c38594c52bd9199fb4a1e31efec3b3e954fd", + "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", + "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/scripts/author.ps1 b/corpus/page-lifecycle/removal/rust-followup/parent/followup/scripts/author.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..1393a1ff21e7ae5c383cf8babf1f4f21d5f0c14b --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/scripts/author.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [Parameter(Mandatory=$true)][string]$CloneHost) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +& "$PSScriptRoot\cold-current.ps1" -Root $Root -CloneHost $CloneHost +$app = New-Object -ComObject OneNote.Application +$notebook = '' +try { + $app.OpenHierarchy((Join-Path $Root 'notebook'), '', [ref]$notebook, 0) + $section = '' + $app.OpenHierarchy('Lifecycle.one', $notebook, [ref]$section, 0) + $page = '' + $app.CreateNewPage($section, [ref]$page, 2) + $title = 'Native after Rust removal ' + [char]::ConvertFromUtf32(0x1F98B) + ' e' + [char]0x0301 + $xml = 'Native body after Rust removal.]]>' + $app.UpdatePageContent($xml, [DateTime]::MinValue, 1, $false) + $app.SyncHierarchy($notebook) + [IO.File]::WriteAllText("$Root\native-title.txt", $title, [Text.Encoding]::UTF8) +} finally { + try { if ($notebook) { $app.CloseNotebook($notebook, $false) } } + finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 120 -ErrorAction SilentlyContinue + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/scripts/cold.ps1 b/corpus/page-lifecycle/removal/rust-followup/parent/followup/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/scripts/cold.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '') +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' } +$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote' +if ($CloneHost) { + if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) { + throw 'The disposable clone hostname does not match this machine.' + } + New-Item "$key\Options\Paths" -Force | Out-Null + New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null +} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or + -not (Test-Path 'C:\one-tests\profile-original-cache')) { + throw 'Park the personal OneNote profile before resetting the test cache.' +} +$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0' +$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root)) +if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' } +New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null +if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked } +if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse } +New-Item "$key\OpenNotebooks" | Out-Null +New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/scripts/read.ps1 b/corpus/page-lifecycle/removal/rust-followup/parent/followup/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..476094ab1e0c47077988a6461db2080cf208642b --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/scripts/read.ps1 @@ -0,0 +1,142 @@ +param( + [Parameter(Mandatory=$true)][string]$Root, + [int]$ExpectedPages = -1, + [switch]$UseCurrentCache, + [switch]$Pdf, + [switch]$KeepOpen, + [string]$CloneHost = '' +) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$root = [IO.Path]::GetFullPath($Root).TrimEnd('\') +if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') { + throw 'Choose a run directly below C:\one-tests\runs.' +} +$notebook = Join-Path $root 'notebook' +$output = Join-Path $root 'read' +if (Test-Path $output) { throw 'Choose a new read destination.' } +if ($UseCurrentCache) { + if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') { + throw 'Park the personal OneNote profile before reading test notebooks.' + } +} else { + & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost +} +New-Item -ItemType Directory -Path $output | Out-Null +$app = New-Object -ComObject OneNote.Application +$notebookId = '' +$failure = $null +try { + $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0) + $process = Get-Process ONENOTE + @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion; + powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } | + ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8 + $sections = @() + foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) { + $id = '' + $app.OpenHierarchy($file.FullName, '', [ref]$id, 0) + $sections += $id + } + $deadline = [DateTime]::UtcNow.AddSeconds(300) + $previous = '' + $lastChange = '' + $stableSince = [DateTime]::UtcNow + $settled = $false + do { + $pages = @{} + foreach ($section in $sections) { + $hierarchy = '' + $app.GetHierarchy($section, 4, [ref]$hierarchy, 1) + [xml]$xml = $hierarchy + foreach ($node in $xml.SelectNodes('//*[@path]')) { + if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) { + throw 'OneNote opened a section outside the copied notebook.' + } + } + foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) { + $id = $node.GetAttribute('ID') + $content = '' + $app.GetPageContent($id, [ref]$content, 1, 1) + $pages[$id] = $content + } + } + $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] })) + if ($signature -ne $previous) { + $lastChange = $previous + $previous = $signature + $stableSince = [DateTime]::UtcNow + } + if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or + ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and + ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break } + Start-Sleep -Milliseconds 250 + } while ([DateTime]::UtcNow -lt $deadline) + if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) { + [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8) + $index = 0 + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + $index++ + } + $hierarchy = '' + $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1) + [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8) + throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled." + } + $index = 0 + $payloads = @() + foreach ($id in @($pages.Keys | Sort-Object)) { + [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8) + if ($Pdf) { + $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index) + $app.NavigateTo($id, '', $false) + $app.Publish($id, $pdfPath, 3, '') + if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' } + } + [xml]$page = $pages[$id] + foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) { + $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache')) + $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant() + [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes) + $payloads += @{ page = $id; object = $file.ParentNode.GetAttribute('objectID'); + kind = $file.LocalName; name = $file.GetAttribute('preferredName'); + sha256 = $hash; bytes = $bytes.Length } + } + $index++ + } + [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8) + $all = '' + $app.GetHierarchy($notebookId, 4, [ref]$all, 1) + [xml]$finalTree = $all + $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique) + if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) { + throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.' + } + [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8) + Write-Output "Read $($sections.Count) sections and $($pages.Count) pages." +} catch { + $failure = $_ + [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + throw +} finally { + try { + try { + if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) } + if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) { + $app.CloseNotebook($notebookId, $false) + } + } catch { + if ($null -eq $failure) { throw } + [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8) + } + } finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + } + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + if (-not $UseCurrentCache -and -not $CloneHost) { + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10 + } +} diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/source.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup/source.json new file mode 100644 index 0000000000000000000000000000000000000000..f73e5732ca13c896dfeda82225eed03d39252e21 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Lifecycle.one", + "bytes": 56688, + "sha256": "f7b163e466afb5c13530beefd2bb7b3303f08ed42f5a6f817a805abf7005d65b", + "mtime_ns": 1789111621160316671 + }, + { + "path": "Open Notebook.onetoc2", + "bytes": 4800, + "sha256": "a0530a11d189eaf6c64273f6997ca3a2e953ab911798993420b872794d1d3cbc", + "mtime_ns": 1789111621162916678 + } +] diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/table-autofit.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup/table-autofit.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/table-autofit.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/corpus/page-lifecycle/removal/rust-followup/parent/followup/teardown.json b/corpus/page-lifecycle/removal/rust-followup/parent/followup/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/parent/followup/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/corpus/page-lifecycle/removal/rust-followup/provenance.json b/corpus/page-lifecycle/removal/rust-followup/provenance.json new file mode 100644 index 0000000000000000000000000000000000000000..c85c2973f1176eaa1643ad5017ae1210ac335ea4 --- /dev/null +++ b/corpus/page-lifecycle/removal/rust-followup/provenance.json @@ -0,0 +1,77 @@ +{ + "generator": "ONESTORE_PAGE_REMOVAL_OUTPUT on removal_matches_all_native_cases_and_retains_every_old_revision", + "author_script": "tools/native/page-removal-followup.ps1", + "cases": { + "parent": { + "candidate_sha256": "f7b163e466afb5c13530beefd2bb7b3303f08ed42f5a6f817a805abf7005d65b", + "captures": { + "followup": { + "clone": "m6-e0700600", + "teardown_absent": true, + "notebook_sha256": "e1c9e25eb5bb28c90ecd2f9bd627497489382e485c47efbc38e719880e7c0f3d", + "pages": 9 + }, + "followup-cold": { + "clone": "m6-e51ea1b2", + "teardown_absent": true, + "notebook_sha256": "253fde4f872bb302632de20eb977180114ab1776d6032cc4e9e46905ff3e6366", + "pages": 9 + } + } + }, + "all": { + "candidate_sha256": "645169c4f76dedd0deb0c928a08339479763ae607c21b7f604c21d823eee53eb", + "captures": { + "followup": { + "clone": "m6-4c5ff100", + "teardown_absent": true, + "notebook_sha256": "36a33c1d7f8a13d93f6bcb0ea9431d5524a1f43aa8a8e121dc2c735e076cc029", + "pages": 1 + }, + "followup-cold": { + "clone": "m6-9d41f146", + "teardown_absent": true, + "notebook_sha256": "36a33c1d7f8a13d93f6bcb0ea9431d5524a1f43aa8a8e121dc2c735e076cc029", + "pages": 1 + } + } + }, + "features": { + "candidate_sha256": "266aaf86a29b942255349dc84ec8a8d9691c965aeafd16122326a97b3acb10ee", + "captures": { + "followup": { + "clone": "m6-49b2cf78", + "teardown_absent": true, + "notebook_sha256": "4c51e1d89b121d74fc15945b3d4e83698956d274bfa092f21a12a817f3acd95a", + "pages": 7 + }, + "followup-cold": { + "clone": "m6-53651ab4", + "teardown_absent": true, + "notebook_sha256": "5d7d209a9320bcd932879da30bd0b6d54e87b7ee85766037bf69954d3f2d66b0", + "pages": 7 + } + } + }, + "leading-parent": { + "candidate_sha256": "4aabf26fa4960cf6d99d76c1f76906ef1023f04bf01b65ea07464ac87449ca3e", + "captures": { + "followup": { + "clone": "m6-9a847cab", + "teardown_absent": true, + "notebook_sha256": "07d44900cbb3e03c7ccb27fb256afee1601fb2473cfa927b1c3e20aa662c3dd4", + "pages": 9 + }, + "followup-cold": { + "clone": "m6-063c5792", + "teardown_absent": true, + "notebook_sha256": "07d44900cbb3e03c7ccb27fb256afee1601fb2473cfa927b1c3e20aa662c3dd4", + "pages": 9 + } + } + } + }, + "rejected_attempts": [ + "features followup-cold and leading-parent followup first ran while the lab configuration was moving to .env and failed before booting a clone; both were rerun" + ] +} diff --git a/crates/onestore/tests/page_deletion.rs b/crates/onestore/tests/page_deletion.rs index 1dd8deab5e63a8f5396de11c9d8e79ffc432b44c..c3ceaa3706ce9617c7fcd4a6edfee76340991093 100644 --- a/crates/onestore/tests/page_deletion.rs +++ b/crates/onestore/tests/page_deletion.rs @@ -1,6 +1,6 @@ use onestore::{ ExGuid, FileDataReference, ObjectData, PreparedEdit, PropertySets, RevisionIndex, Store, - document::Document, + document::{Document, Kind}, }; use std::collections::BTreeSet; @@ -177,6 +177,82 @@ fn removal_rejects_duplicate_missing_and_non_page_spaces_without_publication() { use page_schedule::{current, disk}; +/// OneNote created a page in each section after Rust permanently removed pages from it; +/// the surviving pages keep their revisions through the native save and a cold reopen. +#[test] +fn native_pages_created_after_rust_removal_reopen_cold() { + let root = std::path::Path::new(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../corpus/page-lifecycle/removal/rust-followup" + )); + for case in ["parent", "all", "features", "leading-parent"] { + let read = |phase: &str| { + std::fs::read(root.join(case).join(phase).join("notebook/Lifecycle.one")).unwrap() + }; + let (candidate, followup, cold) = + (read("candidate"), read("followup"), read("followup-cold")); + let title = + std::fs::read_to_string(root.join(case).join("followup/native-title.txt")).unwrap(); + let title = title.trim_start_matches('\u{feff}').trim_end(); + let stores = [&candidate, &followup, &cold].map(|bytes| Store::parse(bytes).unwrap()); + let indexes = stores.each_ref().map(|store| { + assert!(store.checksum_mismatches.is_empty()); + let index = RevisionIndex::parse(store).unwrap(); + index.validate_current().unwrap(); + index + }); + let documents = indexes + .each_ref() + .map(|index| Document::parse(index).unwrap()); + let before = documents[0].pages().unwrap(); + for (document, index) in documents[1..].iter().zip(&indexes[1..]) { + let pages = document.pages().unwrap(); + assert_eq!(pages.len(), before.len() + 1, "{case}"); + assert_eq!( + &pages[..before.len()], + before.as_slice(), + "{case}: surviving page order" + ); + for (sid, _) in &before { + assert_eq!( + index.active(*sid).unwrap(), + indexes[0].active(*sid).unwrap(), + "{case}: {sid}" + ); + } + let (native, page) = pages[before.len()]; + let revision = document.active(native).unwrap(); + let metadata = revision + .roots + .get(&2) + .and_then(|id| revision.nodes.get(id)) + .unwrap(); + assert!( + matches!(&metadata.kind, Kind::Metadata { title: Some(name), .. } if name == title), + "{case}" + ); + let body = revision + .parents(&[page]) + .unwrap() + .into_keys() + .any(|id| matches!(&revision.nodes[&id].kind, Kind::RichText { text, .. } if text == "Native body after Rust removal.")); + assert!(body, "{case}: native body"); + } + // The section root may gain OneNote's per-series navigation metadata copies on open. + for sid in indexes[1] + .spaces + .keys() + .filter(|sid| **sid != indexes[1].root) + { + assert_eq!( + indexes[1].active(*sid).unwrap(), + indexes[2].active(*sid).unwrap(), + "{case}: cold reopen changed {sid}" + ); + } + } +} + #[test] fn tombstones_and_first_page_promotion_survive_each_storage_interruption() { let leading = include_bytes!( diff --git a/tools/codex_usage_report.example.json b/tools/codex_usage_report.example.json deleted file mode 100644 index ce4d61158f7a0178d789b7024e83fde0b696b363..0000000000000000000000000000000000000000 --- a/tools/codex_usage_report.example.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "threadIds": ["01a078fb-d5d2-7ad0-97f0-d14db4e4c94e"], - "sources": [ - "/Users/clo/.codex/sessions/2026/09/06/rollout-2026-09-06T16-09-25-01a078fb-d5d2-7ad0-97f0-d14db4e4c94e.jsonl" - ], - "boundaries": [ - { - "name": "Planning discussion", - "kind": "discussion", - "end": "2026-09-07T01:28:56Z" - }, - { - "name": "Canvas goal", - "kind": "goal", - "start": "2026-09-07T01:28:56Z" - } - ] -} diff --git a/tools/codex_usage_report.mjs b/tools/codex_usage_report.mjs deleted file mode 100644 index 45cd27ff308511f6c86df1d755459e533085ea16..0000000000000000000000000000000000000000 --- a/tools/codex_usage_report.mjs +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env node - -import { mkdir, readFile, readdir, writeFile } from "node:fs/promises"; -import { homedir } from "node:os"; -import { dirname, join, resolve } from "node:path"; - -const rates = { input: 10, cached: 1, output: 50 }; - -function usageCost(usage) { - return ((usage.input_tokens - usage.cached_input_tokens) * rates.input - + usage.cached_input_tokens * rates.cached - + usage.output_tokens * rates.output) / 1_000_000; -} - -function duration(ms) { - const seconds = Math.round(ms / 1000); - const hours = Math.floor(seconds / 3600); - const minutes = Math.floor((seconds % 3600) / 60); - const remainder = seconds % 60; - return hours ? `${hours}h ${minutes}m` : minutes ? `${minutes}m ${remainder}s` : `${remainder}s`; -} - -function unionDuration(intervals) { - const merged = []; - for (const interval of intervals.sort((a, b) => a[0] - b[0])) { - const previous = merged.at(-1); - if (previous && interval[0] <= previous[1]) previous[1] = Math.max(previous[1], interval[1]); - else merged.push([...interval]); - } - return merged.reduce((total, [start, end]) => total + end - start, 0); -} - -async function filesBelow(root) { - const entries = await readdir(root, { withFileTypes: true }); - const nested = await Promise.all(entries.map((entry) => { - const path = join(root, entry.name); - return entry.isDirectory() ? filesBelow(path) : entry.name.endsWith(".jsonl") ? [path] : []; - })); - return nested.flat(); -} - -async function loadEvents(paths) { - const events = []; - for (const path of paths) { - for (const line of (await readFile(path, "utf8")).trim().split("\n")) { - if (!line) continue; - try { events.push(JSON.parse(line)); } catch { /* Ignore a partially-written final line. */ } - } - } - return events; -} - -function snapshotBefore(records, time) { - return records.filter((record) => Date.parse(record.timestamp) <= time).at(-1)?.payload.thread_token_usage; -} - -function taskRows(events, start, end) { - const tasks = new Map(); - for (const event of events) { - if (event.type !== "event_msg" || !["task_started", "task_complete"].includes(event.payload.type)) continue; - const prior = tasks.get(event.payload.turn_id) ?? {}; - tasks.set(event.payload.turn_id, { ...prior, ...event.payload }); - } - return [...tasks.values()].filter((task) => task.started_at >= start && task.started_at < end && task.duration_ms); -} - -function toolIntervals(events, start, end) { - return events.flatMap((event) => { - const item = event.type === "event_msg" && event.payload.type === "item_completed" ? event.payload.item : undefined; - if (!item || !["CommandExecution", "McpToolCall"].includes(item.type)) return []; - const milliseconds = (item.duration?.secs ?? 0) * 1000 + (item.duration?.nanos ?? 0) / 1_000_000; - const finished = Date.parse(event.timestamp); - return milliseconds && finished >= start && finished < end ? [[finished - milliseconds, finished]] : []; - }); -} - -function segment(events, records, boundary, previous, finalTime) { - const start = boundary.start ? Date.parse(boundary.start) : -Infinity; - const end = boundary.end ? Date.parse(boundary.end) : finalTime; - const tasks = taskRows(events, start, end); - const tools = toolIntervals(events, start, end); - const finish = snapshotBefore(records, end); - const begin = previous ?? snapshotBefore(records, start); - return { - name: boundary.name, - kind: boundary.kind ?? "segment", - cost: finish ? usageCost(finish) - (begin ? usageCost(begin) : 0) : null, - durationMs: tasks.reduce((total, task) => total + task.duration_ms, 0), - apiTtftMs: tasks.reduce((total, task) => total + (task.time_to_first_token_ms ?? 0), 0), - toolWallMs: unionDuration(tools), - toolCoreMs: tools.reduce((total, [startTime, endTime]) => total + endTime - startTime, 0), - }; -} - -function chart(segments) { - const maximum = Math.max(...segments.map((segment) => segment.cost ?? 0), 1); - return segments.map((segment) => `
${escapeHtml(segment.name)}
${segment.cost === null ? "—" : `$${segment.cost.toFixed(2)}`}
`).join("\n"); -} - -function escapeHtml(value) { - return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">"); -} - -function html(report) { - const rows = report.segments.map((segment) => `${escapeHtml(segment.name)}${segment.kind}${segment.cost === null ? "—" : `$${segment.cost.toFixed(2)}`}${duration(segment.durationMs)}${duration(segment.toolWallMs)}${duration(segment.toolCoreMs)}≥${duration(segment.apiTtftMs)}`).join("\n"); - return `Codex usage report

Codex usage report

API-equivalent rate: Astra standard ($10/M uncached input, $1/M cached input, $50/M output). Tool core time counts parallel calls separately; API TTFT is a lower bound.

Cost

${chart(report.segments)}

Segments

${rows}
SegmentTypeCostDurationTool wallTool coreRecorded API
`; -} - -function usage() { - console.error("Usage: node tools/codex_usage_report.mjs report.json [output.html]"); - process.exit(1); -} - -const [configPath, outputPath] = process.argv.slice(2); -if (!configPath) usage(); -const config = JSON.parse(await readFile(resolve(configPath), "utf8")); -const codexHome = config.codexHome ?? join(homedir(), ".codex"); -const paths = config.sources ?? (await Promise.all(["sessions", "archived_sessions"].map((name) => filesBelow(join(codexHome, name))))).flat(); -const events = await loadEvents(paths); -const selected = events.filter((event) => event.type !== "token_usage_record" || config.threadIds.includes(event.payload.thread_id)); -const records = selected.filter((event) => event.type === "token_usage_record").sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp)); -if (!records.length) throw new Error("No token records matched config.threadIds."); -const finalTime = Date.parse(records.at(-1).timestamp); -const segments = config.boundaries.map((boundary, index) => segment(selected, records, boundary, index ? snapshotBefore(records, Date.parse(config.boundaries[index - 1].end ?? new Date(finalTime).toISOString())) : undefined, finalTime)); -const report = { threadIds: config.threadIds, total: usageCost(records.at(-1).payload.thread_token_usage), segments }; -if (outputPath) { - await mkdir(dirname(resolve(outputPath)), { recursive: true }); - await writeFile(resolve(outputPath), html(report)); -} else console.log(JSON.stringify(report, null, 2)); diff --git a/tools/native/page-removal-followup.ps1 b/tools/native/page-removal-followup.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..1393a1ff21e7ae5c383cf8babf1f4f21d5f0c14b --- /dev/null +++ b/tools/native/page-removal-followup.ps1 @@ -0,0 +1,27 @@ +param([Parameter(Mandatory=$true)][string]$Root, [Parameter(Mandatory=$true)][string]$CloneHost) +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +& "$PSScriptRoot\cold-current.ps1" -Root $Root -CloneHost $CloneHost +$app = New-Object -ComObject OneNote.Application +$notebook = '' +try { + $app.OpenHierarchy((Join-Path $Root 'notebook'), '', [ref]$notebook, 0) + $section = '' + $app.OpenHierarchy('Lifecycle.one', $notebook, [ref]$section, 0) + $page = '' + $app.CreateNewPage($section, [ref]$page, 2) + $title = 'Native after Rust removal ' + [char]::ConvertFromUtf32(0x1F98B) + ' e' + [char]0x0301 + $xml = 'Native body after Rust removal.]]>' + $app.UpdatePageContent($xml, [DateTime]::MinValue, 1, $false) + $app.SyncHierarchy($notebook) + [IO.File]::WriteAllText("$Root\native-title.txt", $title, [Text.Encoding]::UTF8) +} finally { + try { if ($notebook) { $app.CloseNotebook($notebook, $false) } } + finally { + [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) + $app = $null + [GC]::Collect() + [GC]::WaitForPendingFinalizers() + Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 120 -ErrorAction SilentlyContinue + } +} diff --git a/tools/test_page_removal.py b/tools/test_page_removal.py index e3ae6bbb25f427b19682063c8a26178d2eada497..a974d31aef6ad7f7c343db91a75bc0f8c26cd972 100644 --- a/tools/test_page_removal.py +++ b/tools/test_page_removal.py @@ -59,3 +59,22 @@ class PageRemovalTest(unittest.TestCase): if __name__ == '__main__': unittest.main() + + +class RustRemovalFollowupTest(unittest.TestCase): + """OneNote creates a page in a section after Rust removed pages from it, then reopens cold.""" + + CASES = ('parent', 'all', 'features', 'leading-parent') + + def test_native_page_after_rust_removal_matches_its_captures(self): + for case in self.CASES: + with self.subTest(case=case): + root = FIXTURE / 'rust-followup' / case + title = (root / 'followup/native-title.txt').read_text(encoding='utf-8-sig') + self.assertTrue(title.startswith('Native after Rust removal')) + for phase in ('followup', 'followup-cold'): + compare(root / phase / 'notebook', root / phase / 'read') + hierarchy = ET.parse(root / phase / 'read/hierarchy.xml').getroot() + names = [page.get('name') for page in hierarchy.findall('.//one:Page', ns)] + self.assertEqual(names.count(title), 1, names) + self.assertTrue(json.loads((root / phase / 'teardown.json').read_text())['absent']) diff --git a/tools/zenith-locks b/tools/zenith-locks deleted file mode 100755 index 095817d79bc5bb93f0b901b9db7720ccc1ce2136..0000000000000000000000000000000000000000 --- a/tools/zenith-locks +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env python3 -"""Watch SMB byte-range locks on zenith (TrueNAS) as OneNote takes and drops them.""" -import json -import os -import subprocess -import sys -import time - -HOST = "10.0.0.1" -SSH = ["ssh", "-o", "BatchMode=yes", "-o", "ControlMaster=auto", - "-o", "ControlPath=/tmp/zenith-locks-%r@%h:%p", "-o", "ControlPersist=30s", HOST] -SEP = "--%%--" - - -def agent_socket(): - """SSH_AUTH_SOCK is usually absent here; find a live agent that holds an identity.""" - pids = subprocess.run(["pgrep", "ssh-agent"], capture_output=True, text=True).stdout.split() - for pid in pids: - out = subprocess.run(["lsof", "-a", "-U", "-p", pid], capture_output=True, text=True).stdout - for word in out.split(): - if "/agent." not in word: - continue - env = dict(os.environ, SSH_AUTH_SOCK=word) - if subprocess.run(["ssh-add", "-l"], env=env, capture_output=True).returncode == 0: - return word - return None - - -def connect(): - if subprocess.run(SSH + ["true"], capture_output=True).returncode == 0: - return - sock = agent_socket() - if sock is None: - sys.exit("no ssh-agent with a loaded identity; cannot reach %s" % HOST) - os.environ["SSH_AUTH_SOCK"] = sock - if subprocess.run(SSH + ["true"], capture_output=True).returncode != 0: - sys.exit("ssh %s failed even with agent %s" % (HOST, sock)) - - -def status(*views): - cmd = (" ; echo %s ; " % SEP).join("midclt call smb.status " + v for v in views) - out = subprocess.run(SSH + [cmd], capture_output=True, text=True) - if out.returncode != 0: - sys.exit(out.stderr.strip() or "ssh failed") - return [json.loads(chunk) for chunk in out.stdout.split(SEP + "\n")] - - -def locks(byterange): - """(share_path, file, pid, type, start, size) per held range.""" - for entry in byterange: - for lk in entry["locks"]: - yield (entry["share_path"], entry["file_name"], lk["server_id"]["pid"], - lk["type"], lk["start"], lk["size"]) - - -def table(rows, headers): - rows = [[str(c) for c in r] for r in rows] - width = [max(len(h), *(len(r[i]) for r in rows)) for i, h in enumerate(headers)] - for r in [headers] + rows: - print(" ".join(c.ljust(w) for c, w in zip(r, width)).rstrip()) - - -def show_locks(): - byterange, openfiles, shares = status("BYTERANGE", "LOCKS", "SHARES") - held = sorted(locks(byterange)) - if not held: - print("no byte-range locks held") - return - machine = {s["server_id"]["pid"]: s["machine"] for s in shares} - user = {} - for f in openfiles: - for o in f["opens"].values(): - user[(f["service_path"], f["filename"], o["server_id"]["pid"])] = o["username"] - rows = [] - for share, name, pid, typ, start, size in held: - who = user.get((share, name, pid), "?") - rows.append(["%s/%s" % (os.path.basename(share), os.path.basename(name)), pid, who, - machine.get(pid, "?"), start, size, - {"W": "write", "R": "read"}.get(typ, typ)]) - table(rows, ["FILE", "PID", "USER", "CLIENT", "OFFSET", "LENGTH", "TYPE"]) - - -def show_opens(): - openfiles, shares = status("LOCKS", "SHARES") - machine = {s["server_id"]["pid"]: s["machine"] for s in shares} - rows = [] - for f in openfiles: - share = os.path.basename(f["service_path"]) - for o in f["opens"].values(): - pid = o["server_id"]["pid"] - rows.append(["%s/%s" % (share, f["filename"]), pid, o["username"], - machine.get(pid, "?"), o["access_mask"]["text"] or "-", - o["sharemode"]["text"] or "-", o["opened_at"][11:23]]) - if not rows: - print("no open files") - return - table(sorted(rows), ["FILE", "PID", "USER", "CLIENT", "ACCESS", "SHAREMODE", "OPENED"]) - - -def watch(interval): - prev = set() - while True: - now = set(locks(status("BYTERANGE")[0])) - t = time.time() - stamp = time.strftime("%H:%M:%S", time.localtime(t)) + ".%03d" % (t % 1 * 1000) - for sign, changed in (("-", prev - now), ("+", now - prev)): - for share, name, pid, typ, start, size in sorted(changed): - print("%s %s %s/%s pid=%s %s %d+%d" % - (stamp, sign, os.path.basename(share), os.path.basename(name), pid, - {"W": "write", "R": "read"}.get(typ, typ), start, size), flush=True) - prev = now - time.sleep(interval) - - -def main(): - args = sys.argv[1:] - connect() - if args and args[0] == "--watch": - watch(float(args[1]) if len(args) > 1 else 0.5) - elif args == ["--opens"]: - show_opens() - elif not args: - show_locks() - else: - sys.exit("usage: zenith-locks [--watch [SECONDS] | --opens]") - - -if __name__ == "__main__": - try: - main() - except KeyboardInterrupt: - pass