Skip to content

Commit acff519

Browse files
committed
release: CA-O 2.2.7 con registrador black-box para Overclock
- Registrador de pasos en Refresh de Overclock (CrashLog.Step): el log dice el paso exacto si vuelve a crashear - Seam CrashLog.LogPathOverride para aislar tests del log real - Suite 1701 passed, 0 failed; build 0W/0E
1 parent 9af8d07 commit acff519

8 files changed

Lines changed: 112 additions & 30 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ Formato basado en [Keep a Changelog](https://keepachangelog.com/es-ES/1.1.0/).
44

55
## [Unreleased]
66

7+
## [2.2.7] - 2026-10-11
8+
9+
### Anadido
10+
- **Registrador black-box de pasos de Overclock.** `CrashLog.Step(stage)` escribe una marca por fase del sondeo (`refresh.probe-start`, `probe-done`, `rows-done`, `readings-done`, `games-done`, `delta-done`, `ceilings-done`, `recoveries-done`, `done`/`failed`) en el mismo `cao-ui-crash.log`. Si la pestana vuelve a fallar en un equipo, el log dice el paso exacto en vez de un crash mudo.
11+
12+
### Probado
13+
- Suite completa en Release (2 CrashLogStepTests nuevos).
14+
715
## [2.2.6] - 2026-10-11
816

917
### Corregido

‎README.md‎

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# CA-O 2.2.6 — Plataforma nativa de rendimiento, diagnóstico y optimización para Windows 11
1+
# CA-O 2.2.7 — Plataforma nativa de rendimiento, diagnóstico y optimización para Windows 11
22

33
> **Principio operativo:** diagnosticar primero → recomendar con evidencia → aplicar en transacción → verificar → revertir si falla. Sin promesas numéricas falsas, solo hechos medibles.
44
@@ -7,10 +7,10 @@
77
[![WinUI 3](https://img.shields.io/badge/WinUI%203-00B7C3?style=flat-square&logo=windows&logoColor=white)](https://microsoft.github.io/microsoft-ui-xaml/)
88
[![Build](https://img.shields.io/badge/build-passing-brightgreen?style=flat-square)](https://github.com/Pyromesis/CA-O/actions)
99
[![Tests](https://img.shields.io/badge/tests-1692%20passed-brightgreen?style=flat-square)](#-pruebas)
10-
[![Release](https://img.shields.io/badge/release-v2.2.6-blue?style=flat-square)](https://github.com/Pyromesis/CA-O/releases/tag/v2.2.6)
10+
[![Release](https://img.shields.io/badge/release-v2.2.7-blue?style=flat-square)](https://github.com/Pyromesis/CA-O/releases/tag/v2.2.7)
1111
[![License](https://img.shields.io/badge/license-privado-lightgrey?style=flat-square)](#licencia)
1212

13-
**Descargas v2.2.6:** [CA-O-Instalador.exe (132 MB, instalador Inno Setup)](https://github.com/Pyromesis/CA-O/releases/download/v2.2.6/CA-O-Instalador.exe) | [CA-O.Setup.exe (135 MB, descargador de un solo exe: baja el paquete y abre el instalador)](https://github.com/Pyromesis/CA-O/releases/download/v2.2.6/CA-O.Setup.exe) | [CA-O-Setup-GUI-x64.zip (88 MB, paquete completo offline)](https://github.com/Pyromesis/CA-O/releases/download/v2.2.6/CA-O-Setup-GUI-x64.zip) | [CA-O-Setup-GUI-x64.exe (instalador con asistente Inno Setup)](https://github.com/Pyromesis/CA-O/releases/download/v2.2.6/CA-O-Setup-GUI-x64.exe) | [SHA256SUMS.txt](https://github.com/Pyromesis/CA-O/releases/download/v2.2.6/SHA256SUMS.txt) | [Notas de la versión](https://github.com/Pyromesis/CA-O/releases/tag/v2.2.6) | [Documentación](docs/ARCHITECTURE.md)
13+
**Descargas v2.2.7:** [CA-O-Instalador.exe (132 MB, instalador Inno Setup)](https://github.com/Pyromesis/CA-O/releases/download/v2.2.7/CA-O-Instalador.exe) | [CA-O.Setup.exe (135 MB, descargador de un solo exe: baja el paquete y abre el instalador)](https://github.com/Pyromesis/CA-O/releases/download/v2.2.7/CA-O.Setup.exe) | [CA-O-Setup-GUI-x64.zip (88 MB, paquete completo offline)](https://github.com/Pyromesis/CA-O/releases/download/v2.2.7/CA-O-Setup-GUI-x64.zip) | [CA-O-Setup-GUI-x64.exe (instalador con asistente Inno Setup)](https://github.com/Pyromesis/CA-O/releases/download/v2.2.7/CA-O-Setup-GUI-x64.exe) | [SHA256SUMS.txt](https://github.com/Pyromesis/CA-O/releases/download/v2.2.7/SHA256SUMS.txt) | [Notas de la versión](https://github.com/Pyromesis/CA-O/releases/tag/v2.2.7) | [Documentación](docs/ARCHITECTURE.md)
1414

1515
---
1616

@@ -43,7 +43,7 @@
4343

4444
## Resumen ejecutivo
4545

46-
**CA-O 2.2.6** es una aplicación **100% nativa Windows** escrita en **.NET 10 + WinUI 3 (Windows App SDK 2.4)**. No es una colección de tweaks: **mide** hardware, térmicas, red, almacenamiento, drivers y postura de seguridad **antes** de recomendar. Cada cambio pertenece a un **bucket analizado-primero** (`Recommended` / `Optional` / `Experimental` / `SecuritySensitive` / `NotApplicable` / `Blocked`) y se ejecuta bajo el flujo transaccional `PRECHECK → SNAPSHOT → APPLY → VERIFY → COMMIT` con **rollback automático y verificación post-reversión**.
46+
**CA-O 2.2.7** es una aplicación **100% nativa Windows** escrita en **.NET 10 + WinUI 3 (Windows App SDK 2.4)**. No es una colección de tweaks: **mide** hardware, térmicas, red, almacenamiento, drivers y postura de seguridad **antes** de recomendar. Cada cambio pertenece a un **bucket analizado-primero** (`Recommended` / `Optional` / `Experimental` / `SecuritySensitive` / `NotApplicable` / `Blocked`) y se ejecuta bajo el flujo transaccional `PRECHECK → SNAPSHOT → APPLY → VERIFY → COMMIT` con **rollback automático y verificación post-reversión**.
4747

4848
La UI **siempre eleva** (`app.manifest` `requireAdministrator` → UAC en cada inicio) pero **toda mutación privilegiada cruza** al servicio Windows `CAO.Privileged` (SYSTEM) vía **Named Pipe autenticado** con ACL restrictiva, validación de esquema tipado, ventana de 30 s, nonce y protección anti-replay. Sin el servicio, la app opera en **modo solo lectura** (diagnóstico + benchmark disponibles). El servicio es `start= delayed-auto` por diseño: tras un reinicio arranca solo con retardo; si la app lo encuentra aún detenido, lo levanta (`sc start` + espera de readiness con ping al pipe hasta 8 s) al comprobar, o lo re-registra con `Ajustes → Instalar ahora` sin necesitar el repositorio.
4949

@@ -80,7 +80,7 @@ La UI **siempre eleva** (`app.manifest` `requireAdministrator` → UAC en cada i
8080
| **WMI** | System.Management | **10.0.0** | Lectura hardware/termal/batería |
8181
| **Perf** | System.Diagnostics.PerformanceCounter | **10.0.0** | `% DPC Time` / `% Interrupt Time` |
8282
| **Servicios** | System.ServiceProcess.ServiceController | **8.0.1** | `CAO.Privileged` como `BackgroundService` |
83-
| **Build** | `Directory.Packages.props` + `Directory.Build.props` + `Version.props` (single source `2.2.6`) | Centralizado | Un lugar para bump de versión/paquetes |
83+
| **Build** | `Directory.Packages.props` + `Directory.Build.props` + `Version.props` (single source `2.2.7`) | Centralizado | Un lugar para bump de versión/paquetes |
8484
| **Tests** | xUnit 2.9.2 + Microsoft.NET.Test.Sdk 17.11.1 | — | 1692 tests en Release |
8585
| **Seguridad** | CodeQL + `dotnet audit` + Dependabot + CycloneDX SBOM | CI | Cadena de suministro auditada |
8686

@@ -117,7 +117,7 @@ La UI **siempre eleva** (`app.manifest` `requireAdministrator` → UAC en cada i
117117

118118
| Proyecto | Responsabilidad | Depende de |
119119
|---|---|---|
120-
| `CA-O.Shared` | DTOs, contratos IPC v2 (`IpcProtocol` v2, `Ping`, `GetServiceStatus`), `CaOPaths`, `ErrorCodes CAO-XXX-nnn`, `AppVersion 2.2.6`, `BuildConstants`, `Constants/IpcConstants`, `TimeoutProfile` | — |
120+
| `CA-O.Shared` | DTOs, contratos IPC v2 (`IpcProtocol` v2, `Ping`, `GetServiceStatus`), `CaOPaths`, `ErrorCodes CAO-XXX-nnn`, `AppVersion 2.2.7`, `BuildConstants`, `Constants/IpcConstants`, `TimeoutProfile` | — |
121121
| `CA-O.Core` | `OptimizationCatalog` (93), `OptimizationEngine` transaccional, `RecommendationEngine` + `OptimizationScoreCalculator` (0-100), `GameCompatibilityPolicy` (matriz SAFE/CAUTION/BLOCKED `CAO-GAME-001`), `HealthEngine`, `KnownIssueMatcher`, `CrashRecoveryService`, `Rollback/*` | Shared |
122122
| `CA-O.Infrastructure` | WMI 5 s timeout + `SystemAnalysisService` + `AnalysisStateStore` (atómico, 24 h TTL) + `SnapshotRepository`/`FileSnapshotStore` (TX identity + SHA-256) + `JsonHistoryLogger` (hash-chain) + `SystemBenchmarkRunner` + `Gaming/*` + `Networking/*` + `Storage/*` + `Security/*` + `Windows/*` | Core, Shared |
123123
| `CA-O.Privileged` | Servicio `SYSTEM` + `PrivilegedPipeService` (Named Pipe `CA-O.Privileged.v1`, ACL + `ReplayCache` 30 s, timeout 15 s/conn) + `OptimizationEngine` hosteado + `AdministratorsOnlyAuthorizer` | Core, Infrastructure, Shared |
@@ -156,7 +156,7 @@ BENCHMARK → post-commit, fallo no invalida commit // eventos separados
156156
CA-O.sln (.NET 10 · LangVersion 13 · WinUI 3)
157157
├── src/
158158
│ ├── CA-O.Shared/ # Contratos puros, sin IO
159-
│ │ ├── Constants/ # AppVersion (2.2.6), BuildConstants, CaOPaths, IpcConstants, TimeoutProfile
159+
│ │ ├── Constants/ # AppVersion (2.2.7), BuildConstants, CaOPaths, IpcConstants, TimeoutProfile
160160
│ │ ├── IPC/ # IpcProtocol v2, IpcRequest/Response, Payloads (17 ops)
161161
│ │ ├── Security/ # CommandPolicy, ErrorCodes, CallerIdentity
162162
│ │ ├── Enums/ # RecommendationBucket, RiskLevel, EvidenceLevel, etc.
@@ -219,7 +219,7 @@ CA-O.sln (.NET 10 · LangVersion 13 · WinUI 3)
219219
├── .github/workflows/ci.yml # build + test + CodeQL + dotnet audit
220220
├── Directory.Packages.props # Versiones centralizadas
221221
├── Directory.Build.props # Propiedades MSBuild comunes
222-
├── Version.props # Single source 2.2.6
222+
├── Version.props # Single source 2.2.7
223223
├── global.json # SDK 10.0.400
224224
└── CA-O.sln
225225
```
@@ -677,15 +677,15 @@ Cada perfil es **dinámico** — no es lista fija. Ej. `maximum-power-plan` solo
677677

678678
### Opción A — Un solo exe (lo más fácil)
679679

680-
1. Descarga [CA-O.Setup.exe (135 MB)](https://github.com/Pyromesis/CA-O/releases/download/v2.2.6/CA-O.Setup.exe) — es el único archivo que necesitas
680+
1. Descarga [CA-O.Setup.exe (135 MB)](https://github.com/Pyromesis/CA-O/releases/download/v2.2.7/CA-O.Setup.exe) — es el único archivo que necesitas
681681
2. Ejecútalo (pide UAC) → deriva al instalador gráfico en español: instala app + servicio y abre CA-O. Sin consola: lo trae todo dentro
682682

683683
> Autocontenido, log en `%TEMP%\CA-O-Setup.log`
684-
> Tambien disponible [CA-O-Instalador.exe (132 MB)](https://github.com/Pyromesis/CA-O/releases/download/v2.2.6/CA-O-Instalador.exe): instalador Inno Setup que pone app + servicio + atajos sin pasos extra.
684+
> Tambien disponible [CA-O-Instalador.exe (132 MB)](https://github.com/Pyromesis/CA-O/releases/download/v2.2.7/CA-O-Instalador.exe): instalador Inno Setup que pone app + servicio + atajos sin pasos extra.
685685
686686
### Opción B — Instalador GUI offline (88 MB)
687687

688-
1. Descarga [CA-O-Setup-GUI-x64.zip (88 MB)](https://github.com/Pyromesis/CA-O/releases/download/v2.2.6/CA-O-Setup-GUI-x64.zip) y verifica el hash con `SHA256SUMS.txt`
688+
1. Descarga [CA-O-Setup-GUI-x64.zip (88 MB)](https://github.com/Pyromesis/CA-O/releases/download/v2.2.7/CA-O-Setup-GUI-x64.zip) y verifica el hash con `SHA256SUMS.txt`
689689
2. Descomprime (mantén `ui/`, `service/`, `gui-installer/`, `uninstall/`, `setup/`)
690690
3. Entra en `gui-installer/` → clic derecho **Ejecutar como administrador** en `CA-O.InstallerGui.exe` → UAC Sí
691691
4. Elige destino (`C:\Program Files\CA-O`), atajos Escritorio/Inicio, progress → registra `CAO.Privileged` (`demand`, `failure 86400`) + ARP
@@ -695,7 +695,7 @@ Cada perfil es **dinámico** — no es lista fija. Ej. `maximum-power-plan` solo
695695
696696
### Opción C — Descargador online (135 MB)
697697

698-
1. Descarga [CA-O.Setup.exe (135 MB)](https://github.com/Pyromesis/CA-O/releases/download/v2.2.6/CA-O.Setup.exe)
698+
1. Descarga [CA-O.Setup.exe (135 MB)](https://github.com/Pyromesis/CA-O/releases/download/v2.2.7/CA-O.Setup.exe)
699699
2. Ejecútalo como admin: descarga el paquete `CA-O-Setup-GUI-x64.zip` desde GitHub Releases (requiere internet), deriva al instalador gráfico y continúa solo
700700

701701
> Log: `%TEMP%\CA-O-Setup.log`
@@ -750,7 +750,7 @@ powershell -ExecutionPolicy Bypass -File scripts/build-release.ps1
750750
751751
# Empaquetar (scripts/package.ps1, FullPackageName `CA-O-{versión}-win-x64.zip`)
752752
powershell -ExecutionPolicy Bypass -File scripts/package.ps1
753-
# → artifacts/CA-O-2.2.6-win-x64.zip + .sha256
753+
# → artifacts/CA-O-2.2.7-win-x64.zip + .sha256
754754
755755
# Endurecer ACLs de datos
756756
powershell -ExecutionPolicy Bypass -File scripts/harden-data-acls.ps1
@@ -790,7 +790,7 @@ dotnet test --filter "FullyQualifiedName~GameCompatibility"
790790

791791
## Release y verificación
792792

793-
- **Versión actual:** **2.2.6** — [Releases](https://github.com/Pyromesis/CA-O/releases/tag/v2.2.6) (solo se conserva el último release)
793+
- **Versión actual:** **2.2.7** — [Releases](https://github.com/Pyromesis/CA-O/releases/tag/v2.2.7) (solo se conserva el último release)
794794
- **Artefactos:**
795795
- `CA-O.Setup.exe` (135 MB, descargador single-file con handoff al GUI)
796796
- `CA-O-Setup-GUI-x64.zip` (88 MB, paquete completo offline con `ui/`, `service/`, `gui-installer/`, `setup/`, `uninstall/`)
@@ -801,7 +801,7 @@ dotnet test --filter "FullyQualifiedName~GameCompatibility"
801801
- **Empaquetado:** `scripts/package.ps1` genera ZIP versionado `CA-O-{versión}-win-x64.zip` + hash SHA-256. `scripts/build-release.ps1` publica `ui` y `service` como self-contained y `gui-installer` como self-contained **sin single-file** (requisito WinUI 3)
802802

803803
```powershell
804-
Get-FileHash artifacts/CA-O-2.2.6-win-x64.zip -Algorithm SHA256
804+
Get-FileHash artifacts/CA-O-2.2.7-win-x64.zip -Algorithm SHA256
805805
Get-Content artifacts/release/SHA256SUMS.txt
806806
cat artifacts/sbom/bom.json | ConvertFrom-Json | select -ExpandProperty components | measure
807807
```
@@ -880,4 +880,4 @@ Proyecto privado: no se concede licencia de uso, copia, modificación ni distrib
880880

881881
---
882882

883-
<p align="center"><i>CA-O 2.2.6 — Diagnóstico primero, evidencia después, transacción siempre. Sin promesas, solo hechos medibles.</i></p>
883+
<p align="center"><i>CA-O 2.2.7 — Diagnóstico primero, evidencia después, transacción siempre. Sin promesas, solo hechos medibles.</i></p>

‎Version.props‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<Project>
22
<!-- Single source of truth for versioning (FASE 28). All other files derive from here. -->
33
<PropertyGroup>
4-
<Version>2.2.6</Version>
5-
<AssemblyVersion>2.2.6.0</AssemblyVersion>
6-
<FileVersion>2.2.6.0</FileVersion>
7-
<PackageVersion>2.2.6</PackageVersion>
8-
<ProductVersion>2.2.6</ProductVersion>
9-
<InformationalVersion>2.2.6+$(GHTag)</InformationalVersion>
4+
<Version>2.2.7</Version>
5+
<AssemblyVersion>2.2.7.0</AssemblyVersion>
6+
<FileVersion>2.2.7.0</FileVersion>
7+
<PackageVersion>2.2.7</PackageVersion>
8+
<ProductVersion>2.2.7</ProductVersion>
9+
<InformationalVersion>2.2.7+$(GHTag)</InformationalVersion>
1010
</PropertyGroup>
1111
</Project>
1212

‎installer/CA-O.iss‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
; (lee la versión de BuildConstants.cs y firma con CAO_SIGN_THUMBPRINT si existe)
44

55
#ifndef AppVersion
6-
#define AppVersion "2.2.6"
6+
#define AppVersion "2.2.7"
77
#endif
88
#ifndef RepoRoot
99
#define RepoRoot ".."

‎src/CA-O.Shared/Constants/BuildConstants.cs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ namespace CAO.Shared.Constants;
1111
public static class BuildConstants
1212
{
1313
// Version - SINGLE SOURCE OF TRUTH
14-
public const string ProductVersion = "2.2.6";
14+
public const string ProductVersion = "2.2.7";
1515
public const string ProductVersionMajor = "2";
1616
public const string ProductVersionMinor = "2";
17-
public const string ProductVersionPatch = "6";
17+
public const string ProductVersionPatch = "7";
1818

1919
// Product Identity
2020
public const string ProductName = "CA-O";

‎src/CA-O.UI/Helpers/CrashLog.cs‎

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,23 @@ internal static class CrashLog
1818
/// Appends an exception and its inner chain. Never throws: a logger that
1919
/// can crash the caller is a second bug wearing the first one's clothes.
2020
/// </summary>
21+
/// <summary>Test seam: when set, the log goes here instead of LocalAppData. Production never sets it.</summary>
22+
internal static string? LogPathOverride { get; set; }
23+
24+
private static string ResolvePath()
25+
{
26+
var path = LogPathOverride ?? Path.Combine(
27+
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
28+
"CA-O", "logs", "cao-ui-crash.log");
29+
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
30+
return path;
31+
}
32+
2133
public static void Write(Exception ex)
2234
{
2335
try
2436
{
25-
var logDir = Path.Combine(
26-
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
27-
"CA-O", "logs");
28-
Directory.CreateDirectory(logDir);
29-
var path = Path.Combine(logDir, "cao-ui-crash.log");
37+
var path = ResolvePath();
3038
RotateIfNeeded(path);
3139

3240
var sb = new StringBuilder()
@@ -78,6 +86,28 @@ public static void Write(Exception ex)
7886
}
7987
}
8088

89+
/// <summary>
90+
/// Appends a one-line breadcrumb. The Overclock refresh writes one per
91+
/// phase so a native crash that leaves no managed exception behind still
92+
/// leaves the exact step it died in. Same contract as Write: never throws.
93+
/// </summary>
94+
public static void Step(string stage)
95+
{
96+
try
97+
{
98+
var path = ResolvePath();
99+
RotateIfNeeded(path);
100+
File.AppendAllText(
101+
path,
102+
DateTime.UtcNow.ToString("o") + " step: " + stage + Environment.NewLine,
103+
Encoding.UTF8);
104+
}
105+
catch
106+
{
107+
// Diagnostics must never break the page they observe.
108+
}
109+
}
110+
81111
private static void RotateIfNeeded(string path)
82112
{
83113
var info = new FileInfo(path);

‎src/CA-O.UI/ViewModels/OverclockViewModel.cs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,9 +272,11 @@ private async Task RefreshAsync(CancellationToken ct)
272272
{
273273
Headline = Localizer.Get("overclock_title");
274274
StatusLine = Localizer.Get("overclock_probing");
275+
CrashLog.Step("refresh.probe-start");
275276

276277
// Vendor P/Invoke and WMI both block; none of them belong on the UI thread.
277278
var capabilities = await Task.Run(() => _registry.ProbeAll(ct), ct).ConfigureAwait(true);
279+
CrashLog.Step("refresh.probe-done");
278280

279281
CpuKnobs.Clear();
280282
GpuKnobs.Clear();
@@ -313,15 +315,22 @@ private async Task RefreshAsync(CancellationToken ct)
313315
}
314316
}
315317

318+
CrashLog.Step("refresh.rows-done");
316319
await FillReadingsAsync(ct).ConfigureAwait(true);
320+
CrashLog.Step("refresh.readings-done");
317321
await FillGamesAsync(ct).ConfigureAwait(true);
322+
CrashLog.Step("refresh.games-done");
318323

319324
Summarize(capabilities);
320325
await LoadSavedDeltaAsync(ct).ConfigureAwait(true);
326+
CrashLog.Step("refresh.delta-done");
321327
await LoadCeilingsAsync(ct).ConfigureAwait(true);
328+
CrashLog.Step("refresh.ceilings-done");
322329
await LoadPendingRecoveriesAsync(ct).ConfigureAwait(true);
330+
CrashLog.Step("refresh.recoveries-done");
323331

324332
StatusLine = Localizer.Format("overclock_probe_done", capabilities.Knobs.Count);
333+
CrashLog.Step("refresh.done");
325334
}
326335
catch (OperationCanceledException)
327336
{
@@ -330,6 +339,7 @@ private async Task RefreshAsync(CancellationToken ct)
330339
catch (Exception ex)
331340
{
332341
StatusLine = Localizer.Get("overclock_probe_failed");
342+
CrashLog.Step("refresh.failed");
333343
CrashLog.Write(ex);
334344
}
335345
finally
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using CAO.UI.Helpers;
2+
using Xunit;
3+
4+
namespace CAO.UI.Tests;
5+
6+
/// <summary>El registrador black-box: cada fase del sondeo deja una miga para el siguiente crash nativo.</summary>
7+
public sealed class CrashLogStepTests
8+
{
9+
[Fact]
10+
public void Step_AppendsABreadcrumb_WithTheStageName()
11+
{
12+
var stage = "test-breadcrumb-" + Guid.NewGuid().ToString("N");
13+
var path = Path.Combine(Path.GetTempPath(), "cao-step-" + Guid.NewGuid().ToString("N") + ".log");
14+
CrashLog.LogPathOverride = path;
15+
try
16+
{
17+
CrashLog.Step(stage);
18+
Assert.Contains("step: " + stage, File.ReadAllText(path));
19+
}
20+
finally
21+
{
22+
CrashLog.LogPathOverride = null;
23+
}
24+
25+
File.Delete(path);
26+
}
27+
28+
[Fact]
29+
public void Step_NeverThrows()
30+
{
31+
var ex = Record.Exception(() => CrashLog.Step("test-never-throws"));
32+
Assert.Null(ex);
33+
}
34+
}

0 commit comments

Comments
 (0)