116 lines
3.3 KiB
C#
116 lines
3.3 KiB
C#
using System.Collections.Concurrent;
|
|
using CursorLang.Core.Services;
|
|
using CursorLang.Tests.Shared;
|
|
|
|
namespace CursorLang.Core.Tests.Services;
|
|
|
|
/// <summary>
|
|
/// The one thing the agent says to the settings window: quit with me.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The event names here are the tests' own. The application's name is fixed, and a test
|
|
/// listening on it would answer for a settings window someone is using — or, signalling,
|
|
/// close it.
|
|
/// </remarks>
|
|
public sealed class SettingsCloseSignalTests
|
|
{
|
|
[Fact]
|
|
public void The_request_reaches_the_settings_window()
|
|
{
|
|
string suffix = UniqueSuffix();
|
|
var signal = new SettingsCloseSignal(suffix);
|
|
|
|
ConcurrentQueue<EventArgs> requests = new();
|
|
signal.CloseRequested += (_, e) => requests.Enqueue(e);
|
|
|
|
try
|
|
{
|
|
signal.Listen();
|
|
|
|
Assert.True(RequestApart(suffix), "the request found nobody listening");
|
|
Pump.WaitFor(() => !requests.IsEmpty, "the settings window got the request to close");
|
|
}
|
|
finally
|
|
{
|
|
signal.Dispose();
|
|
}
|
|
}
|
|
|
|
// The usual case: the user quits from the tray with no settings window on the screen
|
|
[Fact]
|
|
public void A_request_with_no_settings_window_open_passes_without_consequence()
|
|
{
|
|
Assert.False(RequestApart(UniqueSuffix()));
|
|
}
|
|
|
|
// The window has closed on its own, and the process is on its way out anyway
|
|
[Fact]
|
|
public void No_request_arrives_after_the_window_is_gone()
|
|
{
|
|
string suffix = UniqueSuffix();
|
|
var signal = new SettingsCloseSignal(suffix);
|
|
|
|
ConcurrentQueue<EventArgs> requests = new();
|
|
signal.CloseRequested += (_, e) => requests.Enqueue(e);
|
|
|
|
signal.Listen();
|
|
signal.Dispose();
|
|
|
|
RequestApart(suffix);
|
|
Pump.Pause(TimeSpan.FromMilliseconds(80));
|
|
|
|
Assert.Empty(requests);
|
|
}
|
|
|
|
[Fact]
|
|
public void Listening_twice_leaves_one_listener()
|
|
{
|
|
string suffix = UniqueSuffix();
|
|
var signal = new SettingsCloseSignal(suffix);
|
|
|
|
var requests = 0;
|
|
signal.CloseRequested += (_, _) => Interlocked.Increment(ref requests);
|
|
|
|
try
|
|
{
|
|
signal.Listen();
|
|
signal.Listen();
|
|
|
|
Assert.True(RequestApart(suffix));
|
|
Pump.WaitFor(() => Volatile.Read(ref requests) > 0, "the request arrived");
|
|
Pump.Pause(TimeSpan.FromMilliseconds(80));
|
|
|
|
Assert.Equal(1, Volatile.Read(ref requests));
|
|
}
|
|
finally
|
|
{
|
|
signal.Dispose();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Closing_without_listening_passes_without_consequence()
|
|
{
|
|
var signal = new SettingsCloseSignal(UniqueSuffix());
|
|
|
|
signal.Dispose();
|
|
signal.Dispose();
|
|
}
|
|
|
|
// Every test gets a namespace of kernel objects of its own
|
|
private static string UniqueSuffix() => "." + Guid.NewGuid().ToString("N");
|
|
|
|
/// <summary>
|
|
/// Asks for the close the way the agent does it — from another process, and here
|
|
/// from another thread, which is as foreign as a test can get.
|
|
/// </summary>
|
|
private static bool RequestApart(string suffix)
|
|
{
|
|
var heard = false;
|
|
|
|
Pump.RunApart(() => heard = SettingsCloseSignal.RequestClose(suffix));
|
|
|
|
return heard;
|
|
}
|
|
}
|