diff --git a/src/RioJoy.Core/Compat/Net48Polyfills.cs b/src/RioJoy.Core/Compat/Net48Polyfills.cs index 1f11890..3411887 100644 --- a/src/RioJoy.Core/Compat/Net48Polyfills.cs +++ b/src/RioJoy.Core/Compat/Net48Polyfills.cs @@ -10,10 +10,12 @@ namespace System.Collections.Generic /// /// Polyfill for CollectionExtensions.GetValueOrDefault (netstandard2.1+), /// absent on net48. Returns the value for or - /// default(TValue) when the key is missing. + /// default(TValue) when the key is missing. Takes IDictionary rather than + /// IReadOnlyDictionary: net40 (Windows XP flavor) predates the IReadOnly* + /// interfaces, so shared code uses the classic ones throughout. /// public static TValue? GetValueOrDefault( - this IReadOnlyDictionary dictionary, TKey key) + this IDictionary dictionary, TKey key) { if (dictionary is null) throw new ArgumentNullException(nameof(dictionary)); return dictionary.TryGetValue(key, out var value) ? value : default; diff --git a/src/RioJoy.Core/Compat/TaskCompat.cs b/src/RioJoy.Core/Compat/TaskCompat.cs new file mode 100644 index 0000000..c4ce827 --- /dev/null +++ b/src/RioJoy.Core/Compat/TaskCompat.cs @@ -0,0 +1,40 @@ +namespace RioJoy.Core.Compat; + +/// +/// net48/net40 bridge for the handful of Task-era statics the code uses. On +/// net40 (the Windows XP flavor) these come from Microsoft.Bcl.Async's +/// TaskEx, and SemaphoreSlim.WaitAsync doesn't exist at all — +/// there the wait blocks briefly instead, which at 9600 baud (tiny writes, +/// rare contention) costs nothing measurable. +/// +internal static class TaskCompat +{ +#if NET40 + public static Task Run(Action action) => TaskEx.Run(action); + + public static Task Delay(TimeSpan delay, CancellationToken cancellationToken) => + TaskEx.Delay(delay, cancellationToken); + + public static Task WhenAny(IEnumerable tasks) => TaskEx.WhenAny(tasks); + + public static Task WhenAll(IEnumerable tasks) => TaskEx.WhenAll(tasks); + + public static Task WaitAsync(SemaphoreSlim semaphore, CancellationToken cancellationToken) + { + semaphore.Wait(cancellationToken); // net40: no WaitAsync; block (see class doc) + return TaskEx.FromResult(true); + } +#else + public static Task Run(Action action) => Task.Run(action); + + public static Task Delay(TimeSpan delay, CancellationToken cancellationToken) => + Task.Delay(delay, cancellationToken); + + public static Task WhenAny(IEnumerable tasks) => Task.WhenAny(tasks); + + public static Task WhenAll(IEnumerable tasks) => Task.WhenAll(tasks); + + public static Task WaitAsync(SemaphoreSlim semaphore, CancellationToken cancellationToken) => + semaphore.WaitAsync(cancellationToken); +#endif +} diff --git a/src/RioJoy.Core/Editing/CockpitPanel.cs b/src/RioJoy.Core/Editing/CockpitPanel.cs index e3a89f7..d49e837 100644 --- a/src/RioJoy.Core/Editing/CockpitPanel.cs +++ b/src/RioJoy.Core/Editing/CockpitPanel.cs @@ -27,7 +27,7 @@ public sealed record PanelGroup( bool LampCapable, int OriginCol, int OriginRow, - IReadOnlyList Addresses); + IList Addresses); /// One address button placed on the panel (absolute cell coordinates). public sealed record PanelButton(int Address, PanelGroup Group, int Col, int Row, bool LampCapable); @@ -66,7 +66,7 @@ public static class CockpitPanel } /// All panel groups, positioned for rendering. - public static IReadOnlyList Groups { get; } = new[] + public static IList Groups { get; } = new[] { // Upper MFD row. new PanelGroup("Upper Left MFD", PanelGroupKind.Mfd, 4, 2, true, 0, 1, Mfd(0x2F)), @@ -90,7 +90,7 @@ public static class CockpitPanel }; /// Flatten the groups to positioned s (absolute cells). - public static IReadOnlyList Buttons() + public static IList Buttons() { var buttons = new List(); foreach (PanelGroup g in Groups) diff --git a/src/RioJoy.Core/Editing/SheetLayout.cs b/src/RioJoy.Core/Editing/SheetLayout.cs index 2e77367..aba2d1c 100644 --- a/src/RioJoy.Core/Editing/SheetLayout.cs +++ b/src/RioJoy.Core/Editing/SheetLayout.cs @@ -34,7 +34,7 @@ public sealed record SheetCell(int Row, int Col, string Text, int? Address, Shee /// public static class SheetLayout { - public static IReadOnlyList Load(string path) => Parse(File.ReadAllText(path)); + public static IList Load(string path) => Parse(File.ReadAllText(path)); /// /// The RIO address a cell's text encodes (two hex digits in 0x00–0x6F), or null. @@ -66,7 +66,7 @@ public static class SheetLayout /// longer than is dropped as a merged-cell /// artifact; the clean export has none. /// - public static IReadOnlyList Parse(string csv, int maxTextLength = 40) + public static IList Parse(string csv, int maxTextLength = 40) { if (csv is null) throw new ArgumentNullException(nameof(csv)); diff --git a/src/RioJoy.Core/Mapping/KeyCatalog.cs b/src/RioJoy.Core/Mapping/KeyCatalog.cs index c94c2ed..2aaacac 100644 --- a/src/RioJoy.Core/Mapping/KeyCatalog.cs +++ b/src/RioJoy.Core/Mapping/KeyCatalog.cs @@ -13,7 +13,7 @@ public readonly record struct KeyName(string Name, byte Value); public static class KeyCatalog { /// All catalogued keys, in menu order (letters, digits, F-keys, …). - public static IReadOnlyList Entries { get; } = Build(); + public static IList Entries { get; } = Build(); private static readonly Dictionary ByValue = Entries.GroupBy(e => e.Value).ToDictionary(g => g.Key, g => g.First().Name); diff --git a/src/RioJoy.Core/Mapping/RioInputMap.cs b/src/RioJoy.Core/Mapping/RioInputMap.cs index 7a3d4cd..3dc9c7f 100644 --- a/src/RioJoy.Core/Mapping/RioInputMap.cs +++ b/src/RioJoy.Core/Mapping/RioInputMap.cs @@ -14,7 +14,7 @@ public sealed class RioInputMap public RioInputMap() { } /// Create a map from raw 16-bit words indexed by address. - public RioInputMap(IReadOnlyDictionary entries) + public RioInputMap(IDictionary entries) { if (entries is null) throw new ArgumentNullException(nameof(entries)); foreach ((int address, ushort raw) in entries) diff --git a/src/RioJoy.Core/Output/HidFeederJoystickSink.cs b/src/RioJoy.Core/Output/HidFeederJoystickSink.cs index a22e91e..56066d9 100644 --- a/src/RioJoy.Core/Output/HidFeederJoystickSink.cs +++ b/src/RioJoy.Core/Output/HidFeederJoystickSink.cs @@ -114,7 +114,7 @@ public sealed class HidFeederJoystickSink : IJoystickSink, IDisposable try { var ifData = new SP_DEVICE_INTERFACE_DATA(); - ifData.cbSize = Marshal.SizeOf(); + ifData.cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DATA)); if (!SetupDiEnumDeviceInterfaces(devInfo, IntPtr.Zero, ref guid, 0, ref ifData)) return null; diff --git a/src/RioJoy.Core/Output/SendInputSink.cs b/src/RioJoy.Core/Output/SendInputSink.cs index 0b745a9..51a6981 100644 --- a/src/RioJoy.Core/Output/SendInputSink.cs +++ b/src/RioJoy.Core/Output/SendInputSink.cs @@ -69,7 +69,7 @@ public sealed class SendInputSink : IInputSink } private static void Send(INPUT input) => - SendInput(1, new[] { input }, Marshal.SizeOf()); + SendInput(1, new[] { input }, Marshal.SizeOf(typeof(INPUT))); // --- Win32 interop -------------------------------------------------------- diff --git a/src/RioJoy.Core/Output/ViGEmJoystickSink.cs b/src/RioJoy.Core/Output/ViGEmJoystickSink.cs index 5d64aaf..357c192 100644 --- a/src/RioJoy.Core/Output/ViGEmJoystickSink.cs +++ b/src/RioJoy.Core/Output/ViGEmJoystickSink.cs @@ -1,3 +1,4 @@ +#if !NET40 // ViGEmBus is Win10+ only; the XP flavor uses RioGamepadXP (PLAN.md §8B) using Nefarius.ViGEm.Client; using Nefarius.ViGEm.Client.Targets; using Nefarius.ViGEm.Client.Targets.Xbox360; @@ -26,7 +27,7 @@ public sealed class ViGEmJoystickSink : IJoystickSink, IDisposable }; /// Display names for the mappable buttons, aligned with . - public static IReadOnlyList ButtonNames { get; } = new[] + public static IList ButtonNames { get; } = new[] { "A", "B", "X", "Y", "LB", "RB", "Back", "Start", "L3", "R3", "Guide", }; @@ -125,3 +126,4 @@ public sealed class ViGEmJoystickSink : IJoystickSink, IDisposable _client.Dispose(); } } +#endif diff --git a/src/RioJoy.Core/Overlay/GoobieDataImporter.cs b/src/RioJoy.Core/Overlay/GoobieDataImporter.cs index 342879d..65975e4 100644 --- a/src/RioJoy.Core/Overlay/GoobieDataImporter.cs +++ b/src/RioJoy.Core/Overlay/GoobieDataImporter.cs @@ -18,8 +18,8 @@ public static class GoobieDataImporter { /// A parsed data file: the field names and one label map per row. public sealed record Sheet( - IReadOnlyList Fields, - IReadOnlyList> Rows); + IList Fields, + IList> Rows); public static Sheet Load(string path) => Parse(File.ReadAllText(path)); @@ -33,7 +33,7 @@ public static class GoobieDataImporter throw new FormatException("No lists found; expected a header list of field names."); List fields = lists[0]; - var rows = new List>(); + var rows = new List>(); for (int i = 1; i < lists.Count; i++) { @@ -51,7 +51,7 @@ public static class GoobieDataImporter /// Convenience: the label map for a single row (default the first), with empty /// values dropped — ready to hand to . /// - public static IReadOnlyDictionary LabelsForRow(Sheet sheet, int rowIndex = 0) + public static IDictionary LabelsForRow(Sheet sheet, int rowIndex = 0) { if (sheet is null) throw new ArgumentNullException(nameof(sheet)); if (rowIndex < 0 || rowIndex >= sheet.Rows.Count) diff --git a/src/RioJoy.Core/Overlay/OverlayHitTester.cs b/src/RioJoy.Core/Overlay/OverlayHitTester.cs index f40209d..1a2c7ab 100644 --- a/src/RioJoy.Core/Overlay/OverlayHitTester.cs +++ b/src/RioJoy.Core/Overlay/OverlayHitTester.cs @@ -15,7 +15,7 @@ public static class OverlayHitTester /// A region's rectangle is its stored cell geometry; label rotation does not /// affect hit-testing. /// - public static IReadOnlyList RegionsAt(OverlayTemplate template, double x, double y) + public static IList RegionsAt(OverlayTemplate template, double x, double y) { if (template is null) throw new ArgumentNullException(nameof(template)); @@ -33,7 +33,7 @@ public static class OverlayHitTester /// public static OverlayRegion? NextAt(OverlayTemplate template, double x, double y, string? currentName) { - IReadOnlyList hits = RegionsAt(template, x, y); + IList hits = RegionsAt(template, x, y); if (hits.Count == 0) return null; diff --git a/src/RioJoy.Core/Overlay/OverlayLayoutEngine.cs b/src/RioJoy.Core/Overlay/OverlayLayoutEngine.cs index 0d774f8..b2d0386 100644 --- a/src/RioJoy.Core/Overlay/OverlayLayoutEngine.cs +++ b/src/RioJoy.Core/Overlay/OverlayLayoutEngine.cs @@ -20,9 +20,9 @@ public sealed class OverlayLayoutEngine /// in (or with empty text) are skipped — matching the /// legacy behavior where a blank field produced no visible text. /// - public IReadOnlyList Layout( + public IList Layout( OverlayTemplate template, - IReadOnlyDictionary labels, + IDictionary labels, OverlayLayoutOptions? options = null) { if (template is null) throw new ArgumentNullException(nameof(template)); diff --git a/src/RioJoy.Core/Profiles/AutoSwitch.cs b/src/RioJoy.Core/Profiles/AutoSwitch.cs index 67bd2fd..5969d62 100644 --- a/src/RioJoy.Core/Profiles/AutoSwitch.cs +++ b/src/RioJoy.Core/Profiles/AutoSwitch.cs @@ -33,7 +33,10 @@ public readonly struct SwitchDecision : IEquatable public bool Equals(SwitchDecision other) => Mode == other.Mode && ReferenceEquals(Profile, other.Profile); public override bool Equals(object? obj) => obj is SwitchDecision d && Equals(d); - public override int GetHashCode() => HashCode.Combine(Mode, Profile); + // Manual combine: HashCode.Combine needs Microsoft.Bcl.HashCode, which has + // no net40 build (Windows XP flavor). + public override int GetHashCode() => + ((int)Mode * 397) ^ (Profile?.GetHashCode() ?? 0); public override string ToString() => Mode == SwitchMode.Activate ? $"Activate({Profile?.Name})" : Mode.ToString(); } diff --git a/src/RioJoy.Core/Profiles/AutoSwitchWatcher.cs b/src/RioJoy.Core/Profiles/AutoSwitchWatcher.cs index 7c481f4..210c25c 100644 --- a/src/RioJoy.Core/Profiles/AutoSwitchWatcher.cs +++ b/src/RioJoy.Core/Profiles/AutoSwitchWatcher.cs @@ -62,7 +62,7 @@ public sealed class AutoSwitchWatcher { try { - await Task.Delay(interval, cancellationToken).ConfigureAwait(false); + await Compat.TaskCompat.Delay(interval, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { diff --git a/src/RioJoy.Core/Profiles/IniFile.cs b/src/RioJoy.Core/Profiles/IniFile.cs index 2160715..bf7bb35 100644 --- a/src/RioJoy.Core/Profiles/IniFile.cs +++ b/src/RioJoy.Core/Profiles/IniFile.cs @@ -44,7 +44,7 @@ public sealed class IniFile } /// All key/value pairs in a section (empty if the section is absent). - public IReadOnlyDictionary Section(string name) => + public IDictionary Section(string name) => _sections.TryGetValue(name, out Dictionary? s) ? s : new Dictionary(); diff --git a/src/RioJoy.Core/RioJoy.Core.csproj b/src/RioJoy.Core/RioJoy.Core.csproj index b050b9b..2e3eec4 100644 --- a/src/RioJoy.Core/RioJoy.Core.csproj +++ b/src/RioJoy.Core/RioJoy.Core.csproj @@ -1,26 +1,47 @@ - - net48 + + net48;net40 x64 - x64 enable enable latest + + + x64 + + + + + + + - - - + all runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/src/RioJoy.Core/Serial/RioSerialLink.cs b/src/RioJoy.Core/Serial/RioSerialLink.cs index 4ad4b73..2d283eb 100644 --- a/src/RioJoy.Core/Serial/RioSerialLink.cs +++ b/src/RioJoy.Core/Serial/RioSerialLink.cs @@ -69,19 +69,19 @@ public sealed class RioSerialLink { // If any running loop ends (transport closed / error / cancellation), // tear the others down too. - await Task.WhenAny(loops).ConfigureAwait(false); + await Compat.TaskCompat.WhenAny(loops).ConfigureAwait(false); } finally { linked.Cancel(); - await Task.WhenAll(loops.Select(Swallow)).ConfigureAwait(false); + await Compat.TaskCompat.WhenAll(loops.Select(Swallow)).ConfigureAwait(false); } } /// Send a pre-built packet (see ) to the RIO. public async Task SendAsync(byte[] packet, CancellationToken cancellationToken = default) { - await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); + await Compat.TaskCompat.WaitAsync(_writeLock, cancellationToken).ConfigureAwait(false); try { await _transport.WriteAsync(packet, cancellationToken).ConfigureAwait(false); @@ -191,7 +191,7 @@ public sealed class RioSerialLink { while (!ct.IsCancellationRequested) { - await Task.Delay(_options.AnalogPollInterval, ct).ConfigureAwait(false); + await Compat.TaskCompat.Delay(_options.AnalogPollInterval, ct).ConfigureAwait(false); await RequestAnalogAsync(ct).ConfigureAwait(false); diff --git a/src/RioJoy.Core/Serial/SerialPortTransport.cs b/src/RioJoy.Core/Serial/SerialPortTransport.cs index cce6bd9..49c1617 100644 --- a/src/RioJoy.Core/Serial/SerialPortTransport.cs +++ b/src/RioJoy.Core/Serial/SerialPortTransport.cs @@ -74,7 +74,7 @@ public sealed class SerialPortTransport : IRioTransport // still releases the handle. } - Task close = Task.Run(() => + Task close = Compat.TaskCompat.Run(() => { try { _port.Dispose(); } catch { /* best-effort release */ } diff --git a/src/RioJoy.Overlay/ProfileWallpaperGenerator.cs b/src/RioJoy.Overlay/ProfileWallpaperGenerator.cs index 9079442..73a740f 100644 --- a/src/RioJoy.Overlay/ProfileWallpaperGenerator.cs +++ b/src/RioJoy.Overlay/ProfileWallpaperGenerator.cs @@ -28,7 +28,7 @@ public sealed class ProfileWallpaperGenerator public string Generate( OverlayTemplate template, string templateDirectory, - IReadOnlyDictionary labels, + IDictionary labels, string outputPath, OverlayLayoutOptions? options = null) { diff --git a/src/RioJoy.Overlay/SkiaOverlayRenderer.cs b/src/RioJoy.Overlay/SkiaOverlayRenderer.cs index f20be11..de50444 100644 --- a/src/RioJoy.Overlay/SkiaOverlayRenderer.cs +++ b/src/RioJoy.Overlay/SkiaOverlayRenderer.cs @@ -31,7 +31,7 @@ public sealed class SkiaOverlayRenderer /// public SKBitmap Render( OverlayTemplate template, - IReadOnlyDictionary labels, + IDictionary labels, SKBitmap baseImage, OverlayLayoutOptions? options = null) { @@ -95,7 +95,7 @@ public sealed class SkiaOverlayRenderer /// Render and encode to PNG bytes. public byte[] RenderToPng( OverlayTemplate template, - IReadOnlyDictionary labels, + IDictionary labels, SKBitmap baseImage, OverlayLayoutOptions? options = null) { @@ -110,7 +110,7 @@ public sealed class SkiaOverlayRenderer /// public void RenderToFile( OverlayTemplate template, - IReadOnlyDictionary labels, + IDictionary labels, string outputPath, OverlayLayoutOptions? options = null) { diff --git a/src/RioJoy.Tray/Editor/PanelCanvas.cs b/src/RioJoy.Tray/Editor/PanelCanvas.cs index 9b6429a..2e4fb00 100644 --- a/src/RioJoy.Tray/Editor/PanelCanvas.cs +++ b/src/RioJoy.Tray/Editor/PanelCanvas.cs @@ -10,7 +10,7 @@ namespace RioJoy.Tray.Editor; /// internal sealed class PanelCanvas : Control { - private static readonly IReadOnlyList AllButtons = CockpitPanel.Buttons(); + private static readonly IList AllButtons = CockpitPanel.Buttons(); public PanelCanvas() { diff --git a/src/RioJoy.Tray/Editor/PanelView.cs b/src/RioJoy.Tray/Editor/PanelView.cs index f842c6d..c314ecd 100644 --- a/src/RioJoy.Tray/Editor/PanelView.cs +++ b/src/RioJoy.Tray/Editor/PanelView.cs @@ -40,7 +40,7 @@ public static class PanelView public const int CalOriginCol = 2; public const int CalOriginRow = 9; - public static IReadOnlyList CalibrationCells { get; } = new[] + public static IList CalibrationCells { get; } = new[] { new CalibrationCell(JoyStickSetting.InvertX, "Inv X", 2, 10), new CalibrationCell(JoyStickSetting.InvertY, "Inv Y", 2, 11), @@ -51,7 +51,7 @@ public static class PanelView new CalibrationCell(JoyStickSetting.EnableZR, "ZR mix", 2, 16), }; - public static Size GridSize(IReadOnlyList buttons) + public static Size GridSize(IList buttons) { int maxCol = 0, maxRow = 0; foreach (PanelButton b in buttons) @@ -69,8 +69,8 @@ public static class PanelView public static void Paint( Graphics g, - IReadOnlyList buttons, - IReadOnlyList groups, + IList buttons, + IList groups, Func label, Func function, Func lit, @@ -225,7 +225,7 @@ public static class PanelView } } - public static PanelButton? HitTest(IReadOnlyList buttons, Point p) + public static PanelButton? HitTest(IList buttons, Point p) { foreach (PanelButton b in buttons) if (Cell(b.Col, b.Row).Contains(p)) diff --git a/tests/RioJoy.Core.Tests/Editing/CockpitPanelTests.cs b/tests/RioJoy.Core.Tests/Editing/CockpitPanelTests.cs index e4cdfa1..3767945 100644 --- a/tests/RioJoy.Core.Tests/Editing/CockpitPanelTests.cs +++ b/tests/RioJoy.Core.Tests/Editing/CockpitPanelTests.cs @@ -50,7 +50,7 @@ public class CockpitPanelTests public void Keypad_PhysicalLayout_MapsDigitsToAddresses() { // Internal keypad: top-left "1" -> 0x51, bottom-middle "0" -> 0x50, "C" -> 0x5C. - IReadOnlyList all = CockpitPanel.Buttons(); + IList all = CockpitPanel.Buttons(); PanelGroup pad = CockpitPanel.Groups.Single(g => g.Title == "Internal Keypad"); Assert.Equal(0x51, pad.Addresses[0]); // row0 col0 = "1" diff --git a/tests/RioJoy.Core.Tests/Editing/SheetLayoutTests.cs b/tests/RioJoy.Core.Tests/Editing/SheetLayoutTests.cs index a025a27..0db2ac1 100644 --- a/tests/RioJoy.Core.Tests/Editing/SheetLayoutTests.cs +++ b/tests/RioJoy.Core.Tests/Editing/SheetLayoutTests.cs @@ -47,7 +47,7 @@ public class SheetLayoutTests "\"\",\"IsLit\",\"\",\"\",\"0F\"\n" + "\"\",\"\",\"\",\"\",\"HOME\""; - IReadOnlyList cells = SheetLayout.Parse(csv); + IList cells = SheetLayout.Parse(csv); SheetCell addr = Assert.Single(cells, c => c.Text == "0F"); Assert.Equal(0x0F, addr.Address); @@ -64,7 +64,7 @@ public class SheetLayoutTests [Fact] public void Parse_RealSheet_HasFullAddressMap() { - IReadOnlyList cells = SheetLayout.Load( + IList cells = SheetLayout.Load( TestRepo.CustomBackground("config-sheet.csv")); // Every RIO address 0x00..0x6F that exists should appear exactly once as a cell. diff --git a/tests/RioJoy.Core.Tests/Overlay/GoobieDataImporterTests.cs b/tests/RioJoy.Core.Tests/Overlay/GoobieDataImporterTests.cs index 547f154..870cfd4 100644 --- a/tests/RioJoy.Core.Tests/Overlay/GoobieDataImporterTests.cs +++ b/tests/RioJoy.Core.Tests/Overlay/GoobieDataImporterTests.cs @@ -26,7 +26,7 @@ public class GoobieDataImporterTests [Fact] public void LabelsForRow_DropsEmptyValues() { - IReadOnlyDictionary labels = + IDictionary labels = GoobieDataImporter.LabelsForRow(GoobieDataImporter.Parse(Sample)); Assert.True(labels.ContainsKey("b-00")); diff --git a/tests/RioJoy.Core.Tests/Overlay/OverlayHitTesterTests.cs b/tests/RioJoy.Core.Tests/Overlay/OverlayHitTesterTests.cs index 4f2d287..29ea2f2 100644 --- a/tests/RioJoy.Core.Tests/Overlay/OverlayHitTesterTests.cs +++ b/tests/RioJoy.Core.Tests/Overlay/OverlayHitTesterTests.cs @@ -21,7 +21,7 @@ public class OverlayHitTesterTests [Fact] public void RegionsAt_ReturnsSmallestFirst() { - IReadOnlyList hits = OverlayHitTester.RegionsAt(Template(), 100, 50); + IList hits = OverlayHitTester.RegionsAt(Template(), 100, 50); Assert.Equal(new[] { "small", "big" }, hits.Select(r => r.Name)); } diff --git a/tests/RioJoy.Core.Tests/Overlay/OverlayLayoutEngineTests.cs b/tests/RioJoy.Core.Tests/Overlay/OverlayLayoutEngineTests.cs index 0c66422..d585c7c 100644 --- a/tests/RioJoy.Core.Tests/Overlay/OverlayLayoutEngineTests.cs +++ b/tests/RioJoy.Core.Tests/Overlay/OverlayLayoutEngineTests.cs @@ -133,7 +133,7 @@ public class OverlayLayoutEngineTests ["b-99"] = "ORPHAN", // no such region -> ignored }; - IReadOnlyList placed = Engine.Layout(template, labels); + IList placed = Engine.Layout(template, labels); Assert.Equal(2, placed.Count); Assert.Equal("b-00", placed[0].RegionName); diff --git a/tests/RioJoy.Core.Tests/Overlay/OverlayRenderIntegrationTests.cs b/tests/RioJoy.Core.Tests/Overlay/OverlayRenderIntegrationTests.cs index c43e622..f8f277b 100644 --- a/tests/RioJoy.Core.Tests/Overlay/OverlayRenderIntegrationTests.cs +++ b/tests/RioJoy.Core.Tests/Overlay/OverlayRenderIntegrationTests.cs @@ -35,7 +35,7 @@ public class OverlayRenderIntegrationTests Assert.Equal(template.Width, baseImg.Width); Assert.Equal(template.Height, baseImg.Height); - IReadOnlyDictionary labels = + IDictionary labels = GoobieDataImporter.LabelsForRow(GoobieDataImporter.Load(TestRepo.CustomBackground("TEST.data"))); using SKBitmap rendered = new SkiaOverlayRenderer().Render(template, labels, baseImg);