-
Notifications
You must be signed in to change notification settings - Fork 0
Add simulated Broadlink device for E2E testing #76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
96cc530
2878e1e
eb3f11b
b553a0d
c83ec96
a6cff4c
0e9cb94
e36cc05
7e3b34f
0146614
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| Feature: Broadlink Device Integration | ||
| As a user | ||
| I want the application to communicate with Broadlink IR devices | ||
| So that I can control my TV and AV equipment using the adaptive remote | ||
|
|
||
| Scenario: Broadlink receives Power command | ||
| Given the application is not running | ||
| When I start the application | ||
| Then I should see the application in the Ready phase | ||
| And I should not see any warning or error messages in the logs | ||
| When I click on the 'Power' button | ||
| Then I should see the Broadlink device recorded at least one inbound packet | ||
| And the recorded Broadlink packet's raw payload should not be empty | ||
| And no Broadlink packets should be marked as malformed | ||
| When I click on the 'Exit' button | ||
| And I wait for the application to shut down | ||
| Then I should not see any warning or error messages in the logs |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| using AdaptiveRemote.EndtoEndTests; | ||
| using AdaptiveRemote.EndtoEndTests.SimulatedBroadlink; | ||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
| using Reqnroll; | ||
|
|
||
| namespace AdaptiveRemote.EndToEndTests.Steps; | ||
|
|
||
| [Binding] | ||
| public class BroadlinkSteps : StepsBase | ||
| { | ||
| [Then(@"I should see the Broadlink device recorded at least one inbound packet")] | ||
| public void ThenIShouldSeeTheBroadlinkDeviceRecordedAtLeastOneInboundPacket() | ||
| { | ||
| ISimulatedBroadlinkDevice? device = Environment.Broadlink; | ||
| if (device == null) | ||
| { | ||
| Assert.Fail("Broadlink device is not running"); | ||
| } | ||
|
|
||
| // Poll for packets with a timeout of 10 seconds | ||
| bool found = WaitHelpers.ExecuteWithRetries(device.HasRecordedInboundPacketWithIrData, timeoutInSeconds: 10); | ||
|
|
||
| if (!found) | ||
| { | ||
| string recordedPackets = device.GetRecordedPacketsDebugString(); | ||
| Assert.Fail($"Expected Broadlink device to record at least one inbound packet with IR data, but none were found. Recorded packets: {recordedPackets}"); | ||
| } | ||
|
|
||
| TestContext.WriteLine("Successfully verified Broadlink device recorded inbound packet with IR data"); | ||
| } | ||
|
|
||
| [Then(@"the recorded Broadlink packet's raw payload should not be empty")] | ||
| public void ThenTheRecordedBroadlinkPacketRawPayloadShouldNotBeEmpty() | ||
| { | ||
| ISimulatedBroadlinkDevice? device = Environment.Broadlink; | ||
| if (device == null) | ||
| { | ||
| Assert.Fail("Broadlink device is not running"); | ||
| } | ||
|
|
||
| RecordedPacket? irPacket = device.GetFirstPacketWithIrData(); | ||
|
|
||
| if (irPacket == null) | ||
| { | ||
| Assert.Fail("No packet with IR payload was recorded"); | ||
| } | ||
|
|
||
| Assert.IsTrue(irPacket.RawPayload!.Length > 0, "IR payload should not be empty"); | ||
| TestContext.WriteLine($"IR payload size: {irPacket.RawPayload.Length} bytes"); | ||
| } | ||
|
|
||
| [Then(@"no Broadlink packets should be marked as malformed")] | ||
| public void ThenNoBroadlinkPacketsShouldBeMarkedAsMalformed() | ||
| { | ||
| ISimulatedBroadlinkDevice? device = Environment.Broadlink; | ||
| if (device == null) | ||
| { | ||
| Assert.Fail("Broadlink device is not running"); | ||
| } | ||
|
|
||
| RecordedPacket? malformedPacket = device.GetFirstMalformedPacket(); | ||
|
|
||
| if (malformedPacket != null) | ||
| { | ||
| Assert.Fail($"Found malformed packet: {malformedPacket.DebugDescription}"); | ||
| } | ||
|
|
||
| TestContext.WriteLine("No malformed packets found"); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,6 @@ | ||||||||||||||||||
| using AdaptiveRemote.EndtoEndTests.SimulatedBroadlink; | ||||||||||||||||||
| using AdaptiveRemote.EndtoEndTests.SimulatedTiVo; | ||||||||||||||||||
| using Microsoft.Extensions.Logging; | ||||||||||||||||||
|
|
||||||||||||||||||
| namespace AdaptiveRemote.EndtoEndTests.Host; | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -7,44 +9,24 @@ namespace AdaptiveRemote.EndtoEndTests.Host; | |||||||||||||||||
| /// </summary> | ||||||||||||||||||
| public sealed class SimulatedEnvironment : ISimulatedEnvironment | ||||||||||||||||||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm thinking this could be implemented like
Suggested change
Then all these objects can be created via Reqnroll's DI system.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated to accept |
||||||||||||||||||
| { | ||||||||||||||||||
| private readonly Dictionary<string, ISimulatedDeviceBuilder> _builders = new(); | ||||||||||||||||||
| private readonly Dictionary<string, ISimulatedDevice> _devices = new(); | ||||||||||||||||||
| private ISimulatedTiVoDevice? _tivo; | ||||||||||||||||||
| private ISimulatedBroadlinkDevice? _broadlink; | ||||||||||||||||||
| private bool _disposed; | ||||||||||||||||||
|
|
||||||||||||||||||
| /// <inheritdoc/> | ||||||||||||||||||
| public void RegisterDevice(string name, ISimulatedDeviceBuilder builder) | ||||||||||||||||||
| public SimulatedEnvironment(ILoggerFactory loggerFactory) | ||||||||||||||||||
| { | ||||||||||||||||||
| if (_builders.ContainsKey(name)) | ||||||||||||||||||
| { | ||||||||||||||||||
| throw new InvalidOperationException($"Device with name '{name}' is already registered."); | ||||||||||||||||||
| } | ||||||||||||||||||
| SimulatedTiVoDeviceBuilder tivoBuilder = new SimulatedTiVoDeviceBuilder(loggerFactory); | ||||||||||||||||||
| SimulatedBroadlinkDeviceBuilder broadlinkBuilder = new SimulatedBroadlinkDeviceBuilder(loggerFactory); | ||||||||||||||||||
|
|
||||||||||||||||||
| _builders[name] = builder; | ||||||||||||||||||
| _tivo = tivoBuilder.Start() as ISimulatedTiVoDevice; | ||||||||||||||||||
| _broadlink = broadlinkBuilder.Start() as ISimulatedBroadlinkDevice; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /// <inheritdoc/> | ||||||||||||||||||
| public ISimulatedDevice StartDevice(string name) | ||||||||||||||||||
| { | ||||||||||||||||||
| if (!_builders.TryGetValue(name, out ISimulatedDeviceBuilder? builder)) | ||||||||||||||||||
| { | ||||||||||||||||||
| throw new InvalidOperationException($"No device builder registered with name '{name}'."); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (_devices.ContainsKey(name)) | ||||||||||||||||||
| { | ||||||||||||||||||
| throw new InvalidOperationException($"Device with name '{name}' is already started."); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| ISimulatedDevice device = builder.Start(); | ||||||||||||||||||
| _devices[name] = device; | ||||||||||||||||||
| return device; | ||||||||||||||||||
| } | ||||||||||||||||||
| public ISimulatedTiVoDevice? TiVo => _tivo; | ||||||||||||||||||
|
|
||||||||||||||||||
| /// <inheritdoc/> | ||||||||||||||||||
| public bool TryGetDevice(string name, out ISimulatedDevice? device) | ||||||||||||||||||
| { | ||||||||||||||||||
| return _devices.TryGetValue(name, out device); | ||||||||||||||||||
| } | ||||||||||||||||||
| public ISimulatedBroadlinkDevice? Broadlink => _broadlink; | ||||||||||||||||||
|
|
||||||||||||||||||
| /// <inheritdoc/> | ||||||||||||||||||
| public void Dispose() | ||||||||||||||||||
|
|
@@ -54,33 +36,26 @@ public void Dispose() | |||||||||||||||||
| return; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| foreach (ISimulatedDevice device in _devices.Values) | ||||||||||||||||||
| try | ||||||||||||||||||
| { | ||||||||||||||||||
| try | ||||||||||||||||||
| { | ||||||||||||||||||
| device.Dispose(); | ||||||||||||||||||
| } | ||||||||||||||||||
| catch | ||||||||||||||||||
| { | ||||||||||||||||||
| // Ignore disposal errors | ||||||||||||||||||
| } | ||||||||||||||||||
| _tivo?.Dispose(); | ||||||||||||||||||
| } | ||||||||||||||||||
| catch | ||||||||||||||||||
| { | ||||||||||||||||||
| // Ignore disposal errors | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| _devices.Clear(); | ||||||||||||||||||
|
|
||||||||||||||||||
| foreach (ISimulatedDeviceBuilder builder in _builders.Values) | ||||||||||||||||||
| try | ||||||||||||||||||
| { | ||||||||||||||||||
| _broadlink?.Dispose(); | ||||||||||||||||||
| } | ||||||||||||||||||
| catch | ||||||||||||||||||
| { | ||||||||||||||||||
| try | ||||||||||||||||||
| { | ||||||||||||||||||
| builder.Dispose(); | ||||||||||||||||||
| } | ||||||||||||||||||
| catch | ||||||||||||||||||
| { | ||||||||||||||||||
| // Ignore disposal errors | ||||||||||||||||||
| } | ||||||||||||||||||
| // Ignore disposal errors | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| _builders.Clear(); | ||||||||||||||||||
| _tivo = null; | ||||||||||||||||||
| _broadlink = null; | ||||||||||||||||||
| _disposed = true; | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally there would not be so much logic in the Step implementations themselves. The logic should be in the TestServices assembly, and the step definitions should be about translating and validating steps and parameters. That way if I need to build non-Gherkin tests, all the logic is available.
I'm not sure the best way to handle this. Either the logic could be build directly into the SimulatedBroadlinkDevice itself, or it could be build as extension methods on ISimulatedBroadlinkDevice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved logic to extension methods in
ISimulatedBroadlinkDeviceExtensions.cs-HasRecordedInboundPacketWithIrData(),GetRecordedPacketsDebugString(),GetFirstPacketWithIrData(), andGetFirstMalformedPacket(). Step definitions now just call these methods. (7e3b34f)