Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions docs/smoke-tests/capture-logs.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# capture-logs.ps1 - snapshot every ws-scrcpy-web diagnostic into one folder, for a smoke capture point.
#
# Run at any capture point during the Windows smoke - ESPECIALLY when a test FAILS, and right after
# an install / uninstall / update. Read-only: it never changes the install. The in-app uninstall
# cleaner runs with logging OFF by design, so for the uninstall tests (15.x) the EVIDENCE is
# filesystem + registry + temp state - which this captures.
#
# Usage: powershell -ExecutionPolicy Bypass -File capture-logs.ps1 [label]
# e.g. powershell -ExecutionPolicy Bypass -File capture-logs.ps1 15.2-wipe
# powershell -ExecutionPolicy Bypass -File capture-logs.ps1 5.7-after-uninstall
# Output: %USERPROFILE%\wssw-smoke-logs\<timestamp>-<label>\ (+ a .zip to attach)

param([string]$Label = "capture")

$ts = Get-Date -Format "yyyyMMdd-HHmmss"
$out = Join-Path $HOME "wssw-smoke-logs\$ts-$Label"
New-Item -ItemType Directory -Force -Path $out | Out-Null

$dataRoot = Join-Path $env:ProgramData "WsScrcpyWeb"
$progFiles = Join-Path $env:ProgramFiles "WsScrcpyWeb"
$elevated = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)

# Capture a command/expression's output (incl. errors) to a named file under $out.
function Save([string]$name, [scriptblock]$block) {
$path = Join-Path $out $name
try { & $block 2>&1 | Out-String | Set-Content -Path $path -Encoding utf8 }
catch { "ERROR capturing ${name}: $_" | Set-Content -Path $path -Encoding utf8 }
}

Write-Host "[capture] capturing to $out"

# 00. environment + bundle index
@(
"label: $Label"
"captured: $(Get-Date -Format o)"
"OS: $([System.Environment]::OSVersion.VersionString)"
"user: $env:USERNAME elevated: $elevated"
""
"# files in this bundle:"
"# 10-procs running launcher/node/tray/adb (want: none after stop-exit / uninstall)"
"# 20-service sc query WsScrcpyWeb (want: 'does not exist' after uninstall)"
"# 21-run-key HKLM ...\Run\WsScrcpyWebTray (want: gone after uninstall)"
"# 22-arp Add/Remove Programs entry (want: gone after uninstall)"
"# 30-programfiles Program Files\WsScrcpyWeb (want: gone after uninstall)"
"# 31-dataroot ProgramData\WsScrcpyWeb recursive - the keep/wipe leftover check"
"# (a wipe must leave NOTHING; watch for a stray control\operation-server\)"
"# 32-temp-cleaner the uninstall cleaner copy in temp (proves Phase-1 staged + ran)"
"# 40-config config.json (kept on --keep, gone on --wipe)"
"# 70-*.log app logs (launcher / server / tray)"
) | Set-Content (Join-Path $out "00-env.txt") -Encoding utf8

Save "01-version.txt" { Get-Content (Join-Path $progFiles "current\VERSION") -ErrorAction SilentlyContinue }

# 10. running processes
Save "10-procs.txt" { Get-Process ws-scrcpy-web-launcher,ws-scrcpy-web-tray,node,adb -ErrorAction SilentlyContinue | Select-Object Name,Id,StartTime,Path | Format-Table -AutoSize }

# 20. service
Save "20-service.txt" { & "$env:SystemRoot\System32\sc.exe" query WsScrcpyWeb }

# 21. tray autostart registry entry
Save "21-run-key.txt" { Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name WsScrcpyWebTray -ErrorAction SilentlyContinue | Select-Object WsScrcpyWebTray }

# 22. Add/Remove Programs (ARP) entry
Save "22-arp.txt" {
Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall","HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" -ErrorAction SilentlyContinue |
ForEach-Object { Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue } |
Where-Object { $_.DisplayName -match "scrcpy" } |
Select-Object DisplayName,DisplayVersion,InstallLocation,UninstallString
}

# 30. Program Files install tree
Save "30-programfiles.txt" { if (Test-Path $progFiles) { Get-ChildItem $progFiles -Recurse -ErrorAction SilentlyContinue | Select-Object FullName,Length } else { "(Program Files\WsScrcpyWeb absent)" } }

