123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- /*
- * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
- */
- #include <gtest/gtest.h>
- #include "can.hpp"
- static bool rxFrameEquals(const uavcan::CanRxFrame& rxframe, const uavcan::CanFrame& frame,
- uint64_t timestamp_usec, int iface_index)
- {
- if (static_cast<const uavcan::CanFrame&>(rxframe) != frame)
- {
- std::cout << "Frame mismatch:\n"
- << " " << rxframe.toString(uavcan::CanFrame::StrAligned) << "\n"
- << " " << frame.toString(uavcan::CanFrame::StrAligned) << std::endl;
- }
- return (static_cast<const uavcan::CanFrame&>(rxframe) == frame) &&
- (rxframe.ts_mono == uavcan::MonotonicTime::fromUSec(timestamp_usec)) &&
- (rxframe.iface_index == iface_index);
- }
- TEST(CanIOManager, Reception)
- {
- // Memory
- uavcan::PoolAllocator<sizeof(uavcan::CanTxQueue::Entry) * 4, sizeof(uavcan::CanTxQueue::Entry)> pool;
- // Platform interface
- SystemClockMock clockmock;
- CanDriverMock driver(2, clockmock);
- // IO Manager
- uavcan::CanIOManager iomgr(driver, pool, clockmock);
- ASSERT_EQ(2, iomgr.getNumIfaces());
- /*
- * Empty, will time out
- */
- uavcan::CanRxFrame frame;
- uavcan::CanIOFlags flags = uavcan::CanIOFlags();
- EXPECT_EQ(0, iomgr.receive(frame, tsMono(100), flags));
- EXPECT_EQ(0, flags);
- EXPECT_EQ(100, clockmock.monotonic);
- EXPECT_EQ(100, clockmock.utc);
- /*
- * Non empty from multiple ifaces
- */
- const uavcan::CanFrame frames[2][3] = {
- { makeCanFrame(1, "a0", EXT), makeCanFrame(99, "a1", EXT), makeCanFrame(803, "a2", STD) },
- { makeCanFrame(6341, "b0", EXT), makeCanFrame(196, "b1", STD), makeCanFrame(73, "b2", EXT) },
- };
- clockmock.advance(10);
- driver.ifaces.at(0).pushRx(frames[0][0]); // Timestamp 110
- driver.ifaces.at(1).pushRx(frames[1][0]);
- clockmock.advance(10);
- driver.ifaces.at(0).pushRx(frames[0][1]); // Timestamp 120
- driver.ifaces.at(1).pushRx(frames[1][1]);
- clockmock.advance(10);
- driver.ifaces.at(0).pushRx(frames[0][2]); // Timestamp 130
- driver.ifaces.at(1).pushRx(frames[1][2]);
- clockmock.advance(10);
- EXPECT_EQ(1, iomgr.receive(frame, uavcan::MonotonicTime(), flags));
- EXPECT_TRUE(rxFrameEquals(frame, frames[0][0], 110, 0));
- EXPECT_EQ(0, flags);
- EXPECT_EQ(1, iomgr.receive(frame, uavcan::MonotonicTime(), flags));
- EXPECT_TRUE(rxFrameEquals(frame, frames[0][1], 120, 0));
- EXPECT_EQ(0, flags);
- EXPECT_EQ(1, iomgr.receive(frame, uavcan::MonotonicTime(), flags));
- EXPECT_TRUE(rxFrameEquals(frame, frames[0][2], 130, 0));
- EXPECT_EQ(0, flags);
- EXPECT_EQ(1, iomgr.receive(frame, uavcan::MonotonicTime(), flags));
- EXPECT_TRUE(rxFrameEquals(frame, frames[1][0], 110, 1));
- EXPECT_EQ(0, flags);
- EXPECT_EQ(1, iomgr.receive(frame, uavcan::MonotonicTime(), flags));
- EXPECT_TRUE(rxFrameEquals(frame, frames[1][1], 120, 1));
- EXPECT_EQ(0, flags);
- EXPECT_EQ(1, iomgr.receive(frame, uavcan::MonotonicTime(), flags));
- EXPECT_TRUE(rxFrameEquals(frame, frames[1][2], 130, 1));
- EXPECT_EQ(0, flags);
- EXPECT_EQ(0, iomgr.receive(frame, uavcan::MonotonicTime(), flags)); // Will time out
- EXPECT_EQ(0, flags);
- /*
- * Perf counters
- */
- driver.select_failure = true;
- EXPECT_EQ(-uavcan::ErrDriver, iomgr.receive(frame, uavcan::MonotonicTime(), flags));
- driver.select_failure = false;
- driver.ifaces.at(1).pushRx(frames[0][0]);
- driver.ifaces.at(1).rx_failure = true;
- EXPECT_EQ(-uavcan::ErrDriver, iomgr.receive(frame, uavcan::MonotonicTime(), flags));
- driver.ifaces.at(0).num_errors = 9000;
- driver.ifaces.at(1).num_errors = 100500;
- EXPECT_EQ(9000, iomgr.getIfacePerfCounters(0).errors);
- EXPECT_EQ(100500, iomgr.getIfacePerfCounters(1).errors);
- EXPECT_EQ(3, iomgr.getIfacePerfCounters(0).frames_rx);
- EXPECT_EQ(3, iomgr.getIfacePerfCounters(1).frames_rx);
- EXPECT_EQ(0, iomgr.getIfacePerfCounters(0).frames_tx);
- EXPECT_EQ(0, iomgr.getIfacePerfCounters(1).frames_tx);
- }
- TEST(CanIOManager, Transmission)
- {
- using uavcan::CanIOManager;
- using uavcan::CanTxQueue;
- // Memory
- uavcan::PoolAllocator<sizeof(CanTxQueue::Entry) * 4, sizeof(CanTxQueue::Entry)> pool;
- // Platform interface
- SystemClockMock clockmock;
- CanDriverMock driver(2, clockmock);
- // IO Manager
- CanIOManager iomgr(driver, pool, clockmock, 9999);
- ASSERT_EQ(2, iomgr.getNumIfaces());
- const int ALL_IFACES_MASK = 3;
- const uavcan::CanFrame frames[] = {
- makeCanFrame(1, "a0", EXT), makeCanFrame(99, "a1", EXT), makeCanFrame(803, "a2", STD)
- };
- uavcan::CanIOFlags flags = uavcan::CanIOFlags();
- /*
- * Simple transmission
- */
- EXPECT_EQ(2, iomgr.send(frames[0], tsMono(100), tsMono(0), ALL_IFACES_MASK, CanTxQueue::Volatile, flags));
- EXPECT_TRUE(driver.ifaces.at(0).matchAndPopTx(frames[0], 100));
- EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[0], 100));
- EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[0]));
- EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[0]));
- EXPECT_EQ(1, iomgr.send(frames[1], tsMono(200), tsMono(100), 2, CanTxQueue::Persistent, flags)); // To #1 only
- EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[1], 200));
- EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(uavcan::CanFrame()));
- EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[1]));
- EXPECT_EQ(0, clockmock.monotonic);
- EXPECT_EQ(0, clockmock.utc);
- EXPECT_TRUE(driver.ifaces.at(0).tx.empty());
- EXPECT_TRUE(driver.ifaces.at(1).tx.empty());
- EXPECT_EQ(0, iomgr.getIfacePerfCounters(0).errors);
- EXPECT_EQ(0, iomgr.getIfacePerfCounters(1).errors);
- /*
- * TX Queue basics
- */
- EXPECT_EQ(0, pool.getNumUsedBlocks());
- // Sending to both, #0 blocked
- driver.ifaces.at(0).writeable = false;
- EXPECT_LT(0, iomgr.send(frames[0], tsMono(201), tsMono(200), ALL_IFACES_MASK, CanTxQueue::Persistent, flags));
- EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[0], 201));
- EXPECT_EQ(200, clockmock.monotonic);
- EXPECT_EQ(200, clockmock.utc);
- EXPECT_TRUE(driver.ifaces.at(0).tx.empty());
- EXPECT_TRUE(driver.ifaces.at(1).tx.empty());
- EXPECT_EQ(1, pool.getNumUsedBlocks()); // One frame went into TX queue, and will expire soon
- EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[0])); // This one will persist
- EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(uavcan::CanFrame())); // This will drop off on the second select()
- // Sending to both, both blocked
- driver.ifaces.at(1).writeable = false;
- EXPECT_EQ(0, iomgr.send(frames[1], tsMono(777), tsMono(300), ALL_IFACES_MASK, CanTxQueue::Volatile, flags));
- EXPECT_EQ(3, pool.getNumUsedBlocks()); // Total 3 frames in TX queue now
- EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[0])); // Still 0
- EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[1])); // 1!!
- // Sending to #0, both blocked
- EXPECT_EQ(0, iomgr.send(frames[2], tsMono(888), tsMono(400), 1, CanTxQueue::Persistent, flags));
- EXPECT_EQ(400, clockmock.monotonic);
- EXPECT_EQ(400, clockmock.utc);
- EXPECT_TRUE(driver.ifaces.at(0).tx.empty());
- EXPECT_TRUE(driver.ifaces.at(1).tx.empty());
- EXPECT_EQ(4, pool.getNumUsedBlocks());
- EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[0]));
- EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[1]));
- // At this time TX queues are containing the following data:
- // iface 0: frames[0] (EXPIRED), frames[1], frames[2]
- // iface 1: frames[1]
- // Sending to #1, both writeable
- driver.ifaces.at(0).writeable = true;
- driver.ifaces.at(1).writeable = true;
- // One frame per each iface will be sent:
- EXPECT_LT(0, iomgr.send(frames[0], tsMono(999), tsMono(500), 2, CanTxQueue::Persistent, flags));
- EXPECT_TRUE(driver.ifaces.at(0).matchAndPopTx(frames[1], 777)); // Note that frame[0] on iface #0 has expired
- EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[0], 999)); // In different order due to prioritization
- EXPECT_TRUE(driver.ifaces.at(0).tx.empty());
- EXPECT_TRUE(driver.ifaces.at(1).tx.empty());
- EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[0])); // Expired but still will be reported
- EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[0]));
- // Calling receive() to flush the rest two frames
- uavcan::CanRxFrame dummy_rx_frame;
- EXPECT_EQ(0, iomgr.receive(dummy_rx_frame, tsMono(0), flags));
- EXPECT_TRUE(driver.ifaces.at(0).matchAndPopTx(frames[2], 888));
- EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[1], 777));
- ASSERT_EQ(0, flags);
- EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[2]));
- EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[1]));
- // Final checks
- EXPECT_TRUE(driver.ifaces.at(0).tx.empty());
- EXPECT_TRUE(driver.ifaces.at(1).tx.empty());
- EXPECT_EQ(0, pool.getNumUsedBlocks()); // Make sure the memory was properly released
- EXPECT_EQ(1, iomgr.getIfacePerfCounters(0).errors); // This is because of expired frame[0]
- EXPECT_EQ(0, iomgr.getIfacePerfCounters(1).errors);
- /*
- * TX Queue updates from receive() call
- */
- driver.ifaces.at(0).writeable = false;
- driver.ifaces.at(1).writeable = false;
- // Sending 5 frames, one will be rejected
- EXPECT_EQ(0, iomgr.send(frames[2], tsMono(2222), tsMono(1000), ALL_IFACES_MASK, CanTxQueue::Persistent, flags));
- EXPECT_EQ(0, iomgr.send(frames[0], tsMono(3333), tsMono(1100), 2, CanTxQueue::Persistent, flags));
- // One frame kicked here:
- EXPECT_EQ(0, iomgr.send(frames[1], tsMono(4444), tsMono(1200), ALL_IFACES_MASK, CanTxQueue::Volatile, flags));
- EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[1]));
- EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[0]));
- // State checks
- EXPECT_EQ(4, pool.getNumUsedBlocks()); // TX queue is full
- EXPECT_EQ(1200, clockmock.monotonic);
- EXPECT_EQ(1200, clockmock.utc);
- EXPECT_TRUE(driver.ifaces.at(0).tx.empty());
- EXPECT_TRUE(driver.ifaces.at(1).tx.empty());
- // Preparing the driver mock for receive() call
- driver.ifaces.at(0).writeable = true;
- driver.ifaces.at(1).writeable = true;
- const uavcan::CanFrame rx_frames[] = { makeCanFrame(123, "rx0", STD), makeCanFrame(321, "rx1", EXT) };
- driver.ifaces.at(0).pushRx(rx_frames[0]);
- driver.ifaces.at(1).pushRx(rx_frames[1]);
- // This shall transmit _some_ frames now, at least one per iface (exact number can be changed - it will be OK)
- uavcan::CanRxFrame rx_frame;
- EXPECT_EQ(1, iomgr.receive(rx_frame, tsMono(0), flags)); // Non-blocking
- EXPECT_TRUE(rxFrameEquals(rx_frame, rx_frames[0], 1200, 0));
- EXPECT_TRUE(driver.ifaces.at(0).matchAndPopTx(frames[1], 4444));
- EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[0], 3333));
- ASSERT_EQ(0, flags);
- EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[1]));
- EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[0]));
- EXPECT_EQ(1, iomgr.receive(rx_frame, tsMono(0), flags));
- EXPECT_TRUE(rxFrameEquals(rx_frame, rx_frames[1], 1200, 1));
- EXPECT_TRUE(driver.ifaces.at(0).matchAndPopTx(frames[2], 2222));
- EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[2], 2222)); // Iface #1, frame[1] was rejected (VOLATILE)
- ASSERT_EQ(0, flags);
- EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[2]));
- EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[2]));
- // State checks
- EXPECT_EQ(0, pool.getNumUsedBlocks()); // TX queue is empty
- EXPECT_EQ(1200, clockmock.monotonic);
- EXPECT_EQ(1200, clockmock.utc);
- EXPECT_TRUE(driver.ifaces.at(0).tx.empty());
- EXPECT_TRUE(driver.ifaces.at(1).tx.empty());
- EXPECT_EQ(1, iomgr.getIfacePerfCounters(0).errors);
- EXPECT_EQ(1, iomgr.getIfacePerfCounters(1).errors); // This is because of rejected frame[1]
- /*
- * Error handling
- */
- // Select failure
- driver.select_failure = true;
- EXPECT_EQ(-uavcan::ErrDriver, iomgr.receive(rx_frame, tsMono(2000), flags));
- EXPECT_EQ(-uavcan::ErrDriver,
- iomgr.send(frames[0], tsMono(2100), tsMono(2000), ALL_IFACES_MASK, CanTxQueue::Volatile, flags));
- EXPECT_EQ(1200, clockmock.monotonic);
- EXPECT_EQ(1200, clockmock.utc);
- ASSERT_EQ(0, flags);
- EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[0]));
- EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[0]));
- // Transmission failure
- driver.select_failure = false;
- driver.ifaces.at(0).writeable = true;
- driver.ifaces.at(1).writeable = true;
- driver.ifaces.at(0).tx_failure = true;
- driver.ifaces.at(1).tx_failure = true;
- // Non-blocking - return < 0
- EXPECT_GE(0, iomgr.send(frames[0], tsMono(2200), tsMono(0), ALL_IFACES_MASK, CanTxQueue::Persistent, flags));
- EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(frames[0]));
- EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(frames[0]));
- ASSERT_EQ(2, pool.getNumUsedBlocks()); // Untransmitted frames will be buffered
- // Failure removed - transmission shall proceed
- driver.ifaces.at(0).tx_failure = false;
- driver.ifaces.at(1).tx_failure = false;
- EXPECT_EQ(0, iomgr.receive(rx_frame, tsMono(2500), flags));
- EXPECT_TRUE(driver.ifaces.at(0).matchAndPopTx(frames[0], 2200));
- EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[0], 2200));
- EXPECT_EQ(0, pool.getNumUsedBlocks()); // All transmitted
- ASSERT_EQ(0, flags);
- EXPECT_TRUE(driver.ifaces.at(0).matchPendingTx(uavcan::CanFrame())); // Last call will be receive-only,
- EXPECT_TRUE(driver.ifaces.at(1).matchPendingTx(uavcan::CanFrame())); // hence empty TX
- /*
- * Perf counters
- */
- EXPECT_EQ(1, iomgr.getIfacePerfCounters(0).frames_rx);
- EXPECT_EQ(1, iomgr.getIfacePerfCounters(1).frames_rx);
- EXPECT_EQ(6, iomgr.getIfacePerfCounters(0).frames_tx);
- EXPECT_EQ(8, iomgr.getIfacePerfCounters(1).frames_tx);
- }
- TEST(CanIOManager, Loopback)
- {
- using uavcan::CanIOManager;
- using uavcan::CanTxQueue;
- using uavcan::CanFrame;
- using uavcan::CanRxFrame;
- // Memory
- uavcan::PoolAllocator<sizeof(CanTxQueue::Entry) * 4, sizeof(CanTxQueue::Entry)> pool;
- // Platform interface
- SystemClockMock clockmock;
- CanDriverMock driver(2, clockmock);
- // IO Manager
- CanIOManager iomgr(driver, pool, clockmock);
- ASSERT_EQ(2, iomgr.getNumIfaces());
- CanFrame fr1;
- fr1.id = 123 | CanFrame::FlagEFF;
- CanFrame fr2;
- fr2.id = 456 | CanFrame::FlagEFF;
- CanRxFrame rfr1;
- CanRxFrame rfr2;
- uavcan::CanIOFlags flags = 0;
- ASSERT_EQ(1, iomgr.send(fr1, tsMono(1000), tsMono(0), 1, CanTxQueue::Volatile, uavcan::CanIOFlagLoopback));
- ASSERT_LE(0, iomgr.receive(rfr1, tsMono(100), flags));
- ASSERT_EQ(uavcan::CanIOFlagLoopback, flags);
- ASSERT_TRUE(rfr1 == fr1);
- flags = 0;
- ASSERT_EQ(1, iomgr.send(fr1, tsMono(1000), tsMono(0), 1, CanTxQueue::Volatile, uavcan::CanIOFlagLoopback));
- ASSERT_EQ(1, iomgr.send(fr2, tsMono(1000), tsMono(0), 1, CanTxQueue::Persistent, uavcan::CanIOFlagLoopback));
- ASSERT_LE(0, iomgr.receive(rfr1, tsMono(100), flags));
- ASSERT_EQ(uavcan::CanIOFlagLoopback, flags);
- ASSERT_LE(0, iomgr.receive(rfr2, tsMono(100), flags));
- ASSERT_EQ(uavcan::CanIOFlagLoopback, flags);
- ASSERT_TRUE(rfr1 == fr1);
- ASSERT_TRUE(rfr2 == fr2);
- /*
- * Perf counters
- * Loopback frames are not registered as RX
- */
- EXPECT_EQ(0, iomgr.getIfacePerfCounters(0).frames_rx);
- EXPECT_EQ(0, iomgr.getIfacePerfCounters(1).frames_rx);
- EXPECT_EQ(3, iomgr.getIfacePerfCounters(0).frames_tx);
- EXPECT_EQ(0, iomgr.getIfacePerfCounters(1).frames_tx);
- }
- TEST(CanIOManager, Size)
- {
- std::cout << sizeof(uavcan::CanIOManager) << std::endl;
- }
|