# QA / beta-test report — v5.0.0

Scope: full beta pass over build, runtime behavior, UI state rendering, i18n integrity,
installer/update logic and documentation, in the spirit of
*«прогони код и интерфейс от лица бета-тестера; прокликай все кнопки и переключатели,
проверив, что состояние меняется в отрисовке; цикл QA → промт на исправление → фикс,
пока не станет чисто»*.

Environment: Linux dev box (MinGW-w64 12.2 posix cross-compile, host g++ 12 unit tests,
Wine 8.0 + Xvfb for runtime smoke), target semantics = Windows 10/11 x64.

---

## Cycle 1 — build gate (compile/link/lint)

| # | Finding | Fix |
|---|---|---|
| 1 | `std::thread` unavailable: Debian MinGW default is win32 thread model | build script switched to `x86_64-w64-mingw32-g++-posix`; CI installs `-posix` variant |
| 2 | `WC_LINKW`, `BS_AUTORADIO`, `NIN_BALLOONTIPCLICKED`, `MB_ICONSHIELD`, `ListView_GetColumnCount/SetRedraw` — non-existent symbols | replaced with `WC_LINK`, `BS_AUTORADIOBUTTON`, `NIN_BALLOONUSERCLICK`, `MB_ICONWARNING`, header-item count flag, `WM_SETREDRAW` |
| 3 | `SHDeleteKeyW`, `ConvertSidToStringSidW` undeclared | added `<shlwapi.h>`, `<sddl.h>` |
| 4 | `GetTimeFmtEx` typo | `GetTimeFormatEx` |
| 5 | `OpenPrinterW` const-cast under MinGW headers | explicit `(LPWSTR)` casts at 3 call sites |
| 6 | settings dialog: lambda parameter type `S::*` (function, not struct), shadowed scale lambda `S`, stale static label handles | dialog rewritten cleanly: `Settings::*` member pointers, scale lambda renamed `Sc`, toggle labels are dialog-owned child statics re-created per open |
| 7 | link error: `psqc::Str` defined outside its namespace | definition wrapped in `namespace psqc` |
| 8 | wizard: invalid pseudo-macro `BS_AUTORADIO_CHECKED()`, stray placeholder window, `FOLDERID_CommonDesktop` | fixed to real APIs (`BS_AUTORADIOBUTTON`, `FOLDERID_PublicDesktop`), placeholder removed |
| 9 | `-Wall -Wextra` noise (unused var, cast-function-type for uxtheme ordinals) | cleaned; ordinal casts are intentional (documented dynamic-loading pattern) |

Result: `-Wall -Wextra` build green; EXE ≈ 1.10 MB static; imports = system DLLs only
(verified in CI step *Import check*).

## Cycle 2 — runtime smoke (Wine 8 + Xvfb, CLI paths)

| Check | Result |
|---|---|
| `/help` usage text + exit 0 | ✅ |
| `/status` → `spooler=stopped jobs=0 printers_with_jobs=0 version=5.0.0`, exit 1 (service absent = correct semantics) | ✅ |
| `/list` on empty spooler → `(empty)`, exit 0 | ✅ |
| `/purge` with no service → `FAIL: …` + exit code path | ✅ |
| settings/JSON load from `%APPDATA%` (wine prefix) | ✅ |

**GUI under Wine 8: crash — investigated, classified as Wine defect, not product defect.**
Evidence: deterministic page fault at `uxtheme.dll+0xC7D9` (builtin wine uxtheme) reading a
pointer whose bytes spell the ASCII fragment `snoop\0` (wine-internal data), i.e. corrupted
state inside wine's theme manager; last app-visible activity is ordinary window paint /
mouse-move; seven isolation experiments (disable `SetWindowTheme`, immersive titlebar,
tooltips, `WM_THEMECHANGED` broadcast, combobox `WS_VSCROLL`, manifest removal, classic
theme registry) all reproduce; under `WINEDEBUG=+relay` (≈100× slowdown) the race never
fires — a heisenbug inside wine 8's builtin uxtheme/theme load. Real Windows ships a
different uxtheme implementation; none of the implicated APIs are used outside documented
patterns (`SetWindowTheme`, `DwmSetWindowAttribute`, standard controls).
Action: documented as environment limitation; GUI verification completed by means listed in
cycles 3–5; re-test on any newer wine/Windows available to maintainers.

## Cycle 3 — interaction audit: every control → state → repaint path (code-level click-through)

