Пример #1
0
TEST(Frame, FrameParsing)
{
    using uavcan::Frame;
    using uavcan::CanFrame;
    using uavcan::NodeID;
    using uavcan::TransferID;

    CanFrame can;
    Frame frame;
    ASSERT_FALSE(frame.parse(can));

    for (unsigned i = 0; i < sizeof(CanFrame::data); i++)
    {
        can.data[i] = uint8_t(i | (i << 4));
    }

    /*
     * Message CAN ID fields and offsets:
     *   24 Priority
     *   8  Message Type ID
     *   7  Service Not Message (0)
     *   0  Source Node ID
     *
     * Service CAN ID fields and offsets:
     *   24 Priority
     *   16 Service Type ID
     *   15 Request Not Response
     *   8  Destination Node ID
     *   7  Service Not Message (1)
     *   0  Source Node ID
     */

    /*
     * SFT message broadcast
     */
    can.id = CanFrame::FlagEFF |
             (2 << 24) |
             (456 << 8) |
             (0 << 7) |
             (42 << 0);

    can.data[7] = 0xcf; // SET=110, TID=0

    ASSERT_FALSE(frame.parse(can));
    can.dlc = 8;

    ASSERT_TRUE(frame.parse(can));
    EXPECT_TRUE(frame.isStartOfTransfer());
    EXPECT_TRUE(frame.isEndOfTransfer());
    EXPECT_FALSE(frame.getToggle());
    ASSERT_EQ(2, frame.getPriority().get());
    ASSERT_EQ(NodeID(42), frame.getSrcNodeID());
    ASSERT_EQ(NodeID::Broadcast, frame.getDstNodeID());
    ASSERT_EQ(456, frame.getDataTypeID().get());
    ASSERT_EQ(TransferID(15), frame.getTransferID());
    ASSERT_EQ(uavcan::TransferTypeMessageBroadcast, frame.getTransferType());

    // TODO: test service frames
    // TODO: test malformed frames
}
Пример #2
0
TEST(Time, Utc)
{
    using uavcan::UtcDuration;
    using uavcan::UtcTime;
    using uavcan::Timestamp;

    Timestamp ts;
    ts.usec = 9000;

    UtcTime u1(ts);
    ASSERT_EQ(9000, u1.toUSec());

    ts.usec *= 2;
    u1 = ts;
    ASSERT_EQ(18000, u1.toUSec());

    ts = UtcTime::fromUSec(12345678900);
    ASSERT_EQ(12345678900, ts.usec);

    /*
     * To string
     */
    ASSERT_EQ("0.018000", u1.toString());
    ASSERT_EQ("12345.678900", UtcTime(ts).toString());
}
Пример #3
0
TEST(Time, Overflow)
{
    using uavcan::MonotonicDuration;
    using uavcan::MonotonicTime;

    MonotonicTime max_4 = MonotonicTime::fromUSec(MonotonicTime::getMax().toUSec() / 4);
    MonotonicDuration max_4_duration = max_4 - MonotonicTime();

    std::cout << max_4 << std::endl;
    ASSERT_EQ(max_4_duration.toUSec(), max_4.toUSec());

    MonotonicTime max = (((max_4 + max_4_duration) + max_4_duration) + max_4_duration) + max_4_duration;
    ASSERT_EQ(max, MonotonicTime::getMax()); // Must not overflow

    MonotonicTime min;
    min -= max_4_duration;
    ASSERT_EQ(min, MonotonicTime()); // Must not underflow
}
Пример #4
0
TEST(Frame, FrameToString)
{
    using uavcan::Frame;
    using uavcan::RxFrame;

    // RX frame default
    RxFrame rx_frame;
    EXPECT_EQ("prio=255 dtid=65535 tt=3 snid=255 dnid=255 sot=0 eot=0 togl=0 tid=0 payload=[] ts_m=0.000000 ts_utc=0.000000 iface=0",
              rx_frame.toString());

    // RX frame max len
    rx_frame = RxFrame(Frame(uavcan::DataTypeID::MaxPossibleDataTypeIDValue, uavcan::TransferTypeMessageBroadcast,
                             uavcan::NodeID::Max, 0, uavcan::TransferID::Max),
                       uavcan::MonotonicTime::getMax(), uavcan::UtcTime::getMax(), 3);

    uint8_t data[8];
    for (unsigned i = 0; i < sizeof(data); i++)
    {
        data[i] = uint8_t(i);
    }
    rx_frame.setPayload(data, sizeof(data));

    rx_frame.setStartOfTransfer(true);
    rx_frame.setEndOfTransfer(true);
    rx_frame.flipToggle();
    rx_frame.setPriority(uavcan::TransferPriority::NumericallyMax);

    EXPECT_EQ("prio=31 dtid=65535 tt=2 snid=127 dnid=0 sot=1 eot=1 togl=1 tid=31 payload=[00 01 02 03 04 05 06] "
              "ts_m=18446744073709.551615 ts_utc=18446744073709.551615 iface=3",
              rx_frame.toString());

    // Plain frame default
    Frame frame;
    EXPECT_EQ("prio=255 dtid=65535 tt=3 snid=255 dnid=255 sot=0 eot=0 togl=0 tid=0 payload=[]", frame.toString());

    // Plain frame max len
    frame = rx_frame;
    EXPECT_EQ("prio=31 dtid=65535 tt=2 snid=127 dnid=0 sot=1 eot=1 togl=1 tid=31 payload=[00 01 02 03 04 05 06]",
              frame.toString());
}
Пример #5
0
TEST(NodeStatusMonitor, Basic)
{
    using uavcan::protocol::NodeStatus;
    using uavcan::NodeID;

    SystemClockMock clock_mock(100);
    clock_mock.monotonic_auto_advance = 1000;

    CanDriverMock can(2, clock_mock);

    TestNode node(can, clock_mock, 64);

    uavcan::GlobalDataTypeRegistry::instance().reset();
    uavcan::DefaultDataTypeRegistrator<uavcan::protocol::NodeStatus> _reg1;

    uavcan::NodeStatusMonitor nsm(node);
    ASSERT_LE(0, nsm.start());

    ASSERT_LE(0, node.spin(uavcan::MonotonicDuration::fromMSec(10)));

    /*
     * Empty NSM, no nodes were registered yet
     */
    ASSERT_FALSE(nsm.findNodeWithWorstStatus().isValid());

    uavcan::NodeStatusMonitor::NodeStatus st = nsm.getNodeStatus(uavcan::NodeID(123));
    ASSERT_FALSE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_OFFLINE, st.status_code);

    /*
     * Some new status messages
     */
    publishNodeStatus(can, 10, NodeStatus::STATUS_OK, 12, 0);
    shortSpin(node);
    ASSERT_EQ(NodeID(10), nsm.findNodeWithWorstStatus());

    publishNodeStatus(can, 9,  NodeStatus::STATUS_INITIALIZING, 0, 0);
    shortSpin(node);
    ASSERT_EQ(NodeID(9), nsm.findNodeWithWorstStatus());

    publishNodeStatus(can, 11, NodeStatus::STATUS_CRITICAL, 999, 0);
    shortSpin(node);
    ASSERT_EQ(NodeID(11), nsm.findNodeWithWorstStatus());

    st = nsm.getNodeStatus(uavcan::NodeID(10));
    ASSERT_TRUE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_OK, st.status_code);

    st = nsm.getNodeStatus(uavcan::NodeID(9));
    ASSERT_TRUE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_INITIALIZING, st.status_code);

    st = nsm.getNodeStatus(uavcan::NodeID(11));
    ASSERT_TRUE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_CRITICAL, st.status_code);

    /*
     * Timeout
     */
    std::cout << "Starting timeout test, current monotime is " << clock_mock.monotonic << std::endl;

    clock_mock.advance(500000);
    shortSpin(node);
    st = nsm.getNodeStatus(uavcan::NodeID(10));
    ASSERT_TRUE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_OK, st.status_code);

    clock_mock.advance(500000);
    shortSpin(node);
    st = nsm.getNodeStatus(uavcan::NodeID(9));
    ASSERT_TRUE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_INITIALIZING, st.status_code);

    clock_mock.advance(500000);
    shortSpin(node);
    st = nsm.getNodeStatus(uavcan::NodeID(11));
    ASSERT_TRUE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_CRITICAL, st.status_code);

    /*
     * Will timeout now
     */
    clock_mock.advance(4000000);
    shortSpin(node);

    st = nsm.getNodeStatus(uavcan::NodeID(10));
    ASSERT_TRUE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_OFFLINE, st.status_code);

    st = nsm.getNodeStatus(uavcan::NodeID(9));
    ASSERT_TRUE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_OFFLINE, st.status_code);

    st = nsm.getNodeStatus(uavcan::NodeID(11));
    ASSERT_TRUE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_OFFLINE, st.status_code);

    /*
     * Recovering one node, adding two extra
     */
    publishNodeStatus(can, 11, NodeStatus::STATUS_WARNING, 999, 0);
    shortSpin(node);

    publishNodeStatus(can, 127, NodeStatus::STATUS_WARNING, 9999, 0);
    shortSpin(node);

    publishNodeStatus(can, 1, NodeStatus::STATUS_OK, 1234, 0);
    shortSpin(node);

    /*
     * Making sure OFFLINE is still worst status
     */
    ASSERT_EQ(NodeID(9), nsm.findNodeWithWorstStatus());

    /*
     * Final validation
     */
    st = nsm.getNodeStatus(uavcan::NodeID(10));
    ASSERT_TRUE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_OFFLINE, st.status_code);

    st = nsm.getNodeStatus(uavcan::NodeID(9));
    ASSERT_TRUE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_OFFLINE, st.status_code);

    st = nsm.getNodeStatus(uavcan::NodeID(11));
    ASSERT_TRUE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_WARNING, st.status_code);

    st = nsm.getNodeStatus(uavcan::NodeID(127));
    ASSERT_TRUE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_WARNING, st.status_code);

    st = nsm.getNodeStatus(uavcan::NodeID(1));
    ASSERT_TRUE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_OK, st.status_code);

    /*
     * Forgetting
     */
    nsm.forgetNode(127);
    st = nsm.getNodeStatus(uavcan::NodeID(127));
    ASSERT_FALSE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_OFFLINE, st.status_code);

    nsm.forgetNode(9);
    st = nsm.getNodeStatus(uavcan::NodeID(9));
    ASSERT_FALSE(st.known);
    ASSERT_EQ(NodeStatus::STATUS_OFFLINE, st.status_code);
}
Пример #6
0
TEST(TransferBufferManager, Basic)
{
    using uavcan::TransferBufferManager;
    using uavcan::TransferBufferManagerKey;
    using uavcan::ITransferBuffer;

    static const int POOL_BLOCKS = 6;
    uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
    uavcan::PoolManager<1> poolmgr;
    poolmgr.addPool(&pool);

    typedef TransferBufferManager<MGR_MAX_BUFFER_SIZE, 2> TransferBufferManagerType;
    std::auto_ptr<TransferBufferManagerType> mgr(new TransferBufferManagerType(poolmgr));

    // Empty
    ASSERT_FALSE(mgr->access(TransferBufferManagerKey(0, uavcan::TransferTypeMessageUnicast)));
    ASSERT_FALSE(mgr->access(TransferBufferManagerKey(127, uavcan::TransferTypeMessageUnicast)));

    ITransferBuffer* tbb = NULL;

    const TransferBufferManagerKey keys[5] =
    {
        TransferBufferManagerKey(0, uavcan::TransferTypeMessageUnicast),
        TransferBufferManagerKey(1, uavcan::TransferTypeMessageBroadcast),
        TransferBufferManagerKey(2, uavcan::TransferTypeServiceRequest),
        TransferBufferManagerKey(127, uavcan::TransferTypeServiceResponse),
        TransferBufferManagerKey(64, uavcan::TransferTypeMessageBroadcast)
    };

    // Static 0
    ASSERT_TRUE((tbb = mgr->create(keys[0])));
    ASSERT_EQ(MGR_MAX_BUFFER_SIZE, fillTestData(MGR_TEST_DATA[0], tbb));
    ASSERT_EQ(1, mgr->getNumStaticBuffers());

    // Static 1
    ASSERT_TRUE((tbb = mgr->create(keys[1])));
    ASSERT_EQ(MGR_MAX_BUFFER_SIZE, fillTestData(MGR_TEST_DATA[1], tbb));
    ASSERT_EQ(2, mgr->getNumStaticBuffers());
    ASSERT_EQ(0, mgr->getNumDynamicBuffers());
    ASSERT_EQ(0, pool.getNumUsedBlocks());

    // Dynamic 0
    ASSERT_TRUE((tbb = mgr->create(keys[2])));
    ASSERT_EQ(1, pool.getNumUsedBlocks());      // Empty dynamic buffer occupies one block
    ASSERT_EQ(MGR_MAX_BUFFER_SIZE, fillTestData(MGR_TEST_DATA[2], tbb));
    ASSERT_EQ(2, mgr->getNumStaticBuffers());
    ASSERT_EQ(1, mgr->getNumDynamicBuffers());
    ASSERT_LT(1, pool.getNumUsedBlocks());

    std::cout << "TransferBufferManager - Basic: Pool usage: " << pool.getNumUsedBlocks() << std::endl;

    // Dynamic 2
    ASSERT_TRUE((tbb = mgr->create(keys[3])));
    ASSERT_LT(0, pool.getNumUsedBlocks());

    ASSERT_LT(0, fillTestData(MGR_TEST_DATA[3], tbb));
    ASSERT_EQ(2, mgr->getNumStaticBuffers());
    ASSERT_EQ(2, mgr->getNumDynamicBuffers());

    // Dynamic 3 - will fail due to OOM
    ASSERT_FALSE((tbb = mgr->create(keys[4])));
    ASSERT_EQ(2, mgr->getNumStaticBuffers());
    ASSERT_EQ(2, mgr->getNumDynamicBuffers());

    // Making sure all buffers contain proper data
    ASSERT_TRUE((tbb = mgr->access(keys[0])));
    ASSERT_TRUE(matchAgainst(MGR_TEST_DATA[0], *tbb));

    ASSERT_TRUE((tbb = mgr->access(keys[1])));
    ASSERT_TRUE(matchAgainst(MGR_TEST_DATA[1], *tbb));

    ASSERT_TRUE((tbb = mgr->access(keys[2])));
    ASSERT_TRUE(matchAgainst(MGR_TEST_DATA[2], *tbb));

    ASSERT_TRUE((tbb = mgr->access(keys[3])));
    ASSERT_TRUE(matchAgainst(MGR_TEST_DATA[3], *tbb));

    // Freeing one static buffer; one dynamic must migrate
    mgr->remove(keys[1]);
    ASSERT_FALSE(mgr->access(keys[1]));
    ASSERT_EQ(2, mgr->getNumStaticBuffers());
    ASSERT_EQ(1, mgr->getNumDynamicBuffers());   // One migrated to the static
    ASSERT_LT(0, pool.getNumFreeBlocks());

    // Removing NodeID 0; one dynamic must migrate
    mgr->remove(keys[0]);
    ASSERT_FALSE(mgr->access(keys[0]));
    ASSERT_EQ(2, mgr->getNumStaticBuffers());
    ASSERT_EQ(0, mgr->getNumDynamicBuffers());

    // At this time we have the following NodeID: 2, 127
    ASSERT_TRUE((tbb = mgr->access(keys[2])));
    ASSERT_TRUE(matchAgainst(MGR_TEST_DATA[2], *tbb));

    ASSERT_TRUE((tbb = mgr->access(keys[3])));
    ASSERT_TRUE(matchAgainst(MGR_TEST_DATA[3], *tbb));

    // These were deleted: 0, 1; 3 is still there
    ASSERT_FALSE(mgr->access(keys[1]));
    ASSERT_FALSE(mgr->access(keys[0]));
    ASSERT_TRUE(mgr->access(keys[3]));

    // Filling the memory again in order to check the destruction below
    ASSERT_TRUE((tbb = mgr->create(keys[1])));
    ASSERT_LT(0, fillTestData(MGR_TEST_DATA[1], tbb));

    // Deleting the object; all memory must be freed
    ASSERT_NE(0, pool.getNumUsedBlocks());
    mgr.reset();
    ASSERT_EQ(0, pool.getNumUsedBlocks());
}
Пример #7
0
TEST(Frame, ServiceParseCompile)
{
    using uavcan::Frame;
    using uavcan::CanFrame;
    using uavcan::TransferID;
    using uavcan::TransferType;

    Frame frame;

    /*
     * Priority
     * Service Type ID
     * Request Not Response
     * Destination Node ID
     * Service Not Message
     * Source Node ID
     */
    const uint32_t can_id =
        (31 << 24) |    // Priority
        (200 << 16) |   // Service Type ID
        (1 << 15) |     // Request Not Response
        (0x42 << 8) |   // Destination Node ID
        (1 << 7) |      // Service Not Message
        (42 << 0);      // Source Node ID

    const std::string payload_string = "hello\x6a"; // SET = 011, TID = 10

    /*
     * Parse
     */
    // Invalid CAN frames
    ASSERT_FALSE(frame.parse(CanFrame(can_id | CanFrame::FlagRTR, (const uint8_t*)"", 0)));
    ASSERT_FALSE(frame.parse(makeCanFrame(can_id, payload_string, STD)));

    // Valid
    ASSERT_TRUE(frame.parse(makeCanFrame(can_id, payload_string, EXT)));

    EXPECT_EQ(TransferID(10), frame.getTransferID());
    EXPECT_FALSE(frame.isStartOfTransfer());
    EXPECT_TRUE(frame.isEndOfTransfer());
    EXPECT_TRUE(frame.getToggle());
    EXPECT_EQ(uavcan::NodeID(42), frame.getSrcNodeID());
    EXPECT_EQ(uavcan::NodeID(0x42), frame.getDstNodeID());
    EXPECT_EQ(uavcan::TransferTypeServiceRequest, frame.getTransferType());
    EXPECT_EQ(200, frame.getDataTypeID().get());
    EXPECT_EQ(31, frame.getPriority().get());

    EXPECT_EQ(payload_string.length(), frame.getPayloadLen() + 1);
    EXPECT_TRUE(std::equal(frame.getPayloadPtr(), frame.getPayloadPtr() + frame.getPayloadLen(),
                           reinterpret_cast<const uint8_t*>(&payload_string[0])));

    std::cout << frame.toString() << std::endl;

    /*
     * Compile
     */
    CanFrame can_frame;
    ASSERT_TRUE(frame.parse(makeCanFrame(can_id, payload_string, EXT)));

    ASSERT_TRUE(frame.compile(can_frame));
    ASSERT_EQ(can_frame, makeCanFrame(can_id, payload_string, EXT));

    EXPECT_EQ(payload_string.length(), can_frame.dlc);
    EXPECT_TRUE(std::equal(can_frame.data, can_frame.data + can_frame.dlc,
                           reinterpret_cast<const uint8_t*>(&payload_string[0])));

    /*
     * Comparison
     */
    ASSERT_FALSE(Frame() == frame);
    ASSERT_TRUE(Frame() != frame);
    frame = Frame();
    ASSERT_TRUE(Frame() == frame);
    ASSERT_FALSE(Frame() != frame);
}
Пример #8
0
TEST(Frame, AnonymousParseCompile)
{
    using uavcan::Frame;
    using uavcan::CanFrame;
    using uavcan::TransferID;
    using uavcan::TransferType;

    Frame frame;

    /*
     * Priority
     * Discriminator
     * Message Type ID
     * Service Not Message
     * Source Node ID
     */
    const uint32_t can_id =
        (16383 << 10) | // Discriminator
        (1 << 8);       // Message Type ID

    const std::string payload_string = "hello\xd4"; // SET = 110, TID = 20

    uavcan::TransferCRC payload_crc;
    payload_crc.add(reinterpret_cast<const uint8_t*>(payload_string.c_str()), unsigned(payload_string.length()));

    /*
     * Parse
     */
    ASSERT_TRUE(frame.parse(makeCanFrame(can_id, payload_string, EXT)));

    EXPECT_EQ(TransferID(20), frame.getTransferID());
    EXPECT_TRUE(frame.isStartOfTransfer());
    EXPECT_TRUE(frame.isEndOfTransfer());
    EXPECT_FALSE(frame.getToggle());
    EXPECT_TRUE(frame.getSrcNodeID().isBroadcast());
    EXPECT_TRUE(frame.getDstNodeID().isBroadcast());
    EXPECT_EQ(uavcan::TransferTypeMessageBroadcast, frame.getTransferType());
    EXPECT_EQ(1, frame.getDataTypeID().get());
    EXPECT_EQ(0, frame.getPriority().get());

    EXPECT_EQ(payload_string.length() - 1, frame.getPayloadLen());
    EXPECT_TRUE(std::equal(frame.getPayloadPtr(), frame.getPayloadPtr() + frame.getPayloadLen(),
                           reinterpret_cast<const uint8_t*>(&payload_string[0])));

    std::cout << frame.toString() << std::endl;

    /*
     * Compile
     */
    const uint32_t DiscriminatorMask = 0x00FFFC00;
    const uint32_t NoDiscriminatorMask = 0xFF0003FF;

    CanFrame can_frame;
    ASSERT_TRUE(frame.parse(makeCanFrame(can_id, payload_string, EXT)));

    ASSERT_TRUE(frame.compile(can_frame));
    ASSERT_EQ(can_id & NoDiscriminatorMask & uavcan::CanFrame::MaskExtID,
              can_frame.id & NoDiscriminatorMask & uavcan::CanFrame::MaskExtID);

    EXPECT_EQ(payload_string.length(), can_frame.dlc);
    EXPECT_TRUE(std::equal(can_frame.data, can_frame.data + can_frame.dlc,
                           reinterpret_cast<const uint8_t*>(&payload_string[0])));

    EXPECT_EQ((can_frame.id & DiscriminatorMask & uavcan::CanFrame::MaskExtID) >> 10, payload_crc.get() & 16383);

    /*
     * Comparison
     */
    ASSERT_FALSE(Frame() == frame);
    ASSERT_TRUE(Frame() != frame);
    frame = Frame();
    ASSERT_TRUE(Frame() == frame);
    ASSERT_FALSE(Frame() != frame);
}
Пример #9
0
TEST(Frame, MessageParseCompile)
{
    using uavcan::Frame;
    using uavcan::CanFrame;
    using uavcan::TransferID;
    using uavcan::TransferType;

    Frame frame;

    /*
     * Priority
     * Message Type ID
     * Service Not Message
     * Source Node ID
     */
    const uint32_t can_id =
        (16 << 24) |    // Priority
        (20000 << 8) |  // Message Type ID
        (0 << 7) |      // Service Not Message
        (42 << 0);      // Source Node ID

    const std::string payload_string = "hello\xD4"; // SET = 110, TID = 20

    /*
     * Parse
     */
    // Invalid CAN frames
    ASSERT_FALSE(frame.parse(CanFrame(can_id | CanFrame::FlagRTR, (const uint8_t*)"", 0)));
    ASSERT_FALSE(frame.parse(makeCanFrame(can_id, payload_string, STD)));

    // Valid
    ASSERT_TRUE(frame.parse(makeCanFrame(can_id, payload_string, EXT)));

    EXPECT_EQ(TransferID(20), frame.getTransferID());
    EXPECT_TRUE(frame.isStartOfTransfer());
    EXPECT_TRUE(frame.isEndOfTransfer());
    EXPECT_FALSE(frame.getToggle());
    EXPECT_EQ(uavcan::NodeID(42), frame.getSrcNodeID());
    EXPECT_TRUE(frame.getDstNodeID().isBroadcast());
    EXPECT_EQ(uavcan::TransferTypeMessageBroadcast, frame.getTransferType());
    EXPECT_EQ(20000, frame.getDataTypeID().get());
    EXPECT_EQ(16, frame.getPriority().get());

    EXPECT_EQ(payload_string.length() - 1, frame.getPayloadLen());
    EXPECT_TRUE(std::equal(frame.getPayloadPtr(), frame.getPayloadPtr() + frame.getPayloadLen(),
                           payload_string.begin()));

    std::cout << frame.toString() << std::endl;

    /*
     * Compile
     */
    CanFrame can_frame;
    ASSERT_TRUE(frame.parse(makeCanFrame(can_id, payload_string, EXT)));

    ASSERT_TRUE(frame.compile(can_frame));
    ASSERT_EQ(can_frame, makeCanFrame(can_id, payload_string, EXT));

    EXPECT_EQ(payload_string.length(), can_frame.dlc);
    std::cout << can_frame.toString() << std::endl;
    /*
     * FUN FACT: comparison of uint8_t with char may fail on the character 0xD4 (depending on the locale),
     * because it will be considered a Unicode character. Hence, we do reinterpret_cast<>.
     */
    EXPECT_TRUE(std::equal(can_frame.data, can_frame.data + can_frame.dlc,
                           reinterpret_cast<const uint8_t*>(&payload_string[0])));

    /*
     * Comparison
     */
    ASSERT_FALSE(Frame() == frame);
    ASSERT_TRUE(Frame() != frame);
    frame = Frame();
    ASSERT_TRUE(Frame() == frame);
    ASSERT_FALSE(Frame() != frame);
}