Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using BrickController2.DeviceManagement;
using BrickController2.DeviceManagement.JieStar;
using BrickController2.PlatformServices.BluetoothLE;
using Moq;

namespace BrickController2.Tests.DeviceManagement.JieStar;

public abstract class JieStarDatagramTestsBase
{
protected const byte AppIdentifier1 = 0x61; // This is the first byte of an randomly choosen AppIdentifier for UnitTesting
protected const byte AppIdentifier2 = 0x62; // This is the second byte of an randomly choosen AppIdentifier for UnitTesting

protected static readonly byte[] AppIdentifier = [AppIdentifier1, AppIdentifier2];

/// <summary>
/// This class is a test implementation of the IJieStarPlatformService interface that simulates the behavior of the TryGetRfPayload method for testing purposes.
/// It always returns true and sets the rfPayload to the rawData provided.
/// </summary>
protected class TestJieStarPlatformService : IJieStarPlatformService
{
public bool TryGetRfPayload(byte ctxValue2, byte[] rawData, out byte[] rfPayload)
{
rfPayload = rawData;
return true; // Simulate success
}
}

protected readonly Mock<IBluetoothLEService> _bluetoothLEService = new(MockBehavior.Strict);
protected readonly Mock<IJieStarDeviceManager> _manager = new(MockBehavior.Strict);
protected readonly TestJieStarPlatformService _jieStarPlatformService = new();
internal readonly Mock<IDeviceRepository> _deviceRepository = new(MockBehavior.Strict);

protected JieStarDatagramTestsBase()
{
_manager.Setup(x => x.GetAppId()).Returns(AppIdentifier);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace BrickController2.Tests.DeviceManagement.JieStar;

public class JieStarDeviceManagerTests
{
private readonly JieStarDeviceManager _manager;
private readonly IJieStarDeviceManager _manager;
Comment thread
J0EK3R marked this conversation as resolved.
private readonly Mock<IPreferencesService> _preferencesService = new(MockBehavior.Strict);

public JieStarDeviceManagerTests()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
using BrickController2.DeviceManagement.JieStar;
using FluentAssertions;
using System;
using Xunit;

namespace BrickController2.Tests.DeviceManagement.JieStar;

public sealed class JieStarSCM4DatagramTests : JieStarDatagramTestsBase
{
private const byte PayloadIdentifierConnect1 = 0xa4;
private const byte PayloadIdentifierConnect2 = 0x5b;
private const byte PayloadIdentifierCommand1 = 0x40;
private const byte PayloadIdentifierCommand2 = 0xbf;

/// <summary>
/// This test checks that the payload identifiers are correctly set in the connect datagram for each device address.
/// </summary>
/// <param name="deviceAddress">The address of the device to test.</param>
[Theory]
[InlineData(JieStarSCM4.Device1)]
[InlineData(JieStarSCM4.Device2)]
[InlineData(JieStarSCM4.Device3)]
public void TryGetTelegram_ConnectDatagram_PayloadIdentifier(string deviceAddress)
{
JieStarSCM4 device = new JieStarSCM4("JieStarSCM4", deviceAddress, [], _deviceRepository.Object, _bluetoothLEService.Object, _jieStarPlatformService, _manager.Object);

device.TryGetTelegram(true, out byte[] payload).Should().BeTrue();
payload[0].Should().Be(PayloadIdentifierConnect1);
payload[7].Should().Be(PayloadIdentifierConnect2);
}

/// <summary>
/// This test checks that the AppIdentifier is correctly included in the connect datagram payload for each device address.
/// </summary>
/// <param name="deviceAddress">The address of the device to test.</param>
[Theory]
[InlineData(JieStarSCM4.Device1)]
[InlineData(JieStarSCM4.Device2)]
[InlineData(JieStarSCM4.Device3)]
public void TryGetTelegram_ConnectDatagram_AppIdentifier(string deviceAddress)
{
JieStarSCM4 device = new JieStarSCM4("JieStarSCM4", deviceAddress, [], _deviceRepository.Object, _bluetoothLEService.Object, _jieStarPlatformService, _manager.Object);

device.TryGetTelegram(true, out byte[] payload).Should().BeTrue();
payload[1].Should().Be(AppIdentifier1);
payload[2].Should().Be(AppIdentifier2);
}

/// <summary>
/// This test checks that the payload identifiers are correctly set in the command datagram for each device address.
/// </summary>
/// <param name="deviceAddress">The address of the device to test.</param>
[Theory]
[InlineData(JieStarSCM4.Device1)]
[InlineData(JieStarSCM4.Device2)]
[InlineData(JieStarSCM4.Device3)]
public void TryGetTelegram_CommandDatagram_PayloadIdentifier(string deviceAddress)
{
JieStarSCM4 device = new JieStarSCM4("JieStarSCM4", deviceAddress, [], _deviceRepository.Object, _bluetoothLEService.Object, _jieStarPlatformService, _manager.Object);

device.TryGetTelegram(false, out byte[] payload).Should().BeTrue();
payload[0].Should().Be(PayloadIdentifierCommand1);
payload[7].Should().Be(PayloadIdentifierCommand2);
}

/// <summary>
/// This test checks that the AppIdentifier is correctly included in the command datagram payload for each device address.
/// </summary>
/// <param name="deviceAddress">The address of the device to test.</param>
[Theory]
[InlineData(JieStarSCM4.Device1)]
[InlineData(JieStarSCM4.Device2)]
[InlineData(JieStarSCM4.Device3)]
public void TryGetTelegram_CommandDatagram_AppIdentifier(string deviceAddress)
{
JieStarSCM4 device = new JieStarSCM4("JieStarSCM4", deviceAddress, [], _deviceRepository.Object, _bluetoothLEService.Object, _jieStarPlatformService, _manager.Object);

device.TryGetTelegram(false, out byte[] payload).Should().BeTrue();
payload[1].Should().Be(AppIdentifier1);
payload[2].Should().Be(AppIdentifier2);
}

/// <summary>
/// This test checks that the command datagram payload is correctly constructed based on the set output values for each device address.
/// </summary>
/// <param name="deviceAddress">The address of the device to test.</param>
/// <param name="setValues">The set values for each channel.</param>
/// <param name="expectedPayload">The expected payload for the command datagram.</param>
[Theory]
[InlineData(JieStarSCM4.Device1, new float[] { 0.0f, 0.0f, 0.0f, 0.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x00, 0x00, 0x80, 0x80, PayloadIdentifierCommand2 })] // all channels neutral
[InlineData(JieStarSCM4.Device1, new float[] { 1.0f, 0.0f, 0.0f, 0.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0xf0, 0x00, 0x80, 0x80, PayloadIdentifierCommand2 })] // channel 1 maximum, others neutral
[InlineData(JieStarSCM4.Device1, new float[] { 0.0f, 1.0f, 0.0f, 0.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x0f, 0x00, 0x80, 0x80, PayloadIdentifierCommand2 })] // channel 2 maximum, others neutral
[InlineData(JieStarSCM4.Device1, new float[] { 0.0f, 0.0f, 1.0f, 0.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x00, 0xf0, 0x80, 0x80, PayloadIdentifierCommand2 })] // channel 3 maximum, others neutral
[InlineData(JieStarSCM4.Device1, new float[] { 0.0f, 0.0f, 0.0f, 1.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x00, 0x0f, 0x80, 0x80, PayloadIdentifierCommand2 })] // channel 4 maximum, others neutral
[InlineData(JieStarSCM4.Device1, new float[] { -1.0f, 0.0f, 0.0f, 0.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x70, 0x00, 0x80, 0x80, PayloadIdentifierCommand2 })] // channel 1 minimum, others neutral
[InlineData(JieStarSCM4.Device1, new float[] { 0.0f, -1.0f, 0.0f, 0.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x07, 0x00, 0x80, 0x80, PayloadIdentifierCommand2 })] // channel 2 minimum, others neutral
[InlineData(JieStarSCM4.Device1, new float[] { 0.0f, 0.0f, -1.0f, 0.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x00, 0x70, 0x80, 0x80, PayloadIdentifierCommand2 })] // channel 3 minimum, others neutral
[InlineData(JieStarSCM4.Device1, new float[] { 0.0f, 0.0f, 0.0f, -1.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x00, 0x07, 0x80, 0x80, PayloadIdentifierCommand2 })] // channel 4 minimum, others neutral
[InlineData(JieStarSCM4.Device1, new float[] { 9.0f, 9.0f, 9.0f, 9.0f, }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0xff, 0xff, 0x80, 0x80, PayloadIdentifierCommand2 })] // all channels above maximum, should be clamped to maximum
[InlineData(JieStarSCM4.Device1, new float[] { -9.0f, -9.0f, -9.0f, -9.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x77, 0x77, 0x80, 0x80, PayloadIdentifierCommand2 })] // all channels below minimum, should be clamped to minimum

[InlineData(JieStarSCM4.Device2, new float[] { 0.0f, 0.0f, 0.0f, 0.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x00, 0x00, 0x80, 0x80, PayloadIdentifierCommand2 })] // all channels neutral
[InlineData(JieStarSCM4.Device2, new float[] { 9.0f, 9.0f, 9.0f, 9.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0xff, 0xff, 0x80, 0x80, PayloadIdentifierCommand2 })] // all channels above maximum, should be clamped to maximum
[InlineData(JieStarSCM4.Device2, new float[] { -9.0f, -9.0f, -9.0f, -9.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x77, 0x77, 0x80, 0x80, PayloadIdentifierCommand2 })] // all channels below minimum, should be clamped to minimum

[InlineData(JieStarSCM4.Device3, new float[] { 0.0f, 0.0f, 0.0f, 0.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x00, 0x00, 0x80, 0x80, PayloadIdentifierCommand2 })] // all channels neutral
[InlineData(JieStarSCM4.Device3, new float[] { 9.0f, 9.0f, 9.0f, 9.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0xff, 0xff, 0x80, 0x80, PayloadIdentifierCommand2 })] // all channels above maximum, should be clamped to maximum
[InlineData(JieStarSCM4.Device3, new float[] { -9.0f, -9.0f, -9.0f, -9.0f }, new byte[] { PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x77, 0x77, 0x80, 0x80, PayloadIdentifierCommand2 })] // all channels below minimum, should be clamped to minimum
public void TryGetTelegram_CommandDatagram_Payload(string deviceAddress, float[] setValues, byte[] expectedPayload)
{
JieStarSCM4 device = new JieStarSCM4("JieStarSCM4", deviceAddress, [], _deviceRepository.Object, _bluetoothLEService.Object, _jieStarPlatformService, _manager.Object);

// Set the output values for the device
for (int i = 0; i < setValues.Length; i++)
{
device.SetOutput(i, setValues[i]);
}

// Get the command datagram payload
device.TryGetTelegram(false, out byte[] payload).Should().BeTrue();

// Check that the payload matches the expected values
for (int i = 0; i < expectedPayload.Length; i++)
{
payload[i].Should().Be(expectedPayload[i]);
}
}

/// <summary>
/// Tests that setting an illegal channel index throws an ArgumentOutOfRangeException.
/// </summary>
/// <param name="deviceAddress">The address of the device to test.</param>
[Theory]
[InlineData(JieStarSCM4.Device1)]
[InlineData(JieStarSCM4.Device2)]
[InlineData(JieStarSCM4.Device3)]
public void TryGetTelegram_CommandDatagram_SetIllegalChannel(string deviceAddress)
{
JieStarSCM4 device = new JieStarSCM4("JieStarSCM4", deviceAddress, [], _deviceRepository.Object, _bluetoothLEService.Object, _jieStarPlatformService, _manager.Object);

Action action = () => device.SetOutput(device.NumberOfChannels, 0);

action.Should().Throw<ArgumentOutOfRangeException>();
}

/// <summary>
/// This test checks that the command datagram payload is correctly constructed based on the set output values for multiple devices, ensuring that each device's payload is independent of the others.
/// </summary>
[Fact]
public void TryGetTelegram_CommandDatagram_InstanceInteraction()
{
float[] setValues_1 = [1.0f, 1.0f, 1.0f, 1.0f];
byte[] expectedPayload_1 = [PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0xff, 0xff, 0x80, 0x80, PayloadIdentifierCommand2];

float[] setValues_2 = [0.0f, 0.0f, 0.0f, 0.0f];
byte[] expectedPayload_2 = [PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x00, 0x00, 0x80, 0x80, PayloadIdentifierCommand2];

float[] setValues_3 = [0.0f, 0.0f, 0.0f, 0.0f];
byte[] expectedPayload_3 = [PayloadIdentifierCommand1, AppIdentifier1, AppIdentifier2, 0x00, 0x00, 0x80, 0x80, PayloadIdentifierCommand2];

JieStarSCM4 device1 = new JieStarSCM4("JieStarSCM4", JieStarSCM4.Device1, [], _deviceRepository.Object, _bluetoothLEService.Object, _jieStarPlatformService, _manager.Object);
JieStarSCM4 device2 = new JieStarSCM4("JieStarSCM4", JieStarSCM4.Device2, [], _deviceRepository.Object, _bluetoothLEService.Object, _jieStarPlatformService, _manager.Object);
JieStarSCM4 device3 = new JieStarSCM4("JieStarSCM4", JieStarSCM4.Device3, [], _deviceRepository.Object, _bluetoothLEService.Object, _jieStarPlatformService, _manager.Object);

// Set the output values for the device
for (int i = 0; i < setValues_1.Length; i++)
{
device1.SetOutput(i, setValues_1[i]);
device2.SetOutput(i, setValues_2[i]);
device3.SetOutput(i, setValues_3[i]);
}

// Get the command datagram payload
device1.TryGetTelegram(false, out byte[] payload1).Should().BeTrue();
device2.TryGetTelegram(false, out byte[] payload2).Should().BeTrue();
device3.TryGetTelegram(false, out byte[] payload3).Should().BeTrue();

// Check that the payload matches the expected values
for (int i = 0; i < expectedPayload_1.Length; i++)
{
payload1[i].Should().Be(expectedPayload_1[i]);
payload2[i].Should().Be(expectedPayload_2[i]);
payload3[i].Should().Be(expectedPayload_3[i]);
}
}
}
Loading
Loading