# 31. dataRoot recursive - the keep/wipe leftover check (shows control\operation-server\)
Save "31-dataroot.txt" { if (Test-Path $dataRoot) { Get-ChildItem $dataRoot -Recurse -Force -ErrorAction SilentlyContinue | Select-Object FullName,Length } else { "(ProgramData\WsScrcpyWeb absent - fully wiped)" } }

# 32. uninstall cleaner copy in temp (Phase-1 stages it here; cleaner is --no-log so this is the trace)
Save "32-temp-cleaner.txt" {
@($env:TEMP, "$env:SystemRoot\Temp", "$env:SystemRoot\SystemTemp") | Select-Object -Unique | ForEach-Object {
Get-ChildItem (Join-Path $_ "ws-scrcpy-web-uninstall-*.exe") -ErrorAction SilentlyContinue
} | Select-Object FullName,Length,LastWriteTime
}

# 40. config.json
Copy-Item (Join-Path $dataRoot "config.json") (Join-Path $out "40-config.json") -ErrorAction SilentlyContinue

# 70. app logs
Get-ChildItem (Join-Path $dataRoot "logs\*.log") -ErrorAction SilentlyContinue | ForEach-Object { Copy-Item $_.FullName (Join-Path $out "70-$($_.Name)") -ErrorAction SilentlyContinue }

# bundle it for easy attachment
Compress-Archive -Path $out -DestinationPath "$out.zip" -Force -ErrorAction SilentlyContinue
Write-Host "[capture] Captured -> $out (and $out.zip)"
106 changes: 106 additions & 0 deletions docs/smoke-tests/capture-logs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/usr/bin/env bash
# capture-logs.sh — snapshot every ws-scrcpy-web diagnostic into one folder, for a smoke capture point.
#
# Run this at any "capture point" during the smoke pass — ESPECIALLY the moment a test FAILS, and
# right after an install / uninstall / update — to collect a complete, attachable evidence bundle.
# Read-only: it never changes the install. Linux (Fedora/SELinux + Ubuntu). Paths are pulled
# verbatim from source, same set as clear-install.sh.
#
# Usage: bash capture-logs.sh [label]
# e.g. bash capture-logs.sh 5.8-after-user-uninstall
# bash capture-logs.sh 6.6-system-update # watch 10-avc.txt + 33-dataroot for the result
# bash capture-logs.sh 14.7-uninstall-clean # 30-fcontext.txt must be empty
# Output: ~/wssw-smoke-logs/<timestamp>-<label>/ (+ a .tar.gz of the same folder to attach)
set -u

LABEL="${1:-capture}"
TS="$(date +%Y%m%d-%H%M%S)"
OUT="$HOME/wssw-smoke-logs/${TS}-${LABEL}"
mkdir -p "$OUT"

GREEN=$'\e[32m'; YEL=$'\e[33m'; RST=$'\e[0m'
note() { printf '%s[capture]%s %s\n' "$YEL" "$RST" "$*"; }

# --- resolved footprint (verbatim from source; mirrors clear-install.sh) ---
UNIT="WsScrcpyWeb.service"
DATA_ROOT="${XDG_DATA_HOME:-$HOME/.local/share}/WsScrcpyWeb"
USER_UNIT="$HOME/.config/systemd/user/$UNIT"
SYS_UNIT="/etc/systemd/system/$UNIT"
OPT_DIR="/opt/ws-scrcpy-web"
VAR_OPT_DIR="/var/opt/ws-scrcpy-web"
PROC_PAT='WsScrcpyWeb|ws-scrcpy-web-tray|ws-scrcpy-web-launcher|scrcpy-server'

HAVE_SEMANAGE=0; command -v semanage >/dev/null 2>&1 && HAVE_SEMANAGE=1
HAVE_AUSEARCH=0; command -v ausearch >/dev/null 2>&1 && HAVE_AUSEARCH=1

note "capturing to $OUT"
note "sudo is used for system-scope items (AVC, system journal, /var/opt, fcontext)"
sudo -v 2>/dev/null || note "sudo unavailable — system-scope items will be partial"

