SerialInterface Unit Tests

As documented in Serial Interface Level, SerialInterface provides functions to open, close, and read from serial ports using Asio. The SerialInterface tests use a SerialInterfaceTest class which derives from SerialInterface specialized in SerialPortMock, which mocks asio::serial_port.

@startuml
hide empty members

class SerialInterface<SerialPortMock>

SerialInterfaceTest : std::size_t read_char(char& c, asio::error_code& ec) noexcept override
SerialInterfaceTest : void set_serial_port(SerialPortMock* serial)
SerialInterfaceTest : asio::io_service& io_service()
SerialInterfaceTest : void set_msg(std::string msg)
SerialInterfaceTest : void use_parent_read_char(bool should_use)
SerialInterfaceTest : std::string msg_
SerialInterfaceTest : uint8_t char_count_
SerialInterfaceTest : bool use_parent_read_char_

SerialPortMock : SerialPortMock(asio::io_service& io_service)
SerialPortMock : MOCK_METHOD(open);
SerialPortMock : MOCK_METHOD(is_open);
SerialPortMock : MOCK_METHOD(set_option);
SerialPortMock : MOCK_METHOD(close);
SerialPortMock : MOCK_METHOD(read_some);

SerialInterface o-- "1" SerialPortMock
SerialInterface <|-- SerialInterfaceTest
@enduml

  1. Since SerialInterfaceTest creates its SerialPortMock in the constructor, no expectations can be set to that object. For this reason, SerialInterfaceTest provides a set_serial_port() public member function that can be used to substitute the SerialPortMock instance with one on which expectations have been set.

  2. To be able to construct this SerialPortMock, a getter io_service() is also provided.

  3. Some tests need to mock SerialPort::read_some() (asio::serial_port::read_some()) so that SerialInterface::read_line() returns a specific std::string. To that end, SerialInterface wraps the call to SerialPort::read_some() with a read_char(), which SerialInterface::read_line() calls to perform the actual read from the port. Since for unit testing purposes SerialPortMock is used instead of asio::serial_port, a mock SerialPortMock::read_some() would be needed. However, due to the function’s signature, it is not possible to set expectations on the read characters. This has led to SerialInterfaceTest overriding SerialInterface::read_char() with an overload that either simply calls to the SerialInterface::read_char() implementation, or returns a character from a string. To do this, SerialInterfaceTest provides a set_msg() function that is used to set the line that read_line will read. To enable SerialInterfaceTest::read_char() to read characters from the set message instead of using read_some(), a use_parent_read_char() is provided. By default, SerialInterfaceTest::read_char() will call SerialInterface::read_char() (which calls read_some()), however, if the use_parent_read_char_ flag is set (calling use_parent_read_char(false)), then SerialInterfaceTest::read_char() will read the characters of the set message one at a time (simulating reading characters one by one from the serial port).

open()

  1. openSuccess: Opens a not previously opened serial port with a valid port and baudrate. The return is expected to be true

  2. openOpened: Attempts to open an already opened port. The return is expected to be false.

  3. openWrongPort: Attempts to open a port on an invalid port. The return is expected to be false.

  4. openWrongBaudrate: Attempts to set a non valid baudrate to the serial port. The return is expected to be false.

is_open()

  1. is_openOpened: Checks whether SerialInterface::is_open() returns true for an open port.

  2. is_openClosed: Checks whether SerialInterface::is_open() returns false for an closed port.

close()

  1. closeSuccess: Closes an already opened port. The return is expected to be true.

  2. closeClosed: Closes an already closed port. The return is expected to be true.

  3. closeAsioError: Attempts to close an open port that Asio cannot close. The return is expected to be false.

read_line()

  1. read_lineSuccess: Checks that lines ending in \n or \r\n are returned correctly. The return is expected to be true. This test is performed on an opened serial port. Furthermore, the function should be called with an empty string, as well as with a non-empty one. Both cases should output just the read line without any characters that it had on calling SerialInterface::read_line().

  2. read_lineClosed: Checks that calling SerialInterface::read_line() on a closed port returns false.

  3. read_lineReadError: Simulates that asio::serial_port::read_some() returns an error and checks that in the case, the SerialInterface::read_line() return is false. This test covers the case when asio::serial_port::close() is called while blocked on asio::serial_port::read_some(), since that breaks the block, making asio::serial_port::read_some() return with a not OK asio::error_code.