test: remove ambient timing from double-click contracts

This commit is contained in:
Erik 2026-08-18 11:38:31 +02:00
parent 3684e7b5e7
commit c8c764a40e
5 changed files with 151 additions and 40 deletions

View file

@ -13,4 +13,7 @@
<ProjectReference Include="..\AcDream.Core\AcDream.Core.csproj" />
<ProjectReference Include="..\AcDream.Runtime\AcDream.Runtime.csproj" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="AcDream.UI.Abstractions.Tests" />
</ItemGroup>
</Project>

View file

@ -34,6 +34,7 @@ public sealed class InputDispatcher : IDisposable
{
private readonly IKeyboardSource _keyboard;
private readonly IMouseSource _mouse;
private readonly Func<long> _getTickCount64;
private KeyBindings _bindings;
private readonly Stack<InputScope> _scopes = new();
private InputScope? _combatScope;
@ -67,11 +68,14 @@ public sealed class InputDispatcher : IDisposable
private InputDispatcher(
IKeyboardSource keyboard,
IMouseSource mouse,
KeyBindings bindings)
KeyBindings bindings,
Func<long> getTickCount64)
{
_keyboard = keyboard ?? throw new ArgumentNullException(nameof(keyboard));
_mouse = mouse ?? throw new ArgumentNullException(nameof(mouse));
_bindings = bindings ?? throw new ArgumentNullException(nameof(bindings));
_getTickCount64 = getTickCount64
?? throw new ArgumentNullException(nameof(getTickCount64));
_scopes.Push(InputScope.Always); // bottom of the stack
_scopes.Push(InputScope.Game); // default top for normal play
@ -87,7 +91,18 @@ public sealed class InputDispatcher : IDisposable
IKeyboardSource keyboard,
IMouseSource mouse,
KeyBindings bindings) =>
new(keyboard, mouse, bindings);
new(keyboard, mouse, bindings, static () => Environment.TickCount64);
/// <summary>
/// Deterministic test seam for time-sensitive input contracts. Production
/// construction always uses <see cref="Environment.TickCount64"/>.
/// </summary>
internal static InputDispatcher CreateDetached(
IKeyboardSource keyboard,
IMouseSource mouse,
KeyBindings bindings,
Func<long> getTickCount64) =>
new(keyboard, mouse, bindings, getTickCount64);
public bool IsDisposalComplete =>
_sourceAttached.All(static attached => !attached);
@ -570,7 +585,7 @@ public sealed class InputDispatcher : IDisposable
// -> additionally fire ActivationType.DoubleClick for any matching
// binding. Press has already fired for the second click (same as a
// single click); DoubleClick is the *additional* signal.
long nowMs = Environment.TickCount64;
long nowMs = _getTickCount64();
if (_lastMouseDownButton == button
&& nowMs - _lastMouseDownTickMs <= DoubleClickThresholdMs)
{