# 00. environment + version + key config fields
{
echo "label: $LABEL"
echo "captured: $(date -Is)"
echo "host: $(uname -a)"
printf 'getenforce: '; getenforce 2>/dev/null || echo "(no SELinux)"
echo
echo "# files in this bundle:"
echo "# 10-avc SELinux denials this boot (want: none)"
echo "# 20/21 user svc journal + status (user scope)"
echo "# 22/23 system svc journal + status (system scope)"
echo "# 30-fcontext SELinux rules (want: empty after uninstall)"
echo "# 31/32/33 ls -Z /opt, /var/opt, dataRoot labels + recursive listing (leftover check)"
echo "# 40/41-config config.json (local / system)"
echo "# 50-procs running ws-scrcpy-web processes"
echo "# 60/61-unit systemd unit files"
echo "# 70/71-*.log app logs (local- / system-)"
} > "$OUT/00-env.txt"
{
echo "# /opt VERSION:"; cat "$OPT_DIR/VERSION" 2>/dev/null || echo "(none)"
echo "# local config webPort/installMode/firstRun:"
grep -Eo '"(webPort|installMode|firstRunComplete)"[^,}]*' "$DATA_ROOT/config.json" 2>/dev/null || echo "(no local config.json)"
} > "$OUT/01-version.txt"

# 10. SELinux AVC denials (this boot) — the single most important signal
if [ -d /sys/fs/selinux ]; then
if [ $HAVE_AUSEARCH -eq 1 ]; then sudo ausearch -m avc -ts boot 2>/dev/null; else sudo journalctl -b 2>/dev/null | grep -i avc; fi > "$OUT/10-avc.txt"
if [ -s "$OUT/10-avc.txt" ]; then note "AVC denials FOUND — see 10-avc.txt"; else echo "(no AVC denials this boot)" > "$OUT/10-avc.txt"; fi
else
echo "(no SELinux on this host)" > "$OUT/10-avc.txt"
fi

# 20. service journals + status (user + system)
journalctl --user -u "$UNIT" --no-pager -n 500 > "$OUT/20-journal-user.txt" 2>&1
systemctl --user status "$UNIT" --no-pager > "$OUT/21-status-user.txt" 2>&1
sudo journalctl -u "$UNIT" --no-pager -n 500 > "$OUT/22-journal-system.txt" 2>&1
sudo systemctl status "$UNIT" --no-pager > "$OUT/23-status-system.txt" 2>&1

# 30. SELinux fcontext rules + file labels + recursive dataRoot listing (catches leftovers)
if [ $HAVE_SEMANAGE -eq 1 ]; then
sudo semanage fcontext -l 2>/dev/null | grep ws-scrcpy-web > "$OUT/30-fcontext.txt"
[ -s "$OUT/30-fcontext.txt" ] || echo "(no ws-scrcpy-web fcontext rules)" > "$OUT/30-fcontext.txt"
else
echo "(semanage absent)" > "$OUT/30-fcontext.txt"
fi
ls -laZ "$OPT_DIR" > "$OUT/31-opt-ls.txt" 2>&1
sudo ls -laZ "$VAR_OPT_DIR" > "$OUT/32-var-opt-ls.txt" 2>&1
ls -laRZ "$DATA_ROOT" > "$OUT/33-dataroot-ls.txt" 2>&1

# 40. config.json (local + system)
cp "$DATA_ROOT/config.json" "$OUT/40-config-local.json" 2>/dev/null || true
sudo cp "$VAR_OPT_DIR/config.json" "$OUT/41-config-system.json" 2>/dev/null || true

# 50. running processes
pgrep -fa "$PROC_PAT" > "$OUT/50-procs.txt" 2>&1 || echo "(no ws-scrcpy-web processes)" > "$OUT/50-procs.txt"

# 60. unit files
cp "$USER_UNIT" "$OUT/60-user-unit.service" 2>/dev/null || true
sudo cp "$SYS_UNIT" "$OUT/61-system-unit.service" 2>/dev/null || true

