modbus_rtu_lib: define read holding register

Add the modbus rtu library commands with initial support for read
holding register. Define a base Message classes which all subsequent and
specific request & response messages can inherit from. Also add the
relevant unit testing for the added command set.

Tested:
```
> meson test -C builddir
ninja: Entering directory `/host/repos/Modbus/phosphor-modbus/builddir'
ninja: no work to do.
1/1 test_modbus_commands        OK              0.01s

Ok:                1
Fail:              0

```

Change-Id: I331b0dee66a0829e9352ae0eac8ac82a9150904c
Signed-off-by: Jagpal Singh Gill <paligill@gmail.com>
diff --git a/tests/meson.build b/tests/meson.build
index 8b13789..a02a690 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -1 +1,30 @@
+gtest_dep = dependency('gtest', main: true, disabler: true, required: false)
+gmock_dep = dependency('gmock', disabler: true, required: false)
+if not gtest_dep.found() or not gmock_dep.found()
+    gtest_proj = import('cmake').subproject('googletest', required: false)
+    if gtest_proj.found()
+        gtest_dep = declare_dependency(
+            dependencies: [
+                dependency('threads'),
+                gtest_proj.dependency('gtest'),
+                gtest_proj.dependency('gtest_main'),
+            ],
+        )
+        gmock_dep = gtest_proj.dependency('gmock')
+    else
+        assert(
+            not get_option('tests').enabled(),
+            'Googletest is required if tests are enabled',
+        )
+    endif
+endif
 
+test(
+    'test_modbus_commands',
+    executable(
+        'test_modbus_commands',
+        'test_modbus_commands.cpp',
+        dependencies: [gtest_dep, gmock_dep, default_deps, modbus_rtu_dep],
+        include_directories: ['.'],
+    ),
+)
diff --git a/tests/test_modbus_commands.cpp b/tests/test_modbus_commands.cpp
new file mode 100644
index 0000000..547e0b2
--- /dev/null
+++ b/tests/test_modbus_commands.cpp
@@ -0,0 +1,177 @@
+#include "modbus/modbus_commands.hpp"
+#include "modbus/modbus_exception.hpp"
+
+#include <cstring>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace RTUIntf = phosphor::modbus::rtu;
+
+class ReadHoldingRegistersResponseTest :
+    public RTUIntf::ReadHoldingRegistersResponse
+{
+  public:
+    ReadHoldingRegistersResponseTest(uint8_t address,
+                                     std::vector<uint16_t>& registers) :
+        RTUIntf::ReadHoldingRegistersResponse(address, registers)
+    {}
+
+    template <std::size_t Length>
+    ReadHoldingRegistersResponseTest& operator=(
+        const std::array<uint8_t, Length>& expectedMessage)
+    {
+        len = Length;
+        std::copy(expectedMessage.begin(), expectedMessage.end(), raw.begin());
+        return *this;
+    }
+};
+
+class ModbusCommandTest : public ::testing::Test
+{
+  public:
+    ModbusCommandTest() = default;
+
+    static constexpr auto failureMessageLength = 11;
+
+    template <typename ExceptionType, std::size_t Length>
+    void TestReadHoldingRegisterResponseFailure(
+        const std::array<uint8_t, Length>& expectedMessage)
+    {
+        constexpr uint8_t expectedAddress = 0xa;
+        constexpr size_t expectedRegisterSize = 3;
+        std::vector<uint16_t> registers(expectedRegisterSize);
+        ReadHoldingRegistersResponseTest response(expectedAddress, registers);
+        response = expectedMessage;
+
+        EXPECT_THROW(response.decode(), ExceptionType);
+    }
+};
+
+TEST_F(ModbusCommandTest, TestReadHoldingRegistersRequestSucess)
+{
+    constexpr size_t expectedLength = 8;
+    constexpr uint8_t expectedAddress = 0x1;
+    constexpr std::array<uint8_t, expectedLength> expectedMessage = {
+        expectedAddress, // addr(1) = 0x01
+        0x03,            // func(1) = 0x03
+        0x12,            // reg_off(2) = 0x1234
+        0x34,
+        0x00,            // reg_cnt(2) = 0x0020,
+        0x20,
+        0x00,            // crc(2) (Pre-computed) = 0xa4
+        0xa4};
+
+    RTUIntf::ReadHoldingRegistersRequest request(expectedAddress, 0x1234, 0x20);
+    request.encode();
+
+    std::array<uint8_t, expectedLength> actualMessage;
+    std::memcpy(actualMessage.data(), request.raw.data(), expectedLength);
+
+    EXPECT_EQ(actualMessage, expectedMessage);
+    EXPECT_EQ(request.address, expectedAddress);
+}
+
+TEST_F(ModbusCommandTest, TestReadHoldingRegistersResponseSucess)
+{
+    constexpr size_t expectedLength = 11;
+    constexpr uint8_t expectedAddress = 0xa;
+    constexpr size_t expectedRegisterSize = 3;
+    constexpr std::array<uint8_t, expectedLength> expectedMessage = {
+        expectedAddress, // addr(1) = 0x0a
+        0x03,            // func(1) = 0x03
+        0x06,            // bytes(1) = 0x06
+        0x11,            // regs(3*2) = 0x1122, 0x3344, 0x5566
+        0x22,
+        0x33,
+        0x44,
+        0x55,
+        0x66,
+        0x59, // crc(2) = 0x5928 (pre-computed)
+        0x28};
+    const std::vector<uint16_t> expectedRegisters{0x1122, 0x3344, 0x5566};
+    std::vector<uint16_t> registers(expectedRegisterSize);
+
+    RTUIntf::ReadHoldingRegistersResponse response(expectedAddress, registers);
+    EXPECT_EQ(response.len, expectedLength);
+
+    std::copy(expectedMessage.begin(), expectedMessage.end(),
+              response.raw.begin());
+
+    EXPECT_EQ(response.len, expectedLength);
+    response.decode();
+    EXPECT_EQ(registers, expectedRegisters);
+}
+
+TEST_F(ModbusCommandTest, TestReadHoldingRegistersResponseError)
+{
+    constexpr std::array<uint8_t, 5> expectedMessage = {
+        0xa,  // addr(1) = 0x0a
+        0x83, // func(1) = 0x83
+        0x03, // exception code(1) = 0x03
+        0x70, // crc(2) = 0x70f3 (pre-computed)
+        0xf3};
+
+    TestReadHoldingRegisterResponseFailure<RTUIntf::ModbusException>(
+        expectedMessage);
+}
+
+TEST_F(ModbusCommandTest, TestReadHoldingRegistersResponseBadAddress)
+{
+    constexpr std::array<uint8_t, failureMessageLength> expectedMessage = {
+        0x1,  // Bad address(1), should be 0x0a
+        0x03, // func(1) = 0x03
+        0x06, // bytes(1) = 0x06
+        0x11, // regs(3*2) = 0x1122, 0x3344, 0x5566
+        0x22, 0x33, 0x44, 0x55, 0x66,
+        0x2a, // crc(2) = 0x2a18 (pre-computed)
+        0x18};
+
+    TestReadHoldingRegisterResponseFailure<RTUIntf::ModbusBadResponseException>(
+        expectedMessage);
+}
+
+TEST_F(ModbusCommandTest, TestReadHoldingRegistersResponseBadCRC)
+{
+    constexpr std::array<uint8_t, failureMessageLength> expectedMessage = {
+        0xa,  // addr(1) = 0x0a
+        0x03, // func(1) = 0x03
+        0x06, // bytes(1) = 0x06
+        0x11, // regs(3*2) = 0x1122, 0x3344, 0x5566
+        0x22, 0x33, 0x44, 0x55, 0x66,
+        0x59, // Bad crc(2), should be 0x5928
+        0x29};
+
+    TestReadHoldingRegisterResponseFailure<RTUIntf::ModbusCRCException>(
+        expectedMessage);
+}
+
+TEST_F(ModbusCommandTest, TestReadHoldingRegistersResponseBadFunctionCode)
+{
+    constexpr std::array<uint8_t, failureMessageLength> expectedMessage = {
+        0xa,  // addr(1) = 0x0a
+        0x04, // Bad function code(1), should be 0x03
+        0x06, // bytes(1) = 0x06
+        0x11, // regs(3*2) = 0x1122, 0x3344, 0x5566
+        0x22, 0x33, 0x44, 0x55, 0x66,
+        0x18, // crc(2) = 0x18ce (pre-computed)
+        0xce};
+
+    TestReadHoldingRegisterResponseFailure<RTUIntf::ModbusBadResponseException>(
+        expectedMessage);
+}
+
+TEST_F(ModbusCommandTest, TestReadHoldingRegistersResponseBadByteCount)
+{
+    constexpr std::array<uint8_t, failureMessageLength> expectedMessage = {
+        0xa,  // addr(1) = 0x0a
+        0x03, // func(1) = 0x03
+        0x04, // Bad bytes(1), should be 0x06
+        0x11, // regs(3*2) = 0x1122, 0x3344, 0x5566
+        0x22, 0x33, 0x44, 0x55, 0x66,
+        0x7a, // crc(2) = 0x7ae8 (pre-computed)
+        0xe8};
+
+    TestReadHoldingRegisterResponseFailure<RTUIntf::ModbusBadResponseException>(
+        expectedMessage);
+}