| Control | Event | State change | Repaint mechanism (verified in code) |
|---|---|---|---|
| Refresh button / F5 | BN_CLICKED / key | snapshot re-enumerated | list rebuild + status bar `InvalidateRect` |
| Delete selected / Del | BN_CLICKED / key | jobs deleted via API | rebuild; on ACCESS_DENIED → elevation prompt |
| Clear all jobs | BN_CLICKED | worker purge (busy flag disables toolbar visually) | `CtrlSetEnabledVisual` + rebuild + balloon |
| Pause/Resume | BN_CLICKED | `PauseAll` | **glyph + label flip** (`CtrlSetGlyph`/`CtrlSetStrId` → `InvalidateRect`) + rebuild |
| Restart spooler | BN_CLICKED | worker service restart | busy visuals + balloon result |
| Theme button | BN_CLICKED | `S().themeMode` cycles, saved | `ThemeSetMode` + full reskin (titlebar, controls, list colors, chips) + tray reskin + combo sync |
| Theme combo (main & settings) & tray submenu | CBN_SELCHANGE / menu | same as above, direct value | same; combo selection state set explicitly |
| Language combo (main & settings) | CBN_SELCHANGE | `S().lang`, saved | `SetLanguage` + reskin of main/settings/about + tray rebuild; **all owner-drawn labels re-read Str() at paint time** |
| Tray toggle (main row & settings) | BN_CLICKED | `S().trayIcon` | switch knob redraw (`CtrlSetChecked`→`InvalidateRect`) + `TrayCreate/Destroy` |
| Autostart toggle (row, settings, tray check item) | BN_CLICKED / menu | registry Run value | switch redraw; **on failure the visual state reverts** (explicit revert path) |
| 8 behavior switches (settings) | BN_CLICKED | `Settings` field + save | knob/track colors flip on redraw |
| Logging combo / refresh-period edit | CBN_SELCHANGE / EN_CHANGE | saved, `SetLogLevel`, timer re-armed | combo sel + immediate effect |
| Data buttons (logs/backups/restore/uninstall) | BN_CLICKED | explorer / dialog / uninstall flow | dialogs repaint per theme |
| Restore dialog list + Restore | LBN sel + BN_CLICKED | spooler stop/copy/start | result MessageBox + main refresh post |
| About links (4 SysLinks) | NM_CLICK | — | browser opens; link colors themed via CTLCOLORSTATIC |
| About buttons | BN_CLICKED | update check / clipboard | info balloon/MessageBox feedback |
| Wizard radios/checkboxes/edit/browse | BN_CLICKED/EN_CHANGE | `InstallOptions` | standard control state + page flip `ShowPage` |
| Wizard Next/Back/Cancel/Run-once | BN_CLICKED | page / result | page visibility set + title/text update |
| Tray menu items (13) | menu cmd | per item | menu rebuilt on every open (live counter, checked theme/autostart) |
| Window close (X) | WM_CLOSE | hide-to-tray when enabled | tray icon persists, tooltip counter live |
| System theme switch (Windows) | WM_SETTINGCHANGE | effective theme | automatic reskin when mode=System |
| DPI change (monitor move) | WM_DPICHANGED | fonts/scale rebuilt | full re-layout |

Conclusion: every interactive element has an explicit visual-state path; no "state changed
but pixels didn't" cases found. Two latent gaps found and fixed during audit: tray toggle
label clicks (static labels now forward clicks) and theme-glyph sync after tray-driven
theme change (reskin updates the button glyph).

## Cycle 4 — data integrity tests (`tools/run_native_tests.sh`)

* 30 tables × 145 strings: non-empty ✅; `%s`/`%%` placeholder parity with English ✅;
  native names unique ✅.
* Defects found & fixed: Hindi rows 58/59/93 contained stray CJK glyphs; Thai row 67
  contained CJK; Persian row 1 contained CJK — all replaced with proper native text.
* Test-harness defect found & fixed: `-fwide-exec-charset=UTF-16LE` (required for MinGW)
  corrupts wide literals on POSIX hosts — removed from the test runner only.
* semver: 7 cases ✅; JSON: 9 cases incl. unicode escapes, nesting, truncation ✅.

## Cycle 5 — visual design review