# 70. app logs (local + system) — prefixed so the two trees never clash
for f in "$DATA_ROOT"/logs/*.log; do [ -e "$f" ] && cp "$f" "$OUT/70-local-$(basename "$f")" 2>/dev/null; done
for f in "$VAR_OPT_DIR"/logs/*.log; do [ -e "$f" ] && sudo cp "$f" "$OUT/71-system-$(basename "$f")" 2>/dev/null; done

# bundle it for easy attachment
tar czf "$OUT.tar.gz" -C "$(dirname "$OUT")" "$(basename "$OUT")" 2>/dev/null && note "bundle: $OUT.tar.gz"
printf '%sCaptured ✓%s %s\n' "$GREEN" "$RST" "$OUT"
13 changes: 10 additions & 3 deletions docs/smoke-tests/smoke-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@ which carries the beta.48 port-discovery fix plus the App-section UX (batch #15)
```bash
gh release download --repo bilbospocketses/ws-scrcpy-web --pattern '*linux-beta.AppImage' --dir ~/Downloads
```
3. **Keep the capture scripts handy.** Beside this doc: [`capture-logs.sh`](./capture-logs.sh) (Linux) / [`capture-logs.ps1`](./capture-logs.ps1) (Windows). Run one at any checkpoint — **especially the instant a row fails** — to snapshot every log + state (AVC, service journal/status, fcontext, SELinux labels, processes, dataRoot / Program Files / temp listings, config, app logs) to a timestamped, labeled folder + archive to attach. The numbered output files map to the "Expected + verify" column.
```bash
bash capture-logs.sh <test-id> # Linux, e.g. ... 5.8-after-uninstall
```
```powershell
powershell -ExecutionPolicy Bypass -File capture-logs.ps1 <test-id> # Windows, e.g. ... 15.2-wipe
```

Boxes start unticked — this is a fresh pass (prior beta.48 ✅ live in the breadcrumb/todo).
Boxes start unticked — this is a fresh pass.

---

Expand Down Expand Up @@ -154,7 +161,7 @@ New in beta.51, the wipe self-deletion fixed in beta.52. Run on the clean Win11
| Test | How to perform | Expected + verify |
|---|---|---|
| ☐ **15.1** `[W]` In-app uninstall — keep | MSI install → Settings → **App** → **uninstall** → keep **checked** (default) → uninstall | **One UAC** (Update.exe self-elevates — VM decision #1); `C:\Program Files\WsScrcpyWeb\` gone; service gone (`sc query WsScrcpyWeb` → not found); tray gone; **ARP entry gone**; `config.json` + `logs\` **survive** under `%ProgramData%\WsScrcpyWeb`, `dependencies\` gone; reinstall reuses the saved port |
| ☐ **15.2** `[W]` In-app uninstall — wipe | Same but **uncheck** keep | As 15.1, **and the whole `%ProgramData%\WsScrcpyWeb` is gone** — incl. `control\operation-server\` (the beta.52 fix: the temp-copy cleaner removes it after the original exits). Confirm **no** leftover dir |
| ☐ **15.2** `[W]` In-app uninstall — wipe | Same but **uncheck** keep | As 15.1, **and the whole `%ProgramData%\WsScrcpyWeb` is gone** — incl. `control\operation-server\` (the beta.52 fix: the temp-copy cleaner removes it after the original exits). Confirm **no** leftover dir — `capture-logs.ps1 15.2-wipe`, then check `31-dataroot` |
| ☐ **15.3** `[W]` Uninstall modal UX | Open the uninstall modal | Top-layer overlay above Settings; **cancel** white-outline, **uninstall** red text + border; keep checkbox **checked by default**; cancel / Esc / backdrop = no action |
| ☐ **15.4** `[W]` Stop-exit reaps tray + adb | Local mode, device + stream live → Settings → **App** → **stop server & exit** | Tab closes / "app stopped"; Task Manager shows **no** lingering `ws-scrcpy-web-launcher.exe` / `node.exe` / `ws-scrcpy-web-tray.exe` / `adb.exe` |
| ☐ **15.5** `[W]` App-section order | Settings → App | Order top→bottom: **reset prompts → stop server & exit → uninstall ws-scrcpy-web** (no "install for all users" on Windows) |
Expand All @@ -174,4 +181,4 @@ New in beta.51, the wipe self-deletion fixed in beta.52. Run on the clean Win11
| **Data preserved** | User config/deps/logs survive uninstall + reinstall |
| **Core flow** | Scan → connect → stream (video + control) → shell works on both platforms |

**Stop-and-report:** a `[Linux]` SELinux/lifecycle failure in Modules 2/4/5, the service-update rows 6.5/6.6, or migration 6.7 — capture `journalctl`/`ausearch`/logs and fix before promoting 0.1.30 stable. Cosmetic/polish → note as beta-territory. **Module 11 (no-libfuse2)** gates closing item 31, not 0.1.30-stable on its own.
**Stop-and-report:** a `[Linux]` SELinux/lifecycle failure in Modules 2/4/5, the service-update rows 6.5/6.6, or migration 6.7 — run `capture-logs.sh <id>` (`.ps1` on Windows) for the evidence bundle, then fix before promoting 0.1.30 stable. Cosmetic/polish → note as beta-territory. **Module 11 (no-libfuse2)** gates closing item 31, not 0.1.30-stable on its own.
Loading
Loading