This commit is contained in:
@@ -17,7 +17,6 @@ public sealed class KeyboardLayoutServiceTests
|
||||
|
||||
private const int English = 0x0409;
|
||||
private const int Russian = 0x0419;
|
||||
private const int German = 0x0407;
|
||||
|
||||
[Fact]
|
||||
public void The_current_layout_is_taken_from_the_foreground_window()
|
||||
@@ -52,7 +51,6 @@ public sealed class KeyboardLayoutServiceTests
|
||||
Pump.Run(service.Start);
|
||||
world.LocaleId = Russian;
|
||||
Pump.Run(service.Poll);
|
||||
Pump.Run(service.Poll);
|
||||
|
||||
LayoutChangedEventArgs change = Assert.Single(world.Changes);
|
||||
Assert.Equal(LayoutChangeReason.UserSwitched, change.Reason);
|
||||
@@ -70,7 +68,6 @@ public sealed class KeyboardLayoutServiceTests
|
||||
world.ForegroundWindow = SecondWindow;
|
||||
world.LocaleId = Russian;
|
||||
Pump.Run(service.Poll);
|
||||
Pump.Run(service.Poll);
|
||||
|
||||
LayoutChangedEventArgs change = Assert.Single(world.Changes);
|
||||
Assert.Equal(LayoutChangeReason.ApplicationSwitched, change.Reason);
|
||||
@@ -125,91 +122,10 @@ public sealed class KeyboardLayoutServiceTests
|
||||
// And the layout was not remembered: the change is noticed once a window is back
|
||||
world.ForegroundWindow = FirstWindow;
|
||||
Pump.Run(service.Poll);
|
||||
Pump.Run(service.Poll);
|
||||
|
||||
Assert.Single(world.Changes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A value the switch is only passing through never reaches the subscribers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A layout change is rarely a single step: the Windows language switcher takes the
|
||||
/// focus while it is up, and applications with a rendering engine of their own move
|
||||
/// a helper thread first. Reporting what polling caught in between meant the popup
|
||||
/// coming up with one layout and turning into another in front of the user.
|
||||
/// </remarks>
|
||||
[Fact]
|
||||
public void A_layout_the_switch_only_passes_through_is_never_reported()
|
||||
{
|
||||
using var world = new World { LocaleId = English };
|
||||
KeyboardLayoutService service = world.CreateService();
|
||||
|
||||
Pump.Run(service.Start);
|
||||
|
||||
world.LocaleId = German;
|
||||
Pump.Run(service.Poll);
|
||||
|
||||
Assert.Empty(world.Changes);
|
||||
|
||||
world.LocaleId = Russian;
|
||||
Pump.Run(service.Poll);
|
||||
Pump.Run(service.Poll);
|
||||
|
||||
LayoutChangedEventArgs change = Assert.Single(world.Changes);
|
||||
Assert.Equal("RU", change.Layout.ShortName);
|
||||
}
|
||||
|
||||
// A switch that goes out and comes back is not a switch at all
|
||||
[Fact]
|
||||
public void A_layout_that_returns_to_where_it_was_yields_no_events()
|
||||
{
|
||||
using var world = new World { LocaleId = English };
|
||||
KeyboardLayoutService service = world.CreateService();
|
||||
|
||||
Pump.Run(service.Start);
|
||||
|
||||
world.LocaleId = Russian;
|
||||
Pump.Run(service.Poll);
|
||||
|
||||
world.LocaleId = English;
|
||||
Pump.Run(service.Poll);
|
||||
Pump.Run(service.Poll);
|
||||
|
||||
Assert.Empty(world.Changes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A window the switch passes through does not make it an application switch.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The Windows language switcher is a window of its own and holds the foreground
|
||||
/// while it is up. Judging by the window seen on the previous tick would call that
|
||||
/// an application switch and swallow the popup, so the judgement is made against
|
||||
/// the window that was there before the whole transition started.
|
||||
/// </remarks>
|
||||
[Fact]
|
||||
public void A_window_the_switch_passes_through_is_not_an_application_switch()
|
||||
{
|
||||
using var world = new World { LocaleId = English };
|
||||
KeyboardLayoutService service = world.CreateService();
|
||||
|
||||
Pump.Run(service.Start);
|
||||
|
||||
world.ForegroundWindow = SecondWindow;
|
||||
world.LocaleId = German;
|
||||
Pump.Run(service.Poll);
|
||||
|
||||
world.ForegroundWindow = FirstWindow;
|
||||
world.LocaleId = Russian;
|
||||
Pump.Run(service.Poll);
|
||||
Pump.Run(service.Poll);
|
||||
|
||||
LayoutChangedEventArgs change = Assert.Single(world.Changes);
|
||||
Assert.Equal(LayoutChangeReason.UserSwitched, change.Reason);
|
||||
Assert.Equal("RU", change.Layout.ShortName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void One_change_yields_exactly_one_event()
|
||||
{
|
||||
@@ -221,7 +137,6 @@ public sealed class KeyboardLayoutServiceTests
|
||||
world.LocaleId = Russian;
|
||||
Pump.Run(service.Poll);
|
||||
Pump.Run(service.Poll);
|
||||
Pump.Run(service.Poll);
|
||||
|
||||
Assert.Single(world.Changes);
|
||||
}
|
||||
@@ -344,11 +259,9 @@ public sealed class KeyboardLayoutServiceTests
|
||||
|
||||
internal KeyboardLayoutService CreateService(TimeSpan? pollInterval = null)
|
||||
{
|
||||
TimeSpan interval = pollInterval ?? TimeSpan.FromHours(1);
|
||||
var options = new KeyboardLayoutOptions
|
||||
{
|
||||
PollInterval = interval,
|
||||
SettleInterval = interval,
|
||||
PollInterval = pollInterval ?? TimeSpan.FromHours(1),
|
||||
};
|
||||
|
||||
_service = Pump.Run(() => new KeyboardLayoutService(
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace CursorLang.Core.Tests.Services;
|
||||
public sealed class LayoutNotificationCoordinatorTests
|
||||
{
|
||||
private static readonly KeyboardLayout Russian = KeyboardLayout.FromLocaleId(0x0419);
|
||||
private static readonly KeyboardLayout English = KeyboardLayout.FromLocaleId(0x0409);
|
||||
|
||||
[Fact]
|
||||
public void Starting_turns_on_the_layout_watch()
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
using CursorLang.Core.Threading;
|
||||
using CursorLang.Tests.Shared;
|
||||
|
||||
namespace CursorLang.Core.Tests.Threading;
|
||||
|
||||
/// <summary>
|
||||
/// The timer the agent has instead of a dispatcher timer.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It ticks on the message loop, so everything here runs on the pump thread — a timer
|
||||
/// started on one thread and awaited on another would never be seen to fire.
|
||||
/// </remarks>
|
||||
public sealed class MessageTimerTests
|
||||
{
|
||||
[Fact]
|
||||
public void A_started_timer_ticks()
|
||||
{
|
||||
int ticks = 0;
|
||||
|
||||
Pump.Run(() =>
|
||||
{
|
||||
using var timer = new MessageTimer();
|
||||
timer.Interval = TimeSpan.FromMilliseconds(15);
|
||||
timer.Tick += (_, _) => ticks++;
|
||||
timer.Start();
|
||||
|
||||
Pump.WaitFor(() => ticks > 0, "the timer ticked");
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_timer_keeps_ticking_until_it_is_stopped()
|
||||
{
|
||||
var ticks = 0;
|
||||
|
||||
Pump.Run(() =>
|
||||
{
|
||||
using var timer = new MessageTimer();
|
||||
timer.Interval = TimeSpan.FromMilliseconds(15);
|
||||
timer.Tick += (_, _) => ticks++;
|
||||
timer.Start();
|
||||
|
||||
Pump.WaitFor(() => ticks >= 3, "the timer ticked more than once");
|
||||
});
|
||||
}
|
||||
|
||||
// A stopped countdown does not go off, however long the loop runs afterwards
|
||||
[Fact]
|
||||
public void A_stopped_timer_does_not_tick()
|
||||
{
|
||||
var ticks = 0;
|
||||
|
||||
Pump.Run(() =>
|
||||
{
|
||||
using var timer = new MessageTimer();
|
||||
timer.Interval = TimeSpan.FromMilliseconds(10);
|
||||
timer.Tick += (_, _) => ticks++;
|
||||
timer.Start();
|
||||
timer.Stop();
|
||||
|
||||
Pump.Pause(TimeSpan.FromMilliseconds(80));
|
||||
});
|
||||
|
||||
Assert.Equal(0, ticks);
|
||||
}
|
||||
|
||||
// Restarting means from zero, so a countdown kept short by repeated restarts
|
||||
// never reaches its end
|
||||
[Fact]
|
||||
public void Restarting_begins_the_countdown_again()
|
||||
{
|
||||
var ticks = 0;
|
||||
|
||||
Pump.Run(() =>
|
||||
{
|
||||
using var timer = new MessageTimer();
|
||||
timer.Interval = TimeSpan.FromMilliseconds(60);
|
||||
timer.Tick += (_, _) => ticks++;
|
||||
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
timer.Start();
|
||||
Pump.Pause(TimeSpan.FromMilliseconds(20));
|
||||
}
|
||||
|
||||
Assert.Equal(0, ticks);
|
||||
|
||||
Pump.WaitFor(() => ticks > 0, "left alone, the timer reached its end");
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Closing_the_timer_ends_the_ticking()
|
||||
{
|
||||
var ticks = 0;
|
||||
|
||||
Pump.Run(() =>
|
||||
{
|
||||
var timer = new MessageTimer { Interval = TimeSpan.FromMilliseconds(15) };
|
||||
timer.Tick += (_, _) => ticks++;
|
||||
timer.Start();
|
||||
|
||||
Pump.WaitFor(() => ticks > 0, "the timer ticked");
|
||||
timer.Dispose();
|
||||
|
||||
int seen = ticks;
|
||||
Pump.Pause(TimeSpan.FromMilliseconds(80));
|
||||
|
||||
Assert.Equal(seen, ticks);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_timer_that_was_never_started_says_so()
|
||||
{
|
||||
Pump.Run(() =>
|
||||
{
|
||||
using var timer = new MessageTimer();
|
||||
|
||||
Assert.False(timer.IsRunning);
|
||||
|
||||
timer.Start();
|
||||
Assert.True(timer.IsRunning);
|
||||
|
||||
timer.Stop();
|
||||
Assert.False(timer.IsRunning);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user