Files
LlamaCasty/ytLive.Tests/TraxProbeTests.cs
T
gramps cc2d1ef622 TRAX: a share that is gone must never freeze the startup window
Startup took 44s and 42 of them were one line. Measured from startup.log:
OnStartup 08:42:59 -> MainWindow loaded 08:43:43, with the whole gap inside
LoadLayout, ending at

  TRAX: failed to load '\\192.168.50.86\llamaVault\Assets\Music\...':
  The network path was not found. (0x80070035)

LoadTrax called MusicPlayer.Load synchronously on the UI thread, and
new MediaFoundationReader(path) does not fail fast on a dead SMB session — it
burns the full ARP/TCP/SMB timeout and only then throws. The NAS had moved to
.87 after the power outage, so an unreachable path became a 40-second frozen
window on every launch.

The rule, from the creator: a probe may time out; a bind should return
immediately. Only one side of that is achievable here, and the reason matters —
an unreachable UNC path has no fast-fail at all, so probing it on the calling
thread is the worst possible probe. (A device open is the opposite: it normally
fails fast on a sharing violation, which is why this bug is network-specific.)

So: IsTrackReachableAsync runs the probe on a thread-pool thread under a
Task.WhenAny ceiling (TraxProbeBudget, 2s) and treats offline/missing/slow as a
normal state rather than an error. Only a probe that passed gets the real bind,
and that bind deliberately stays on the UI thread alongside every other
MusicPlayer mutation — moving the whole load off-thread would have introduced a
race against PlayTrax/PauseTrax that does not exist today. The orphan probe
thread stays parked until SMB gives up; it is not the thread anyone waits on.

One test, per the Good Dog rule: TraxProbeTests.
AHangingProbeIsAbandonedAtTheBudget_AndReportsOffline stands in a probe that
never returns and asserts the caller is released and told "offline". Drop the
ceiling and it fails on elapsed time — which is the frozen window returning.

Suite 381/381, 0 warnings.
2026-10-04 09:24:12 -07:00

51 lines
1.9 KiB
C#

using Xunit;
using ytLive.ViewModels;
namespace ytLive.Tests;
/// <summary>
/// TRAX load-path guarding (2026-10-04). Startup used to freeze for ~40s when the
/// music track lived on an offline network share: <c>LoadLayout</c> called
/// <c>MusicPlayer.Load</c> synchronously, and <c>new MediaFoundationReader(path)</c>
/// does not fail fast on a dead SMB session — it burns the full timeout and then
/// throws 0x80070035. The fix is a bounded reachability probe on a background thread,
/// so an unreachable share can never sit on the path the creator waits on.
/// </summary>
public class TraxProbeTests
{
/// <summary>The regression this guards: a probe that hangs must be abandoned at the
/// budget and reported offline. Without the ceiling the caller inherits the SMB
/// timeout, which is the 40-second frozen startup window. The orphaned probe thread
/// is released at the end so the test does not leak it.</summary>
[Fact]
public async Task AHangingProbeIsAbandonedAtTheBudget_AndReportsOffline()
{
var orphan = new ManualResetEventSlim(false);
var started = DateTime.UtcNow;
try
{
var reachable = await MainViewModel.IsTrackReachableAsync(
@"\\llamavault\llamaVault\Assets\Music\VBI - Mayhem OST.mp3",
TimeSpan.FromMilliseconds(150),
// Stands in for a share that has gone away: the open never completes.
_ =>
{
orphan.Wait(TimeSpan.FromSeconds(30));
return true;
});
var elapsed = DateTime.UtcNow - started;
Assert.False(reachable);
Assert.True(
elapsed < TimeSpan.FromSeconds(5),
$"probe was not cut off at the budget — took {elapsed.TotalSeconds:0.0}s, which is the frozen-startup bug returning");
}
finally
{
orphan.Set();
}
}
}