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 }
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()); }
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 }
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()); }
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); }
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()); }
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); }
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); }
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); }