Skip to content

Commit

Permalink
Add unit tests of I2cRegisterAccess
Browse files Browse the repository at this point in the history
  • Loading branch information
EsbenVis committed Feb 3, 2021
1 parent 52b1a93 commit 6050b40
Show file tree
Hide file tree
Showing 6 changed files with 351 additions and 2 deletions.
5 changes: 3 additions & 2 deletions src/devices/QwiicButton/I2cRegisterAccess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public T ReadRegister<T>(byte registerAddress, bool useLittleEndian)

switch (default(T))
{
case sbyte sbyteType:
case byte byteType:
return MemoryMarshal.Read<T>(readBuffer);
case short shortType:
Expand Down Expand Up @@ -71,7 +72,7 @@ public T ReadRegister<T>(byte registerAddress, bool useLittleEndian)
? (T)(object)BinaryPrimitives.ReadUInt64LittleEndian(readBuffer)
: (T)(object)BinaryPrimitives.ReadUInt64BigEndian(readBuffer);
default:
throw new InvalidOperationException($"Type '{default(T).GetType()}' is not a supported integral numeric type.");
throw new InvalidOperationException($"Type {default(T).GetType()} is not a supported integral numeric type.");
}
}

Expand Down Expand Up @@ -165,7 +166,7 @@ public void WriteRegister<T>(byte registerAddress, T data, bool useLittleEndian)
break;

default:
throw new InvalidOperationException($"Type '{data.GetType()}' is not a supported integral numeric type.");
throw new InvalidOperationException($"Type {data.GetType()} is not a supported integral numeric type.");
}

