using System;
using System.IO;
using System.IO.Pipes;
using Xunit;
using ytLive.Helpers;
using ytLive.Services.Audio;
namespace ytLive.Tests;
///
/// 2026-11-06 crash: two LlamaCasty instances on one machine, the second one died on the Go Live click with
/// IOException: All pipe instances are busy. The audio pipe server allows exactly ONE instance and the
/// name was a shared constant, so this pins both halves of the fix — the collision is real, and each dev
/// instance now gets its own name.
///
public sealed class AudioPipeInstanceTests : IDisposable
{
private readonly string? _original = Environment.GetEnvironmentVariable(InstanceProfile.InstanceVariable);
public void Dispose() => Environment.SetEnvironmentVariable(InstanceProfile.InstanceVariable, _original);
[Fact]
public void ASecondServerOnTheSameName_CannotStart_WhichIsWhatKilledTheSecondInstance()
{
var name = $"ytllive_test_{Guid.NewGuid():N}";
using var first = new NamedPipeServerStream(name, PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
var error = Record.Exception(() =>
new NamedPipeServerStream(name, PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous));
Assert.NotNull(error);
Assert.IsType(error);
}
[Fact]
public void ADevInstanceServesItsOwnPipeName()
{
Environment.SetEnvironmentVariable(InstanceProfile.InstanceVariable, null);
#if DEBUG
// No claimed id (launched from the IDE) must still never collide with the Release build's pipe.
var unnamed = InstanceProfile.AudioPipeName("ytllive_audio");
Assert.StartsWith("ytllive_audio_pid", unnamed);
Assert.Equal(unnamed, InstanceProfile.AudioPipeName("ytllive_audio"));
#else
Assert.Equal("ytllive_audio", InstanceProfile.AudioPipeName("ytllive_audio"));
#endif
Environment.SetEnvironmentVariable(InstanceProfile.InstanceVariable, "2");
Assert.Equal("ytllive_audio_2", InstanceProfile.AudioPipeName("ytllive_audio"));
Environment.SetEnvironmentVariable(InstanceProfile.InstanceVariable, "qa");
Assert.Equal("ytllive_audio_qa", InstanceProfile.AudioPipeName("ytllive_audio"));
Assert.NotEqual(InstanceProfile.AudioPipeName("ytllive_audio"),
WithInstance("3", () => InstanceProfile.AudioPipeName("ytllive_audio")));
}
private static string WithInstance(string id, Func read)
{
var original = Environment.GetEnvironmentVariable(InstanceProfile.InstanceVariable);
try
{
Environment.SetEnvironmentVariable(InstanceProfile.InstanceVariable, id);
return read();
}
finally
{
Environment.SetEnvironmentVariable(InstanceProfile.InstanceVariable, original);
}
}
[Fact]
public void TheMixerRefusesAStartedSecondTime_ButAWriterOnATakenNameThrows()
{
// The mixer's own guard is idempotent (StartLive twice is a no-op); the exception came from the
// pipe itself, which the VM now catches and reports instead of letting it end the process.
var name = $"ytllive_test_{Guid.NewGuid():N}";
var writer = new NamedPipeAudioWriter();
writer.Start(name);
try
{
var second = Record.Exception(() => new NamedPipeAudioWriter().Start(name));
Assert.IsType(second);
}
finally
{
writer.Stop();
}
}
}