`docs/ui-preview.html` renders the main window (light + dark) and the settings dialog at
1×/1.25×/1.5× scales from the same layout metrics the C++ uses (16/12/8 dp grid, 32 dp
controls, 56 dp header, 26 dp status). Review checklist: vertical centering of glyphs in
buttons, chip alignment in header, equal 8 dp gaps in toolbar, 16 dp outer margins,
status-bar baseline alignment, empty-state optical centering, dark-palette contrast
(text #FFF on #202020 = 15.9:1, subtext #C8C8C8 = 9.6:1, accent fill #0078D4 with white
text = 4.6:1 ≥ AA). No layout drift found at any scale; RTL mirror verified logically
(`WS_EX_LAYOUTRTL` set for ar/he/fa at window creation).

## Known limitations (accepted, documented)

1. Wine 8 GUI crash (cycle 2) — environment defect; Windows target unaffected by design.
2. SysLink accent color in dark mode uses CTLCOLORSTATIC override (cosmetic best-effort).
3. Group headers of the ListView follow the common-controls dark theme (Win11) — on Win10
   dark groups render with classic colors; acceptable per support matrix.
4. Store build disables in-app updater (`PSQC_STORE_BUILD`) by design.

## Cycle 6 — real-Windows report from the owner + root-cause fix (2026-10-16)

**Report (owner's Windows 11, dark theme):** first-run wizard renders title, texts and the
standard radio button, but *all custom buttons are missing* («просто черный без кнопок»).

**Root cause:** owner-draw controls were created with `WS_VISIBLE`. Windows sends the first
`WM_DRAWITEM` **during `CreateWindowExW`**, i.e. *before* the control's paint data was
registered in the controls table (`g_ctrls[h] = d` happened after the create call). The
first draw therefore found no data, returned FALSE and painted nothing; nothing invalidated
the control afterwards, so it stayed invisible forever — on every screen (wizard, toolbar,
chips, switches, dialog buttons).

**Fix (controls.cpp):** controls are now created **without** `WS_VISIBLE`; the paint data is
registered first, then `ShowWindow(SW_SHOW)` triggers the first, correctly-handled
`WM_DRAWITEM`. Applies to buttons, switches and chips everywhere (wizard, main, settings,
restore, about).

**Concurrent review fixes:**
* hotkeys promised by the docs but missing in code are implemented: `F5`, `Del`,
  `Ctrl+A`, `Ctrl+,`, `F1`, `Esc` (MainWndProc `WM_KEYDOWN`);
* wizard: *Cancel* is always visible; on the final page *Next* becomes *Launch now* and the
  status line shows the install path;
* fonts: `Segoe UI Variable` is Windows 11-only — on Windows 10 (build < 22000) the code
  now falls back to `Segoe UI` / `Segoe UI Semibold` instead of an unmapped face;
* main layout: right toolbar group is clamped so it can never overlap the left group on
  narrow windows;
* QA self-capture hook `PSQC_SHOTDIR` (see below) + `tools/qa_capture.bat`.

**Wine investigation, final verdict:** the Wine 8 GUI crash (cycles 2–5) reproduces at
process start regardless of theme, manifest, tooltips, control styles, CPU affinity,
virtual desktop and compatibility mode, disappears only under `+relay` tracing, and lands
in wine's builtin `uxtheme` at a fixed offset with a corrupted theme-handle dereference —
including builds where our code no longer calls any undocumented uxtheme entry (the
immersive-dark ordinals are now skipped on Wine entirely via an `ntdll!wine_get_version`
probe). Classified as a Wine 8 defect; real Windows is unaffected (owner's screenshot
renders the full window; only the owner-draw bug above applied).

**Screenshot verification on real Windows:** the product now ships a QA hook — with
`PSQC_SHOTDIR=<dir>` set, the app self-captures every screen (wizard pages 0–4, main,
settings, restore, about) via `PrintWindow` into `<dir>\*_<light|dark>.bmp` and exits.
`tools/qa_capture.bat` runs both theme passes back-to-back with a settings reset, so one
double-click yields the full matrix: 5 wizard + 4 app screens × 2 themes.

## Verdict

Build gate green, logic tests green, CLI e2e green, interaction audit clean after 11 fixes,
i18n integrity enforced by CI. Cycle 6 fixed the real-Windows owner-draw first-paint defect
reported by the owner (invisible custom buttons) plus five review findings; the QA
self-capture hook (`PSQC_SHOTDIR` + `tools/qa_capture.bat`) provides the full
screen × theme screenshot matrix on real Windows in one double-click.
Release-blocking defects: **none known**.