_device.Write(outArray);
Expand Down
2 changes: 2 additions & 0 deletions src/devices/QwiicButton/I2cRegisterAccessOfT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace Iot.Device.QwiicButton
/// }
/// </code>
/// </example>
/// <remarks>
/// Notice: If the I2C device uses register addresses larger than a <see cref="byte"/>,
/// it often means that you have to read the data at several individual 8-bit addresses and
/// assemble this data in the correct order to extract the corresponding 16/32/64-bit word.
Expand All @@ -31,6 +32,7 @@ namespace Iot.Device.QwiicButton
/// actual 16-bit word referenced by the hexadecimal 0x1234.
/// Read through the data sheets/manuals for your specific I2C device for more information
/// on its register addressing.
/// </remarks>
/// </summary>
public sealed class I2cRegisterAccess<TRegisterMap> : IDisposable
where TRegisterMap : Enum
Expand Down
17 changes: 17 additions & 0 deletions src/devices/QwiicButton/QwiicButton.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{6A4D
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QwiicButton.Samples", "samples\QwiicButton.Samples.csproj", "{3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{5EDC3F91-8614-493E-B835-2EB30673FA81}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QwiicButton.Tests", "tests\QwiicButton.Tests.csproj", "{95E25429-8C41-48D2-B53F-75C59C8911C3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -43,12 +47,25 @@ Global
{3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}.Release|x64.Build.0 = Release|Any CPU
{3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}.Release|x86.ActiveCfg = Release|Any CPU
{3CFA13D6-1D29-4C87-B0C1-01A6901A50EF}.Release|x86.Build.0 = Release|Any CPU
{95E25429-8C41-48D2-B53F-75C59C8911C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{95E25429-8C41-48D2-B53F-75C59C8911C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{95E25429-8C41-48D2-B53F-75C59C8911C3}.Debug|x64.ActiveCfg = Debug|Any CPU
{95E25429-8C41-48D2-B53F-75C59C8911C3}.Debug|x64.Build.0 = Debug|Any CPU
{95E25429-8C41-48D2-B53F-75C59C8911C3}.Debug|x86.ActiveCfg = Debug|Any CPU
{95E25429-8C41-48D2-B53F-75C59C8911C3}.Debug|x86.Build.0 = Debug|Any CPU
{95E25429-8C41-48D2-B53F-75C59C8911C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{95E25429-8C41-48D2-B53F-75C59C8911C3}.Release|Any CPU.Build.0 = Release|Any CPU
{95E25429-8C41-48D2-B53F-75C59C8911C3}.Release|x64.ActiveCfg = Release|Any CPU
{95E25429-8C41-48D2-B53F-75C59C8911C3}.Release|x64.Build.0 = Release|Any CPU
{95E25429-8C41-48D2-B53F-75C59C8911C3}.Release|x86.ActiveCfg = Release|Any CPU
{95E25429-8C41-48D2-B53F-75C59C8911C3}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{3CFA13D6-1D29-4C87-B0C1-01A6901A50EF} = {6A4DE7B1-03F3-4EE0-BF73-A0BAEF88BA2B}
{95E25429-8C41-48D2-B53F-75C59C8911C3} = {5EDC3F91-8614-493E-B835-2EB30673FA81}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CE2AFD04-A916-4466-99FB-89DB070F1E49}
Expand Down
42 changes: 42 additions & 0 deletions src/devices/QwiicButton/tests/I2cDeviceMock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Device.I2c;

namespace QwiicButton.Tests
{
internal class I2cDeviceMock : I2cDevice
{
public override byte ReadByte()
{
throw new NotImplementedException();
}

public override void Read(Span<byte> buffer)
{
throw new NotImplementedException();
}

public override void WriteByte(byte value)
{
throw new NotImplementedException();
}

public byte[] WriteBuffer { get; set; }
public override void Write(ReadOnlySpan<byte> buffer)
{
WriteBuffer = buffer.ToArray();
}

public byte[] ReadBuffer { get; set; }
public override void WriteRead(ReadOnlySpan<byte> writeBuffer, Span<byte> readBuffer)
{
WriteBuffer = writeBuffer.ToArray();

for (int index = 0; index < ReadBuffer.Length; index++)
{
readBuffer[index] = ReadBuffer[index];
}
}

public override I2cConnectionSettings ConnectionSettings { get; }
}
}
266 changes: 266 additions & 0 deletions src/devices/QwiicButton/tests/I2cRegisterAccessTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using QwiicButton.Tests;
using Xunit;

namespace Iot.Device.QwiicButton.Tests
{
public class I2cRegisterAccessTests
{
#region WriteRegister Tests

[Theory]
[InlineData(55, 12, true, new byte[] { 55, 12 })]
[InlineData(55, 12, false, new byte[] { 55, 12 })]
public void WriteRegister_GivenAddressAndSByteData_WhenUsingEndianness_ThenWritesExpectedArray(
byte registerAddress,
sbyte data,
bool useLittleEndian,
byte[] expected)
{
RunWriteTest(registerAddress, data, useLittleEndian, expected);
}

[Theory]
[InlineData(55, 12, true, new byte[] { 55, 12 })]
[InlineData(55, 12, false, new byte[] { 55, 12 })]
public void WriteRegister_GivenAddressAndByteData_WhenUsingEndianness_ThenWritesExpectedArray(
byte registerAddress,
byte data,
bool useLittleEndian,
byte[] expected)
{
RunWriteTest(registerAddress, data, useLittleEndian, expected);
}

[Theory]
[InlineData(55, 6528, true, new byte[] { 55, 128, 25 })]
[InlineData(55, 6528, false, new byte[] { 55, 25, 128 })]
public void WriteRegister_GivenAddressAndShortData_WhenUsingEndianness_ThenWritesExpectedArray(
byte registerAddress,
short data,
bool useLittleEndian,
byte[] expected)
{
RunWriteTest(registerAddress, data, useLittleEndian, expected);
}

[Theory]
[InlineData(55, 65280, true, new byte[] { 55, 0, 255 })]
[InlineData(55, 65280, false, new byte[] { 55, 255, 0 })]
public void WriteRegister_GivenAddressAndUShortData_WhenUsingEndianness_ThenWritesExpectedArray(
byte registerAddress,
ushort data,
bool useLittleEndian,
byte[] expected)
{
RunWriteTest(registerAddress, data, useLittleEndian, expected);
}

[Theory]
[InlineData(55, -214718364, true, new byte[] { 55, 100, 168, 51, 243 })]
[InlineData(55, -214718364, false, new byte[] { 55, 243, 51, 168, 100 })]
public void WriteRegister_GivenAddressAndIntData_WhenUsingEndianness_ThenWritesExpectedArray(
byte registerAddress,
int data,
bool useLittleEndian,
byte[] expected)
{
RunWriteTest(registerAddress, data, useLittleEndian, expected);
}

[Theory]
[InlineData(55, 4278255360, true, new byte[] { 55, 0, 255, 0, 255 })]
[InlineData(55, 4278255360, false, new byte[] { 55, 255, 0, 255, 0 })]
public void WriteRegister_GivenAddressAndUIntData_WhenUsingEndianness_ThenWritesExpectedArray(
byte registerAddress,
uint data,
bool useLittleEndian,
byte[] expected)
{
RunWriteTest(registerAddress, data, useLittleEndian, expected);
}

[Theory]
[InlineData(55, 280379743338240, true, new byte[] { 55, 0, 255, 0, 255, 0, 255, 0, 0 })]
[InlineData(55, 280379743338240, false, new byte[] { 55, 0, 0, 255, 0, 255, 0, 255, 0 })]
public void WriteRegister_GivenAddressAndLongData_WhenUsingEndianness_ThenWritesExpectedArray(
byte registerAddress,
long data,
bool useLittleEndian,
byte[] expected)
{
RunWriteTest(registerAddress, data, useLittleEndian, expected);
}

[Theory]
[InlineData(55, 18374966859414961920, true, new byte[] { 55, 0, 255, 0, 255, 0, 255, 0, 255 })]
[InlineData(55, 18374966859414961920, false, new byte[] { 55, 255, 0, 255, 0, 255, 0, 255, 0 })]
public void WriteRegister_GivenAddressAndULongData_WhenUsingEndianness_ThenWritesExpectedArray(
byte registerAddress,
ulong data,
bool useLittleEndian,
byte[] expected)
{
RunWriteTest(registerAddress, data, useLittleEndian, expected);
}

[Fact]
public void WriteRegister_GivenAddress_WhenUsingUnsupportedDataType_ThenThrowsException()
{
// Arrange
var deviceMock = new I2cDeviceMock();
var sut = new I2cRegisterAccess(deviceMock);

// Act + Assert
Assert.Throws<InvalidOperationException>(() => sut.WriteRegister(55, char.MaxValue, true));
}

#endregion

#region ReadRegister Tests

[Theory]
[InlineData(55, new byte[] { 240 }, true, -16)]
[InlineData(55, new byte[] { 240 }, false, -16)]
public void ReadRegister_GivenAddress_WhenGettingDataFromDeviceAndUsingEndianness_ThenReturnsExpectedSByte(
byte registerAddress,
byte[] dataFromDevice,
bool useLittleEndian,
sbyte expected)
{
RunReadTest(registerAddress, dataFromDevice, useLittleEndian, expected);
}

[Theory]
[InlineData(55, new byte[] { 198 }, true, 198)]
[InlineData(55, new byte[] { 198 }, false, 198)]
public void ReadRegister_GivenAddress_WhenGettingDataFromDeviceAndUsingEndianness_ThenReturnsExpectedByte(
byte registerAddress,
byte[] dataFromDevice,
bool useLittleEndian,
byte expected)
{
RunReadTest(registerAddress, dataFromDevice, useLittleEndian, expected);
}

[Theory]
[InlineData(55, new byte[] { 128, 25 }, true, 6528)]
[InlineData(55, new byte[] { 25, 128 }, false, 6528)]
public void ReadRegister_GivenAddress_WhenGettingDataFromDeviceAndUsingEndianness_ThenReturnsExpectedShort(
byte registerAddress,
byte[] dataFromDevice,
bool useLittleEndian,
short expected)
{
RunReadTest(registerAddress, dataFromDevice, useLittleEndian, expected);
}

[Theory]
[InlineData(55, new byte[] { 0, 255 }, true, 65280)]
[InlineData(55, new byte[] { 255, 0 }, false, 65280)]
public void ReadRegister_GivenAddress_WhenGettingDataFromDeviceAndUsingEndianness_ThenReturnsExpectedUShort(
byte registerAddress,
byte[] dataFromDevice,
bool useLittleEndian,
ushort expected)
{
RunReadTest(registerAddress, dataFromDevice, useLittleEndian, expected);
}

[Theory]
[InlineData(55, new byte[] { 100, 168, 51, 243 }, true, -214718364)]
[InlineData(55, new byte[] { 243, 51, 168, 100 }, false, -214718364)]
public void ReadRegister_GivenAddress_WhenGettingDataFromDeviceAndUsingEndianness_ThenReturnsExpectedInt(
byte registerAddress,
byte[] dataFromDevice,
bool useLittleEndian,
int expected)
{
RunReadTest(registerAddress, dataFromDevice, useLittleEndian, expected);
}

[Theory]
[InlineData(55, new byte[] { 0, 255, 0, 255 }, true, 4278255360)]
[InlineData(55, new byte[] { 255, 0, 255, 0 }, false, 4278255360)]
public void ReadRegister_GivenAddress_WhenGettingDataFromDeviceAndUsingEndianness_ThenReturnsExpectedUInt(
byte registerAddress,
byte[] dataFromDevice,
bool useLittleEndian,
uint expected)
{
RunReadTest(registerAddress, dataFromDevice, useLittleEndian, expected);
}

[Theory]
[InlineData(55, new byte[] { 0, 255, 0, 255, 0, 255, 0, 0 }, true, 280379743338240)]
[InlineData(55, new byte[] { 0, 0, 255, 0, 255, 0, 255, 0 }, false, 280379743338240)]
public void ReadRegister_GivenAddress_WhenGettingDataFromDeviceAndUsingEndianness_ThenReturnsExpectedLong(
byte registerAddress,
byte[] dataFromDevice,
bool useLittleEndian,
long expected)
{
RunReadTest(registerAddress, dataFromDevice, useLittleEndian, expected);
}

[Theory]
[InlineData(55, new byte[] { 0, 255, 0, 255, 0, 255, 0, 255 }, true, 18374966859414961920)]
[InlineData(55, new byte[] { 255, 0, 255, 0, 255, 0, 255, 0 }, false, 18374966859414961920)]
public void ReadRegister_GivenAddress_WhenGettingDataFromDeviceAndUsingEndianness_ThenReturnsExpectedULong(
byte registerAddress,
byte[] dataFromDevice,
bool useLittleEndian,
ulong expected)
{
RunReadTest(registerAddress, dataFromDevice, useLittleEndian, expected);
}

[Fact]
public void ReadRegister_GivenAddress_WhenRequestingUnsupportedDataType_ThenThrowsException()
{
// Arrange
var deviceMock = new I2cDeviceMock();
var sut = new I2cRegisterAccess(deviceMock);
deviceMock.ReadBuffer = new byte[0];

// Act + Assert
Assert.Throws<InvalidOperationException>(() => sut.ReadRegister<char>(55, true));
}

#endregion

private static void RunWriteTest<T>(byte registerAddress, T data, bool useLittleEndian, byte[] expected)
where T : struct
{
// Arrange
var deviceMock = new I2cDeviceMock();
var sut = new I2cRegisterAccess(deviceMock);

// Act
sut.WriteRegister(registerAddress, data, useLittleEndian);

// Assert
Assert.Equal(deviceMock.WriteBuffer, expected);
}

private static void RunReadTest<T>(byte registerAddress, byte[] dataFromDevice, bool useLittleEndian, T expected)
where T : struct
{
// Arrange
var deviceMock = new I2cDeviceMock();
var sut = new I2cRegisterAccess(deviceMock);
deviceMock.ReadBuffer = dataFromDevice;

// Act
var actual = sut.ReadRegister<T>(registerAddress, useLittleEndian);

// Assert
Assert.Equal(deviceMock.WriteBuffer, new[] { registerAddress });
Assert.Equal(expected, actual);
}
}
}
Loading

0 comments on commit 6050b40

Please sign in to comment.