mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-23 16:33:34 +08:00
Reorganized directory structure for tests and src
This commit is contained in:
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "iface_mock.hpp"
|
||||
|
||||
TEST(CanIOManager, CanDriverMock)
|
||||
{
|
||||
using uavcan::CanFrame;
|
||||
|
||||
SystemClockMock clockmock;
|
||||
CanDriverMock driver(3, clockmock);
|
||||
|
||||
ASSERT_EQ(3, driver.getNumIfaces());
|
||||
|
||||
// All WR, no RD
|
||||
int mask_wr = 7;
|
||||
int mask_rd = 7;
|
||||
EXPECT_LT(0, driver.select(mask_wr, mask_rd, 100));
|
||||
EXPECT_EQ(7, mask_wr);
|
||||
EXPECT_EQ(0, mask_rd);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
driver.ifaces.at(i).writeable = false;
|
||||
|
||||
// No WR, no RD
|
||||
mask_wr = 7;
|
||||
mask_rd = 7;
|
||||
EXPECT_EQ(0, driver.select(mask_wr, mask_rd, 100));
|
||||
EXPECT_EQ(0, mask_wr);
|
||||
EXPECT_EQ(0, mask_rd);
|
||||
EXPECT_EQ(100, clockmock.monotonic);
|
||||
EXPECT_EQ(100, clockmock.utc);
|
||||
|
||||
// No WR, #1 RD
|
||||
const CanFrame fr1 = makeCanFrame(123, "foo", EXT);
|
||||
driver.ifaces.at(1).pushRx(fr1);
|
||||
mask_wr = 7;
|
||||
mask_rd = 6;
|
||||
EXPECT_LT(0, driver.select(mask_wr, mask_rd, 100));
|
||||
EXPECT_EQ(0, mask_wr);
|
||||
EXPECT_EQ(2, mask_rd);
|
||||
CanFrame fr2;
|
||||
uint64_t ts_monotonic, ts_utc;
|
||||
EXPECT_EQ(1, driver.getIface(1)->receive(fr2, ts_monotonic, ts_utc));
|
||||
EXPECT_EQ(fr1, fr2);
|
||||
EXPECT_EQ(100, ts_monotonic);
|
||||
EXPECT_EQ(0, ts_utc);
|
||||
|
||||
// #0 WR, #1 RD, Select failure
|
||||
driver.ifaces.at(0).writeable = true;
|
||||
driver.select_failure = true;
|
||||
mask_wr = 1;
|
||||
mask_rd = 7;
|
||||
EXPECT_EQ(-1, driver.select(mask_wr, mask_rd, 100));
|
||||
EXPECT_EQ(1, mask_wr); // Leaving masks unchanged - library must ignore them
|
||||
EXPECT_EQ(7, mask_rd);
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <gtest/gtest.h>
|
||||
#include <uavcan/internal/transport/can_io.hpp>
|
||||
#include <uavcan/internal/transport/transfer.hpp>
|
||||
#include "../../common.hpp"
|
||||
|
||||
|
||||
class CanIfaceMock : public uavcan::ICanIface
|
||||
{
|
||||
public:
|
||||
struct FrameWithTime
|
||||
{
|
||||
uavcan::CanFrame frame;
|
||||
uint64_t time;
|
||||
|
||||
FrameWithTime(const uavcan::CanFrame& frame, uint64_t time)
|
||||
: frame(frame)
|
||||
, time(time)
|
||||
{ }
|
||||
};
|
||||
|
||||
std::queue<FrameWithTime> tx; ///< Queue of outgoing frames (bus <-- library)
|
||||
std::queue<FrameWithTime> rx; ///< Queue of incoming frames (bus --> library)
|
||||
bool writeable;
|
||||
bool tx_failure;
|
||||
bool rx_failure;
|
||||
uint64_t num_errors;
|
||||
uavcan::ISystemClock& iclock;
|
||||
|
||||
CanIfaceMock(uavcan::ISystemClock& iclock)
|
||||
: writeable(true)
|
||||
, tx_failure(false)
|
||||
, rx_failure(false)
|
||||
, num_errors(0)
|
||||
, iclock(iclock)
|
||||
{ }
|
||||
|
||||
void pushRx(const uavcan::CanFrame& frame)
|
||||
{
|
||||
rx.push(FrameWithTime(frame, iclock.getMonotonicMicroseconds()));
|
||||
}
|
||||
|
||||
void pushRx(const uavcan::RxFrame& frame)
|
||||
{
|
||||
uavcan::CanFrame can_frame;
|
||||
EXPECT_TRUE(frame.compile(can_frame));
|
||||
rx.push(FrameWithTime(can_frame, frame.getMonotonicTimestamp()));
|
||||
}
|
||||
|
||||
bool matchAndPopTx(const uavcan::CanFrame& frame, uint64_t tx_deadline)
|
||||
{
|
||||
if (tx.empty())
|
||||
{
|
||||
std::cout << "Tx buffer is empty" << std::endl;
|
||||
return false;
|
||||
}
|
||||
const FrameWithTime frame_time = tx.front();
|
||||
tx.pop();
|
||||
return (frame_time.frame == frame) && (frame_time.time == tx_deadline);
|
||||
}
|
||||
|
||||
int send(const uavcan::CanFrame& frame, uint64_t tx_timeout_usec)
|
||||
{
|
||||
assert(this);
|
||||
EXPECT_TRUE(writeable); // Shall never be called when not writeable
|
||||
if (tx_failure)
|
||||
return -1;
|
||||
if (!writeable)
|
||||
return 0;
|
||||
const uint64_t monotonic_deadline = tx_timeout_usec + iclock.getMonotonicMicroseconds();
|
||||
tx.push(FrameWithTime(frame, monotonic_deadline));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int receive(uavcan::CanFrame& out_frame, uint64_t& out_ts_monotonic_usec, uint64_t& out_ts_utc_usec)
|
||||
{
|
||||
assert(this);
|
||||
EXPECT_TRUE(rx.size()); // Shall never be called when not readable
|
||||
if (rx_failure)
|
||||
return -1;
|
||||
if (rx.empty())
|
||||
return 0;
|
||||
const FrameWithTime frame = rx.front();
|
||||
rx.pop();
|
||||
out_frame = frame.frame;
|
||||
out_ts_monotonic_usec = frame.time;
|
||||
out_ts_utc_usec = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// cppcheck-suppress unusedFunction
|
||||
// cppcheck-suppress functionConst
|
||||
int configureFilters(const uavcan::CanFilterConfig* filter_configs, int num_configs) { return -1; }
|
||||
// cppcheck-suppress unusedFunction
|
||||
int getNumFilters() const { return 0; }
|
||||
uint64_t getNumErrors() const { return num_errors; }
|
||||
};
|
||||
|
||||
class CanDriverMock : public uavcan::ICanDriver
|
||||
{
|
||||
public:
|
||||
std::vector<CanIfaceMock> ifaces;
|
||||
uavcan::ISystemClock& iclock;
|
||||
bool select_failure;
|
||||
|
||||
CanDriverMock(int num_ifaces, uavcan::ISystemClock& iclock)
|
||||
: ifaces(num_ifaces, CanIfaceMock(iclock))
|
||||
, iclock(iclock)
|
||||
, select_failure(false)
|
||||
{ }
|
||||
|
||||
int select(int& inout_write_iface_mask, int& inout_read_iface_mask, uint64_t timeout_usec)
|
||||
{
|
||||
assert(this);
|
||||
//std::cout << "Write/read masks: " << inout_write_iface_mask << "/" << inout_read_iface_mask << std::endl;
|
||||
|
||||
if (select_failure)
|
||||
return -1;
|
||||
|
||||
const int valid_iface_mask = (1 << getNumIfaces()) - 1;
|
||||
EXPECT_FALSE(inout_write_iface_mask & ~valid_iface_mask);
|
||||
EXPECT_FALSE(inout_read_iface_mask & ~valid_iface_mask);
|
||||
|
||||
int out_write_mask = 0;
|
||||
int out_read_mask = 0;
|
||||
for (int i = 0; i < getNumIfaces(); i++)
|
||||
{
|
||||
const int mask = 1 << i;
|
||||
if ((inout_write_iface_mask & mask) && ifaces.at(i).writeable)
|
||||
out_write_mask |= mask;
|
||||
if ((inout_read_iface_mask & mask) && ifaces.at(i).rx.size())
|
||||
out_read_mask |= mask;
|
||||
}
|
||||
inout_write_iface_mask = out_write_mask;
|
||||
inout_read_iface_mask = out_read_mask;
|
||||
if ((out_write_mask | out_read_mask) == 0)
|
||||
{
|
||||
SystemClockMock* const mock = dynamic_cast<SystemClockMock*>(&iclock);
|
||||
if (mock)
|
||||
mock->advance(timeout_usec); // Emulating timeout
|
||||
else
|
||||
usleep(timeout_usec);
|
||||
return 0;
|
||||
}
|
||||
return 1; // This value is not being checked anyway, it just has to be greater than zero
|
||||
}
|
||||
|
||||
uavcan::ICanIface* getIface(int iface_index) { return &ifaces.at(iface_index); }
|
||||
int getNumIfaces() const { return ifaces.size(); }
|
||||
};
|
||||
@@ -1,271 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "iface_mock.hpp"
|
||||
|
||||
|
||||
static bool rxFrameEquals(const uavcan::CanRxFrame& rxframe, const uavcan::CanFrame& frame,
|
||||
uint64_t timestamp, 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_monotonic == timestamp) && (rxframe.iface_index == iface_index);
|
||||
}
|
||||
|
||||
TEST(CanIOManager, Reception)
|
||||
{
|
||||
// Memory
|
||||
uavcan::PoolAllocator<sizeof(uavcan::CanTxQueue::Entry) * 4, sizeof(uavcan::CanTxQueue::Entry)> pool;
|
||||
uavcan::PoolManager<2> poolmgr;
|
||||
poolmgr.addPool(&pool);
|
||||
|
||||
// Platform interface
|
||||
SystemClockMock clockmock;
|
||||
CanDriverMock driver(2, clockmock);
|
||||
|
||||
// IO Manager
|
||||
uavcan::CanIOManager iomgr(driver, poolmgr, clockmock);
|
||||
ASSERT_EQ(2, iomgr.getNumIfaces());
|
||||
|
||||
/*
|
||||
* Empty, will time out
|
||||
*/
|
||||
uavcan::CanRxFrame frame;
|
||||
EXPECT_EQ(0, iomgr.receive(frame, 100));
|
||||
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, 0));
|
||||
EXPECT_TRUE(rxFrameEquals(frame, frames[0][0], 110, 0));
|
||||
|
||||
EXPECT_EQ(1, iomgr.receive(frame, 0));
|
||||
EXPECT_TRUE(rxFrameEquals(frame, frames[0][1], 120, 0));
|
||||
|
||||
EXPECT_EQ(1, iomgr.receive(frame, 0));
|
||||
EXPECT_TRUE(rxFrameEquals(frame, frames[0][2], 130, 0));
|
||||
|
||||
EXPECT_EQ(1, iomgr.receive(frame, 0));
|
||||
EXPECT_TRUE(rxFrameEquals(frame, frames[1][0], 110, 1));
|
||||
|
||||
EXPECT_EQ(1, iomgr.receive(frame, 0));
|
||||
EXPECT_TRUE(rxFrameEquals(frame, frames[1][1], 120, 1));
|
||||
|
||||
EXPECT_EQ(1, iomgr.receive(frame, 0));
|
||||
EXPECT_TRUE(rxFrameEquals(frame, frames[1][2], 130, 1));
|
||||
|
||||
EXPECT_EQ(0, iomgr.receive(frame, 0)); // Will time out
|
||||
|
||||
/*
|
||||
* Errors
|
||||
*/
|
||||
driver.select_failure = true;
|
||||
EXPECT_EQ(-1, iomgr.receive(frame, 0));
|
||||
|
||||
driver.select_failure = false;
|
||||
driver.ifaces.at(1).pushRx(frames[0][0]);
|
||||
driver.ifaces.at(1).rx_failure = true;
|
||||
EXPECT_EQ(-1, iomgr.receive(frame, 0));
|
||||
|
||||
driver.ifaces.at(0).num_errors = 9000;
|
||||
driver.ifaces.at(1).num_errors = 100500;
|
||||
EXPECT_EQ(9000, iomgr.getNumErrors(0));
|
||||
EXPECT_EQ(100500, iomgr.getNumErrors(1));
|
||||
}
|
||||
|
||||
TEST(CanIOManager, Transmission)
|
||||
{
|
||||
using uavcan::CanIOManager;
|
||||
using uavcan::CanTxQueue;
|
||||
|
||||
// Memory
|
||||
typedef uavcan::PoolAllocator<sizeof(CanTxQueue::Entry) * 4, sizeof(CanTxQueue::Entry)> Pool1;
|
||||
Pool1* ppool = new Pool1();
|
||||
Pool1& pool = *ppool;
|
||||
uavcan::PoolManager<2> poolmgr;
|
||||
poolmgr.addPool(&pool);
|
||||
|
||||
// Platform interface
|
||||
SystemClockMock clockmock;
|
||||
CanDriverMock driver(2, clockmock);
|
||||
|
||||
// IO Manager
|
||||
CanIOManager iomgr(driver, poolmgr, clockmock);
|
||||
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)
|
||||
};
|
||||
|
||||
/*
|
||||
* Simple transmission
|
||||
*/
|
||||
EXPECT_EQ(2, iomgr.send(frames[0], 100, 0, ALL_IFACES_MASK, CanTxQueue::Volatile)); // To both
|
||||
EXPECT_TRUE(driver.ifaces.at(0).matchAndPopTx(frames[0], 100));
|
||||
EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[0], 100));
|
||||
|
||||
EXPECT_EQ(1, iomgr.send(frames[1], 200, 100, 2, CanTxQueue::Persistent)); // To #1
|
||||
EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[1], 200));
|
||||
|
||||
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.getNumErrors(0));
|
||||
EXPECT_EQ(0, iomgr.getNumErrors(1));
|
||||
|
||||
/*
|
||||
* 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], 201, 200, ALL_IFACES_MASK, CanTxQueue::Persistent));
|
||||
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
|
||||
|
||||
// Sending to both, both blocked
|
||||
driver.ifaces.at(1).writeable = false;
|
||||
EXPECT_EQ(0, iomgr.send(frames[1], 777, 300, ALL_IFACES_MASK, CanTxQueue::Volatile));
|
||||
EXPECT_EQ(3, pool.getNumUsedBlocks()); // Total 3 frames in TX queue now
|
||||
|
||||
// Sending to #0, both blocked
|
||||
EXPECT_EQ(0, iomgr.send(frames[2], 888, 400, 1, CanTxQueue::Persistent));
|
||||
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());
|
||||
|
||||
// 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;
|
||||
EXPECT_LT(0, iomgr.send(frames[0], 999, 500, 2, CanTxQueue::Persistent)); // One frame per each iface will be sent
|
||||
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());
|
||||
|
||||
// Calling receive() to flush the rest two frames
|
||||
uavcan::CanRxFrame dummy_rx_frame;
|
||||
EXPECT_EQ(0, iomgr.receive(dummy_rx_frame, 0));
|
||||
EXPECT_TRUE(driver.ifaces.at(0).matchAndPopTx(frames[2], 888));
|
||||
EXPECT_TRUE(driver.ifaces.at(1).matchAndPopTx(frames[1], 777));
|
||||
|
||||
// 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.getNumErrors(0)); // This is because of expired frame[0]
|
||||
EXPECT_EQ(0, iomgr.getNumErrors(1));
|
||||
|
||||
/*
|
||||
* 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], 2222, 1000, ALL_IFACES_MASK, CanTxQueue::Persistent));
|
||||
EXPECT_EQ(0, iomgr.send(frames[0], 3333, 1100, 2, CanTxQueue::Persistent));
|
||||
EXPECT_EQ(0, iomgr.send(frames[1], 4444, 1200, ALL_IFACES_MASK, CanTxQueue::Volatile)); // One frame kicked here
|
||||
|
||||
// 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, 0)); // 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));
|
||||
|
||||
EXPECT_EQ(1, iomgr.receive(rx_frame, 0));
|
||||
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)
|
||||
|
||||
// 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.getNumErrors(0));
|
||||
EXPECT_EQ(1, iomgr.getNumErrors(1)); // This is because of rejected frame[1]
|
||||
|
||||
/*
|
||||
* Error handling
|
||||
*/
|
||||
// Select failure
|
||||
driver.select_failure = true;
|
||||
EXPECT_EQ(-1, iomgr.receive(rx_frame, 2000));
|
||||
EXPECT_EQ(-1, iomgr.send(frames[0], 2100, 2000, ALL_IFACES_MASK, CanTxQueue::Volatile));
|
||||
EXPECT_EQ(1200, clockmock.monotonic);
|
||||
EXPECT_EQ(1200, clockmock.utc);
|
||||
|
||||
// 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;
|
||||
EXPECT_GE(0, iomgr.send(frames[0], 2200, 0, ALL_IFACES_MASK, CanTxQueue::Persistent)); // Non-blocking - return < 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, 2500));
|
||||
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
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <uavcan/internal/transport/can_io.hpp>
|
||||
#include "../../common.hpp"
|
||||
|
||||
|
||||
static int getQueueLength(uavcan::CanTxQueue& queue)
|
||||
{
|
||||
const uavcan::CanTxQueue::Entry* p = queue.peek();
|
||||
int length = 0;
|
||||
while (p)
|
||||
{
|
||||
length++;
|
||||
p = p->getNextListNode();
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static bool isInQueue(uavcan::CanTxQueue& queue, const uavcan::CanFrame& frame)
|
||||
{
|
||||
const uavcan::CanTxQueue::Entry* p = queue.peek();
|
||||
while (p)
|
||||
{
|
||||
if (frame == p->frame)
|
||||
return true;
|
||||
p = p->getNextListNode();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST(CanTxQueue, Qos)
|
||||
{
|
||||
uavcan::CanTxQueue::Entry e1(makeCanFrame(100, "", EXT), 1000, uavcan::CanTxQueue::Volatile);
|
||||
uavcan::CanTxQueue::Entry e2(makeCanFrame(100, "", EXT), 1000, uavcan::CanTxQueue::Volatile);
|
||||
|
||||
EXPECT_FALSE(e1.qosHigherThan(e2));
|
||||
EXPECT_FALSE(e2.qosHigherThan(e1));
|
||||
EXPECT_FALSE(e1.qosLowerThan(e2));
|
||||
EXPECT_FALSE(e2.qosLowerThan(e1));
|
||||
|
||||
e2.qos = uavcan::CanTxQueue::Persistent;
|
||||
|
||||
EXPECT_FALSE(e1.qosHigherThan(e2));
|
||||
EXPECT_TRUE(e2.qosHigherThan(e1));
|
||||
EXPECT_TRUE(e1.qosLowerThan(e2));
|
||||
EXPECT_FALSE(e2.qosLowerThan(e1));
|
||||
|
||||
e1.qos = uavcan::CanTxQueue::Persistent;
|
||||
e1.frame.id -= 1;
|
||||
|
||||
EXPECT_TRUE(e1.qosHigherThan(e2));
|
||||
EXPECT_FALSE(e2.qosHigherThan(e1));
|
||||
EXPECT_FALSE(e1.qosLowerThan(e2));
|
||||
EXPECT_TRUE(e2.qosLowerThan(e1));
|
||||
}
|
||||
|
||||
TEST(CanTxQueue, TxQueue)
|
||||
{
|
||||
using uavcan::CanTxQueue;
|
||||
using uavcan::CanFrame;
|
||||
|
||||
ASSERT_GE(32, sizeof(CanTxQueue::Entry)); // should be true for any platforms, though not required
|
||||
|
||||
uavcan::PoolAllocator<32 * 4, 32> pool32;
|
||||
uavcan::PoolManager<2> poolmgr;
|
||||
poolmgr.addPool(&pool32);
|
||||
|
||||
SystemClockMock clockmock;
|
||||
|
||||
CanTxQueue queue(&poolmgr, &clockmock);
|
||||
EXPECT_TRUE(queue.isEmpty());
|
||||
|
||||
// Descending priority
|
||||
const CanFrame f0 = makeCanFrame(0, "f0", EXT);
|
||||
const CanFrame f1 = makeCanFrame(10, "f1", EXT);
|
||||
const CanFrame f2 = makeCanFrame(20, "f2", EXT);
|
||||
const CanFrame f3 = makeCanFrame(100, "f3", EXT);
|
||||
const CanFrame f4 = makeCanFrame(10000, "f4", EXT);
|
||||
const CanFrame f5 = makeCanFrame(99999, "f5", EXT);
|
||||
const CanFrame f5a = makeCanFrame(99999, "f5a", EXT);
|
||||
const CanFrame f6 = makeCanFrame(999999, "f6", EXT);
|
||||
|
||||
/*
|
||||
* Priority insertion
|
||||
*/
|
||||
queue.push(f4, 100, CanTxQueue::Persistent);
|
||||
EXPECT_FALSE(queue.isEmpty());
|
||||
EXPECT_EQ(1, pool32.getNumUsedBlocks());
|
||||
EXPECT_EQ(f4, queue.peek()->frame);
|
||||
EXPECT_TRUE(queue.topPriorityHigherOrEqual(f5));
|
||||
EXPECT_TRUE(queue.topPriorityHigherOrEqual(f4)); // Equal
|
||||
EXPECT_FALSE(queue.topPriorityHigherOrEqual(f3));
|
||||
|
||||
queue.push(f3, 200, CanTxQueue::Persistent);
|
||||
EXPECT_EQ(f3, queue.peek()->frame);
|
||||
|
||||
queue.push(f0, 300, CanTxQueue::Volatile);
|
||||
EXPECT_EQ(f0, queue.peek()->frame);
|
||||
|
||||
queue.push(f1, 400, CanTxQueue::Volatile);
|
||||
EXPECT_EQ(f0, queue.peek()->frame); // Still f0, since it is highest
|
||||
EXPECT_TRUE(queue.topPriorityHigherOrEqual(f0)); // Equal
|
||||
EXPECT_TRUE(queue.topPriorityHigherOrEqual(f1));
|
||||
|
||||
// Out of free memory now
|
||||
|
||||
EXPECT_EQ(0, queue.getNumRejectedFrames());
|
||||
EXPECT_EQ(4, getQueueLength(queue));
|
||||
EXPECT_TRUE(isInQueue(queue, f0));
|
||||
EXPECT_TRUE(isInQueue(queue, f1));
|
||||
EXPECT_TRUE(isInQueue(queue, f3));
|
||||
EXPECT_TRUE(isInQueue(queue, f4));
|
||||
|
||||
const CanTxQueue::Entry* p = queue.peek();
|
||||
while (p)
|
||||
{
|
||||
std::cout << p->toString() << std::endl;
|
||||
p = p->getNextListNode();
|
||||
}
|
||||
|
||||
/*
|
||||
* QoS
|
||||
*/
|
||||
EXPECT_FALSE(isInQueue(queue, f2));
|
||||
queue.push(f2, 100, CanTxQueue::Volatile); // Non preempting, will be rejected
|
||||
EXPECT_FALSE(isInQueue(queue, f2));
|
||||
|
||||
queue.push(f2, 500, CanTxQueue::Persistent); // Will override f1 (f3 and f4 are presistent)
|
||||
EXPECT_TRUE(isInQueue(queue, f2));
|
||||
EXPECT_FALSE(isInQueue(queue, f1));
|
||||
EXPECT_EQ(4, getQueueLength(queue));
|
||||
EXPECT_EQ(2, queue.getNumRejectedFrames());
|
||||
EXPECT_EQ(f0, queue.peek()->frame); // Check the priority
|
||||
|
||||
queue.push(f5, 600, CanTxQueue::Persistent); // Will override f0 (rest are presistent)
|
||||
EXPECT_TRUE(isInQueue(queue, f5));
|
||||
EXPECT_FALSE(isInQueue(queue, f0));
|
||||
EXPECT_EQ(f2, queue.peek()->frame); // Check the priority
|
||||
|
||||
// No volatile frames left now
|
||||
|
||||
queue.push(f5a, 700, CanTxQueue::Persistent); // Will override f5 (same frame, same QoS)
|
||||
EXPECT_TRUE(isInQueue(queue, f5a));
|
||||
EXPECT_FALSE(isInQueue(queue, f5));
|
||||
|
||||
queue.push(f6, 700, CanTxQueue::Persistent); // Will be rejected (lowest QoS)
|
||||
EXPECT_FALSE(isInQueue(queue, f6));
|
||||
|
||||
EXPECT_FALSE(queue.topPriorityHigherOrEqual(f0));
|
||||
EXPECT_TRUE(queue.topPriorityHigherOrEqual(f2)); // Equal
|
||||
EXPECT_TRUE(queue.topPriorityHigherOrEqual(f5a));
|
||||
EXPECT_EQ(4, getQueueLength(queue));
|
||||
EXPECT_EQ(4, pool32.getNumUsedBlocks());
|
||||
EXPECT_EQ(5, queue.getNumRejectedFrames());
|
||||
EXPECT_TRUE(isInQueue(queue, f2));
|
||||
EXPECT_TRUE(isInQueue(queue, f3));
|
||||
EXPECT_TRUE(isInQueue(queue, f4));
|
||||
EXPECT_TRUE(isInQueue(queue, f5a));
|
||||
EXPECT_EQ(f2, queue.peek()->frame); // Check the priority
|
||||
|
||||
/*
|
||||
* Expiration
|
||||
*/
|
||||
clockmock.monotonic = 101;
|
||||
queue.push(f0, 800, CanTxQueue::Volatile); // Will replace f4 which is expired now
|
||||
EXPECT_TRUE(isInQueue(queue, f0));
|
||||
EXPECT_FALSE(isInQueue(queue, f4));
|
||||
EXPECT_EQ(6, queue.getNumRejectedFrames());
|
||||
|
||||
clockmock.monotonic = 1001;
|
||||
queue.push(f5, 2000, CanTxQueue::Volatile); // Entire queue is expired
|
||||
EXPECT_TRUE(isInQueue(queue, f5));
|
||||
EXPECT_EQ(1, getQueueLength(queue)); // Just one entry left - f5
|
||||
EXPECT_EQ(1, pool32.getNumUsedBlocks()); // Make sure there is no leaks
|
||||
EXPECT_EQ(10, queue.getNumRejectedFrames());
|
||||
|
||||
queue.push(f0, 1000, CanTxQueue::Persistent); // This entry is already expired
|
||||
EXPECT_EQ(1, getQueueLength(queue));
|
||||
EXPECT_EQ(1, pool32.getNumUsedBlocks());
|
||||
EXPECT_EQ(11, queue.getNumRejectedFrames());
|
||||
|
||||
/*
|
||||
* Removing
|
||||
*/
|
||||
queue.push(f4, 5000, CanTxQueue::Volatile);
|
||||
EXPECT_EQ(2, getQueueLength(queue));
|
||||
EXPECT_TRUE(isInQueue(queue, f4));
|
||||
EXPECT_EQ(f4, queue.peek()->frame);
|
||||
|
||||
CanTxQueue::Entry* entry = queue.peek();
|
||||
EXPECT_TRUE(entry);
|
||||
queue.remove(entry);
|
||||
EXPECT_FALSE(entry);
|
||||
|
||||
EXPECT_FALSE(isInQueue(queue, f4));
|
||||
EXPECT_TRUE(isInQueue(queue, f5));
|
||||
|
||||
entry = queue.peek();
|
||||
EXPECT_TRUE(entry);
|
||||
queue.remove(entry);
|
||||
EXPECT_FALSE(entry);
|
||||
|
||||
EXPECT_FALSE(isInQueue(queue, f5));
|
||||
|
||||
EXPECT_EQ(0, getQueueLength(queue)); // Final state checks
|
||||
EXPECT_EQ(0, pool32.getNumUsedBlocks());
|
||||
EXPECT_EQ(11, queue.getNumRejectedFrames());
|
||||
EXPECT_FALSE(queue.peek());
|
||||
EXPECT_FALSE(queue.topPriorityHigherOrEqual(f0));
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <uavcan/internal/transport/crc.hpp>
|
||||
|
||||
/*
|
||||
import crcmod
|
||||
crc = crcmod.predefined.Crc('crc-ccitt-false')
|
||||
crc.update('123')
|
||||
crc.hexdigest()
|
||||
'5BCE'
|
||||
crc.update('456789')
|
||||
crc.hexdigest()
|
||||
'29B1'
|
||||
*/
|
||||
|
||||
TEST(TransferCRC, Correctness)
|
||||
{
|
||||
uavcan::TransferCRC crc;
|
||||
|
||||
ASSERT_EQ(0xFFFF, crc.get());
|
||||
|
||||
crc.add('1');
|
||||
crc.add('2');
|
||||
crc.add('3');
|
||||
ASSERT_EQ(0x5BCE, crc.get());
|
||||
|
||||
crc.add(reinterpret_cast<const uint8_t*>("456789"), 6);
|
||||
ASSERT_EQ(0x29B1, crc.get());
|
||||
}
|
||||
@@ -1,243 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <gtest/gtest.h>
|
||||
#include "transfer_test_helpers.hpp"
|
||||
#include "can/iface_mock.hpp"
|
||||
#include <uavcan/internal/transport/dispatcher.hpp>
|
||||
|
||||
|
||||
class DispatcherTransferEmulator : public IncomingTransferEmulatorBase
|
||||
{
|
||||
CanDriverMock& target_;
|
||||
|
||||
public:
|
||||
DispatcherTransferEmulator(CanDriverMock& target, uavcan::NodeID dst_node_id = 127)
|
||||
: IncomingTransferEmulatorBase(dst_node_id)
|
||||
, target_(target)
|
||||
{ }
|
||||
|
||||
void sendOneFrame(const uavcan::RxFrame& frame)
|
||||
{
|
||||
CanIfaceMock* const iface = static_cast<CanIfaceMock*>(target_.getIface(frame.getIfaceIndex()));
|
||||
EXPECT_TRUE(iface);
|
||||
if (iface)
|
||||
iface->pushRx(frame);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static const uavcan::NodeID SELF_NODE_ID(64);
|
||||
|
||||
|
||||
TEST(Dispatcher, Reception)
|
||||
{
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 8, uavcan::MemPoolBlockSize> pool;
|
||||
uavcan::PoolManager<1> poolmgr;
|
||||
poolmgr.addPool(&pool);
|
||||
|
||||
SystemClockMock clockmock(100);
|
||||
CanDriverMock driver(2, clockmock);
|
||||
|
||||
uavcan::OutgoingTransferRegistry<8> out_trans_reg(poolmgr);
|
||||
|
||||
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock, out_trans_reg, SELF_NODE_ID);
|
||||
|
||||
DispatcherTransferEmulator emulator(driver, SELF_NODE_ID);
|
||||
|
||||
/*
|
||||
* Test environment
|
||||
*/
|
||||
static const uavcan::DataTypeDescriptor TYPES[4] =
|
||||
{
|
||||
makeDataType(uavcan::DataTypeKindMessage, 1),
|
||||
makeDataType(uavcan::DataTypeKindMessage, 2),
|
||||
makeDataType(uavcan::DataTypeKindService, 1),
|
||||
makeDataType(uavcan::DataTypeKindService, 1)
|
||||
};
|
||||
|
||||
typedef TestSubscriber<512, 2, 2> Subscriber;
|
||||
typedef std::auto_ptr<Subscriber> SubscriberPtr;
|
||||
static const int NUM_SUBSCRIBERS = 6;
|
||||
SubscriberPtr subscribers[NUM_SUBSCRIBERS] =
|
||||
{
|
||||
SubscriberPtr(new Subscriber(TYPES[0], poolmgr)), // msg
|
||||
SubscriberPtr(new Subscriber(TYPES[0], poolmgr)), // msg // Two similar, yes
|
||||
SubscriberPtr(new Subscriber(TYPES[1], poolmgr)), // msg
|
||||
SubscriberPtr(new Subscriber(TYPES[2], poolmgr)), // srv
|
||||
SubscriberPtr(new Subscriber(TYPES[3], poolmgr)), // srv
|
||||
SubscriberPtr(new Subscriber(TYPES[3], poolmgr)) // srv // Repeat again
|
||||
};
|
||||
|
||||
static const std::string DATA[6] =
|
||||
{
|
||||
"Yes, man is mortal, but that would be only half the trouble. "
|
||||
"The worst of it is that he's sometimes unexpectedly mortal - there's the trick!",
|
||||
|
||||
"In fact, I'm beginning to fear that this confusion will go on for a long time. "
|
||||
"And all because he writes down what I said incorrectly.",
|
||||
|
||||
"I had the pleasure of meeting that young man at the Patriarch's Ponds. "
|
||||
"He almost drove me mad myself, proving to me that I don't exist.",
|
||||
|
||||
"He was a dreamer, a thinker, a speculative philosopher... or, as his wife would have it, an idiot.",
|
||||
|
||||
"The only way to get ideas for stories is to drink way too much coffee and buy a desk that doesn't "
|
||||
"collapse when you beat your head against it",
|
||||
|
||||
""
|
||||
};
|
||||
|
||||
const Transfer transfers[9] =
|
||||
{
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageBroadcast, 10, DATA[0], TYPES[0]),
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageUnicast, 11, DATA[1], TYPES[1]),
|
||||
emulator.makeTransfer(uavcan::TransferTypeServiceRequest, 12, DATA[2], TYPES[2]),
|
||||
emulator.makeTransfer(uavcan::TransferTypeServiceResponse, 13, DATA[3], TYPES[3]),
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageUnicast, 14, DATA[4], TYPES[0]),
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageBroadcast, 15, DATA[5], TYPES[1]),
|
||||
// Wrongly addressed:
|
||||
emulator.makeTransfer(uavcan::TransferTypeServiceResponse, 10, DATA[0], TYPES[3], 100),
|
||||
emulator.makeTransfer(uavcan::TransferTypeServiceRequest, 11, DATA[1], TYPES[2], 101),
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageUnicast, 12, DATA[2], TYPES[1], 102)
|
||||
};
|
||||
|
||||
/*
|
||||
* Sending the transfers
|
||||
*/
|
||||
ASSERT_TRUE(dispatcher.registerMessageListener(subscribers[0].get()));
|
||||
ASSERT_TRUE(dispatcher.registerMessageListener(subscribers[1].get()));
|
||||
ASSERT_TRUE(dispatcher.registerMessageListener(subscribers[2].get()));
|
||||
ASSERT_TRUE(dispatcher.registerServiceRequestListener(subscribers[3].get()));
|
||||
ASSERT_TRUE(dispatcher.registerServiceResponseListener(subscribers[4].get()));
|
||||
ASSERT_TRUE(dispatcher.registerServiceResponseListener(subscribers[5].get()));
|
||||
|
||||
// Multiple service request listeners are not allowed
|
||||
ASSERT_FALSE(dispatcher.registerServiceRequestListener(subscribers[3].get()));
|
||||
|
||||
// Item count validation
|
||||
ASSERT_EQ(3, dispatcher.getNumMessageListeners());
|
||||
ASSERT_EQ(1, dispatcher.getNumServiceRequestListeners());
|
||||
ASSERT_EQ(2, dispatcher.getNumServiceResponseListeners());
|
||||
|
||||
for (int i = 0; i < NUM_SUBSCRIBERS; i++)
|
||||
ASSERT_TRUE(subscribers[i]->isEmpty());
|
||||
|
||||
emulator.send(transfers);
|
||||
emulator.send(transfers); // Just for fun, they will be ignored anyway.
|
||||
|
||||
while (true)
|
||||
{
|
||||
const int res = dispatcher.spin(0);
|
||||
ASSERT_LE(0, res);
|
||||
clockmock.advance(100);
|
||||
if (res == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Matching.
|
||||
* Expected reception order per subsciber:
|
||||
* 0: 0, 4
|
||||
* 1: 0, 4
|
||||
* 2: 5, 1
|
||||
* 3: 2
|
||||
* 4: 3
|
||||
* 5: 3
|
||||
*/
|
||||
ASSERT_TRUE(subscribers[0]->matchAndPop(transfers[0]));
|
||||
ASSERT_TRUE(subscribers[0]->matchAndPop(transfers[4]));
|
||||
|
||||
ASSERT_TRUE(subscribers[1]->matchAndPop(transfers[0]));
|
||||
ASSERT_TRUE(subscribers[1]->matchAndPop(transfers[4]));
|
||||
|
||||
ASSERT_TRUE(subscribers[2]->matchAndPop(transfers[5]));
|
||||
ASSERT_TRUE(subscribers[2]->matchAndPop(transfers[1]));
|
||||
|
||||
ASSERT_TRUE(subscribers[3]->matchAndPop(transfers[2]));
|
||||
|
||||
ASSERT_TRUE(subscribers[4]->matchAndPop(transfers[3]));
|
||||
|
||||
ASSERT_TRUE(subscribers[5]->matchAndPop(transfers[3]));
|
||||
|
||||
for (int i = 0; i < NUM_SUBSCRIBERS; i++)
|
||||
ASSERT_TRUE(subscribers[i]->isEmpty());
|
||||
|
||||
/*
|
||||
* Unregistering all transfers
|
||||
*/
|
||||
dispatcher.unregisterMessageListener(subscribers[0].get());
|
||||
dispatcher.unregisterMessageListener(subscribers[1].get());
|
||||
dispatcher.unregisterMessageListener(subscribers[2].get());
|
||||
dispatcher.unregisterServiceRequestListener(subscribers[3].get());
|
||||
dispatcher.unregisterServiceResponseListener(subscribers[4].get());
|
||||
dispatcher.unregisterServiceResponseListener(subscribers[5].get());
|
||||
|
||||
ASSERT_EQ(0, dispatcher.getNumMessageListeners());
|
||||
ASSERT_EQ(0, dispatcher.getNumServiceRequestListeners());
|
||||
ASSERT_EQ(0, dispatcher.getNumServiceResponseListeners());
|
||||
}
|
||||
|
||||
|
||||
TEST(Dispatcher, Transmission)
|
||||
{
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 8, uavcan::MemPoolBlockSize> pool;
|
||||
uavcan::PoolManager<1> poolmgr;
|
||||
poolmgr.addPool(&pool);
|
||||
|
||||
SystemClockMock clockmock(100);
|
||||
CanDriverMock driver(2, clockmock);
|
||||
|
||||
uavcan::OutgoingTransferRegistry<8> out_trans_reg(poolmgr);
|
||||
|
||||
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock, out_trans_reg, SELF_NODE_ID);
|
||||
|
||||
/*
|
||||
* Transmission
|
||||
*/
|
||||
static const int TX_DEADLINE = 123456;
|
||||
|
||||
// uint_fast16_t data_type_id, TransferType transfer_type, NodeID src_node_id, NodeID dst_node_id,
|
||||
// uint_fast8_t frame_index, TransferID transfer_id, bool last_frame = false
|
||||
uavcan::Frame frame(123, uavcan::TransferTypeMessageUnicast, SELF_NODE_ID, 2, 0, 0, true);
|
||||
frame.setPayload(reinterpret_cast<const uint8_t*>("123"), 3);
|
||||
|
||||
ASSERT_EQ(2, dispatcher.send(frame, TX_DEADLINE, 0, uavcan::CanTxQueue::Volatile));
|
||||
|
||||
/*
|
||||
* Validation
|
||||
*/
|
||||
uavcan::CanFrame expected_can_frame;
|
||||
ASSERT_TRUE(frame.compile(expected_can_frame));
|
||||
|
||||
ASSERT_TRUE(driver.ifaces.at(0).matchAndPopTx(expected_can_frame, TX_DEADLINE));
|
||||
ASSERT_TRUE(driver.ifaces.at(1).matchAndPopTx(expected_can_frame, TX_DEADLINE));
|
||||
|
||||
ASSERT_TRUE(driver.ifaces.at(0).tx.empty());
|
||||
ASSERT_TRUE(driver.ifaces.at(1).tx.empty());
|
||||
}
|
||||
|
||||
|
||||
TEST(Dispatcher, Spin)
|
||||
{
|
||||
uavcan::PoolManager<1> poolmgr;
|
||||
|
||||
SystemClockMock clockmock(100);
|
||||
CanDriverMock driver(2, clockmock);
|
||||
|
||||
uavcan::OutgoingTransferRegistry<8> out_trans_reg(poolmgr);
|
||||
|
||||
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock, out_trans_reg, SELF_NODE_ID);
|
||||
|
||||
clockmock.monotonic_auto_advance = 100;
|
||||
|
||||
ASSERT_EQ(100, clockmock.monotonic);
|
||||
ASSERT_EQ(0, dispatcher.spin(1000));
|
||||
ASSERT_LE(1000, clockmock.monotonic);
|
||||
ASSERT_EQ(0, dispatcher.spin(0));
|
||||
ASSERT_LE(1000, clockmock.monotonic);
|
||||
ASSERT_EQ(0, dispatcher.spin(1100));
|
||||
ASSERT_LE(1100, clockmock.monotonic);
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <gtest/gtest.h>
|
||||
#include <uavcan/internal/transport/transfer_listener.hpp>
|
||||
|
||||
|
||||
static uavcan::RxFrame makeFrame()
|
||||
{
|
||||
uavcan::RxFrame frame(
|
||||
uavcan::Frame(123, uavcan::TransferTypeMessageBroadcast, 1, uavcan::NodeID::Broadcast, 0, 1, true),
|
||||
123, 456, 0);
|
||||
uint8_t data[8];
|
||||
for (unsigned int i = 0; i < sizeof(data); i++)
|
||||
data[i] = i;
|
||||
frame.setPayload(data, sizeof(data));
|
||||
return frame;
|
||||
}
|
||||
|
||||
|
||||
static bool match(const uavcan::IncomingTransfer& it, const uavcan::RxFrame& frame,
|
||||
const uint8_t* payload, unsigned int payload_len)
|
||||
{
|
||||
// Fields extracted from the frame struct
|
||||
EXPECT_EQ(it.getMonotonicTimestamp(), frame.getMonotonicTimestamp());
|
||||
EXPECT_EQ(it.getUtcTimestamp(), frame.getUtcTimestamp());
|
||||
EXPECT_EQ(it.getSrcNodeID(), frame.getSrcNodeID());
|
||||
EXPECT_EQ(it.getTransferID(), frame.getTransferID());
|
||||
EXPECT_EQ(it.getTransferType(), frame.getTransferType());
|
||||
|
||||
// Payload comparison
|
||||
static const unsigned int BUFLEN = 1024;
|
||||
uint8_t buf_reference[BUFLEN], buf_actual[BUFLEN];
|
||||
|
||||
if (payload_len > BUFLEN)
|
||||
{
|
||||
std::cout << "match(): Payload is too long" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::fill(buf_reference, buf_reference + BUFLEN, 0);
|
||||
std::fill(buf_actual, buf_actual + BUFLEN, 0);
|
||||
std::copy(payload, payload + payload_len, buf_reference);
|
||||
|
||||
EXPECT_EQ(payload_len, it.read(0, buf_actual, payload_len * 3));
|
||||
EXPECT_EQ(0, it.read(payload_len, buf_actual, payload_len * 3));
|
||||
|
||||
return std::equal(buf_reference, buf_reference + BUFLEN, buf_actual);
|
||||
}
|
||||
|
||||
|
||||
TEST(SingleFrameIncomingTransfer, Basic)
|
||||
{
|
||||
using uavcan::RxFrame;
|
||||
using uavcan::SingleFrameIncomingTransfer;
|
||||
|
||||
const RxFrame frame = makeFrame();
|
||||
SingleFrameIncomingTransfer it(frame);
|
||||
|
||||
ASSERT_TRUE(match(it, frame, frame.getPayloadPtr(), frame.getPayloadLen()));
|
||||
}
|
||||
|
||||
|
||||
TEST(MultiFrameIncomingTransfer, Basic)
|
||||
{
|
||||
using uavcan::RxFrame;
|
||||
using uavcan::MultiFrameIncomingTransfer;
|
||||
|
||||
uavcan::PoolManager<1> poolmgr; // We don't need dynamic memory
|
||||
uavcan::TransferBufferManager<256, 1> bufmgr(poolmgr);
|
||||
|
||||
const RxFrame frame = makeFrame();
|
||||
uavcan::TransferBufferManagerKey bufmgr_key(frame.getSrcNodeID(), frame.getTransferType());
|
||||
uavcan::TransferBufferAccessor tba(bufmgr, bufmgr_key);
|
||||
|
||||
MultiFrameIncomingTransfer it(frame.getMonotonicTimestamp(), frame.getUtcTimestamp(), frame, tba);
|
||||
|
||||
/*
|
||||
* Empty read must fail
|
||||
*/
|
||||
uint8_t data_byte = 0;
|
||||
ASSERT_GT(0, it.read(0, &data_byte, 1)); // Error - no such buffer
|
||||
|
||||
/*
|
||||
* Filling the test data
|
||||
*/
|
||||
const std::string data = "123Hello world";
|
||||
const uint8_t* const data_ptr = reinterpret_cast<const uint8_t*>(data.c_str());
|
||||
ASSERT_FALSE(bufmgr.access(bufmgr_key));
|
||||
ASSERT_TRUE(bufmgr.create(bufmgr_key));
|
||||
ASSERT_EQ(data.length(), bufmgr.access(bufmgr_key)->write(0, data_ptr, data.length()));
|
||||
|
||||
/*
|
||||
* Check
|
||||
*/
|
||||
ASSERT_TRUE(match(it, frame, data_ptr, data.length()));
|
||||
|
||||
/*
|
||||
* Buffer release
|
||||
*/
|
||||
ASSERT_TRUE(bufmgr.access(bufmgr_key));
|
||||
it.release();
|
||||
ASSERT_FALSE(bufmgr.access(bufmgr_key));
|
||||
}
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <gtest/gtest.h>
|
||||
#include <uavcan/internal/transport/outgoing_transfer_registry.hpp>
|
||||
|
||||
|
||||
TEST(OutgoingTransferRegistry, Basic)
|
||||
{
|
||||
using uavcan::OutgoingTransferRegistryKey;
|
||||
uavcan::PoolManager<1> poolmgr; // Empty
|
||||
uavcan::OutgoingTransferRegistry<4> otr(poolmgr);
|
||||
|
||||
otr.cleanup(1000);
|
||||
|
||||
static const int NUM_KEYS = 5;
|
||||
const OutgoingTransferRegistryKey keys[NUM_KEYS] =
|
||||
{
|
||||
OutgoingTransferRegistryKey(123, uavcan::TransferTypeMessageUnicast, 42),
|
||||
OutgoingTransferRegistryKey(123, uavcan::TransferTypeMessageBroadcast, 0),
|
||||
OutgoingTransferRegistryKey(123, uavcan::TransferTypeServiceRequest, 2),
|
||||
OutgoingTransferRegistryKey(123, uavcan::TransferTypeMessageUnicast, 4),
|
||||
OutgoingTransferRegistryKey(456, uavcan::TransferTypeServiceRequest, 2)
|
||||
};
|
||||
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[0], 1000000)->get());
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[1], 1000000)->get());
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[2], 1000000)->get());
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[3], 1000000)->get());
|
||||
ASSERT_FALSE(otr.accessOrCreate(keys[4], 1000000)); // OOM
|
||||
|
||||
/*
|
||||
* Incrementing a little
|
||||
*/
|
||||
otr.accessOrCreate(keys[0], 2000000)->increment();
|
||||
otr.accessOrCreate(keys[0], 4000000)->increment();
|
||||
otr.accessOrCreate(keys[0], 3000000)->increment();
|
||||
ASSERT_EQ(3, otr.accessOrCreate(keys[0], 5000000)->get());
|
||||
|
||||
otr.accessOrCreate(keys[2], 2000000)->increment();
|
||||
otr.accessOrCreate(keys[2], 3000000)->increment();
|
||||
ASSERT_EQ(2, otr.accessOrCreate(keys[2], 6000000)->get());
|
||||
|
||||
otr.accessOrCreate(keys[3], 9000000)->increment();
|
||||
ASSERT_EQ(1, otr.accessOrCreate(keys[3], 4000000)->get());
|
||||
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[1], 4000000)->get());
|
||||
|
||||
ASSERT_FALSE(otr.accessOrCreate(keys[4], 1000000)); // Still OOM
|
||||
|
||||
/*
|
||||
* Cleaning up
|
||||
*/
|
||||
otr.cleanup(4000001); // Kills 1, 3
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[1], 1000000)->get());
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[3], 1000000)->get());
|
||||
otr.accessOrCreate(keys[1], 5000000)->increment();
|
||||
otr.accessOrCreate(keys[3], 5000000)->increment();
|
||||
|
||||
ASSERT_EQ(3, otr.accessOrCreate(keys[0], 5000000)->get());
|
||||
ASSERT_EQ(2, otr.accessOrCreate(keys[2], 6000000)->get());
|
||||
|
||||
otr.cleanup(5000001); // Kills 1, 3 (He needs a bath, Jud. He stinks of the ground you buried him in.), 0
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[0], 1000000)->get());
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[1], 1000000)->get());
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[3], 1000000)->get());
|
||||
|
||||
ASSERT_EQ(2, otr.accessOrCreate(keys[2], 1000000)->get());
|
||||
|
||||
otr.cleanup(5000001); // Frees some memory for 4
|
||||
ASSERT_EQ(0, otr.accessOrCreate(keys[0], 1000000)->get());
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <gtest/gtest.h>
|
||||
#include <uavcan/internal/transport/transfer.hpp>
|
||||
#include "../common.hpp"
|
||||
|
||||
|
||||
TEST(Transfer, TransferID)
|
||||
{
|
||||
using uavcan::TransferID;
|
||||
|
||||
// Tests below are based on this assumption
|
||||
ASSERT_EQ(8, 1 << TransferID::BitLen);
|
||||
|
||||
/*
|
||||
* forwardDistance()
|
||||
*/
|
||||
EXPECT_EQ(0, TransferID(0).forwardDistance(0));
|
||||
EXPECT_EQ(1, TransferID(0).forwardDistance(1));
|
||||
EXPECT_EQ(7, TransferID(0).forwardDistance(7));
|
||||
|
||||
EXPECT_EQ(0, TransferID(7).forwardDistance(7));
|
||||
EXPECT_EQ(7, TransferID(7).forwardDistance(6));
|
||||
EXPECT_EQ(1, TransferID(7).forwardDistance(0));
|
||||
|
||||
EXPECT_EQ(7, TransferID(7).forwardDistance(6));
|
||||
EXPECT_EQ(5, TransferID(0).forwardDistance(5));
|
||||
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
EXPECT_TRUE(TransferID(2) == TransferID(2));
|
||||
EXPECT_FALSE(TransferID(2) != TransferID(2));
|
||||
EXPECT_FALSE(TransferID(2) == TransferID(0));
|
||||
EXPECT_TRUE(TransferID(2) != TransferID(0));
|
||||
|
||||
TransferID tid;
|
||||
for (int i = 0; i < 999; i++)
|
||||
{
|
||||
ASSERT_EQ(i & ((1 << TransferID::BitLen) - 1), tid.get());
|
||||
const TransferID copy = tid;
|
||||
tid.increment();
|
||||
ASSERT_EQ(1, copy.forwardDistance(tid));
|
||||
ASSERT_EQ(7, tid.forwardDistance(copy));
|
||||
ASSERT_EQ(0, tid.forwardDistance(tid));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Transfer, FrameParseCompile)
|
||||
{
|
||||
using uavcan::Frame;
|
||||
using uavcan::CanFrame;
|
||||
using uavcan::TransferID;
|
||||
using uavcan::TransferType;
|
||||
|
||||
Frame frame;
|
||||
|
||||
const uint32_t can_id =
|
||||
(2 << 0) | // Transfer ID
|
||||
(1 << 3) | // Last Frame
|
||||
(29 << 4) | // Frame Index
|
||||
(42 << 10) | // Source Node ID
|
||||
(uavcan::TransferTypeMessageBroadcast << 17) |
|
||||
(456 << 19); // Data Type ID
|
||||
|
||||
const std::string payload_string = "hello";
|
||||
|
||||
/*
|
||||
* 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(2), frame.getTransferID());
|
||||
EXPECT_TRUE(frame.isLast());
|
||||
EXPECT_EQ(29, frame.getIndex());
|
||||
EXPECT_EQ(uavcan::NodeID(42), frame.getSrcNodeID());
|
||||
EXPECT_EQ(uavcan::TransferTypeMessageBroadcast, frame.getTransferType());
|
||||
EXPECT_EQ(456, frame.getDataTypeID());
|
||||
|
||||
EXPECT_EQ(payload_string.length(), frame.getPayloadLen());
|
||||
EXPECT_TRUE(std::equal(frame.getPayloadPtr(), frame.getPayloadPtr() + frame.getPayloadLen(),
|
||||
payload_string.begin()));
|
||||
|
||||
/*
|
||||
* 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, payload_string.begin()));
|
||||
|
||||
/*
|
||||
* Comparison
|
||||
*/
|
||||
ASSERT_FALSE(Frame() == frame);
|
||||
ASSERT_TRUE(Frame() != frame);
|
||||
frame = Frame();
|
||||
ASSERT_TRUE(Frame() == frame);
|
||||
ASSERT_FALSE(Frame() != frame);
|
||||
}
|
||||
|
||||
|
||||
TEST(Transfer, FrameParsing)
|
||||
{
|
||||
using uavcan::Frame;
|
||||
using uavcan::CanFrame;
|
||||
using uavcan::NodeID;
|
||||
using uavcan::TransferID;
|
||||
|
||||
CanFrame can;
|
||||
Frame frame;
|
||||
ASSERT_FALSE(frame.parse(can));
|
||||
|
||||
for (unsigned int i = 0; i < sizeof(CanFrame::data); i++)
|
||||
can.data[i] = i | (i << 4);
|
||||
|
||||
// CAN ID field order: Transfer ID, Last Frame, Frame Index, Source Node ID, Transfer Type, Data Type ID
|
||||
|
||||
/*
|
||||
* SFT broadcast
|
||||
*/
|
||||
can.id = CanFrame::FlagEFF |
|
||||
(2 << 0) | (1 << 3) | (0 << 4) | (42 << 10) | (uavcan::TransferTypeMessageBroadcast << 17) | (456 << 19);
|
||||
|
||||
ASSERT_TRUE(frame.parse(can));
|
||||
ASSERT_TRUE(frame.isFirst());
|
||||
ASSERT_TRUE(frame.isLast());
|
||||
ASSERT_EQ(0, frame.getIndex());
|
||||
ASSERT_EQ(NodeID(42), frame.getSrcNodeID());
|
||||
ASSERT_EQ(NodeID::Broadcast, frame.getDstNodeID());
|
||||
ASSERT_EQ(456, frame.getDataTypeID());
|
||||
ASSERT_EQ(TransferID(2), frame.getTransferID());
|
||||
ASSERT_EQ(uavcan::TransferTypeMessageBroadcast, frame.getTransferType());
|
||||
ASSERT_EQ(sizeof(CanFrame::data), frame.getMaxPayloadLen());
|
||||
|
||||
/*
|
||||
* SFT addressed
|
||||
*/
|
||||
can.id = CanFrame::FlagEFF |
|
||||
(2 << 0) | (1 << 3) | (0 << 4) | (42 << 10) | (uavcan::TransferTypeMessageUnicast << 17) | (456 << 19);
|
||||
|
||||
ASSERT_FALSE(frame.parse(can)); // No payload - failure
|
||||
|
||||
can.dlc = 1;
|
||||
can.data[0] = 0xFF;
|
||||
ASSERT_FALSE(frame.parse(can)); // Invalid first byte - failure
|
||||
|
||||
can.data[0] = 127;
|
||||
ASSERT_TRUE(frame.parse(can));
|
||||
|
||||
ASSERT_TRUE(frame.isFirst());
|
||||
ASSERT_TRUE(frame.isLast());
|
||||
ASSERT_EQ(0, frame.getIndex());
|
||||
ASSERT_EQ(NodeID(42), frame.getSrcNodeID());
|
||||
ASSERT_EQ(NodeID(127), frame.getDstNodeID());
|
||||
ASSERT_EQ(456, frame.getDataTypeID());
|
||||
ASSERT_EQ(TransferID(2), frame.getTransferID());
|
||||
ASSERT_EQ(uavcan::TransferTypeMessageUnicast, frame.getTransferType());
|
||||
ASSERT_EQ(sizeof(CanFrame::data) - 1, frame.getMaxPayloadLen());
|
||||
|
||||
/*
|
||||
* MFT invalid - unterminated transfer
|
||||
*/
|
||||
can.id = CanFrame::FlagEFF |
|
||||
(2 << 0) | (0 << 3) | (Frame::MaxIndex << 4) | (42 << 10) |
|
||||
(uavcan::TransferTypeMessageUnicast << 17) | (456 << 19);
|
||||
|
||||
ASSERT_FALSE(frame.parse(can));
|
||||
|
||||
/*
|
||||
* MFT invalid - invalid frame index
|
||||
*/
|
||||
can.id = CanFrame::FlagEFF |
|
||||
(2 << 0) | (0 << 3) | (63 << 4) | (42 << 10) | (uavcan::TransferTypeMessageUnicast << 17) | (456 << 19);
|
||||
|
||||
ASSERT_FALSE(frame.parse(can));
|
||||
|
||||
/*
|
||||
* Malformed frames
|
||||
*/
|
||||
can.id = CanFrame::FlagEFF |
|
||||
(2 << 0) | (1 << 3) | (0 << 4) | (42 << 10) | (uavcan::TransferTypeMessageUnicast << 17) | (456 << 19);
|
||||
can.dlc = 3;
|
||||
can.data[0] = 42;
|
||||
ASSERT_FALSE(frame.parse(can)); // Src == Dst Node ID
|
||||
can.data[0] = 41;
|
||||
ASSERT_TRUE(frame.parse(can));
|
||||
|
||||
can.id = CanFrame::FlagEFF | // cppcheck-suppress duplicateExpression
|
||||
(2 << 0) | (1 << 3) | (0 << 4) | (0 << 10) | (uavcan::TransferTypeMessageUnicast << 17) | (456 << 19);
|
||||
ASSERT_FALSE(frame.parse(can)); // Broadcast Src Node ID
|
||||
}
|
||||
|
||||
|
||||
TEST(Transfer, RxFrameParse)
|
||||
{
|
||||
using uavcan::Frame;
|
||||
using uavcan::RxFrame;
|
||||
using uavcan::CanFrame;
|
||||
using uavcan::CanRxFrame;
|
||||
|
||||
CanRxFrame can_rx_frame;
|
||||
RxFrame rx_frame;
|
||||
|
||||
// Failure
|
||||
ASSERT_FALSE(rx_frame.parse(can_rx_frame));
|
||||
|
||||
// Valid
|
||||
can_rx_frame.id = CanFrame::FlagEFF |
|
||||
(2 << 0) | // Transfer ID
|
||||
(1 << 3) | // Last Frame
|
||||
(29 << 4) | // Frame Index
|
||||
(42 << 10) | // Source Node ID
|
||||
(uavcan::TransferTypeMessageBroadcast << 17) |
|
||||
(456 << 19); // Data Type ID
|
||||
|
||||
ASSERT_TRUE(rx_frame.parse(can_rx_frame));
|
||||
ASSERT_EQ(0, rx_frame.getMonotonicTimestamp());
|
||||
ASSERT_EQ(0, rx_frame.getIfaceIndex());
|
||||
|
||||
can_rx_frame.ts_monotonic = 123;
|
||||
can_rx_frame.iface_index = 2;
|
||||
|
||||
Frame frame(456, uavcan::TransferTypeMessageBroadcast, 1, uavcan::NodeID::Broadcast, 0, 0);
|
||||
ASSERT_TRUE(frame.compile(can_rx_frame));
|
||||
|
||||
ASSERT_TRUE(rx_frame.parse(can_rx_frame));
|
||||
ASSERT_EQ(123, rx_frame.getMonotonicTimestamp());
|
||||
ASSERT_EQ(2, rx_frame.getIfaceIndex());
|
||||
ASSERT_EQ(456, rx_frame.getDataTypeID());
|
||||
ASSERT_EQ(uavcan::TransferTypeMessageBroadcast, rx_frame.getTransferType());
|
||||
}
|
||||
|
||||
|
||||
TEST(Transfer, FrameToString)
|
||||
{
|
||||
using uavcan::Frame;
|
||||
using uavcan::RxFrame;
|
||||
|
||||
// RX frame default
|
||||
RxFrame rx_frame;
|
||||
EXPECT_EQ("dtid=0 tt=4 snid=255 dnid=255 idx=0 last=0 tid=0 payload=[] ts_m=0 ts_utc=0 iface=0", rx_frame.toString());
|
||||
|
||||
// RX frame max len
|
||||
rx_frame = RxFrame(Frame(Frame::MaxDataTypeID, uavcan::TransferTypeMessageUnicast,
|
||||
uavcan::NodeID::Max, uavcan::NodeID::Max - 1, Frame::MaxIndex,
|
||||
uavcan::TransferID::Max, true),
|
||||
0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 3);
|
||||
|
||||
uint8_t data[8];
|
||||
for (unsigned int i = 0; i < sizeof(data); i++)
|
||||
data[i] = i;
|
||||
rx_frame.setPayload(data, sizeof(data));
|
||||
|
||||
EXPECT_EQ(
|
||||
"dtid=1023 tt=3 snid=127 dnid=126 idx=62 last=1 tid=7 payload=[00 01 02 03 04 05 06] ts_m=18446744073709551615 ts_utc=18446744073709551615 iface=3",
|
||||
rx_frame.toString());
|
||||
|
||||
// Plain frame default
|
||||
Frame frame;
|
||||
EXPECT_EQ("dtid=0 tt=4 snid=255 dnid=255 idx=0 last=0 tid=0 payload=[]", frame.toString());
|
||||
|
||||
// Plain frame max len
|
||||
frame = rx_frame;
|
||||
EXPECT_EQ("dtid=1023 tt=3 snid=127 dnid=126 idx=62 last=1 tid=7 payload=[00 01 02 03 04 05 06]", frame.toString());
|
||||
}
|
||||
@@ -1,348 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include <uavcan/internal/transport/transfer_buffer.hpp>
|
||||
|
||||
static const std::string TEST_DATA =
|
||||
"It was like this: I asked myself one day this question - what if Napoleon, for instance, had happened to be in my "
|
||||
"place, and if he had not had Toulon nor Egypt nor the passage of Mont Blanc to begin his career with, but "
|
||||
"instead of all those picturesque and monumental things, there had simply been some ridiculous old hag, a "
|
||||
"pawnbroker, who had to be murdered too to get money from her trunk (for his career, you understand). "
|
||||
"Well, would he have brought himself to that if there had been no other means?";
|
||||
|
||||
template <typename T>
|
||||
static bool allEqual(const T a)
|
||||
{
|
||||
int n = sizeof(a) / sizeof(a[0]);
|
||||
while (--n > 0 && a[n] == a[0]) { }
|
||||
return n == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void fill(const T a, int value)
|
||||
{
|
||||
for (unsigned int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
|
||||
a[i] = value;
|
||||
}
|
||||
|
||||
static bool matchAgainst(const std::string& data, const uavcan::ITransferBuffer& tbb,
|
||||
unsigned int offset = 0, int len = -1)
|
||||
{
|
||||
uint8_t local_buffer[1024];
|
||||
fill(local_buffer, 0);
|
||||
assert((len < 0) || (sizeof(local_buffer) >= static_cast<unsigned int>(len)));
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
const int res = tbb.read(offset, local_buffer, sizeof(local_buffer));
|
||||
if (res < 0)
|
||||
{
|
||||
std::cout << "matchAgainst(): res " << res << std::endl;
|
||||
return false;
|
||||
}
|
||||
len = res;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int res = tbb.read(offset, local_buffer, len);
|
||||
if (res != len)
|
||||
{
|
||||
std::cout << "matchAgainst(): res " << res << " expected " << len << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const bool equals = std::equal(local_buffer, local_buffer + len, data.begin() + offset);
|
||||
if (!equals)
|
||||
{
|
||||
std::cout
|
||||
<< "local_buffer:\n\t" << local_buffer
|
||||
<< std::endl;
|
||||
std::cout
|
||||
<< "test_data:\n\t" << std::string(data.begin() + offset, data.begin() + offset + len)
|
||||
<< std::endl;
|
||||
}
|
||||
return equals;
|
||||
}
|
||||
|
||||
static bool matchAgainstTestData(const uavcan::ITransferBuffer& tbb, unsigned int offset, int len = -1)
|
||||
{
|
||||
return matchAgainst(TEST_DATA, tbb, offset, len);
|
||||
}
|
||||
|
||||
TEST(TransferBuffer, TestDataValidation)
|
||||
{
|
||||
ASSERT_LE(4, TEST_DATA.length() / uavcan::MemPoolBlockSize);
|
||||
uint8_t local_buffer[50];
|
||||
std::copy(TEST_DATA.begin(), TEST_DATA.begin() + sizeof(local_buffer), local_buffer);
|
||||
ASSERT_FALSE(allEqual(local_buffer));
|
||||
}
|
||||
|
||||
static const int TEST_BUFFER_SIZE = 200;
|
||||
|
||||
TEST(StaticTransferBuffer, Basic)
|
||||
{
|
||||
using uavcan::StaticTransferBufferManagerEntry;
|
||||
StaticTransferBufferManagerEntry<TEST_BUFFER_SIZE> buf;
|
||||
|
||||
uint8_t local_buffer[TEST_BUFFER_SIZE * 2];
|
||||
const uint8_t* const test_data_ptr = reinterpret_cast<const uint8_t*>(TEST_DATA.c_str());
|
||||
|
||||
// Empty reads
|
||||
fill(local_buffer, 0xA5);
|
||||
ASSERT_EQ(0, buf.read(0, local_buffer, 999));
|
||||
ASSERT_EQ(0, buf.read(0, local_buffer, 0));
|
||||
ASSERT_EQ(0, buf.read(999, local_buffer, 0));
|
||||
ASSERT_TRUE(allEqual(local_buffer));
|
||||
|
||||
// Bulk write
|
||||
ASSERT_EQ(TEST_BUFFER_SIZE, buf.write(0, test_data_ptr, TEST_DATA.length()));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, 0));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, TEST_BUFFER_SIZE));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, TEST_BUFFER_SIZE / 2));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, TEST_BUFFER_SIZE / 2, TEST_BUFFER_SIZE / 4));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, TEST_BUFFER_SIZE / 4, TEST_BUFFER_SIZE / 2));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, 0, TEST_BUFFER_SIZE / 4));
|
||||
|
||||
// Reset
|
||||
fill(local_buffer, 0xA5);
|
||||
buf.reset();
|
||||
ASSERT_EQ(0, buf.read(0, local_buffer, 0));
|
||||
ASSERT_EQ(0, buf.read(0, local_buffer, 999));
|
||||
ASSERT_TRUE(allEqual(local_buffer));
|
||||
|
||||
// Random write
|
||||
ASSERT_EQ(21, buf.write(12, test_data_ptr + 12, 21));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, 12, 21));
|
||||
|
||||
ASSERT_EQ(12, buf.write(0, test_data_ptr, 12));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, 0));
|
||||
|
||||
ASSERT_EQ(0, buf.write(21, test_data_ptr + 21, 0));
|
||||
ASSERT_EQ(TEST_BUFFER_SIZE - 21, buf.write(21, test_data_ptr + 21, 999));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, 21, TEST_BUFFER_SIZE - 21));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, 0));
|
||||
}
|
||||
|
||||
|
||||
TEST(DynamicTransferBufferManagerEntry, Basic)
|
||||
{
|
||||
using uavcan::DynamicTransferBufferManagerEntry;
|
||||
|
||||
static const int MAX_SIZE = TEST_BUFFER_SIZE;
|
||||
static const int POOL_BLOCKS = 8;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
|
||||
uavcan::PoolManager<2> poolmgr;
|
||||
poolmgr.addPool(&pool);
|
||||
|
||||
DynamicTransferBufferManagerEntry buf(poolmgr, MAX_SIZE);
|
||||
|
||||
uint8_t local_buffer[TEST_BUFFER_SIZE * 2];
|
||||
const uint8_t* const test_data_ptr = reinterpret_cast<const uint8_t*>(TEST_DATA.c_str());
|
||||
|
||||
// Empty reads
|
||||
fill(local_buffer, 0xA5);
|
||||
ASSERT_EQ(0, buf.read(0, local_buffer, 999));
|
||||
ASSERT_EQ(0, buf.read(0, local_buffer, 0));
|
||||
ASSERT_EQ(0, buf.read(999, local_buffer, 0));
|
||||
ASSERT_TRUE(allEqual(local_buffer));
|
||||
|
||||
// Bulk write
|
||||
ASSERT_EQ(MAX_SIZE, buf.write(0, test_data_ptr, TEST_DATA.length()));
|
||||
|
||||
ASSERT_LT(0, pool.getNumUsedBlocks()); // Making sure some memory was used
|
||||
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, 0));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, TEST_BUFFER_SIZE));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, TEST_BUFFER_SIZE / 2));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, TEST_BUFFER_SIZE / 2, TEST_BUFFER_SIZE / 4));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, TEST_BUFFER_SIZE / 4, TEST_BUFFER_SIZE / 2));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, 0, TEST_BUFFER_SIZE / 4));
|
||||
|
||||
// Reset
|
||||
fill(local_buffer, 0xA5);
|
||||
buf.reset();
|
||||
ASSERT_EQ(0, buf.read(0, local_buffer, 0));
|
||||
ASSERT_EQ(0, buf.read(0, local_buffer, 999));
|
||||
ASSERT_TRUE(allEqual(local_buffer));
|
||||
ASSERT_EQ(0, pool.getNumUsedBlocks());
|
||||
|
||||
// Random write
|
||||
ASSERT_EQ(21, buf.write(12, test_data_ptr + 12, 21));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, 12, 21));
|
||||
|
||||
ASSERT_EQ(60, buf.write(TEST_BUFFER_SIZE - 60, test_data_ptr + TEST_BUFFER_SIZE - 60, 60));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, TEST_BUFFER_SIZE - 60));
|
||||
|
||||
// Now we have two empty regions: empty-data-empty-data
|
||||
|
||||
ASSERT_EQ(0, buf.write(0, test_data_ptr, 0));
|
||||
ASSERT_EQ(TEST_BUFFER_SIZE - 21, buf.write(21, test_data_ptr + 21, TEST_BUFFER_SIZE - 21));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, 21, TEST_BUFFER_SIZE - 21));
|
||||
|
||||
// Now: empty-data-data-data
|
||||
|
||||
ASSERT_EQ(21, buf.write(0, test_data_ptr, 21));
|
||||
ASSERT_TRUE(matchAgainstTestData(buf, 0));
|
||||
|
||||
// Destroying the object; memory should be released
|
||||
ASSERT_LT(0, pool.getNumUsedBlocks());
|
||||
buf.~DynamicTransferBufferManagerEntry();
|
||||
ASSERT_EQ(0, pool.getNumUsedBlocks());
|
||||
}
|
||||
|
||||
|
||||
static const std::string MGR_TEST_DATA[4] =
|
||||
{
|
||||
"I thought you would cry out again \'don\'t speak of it, leave off.\'\" Raskolnikov gave a laugh, but rather a "
|
||||
"forced one. \"What, silence again?\" he asked a minute later. \"We must talk about something, you know. ",
|
||||
|
||||
"It would be interesting for me to know how you would decide a certain \'problem\' as Lebeziatnikov would say.\" "
|
||||
"(He was beginning to lose the thread.) \"No, really, I am serious. Imagine, Sonia, that you had known all ",
|
||||
|
||||
"Luzhin\'s intentions beforehand. Known, that is, for a fact, that they would be the ruin of Katerina Ivanovna "
|
||||
"and the children and yourself thrown in--since you don\'t count yourself for anything--Polenka too... for ",
|
||||
|
||||
"she\'ll go the same way. Well, if suddenly it all depended on your decision whether he or they should go on "
|
||||
"living, that is whether Luzhin should go on living and doing wicked things, or Katerina Ivanovna should die? "
|
||||
"How would you decide which of them was to die? I ask you?"
|
||||
};
|
||||
|
||||
static const int MGR_MAX_BUFFER_SIZE = 100;
|
||||
|
||||
TEST(TransferBufferManager, TestDataValidation)
|
||||
{
|
||||
for (unsigned int i = 0; i < sizeof(MGR_TEST_DATA) / sizeof(MGR_TEST_DATA[0]); i++)
|
||||
{
|
||||
ASSERT_LT(MGR_MAX_BUFFER_SIZE, MGR_TEST_DATA[i].length());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int fillTestData(const std::string& data, uavcan::ITransferBuffer* tbb)
|
||||
{
|
||||
return tbb->write(0, reinterpret_cast<const uint8_t*>(data.c_str()), data.length());
|
||||
}
|
||||
|
||||
TEST(TransferBufferManager, Basic)
|
||||
{
|
||||
using uavcan::TransferBufferManager;
|
||||
using uavcan::TransferBufferManagerKey;
|
||||
using uavcan::ITransferBuffer;
|
||||
|
||||
static const int POOL_BLOCKS = 8;
|
||||
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(TransferBufferManager, EmptySpecialization)
|
||||
{
|
||||
uavcan::TransferBufferManager<0, 0> mgr;
|
||||
(void)mgr;
|
||||
ASSERT_GE(sizeof(void*), sizeof(mgr));
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "transfer_test_helpers.hpp"
|
||||
|
||||
|
||||
class TransferListenerEmulator : public IncomingTransferEmulatorBase
|
||||
{
|
||||
uavcan::TransferListenerBase& target_;
|
||||
const uavcan::DataTypeDescriptor data_type_;
|
||||
|
||||
public:
|
||||
TransferListenerEmulator(uavcan::TransferListenerBase& target, const uavcan::DataTypeDescriptor& type,
|
||||
uavcan::NodeID dst_node_id = 127)
|
||||
: IncomingTransferEmulatorBase(dst_node_id)
|
||||
, target_(target)
|
||||
, data_type_(type)
|
||||
{ }
|
||||
|
||||
void sendOneFrame(const uavcan::RxFrame& frame) { target_.handleFrame(frame); }
|
||||
|
||||
Transfer makeTransfer(uavcan::TransferType transfer_type, uint8_t source_node_id, const std::string& payload)
|
||||
{
|
||||
return IncomingTransferEmulatorBase::makeTransfer(transfer_type, source_node_id, payload, data_type_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
TEST(TransferListener, BasicMFT)
|
||||
{
|
||||
const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A");
|
||||
|
||||
static const int NUM_POOL_BLOCKS = 12; // This number is just enough to pass the test
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * NUM_POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
|
||||
uavcan::PoolManager<1> poolmgr;
|
||||
poolmgr.addPool(&pool);
|
||||
|
||||
TestSubscriber<256, 1, 1> subscriber(type, poolmgr);
|
||||
|
||||
/*
|
||||
* Test data
|
||||
*/
|
||||
static const std::string DATA[] =
|
||||
{
|
||||
"123456789",
|
||||
|
||||
"Build a man a fire, and he'll be warm for a day. "
|
||||
"Set a man on fire, and he'll be warm for the rest of his life.",
|
||||
|
||||
"The USSR, which they'd begun to renovate and improve at about the time when Tatarsky decided to "
|
||||
"change his profession, improved so much that it ceased to exist",
|
||||
|
||||
"In the beginning there was nothing, which exploded.",
|
||||
|
||||
"BEWARE JET BLAST"
|
||||
};
|
||||
|
||||
TransferListenerEmulator emulator(subscriber, type);
|
||||
const Transfer transfers[] =
|
||||
{
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageBroadcast, 1, DATA[0]),
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageUnicast, 1, DATA[1]), // Same NID
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageUnicast, 2, DATA[2]),
|
||||
emulator.makeTransfer(uavcan::TransferTypeServiceRequest, 3, DATA[3]),
|
||||
emulator.makeTransfer(uavcan::TransferTypeServiceResponse, 4, DATA[4]),
|
||||
};
|
||||
|
||||
/*
|
||||
* Sending concurrently
|
||||
* Expected reception order: 0, 4, 3, 1, 2
|
||||
*/
|
||||
emulator.send(transfers);
|
||||
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[0]));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[4]));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[3]));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[1]));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[2]));
|
||||
|
||||
ASSERT_TRUE(subscriber.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
TEST(TransferListener, CrcFailure)
|
||||
{
|
||||
const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A");
|
||||
|
||||
uavcan::PoolManager<1> poolmgr; // No dynamic memory
|
||||
TestSubscriber<256, 2, 2> subscriber(type, poolmgr); // Static buffer only, 2 entries
|
||||
|
||||
/*
|
||||
* Generating transfers with damaged payload (CRC is not valid)
|
||||
*/
|
||||
TransferListenerEmulator emulator(subscriber, type);
|
||||
const Transfer tr_mft = emulator.makeTransfer(uavcan::TransferTypeMessageBroadcast, 42, "123456789abcdefghik");
|
||||
const Transfer tr_sft = emulator.makeTransfer(uavcan::TransferTypeMessageUnicast, 11, "abcd");
|
||||
|
||||
std::vector<uavcan::RxFrame> ser_mft = serializeTransfer(tr_mft);
|
||||
std::vector<uavcan::RxFrame> ser_sft = serializeTransfer(tr_sft);
|
||||
|
||||
ASSERT_TRUE(ser_mft.size() > 1);
|
||||
ASSERT_TRUE(ser_sft.size() == 1);
|
||||
|
||||
// Fuck my brain.
|
||||
const_cast<uint8_t*>(ser_mft[1].getPayloadPtr())[1] = ~ser_mft[1].getPayloadPtr()[1];// CRC is no longer valid
|
||||
const_cast<uint8_t*>(ser_sft[0].getPayloadPtr())[2] = ~ser_sft[0].getPayloadPtr()[2];// no CRC - will be undetected
|
||||
|
||||
/*
|
||||
* Sending and making sure that MFT was not received, but SFT was.
|
||||
*/
|
||||
std::vector<std::vector<uavcan::RxFrame> > sers;
|
||||
sers.push_back(ser_mft);
|
||||
sers.push_back(ser_sft);
|
||||
sers.push_back(ser_mft); // Ignored
|
||||
sers.push_back(ser_sft); // Ignored
|
||||
|
||||
emulator.send(sers);
|
||||
|
||||
Transfer tr_sft_damaged = tr_sft;
|
||||
tr_sft_damaged.payload[2] = ~tr_sft.payload[2]; // Damaging the data similarly, so that it can be matched
|
||||
|
||||
ASSERT_TRUE(subscriber.matchAndPop(tr_sft_damaged));
|
||||
ASSERT_TRUE(subscriber.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
TEST(TransferListener, BasicSFT)
|
||||
{
|
||||
const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A");
|
||||
|
||||
uavcan::PoolManager<1> poolmgr; // No dynamic memory. At all.
|
||||
TestSubscriber<0, 0, 5> subscriber(type, poolmgr); // Max buf size is 0, i.e. SFT-only
|
||||
|
||||
TransferListenerEmulator emulator(subscriber, type);
|
||||
const Transfer transfers[] =
|
||||
{
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageBroadcast, 1, "123"),
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageUnicast, 1, "456"), // Same NID
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageUnicast, 2, ""),
|
||||
emulator.makeTransfer(uavcan::TransferTypeServiceRequest, 3, "abc"),
|
||||
emulator.makeTransfer(uavcan::TransferTypeServiceResponse, 4, ""),
|
||||
emulator.makeTransfer(uavcan::TransferTypeServiceResponse, 2, ""), // New TT, ignored due to OOM
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageUnicast, 2, "foo"), // Same as 2, not ignored
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageUnicast, 2, "123456789abc"),// Same as 2, not SFT - ignore
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageUnicast, 2, "bar"), // Same as 2, not ignored
|
||||
};
|
||||
|
||||
emulator.send(transfers);
|
||||
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[0]));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[1]));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[2]));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[3]));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[4]));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[6]));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[8]));
|
||||
|
||||
ASSERT_TRUE(subscriber.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
TEST(TransferListener, Cleanup)
|
||||
{
|
||||
const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A");
|
||||
|
||||
uavcan::PoolManager<1> poolmgr; // No dynamic memory
|
||||
TestSubscriber<256, 1, 2> subscriber(type, poolmgr); // Static buffer only, 1 entry
|
||||
|
||||
/*
|
||||
* Generating transfers
|
||||
*/
|
||||
TransferListenerEmulator emulator(subscriber, type);
|
||||
const Transfer tr_mft = emulator.makeTransfer(uavcan::TransferTypeMessageBroadcast, 42, "123456789abcdefghik");
|
||||
const Transfer tr_sft = emulator.makeTransfer(uavcan::TransferTypeMessageUnicast, 11, "abcd");
|
||||
|
||||
const std::vector<uavcan::RxFrame> ser_mft = serializeTransfer(tr_mft);
|
||||
const std::vector<uavcan::RxFrame> ser_sft = serializeTransfer(tr_sft);
|
||||
|
||||
ASSERT_TRUE(ser_mft.size() > 1);
|
||||
ASSERT_TRUE(ser_sft.size() == 1);
|
||||
|
||||
const std::vector<uavcan::RxFrame> ser_mft_begin(ser_mft.begin(), ser_mft.begin() + 1);
|
||||
|
||||
/*
|
||||
* Sending the first part and SFT
|
||||
*/
|
||||
std::vector<std::vector<uavcan::RxFrame> > sers;
|
||||
sers.push_back(ser_mft_begin); // Only the first part
|
||||
sers.push_back(ser_sft);
|
||||
|
||||
emulator.send(sers);
|
||||
|
||||
ASSERT_TRUE(subscriber.matchAndPop(tr_sft));
|
||||
ASSERT_TRUE(subscriber.isEmpty());
|
||||
|
||||
/*
|
||||
* Cleanup with huge timestamp value will remove all entries
|
||||
*/
|
||||
static_cast<uavcan::TransferListenerBase&>(subscriber).cleanup(100000000);
|
||||
|
||||
/*
|
||||
* Sending the same transfers again - they will be accepted since registres were cleared
|
||||
*/
|
||||
sers.clear();
|
||||
sers.push_back(ser_mft); // Complete transfer
|
||||
sers.push_back(ser_sft);
|
||||
|
||||
emulator.send(sers);
|
||||
|
||||
ASSERT_TRUE(subscriber.matchAndPop(tr_sft));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(tr_mft));
|
||||
ASSERT_TRUE(subscriber.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
TEST(TransferListener, MaximumTransferLength)
|
||||
{
|
||||
const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A");
|
||||
|
||||
uavcan::PoolManager<1> poolmgr;
|
||||
TestSubscriber<uavcan::MaxTransferPayloadLen * 2, 2, 2> subscriber(type, poolmgr);
|
||||
|
||||
static const std::string DATA_OK(uavcan::MaxTransferPayloadLen, 'z');
|
||||
|
||||
TransferListenerEmulator emulator(subscriber, type);
|
||||
const Transfer transfers[] =
|
||||
{
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageUnicast, 1, DATA_OK),
|
||||
emulator.makeTransfer(uavcan::TransferTypeMessageBroadcast, 1, DATA_OK)
|
||||
};
|
||||
|
||||
emulator.send(transfers);
|
||||
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[1])); // Broadcast is shorter, so will complete first
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[0]));
|
||||
|
||||
ASSERT_TRUE(subscriber.isEmpty());
|
||||
}
|
||||
@@ -1,508 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <gtest/gtest.h>
|
||||
#include <uavcan/internal/transport/transfer_receiver.hpp>
|
||||
|
||||
/*
|
||||
* Beware!
|
||||
* The code you're about to look at desperately needs some cleaning.
|
||||
*/
|
||||
|
||||
struct RxFrameGenerator
|
||||
{
|
||||
static const uavcan::TransferBufferManagerKey DEFAULT_KEY;
|
||||
enum { TARGET_NODE_ID = 126 };
|
||||
|
||||
uint16_t data_type_id;
|
||||
uavcan::TransferBufferManagerKey bufmgr_key;
|
||||
|
||||
RxFrameGenerator(uint16_t data_type_id, uavcan::TransferType ttype,
|
||||
const uavcan::TransferBufferManagerKey& bufmgr_key = DEFAULT_KEY)
|
||||
: data_type_id(data_type_id)
|
||||
, bufmgr_key(bufmgr_key)
|
||||
{ }
|
||||
|
||||
uavcan::RxFrame operator()(int iface_index, const std::string& data, uint8_t frame_index, bool last,
|
||||
uint8_t transfer_id, uint64_t ts_monotonic, uint64_t ts_utc = 0)
|
||||
{
|
||||
const uavcan::NodeID dst_nid =
|
||||
(bufmgr_key.getTransferType() == uavcan::TransferTypeMessageBroadcast) ?
|
||||
uavcan::NodeID::Broadcast : TARGET_NODE_ID;
|
||||
|
||||
uavcan::Frame frame(data_type_id, bufmgr_key.getTransferType(), bufmgr_key.getNodeID(),
|
||||
dst_nid, frame_index, transfer_id, last);
|
||||
|
||||
EXPECT_EQ(data.length(), frame.setPayload(reinterpret_cast<const uint8_t*>(data.c_str()), data.length()));
|
||||
|
||||
return uavcan::RxFrame(frame, ts_monotonic, ts_utc, iface_index);
|
||||
}
|
||||
};
|
||||
|
||||
const uavcan::TransferBufferManagerKey RxFrameGenerator::DEFAULT_KEY(42, uavcan::TransferTypeMessageBroadcast);
|
||||
|
||||
|
||||
template <unsigned int BUFSIZE>
|
||||
struct Context
|
||||
{
|
||||
uavcan::PoolManager<1> poolmgr; // We don't need dynamic memory for this test
|
||||
uavcan::TransferReceiver receiver; // Must be default constructible and copyable
|
||||
uavcan::TransferBufferManager<BUFSIZE, 1> bufmgr;
|
||||
|
||||
Context()
|
||||
: bufmgr(poolmgr)
|
||||
{
|
||||
assert(poolmgr.allocate(1) == NULL);
|
||||
}
|
||||
|
||||
~Context()
|
||||
{
|
||||
// We need to destroy the receiver before its buffer manager
|
||||
receiver = uavcan::TransferReceiver();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static bool matchBufferContent(const uavcan::ITransferBuffer* tbb, const std::string& content)
|
||||
{
|
||||
uint8_t data[1024];
|
||||
std::fill(data, data + sizeof(data), 0);
|
||||
if (content.length() > sizeof(data))
|
||||
{
|
||||
std::cerr << "matchBufferContent(): Content is too long" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
tbb->read(0, data, content.length());
|
||||
if (std::equal(content.begin(), content.end(), data))
|
||||
return true;
|
||||
std::cout << "Buffer content mismatch:"
|
||||
<< "\n\tExpected: " << content
|
||||
<< "\n\tActually: " << reinterpret_cast<const char*>(data)
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#define CHECK_NOT_COMPLETE(x) ASSERT_EQ(uavcan::TransferReceiver::ResultNotComplete, (x))
|
||||
#define CHECK_COMPLETE(x) ASSERT_EQ(uavcan::TransferReceiver::ResultComplete, (x))
|
||||
#define CHECK_SINGLE_FRAME(x) ASSERT_EQ(uavcan::TransferReceiver::ResultSingleFrame, (x))
|
||||
|
||||
TEST(TransferReceiver, Basic)
|
||||
{
|
||||
using uavcan::TransferReceiver;
|
||||
Context<32> context;
|
||||
RxFrameGenerator gen(789, uavcan::TransferTypeMessageBroadcast);
|
||||
uavcan::TransferReceiver& rcv = context.receiver;
|
||||
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
|
||||
|
||||
/*
|
||||
* Empty
|
||||
*/
|
||||
ASSERT_EQ(TransferReceiver::DefaultTransferInterval, rcv.getInterval());
|
||||
ASSERT_EQ(0, rcv.getLastTransferTimestampMonotonic());
|
||||
|
||||
/*
|
||||
* Single frame transfer with zero ts, must be ignored
|
||||
*/
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "Foo", 0, true, 0, 0), bk));
|
||||
ASSERT_EQ(TransferReceiver::DefaultTransferInterval, rcv.getInterval());
|
||||
ASSERT_EQ(0, rcv.getLastTransferTimestampMonotonic());
|
||||
|
||||
/*
|
||||
* Valid compound transfer
|
||||
* Args: iface_index, data, frame_index, last, transfer_id, timestamp
|
||||
*/
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "\x34\x12" "345678", 0, false, 0, 100), bk));
|
||||
CHECK_COMPLETE(rcv.addFrame(gen(0, "foo", 1, true, 0, 200), bk));
|
||||
|
||||
ASSERT_TRUE(matchBufferContent(bufmgr.access(gen.bufmgr_key), "345678foo"));
|
||||
ASSERT_EQ(0x1234, rcv.getLastTransferCrc());
|
||||
ASSERT_EQ(TransferReceiver::DefaultTransferInterval, rcv.getInterval()); // Not initialized yet
|
||||
ASSERT_EQ(100, rcv.getLastTransferTimestampMonotonic());
|
||||
|
||||
/*
|
||||
* Compound transfer mixed with invalid frames; buffer was not released explicitly
|
||||
*/
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "qwe", 0, false, 0, 300), bk)); // Previous TID, rejected
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "rty", 0, false, 0, 300), bk)); // Previous TID, wrong iface
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "\x9a\x78" "345678", 0, false, 1, 1000), bk));
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "qwertyui", 0, false, 1, 1100), bk)); // Old FI
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "abcdefgh", 1, false, 1, 1200), bk));
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "45678910", 1, false, 2, 1300), bk)); // Next TID, but FI > 0
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "", 2, true, 1, 1300), bk)); // Wrong iface
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "", 31,true, 1, 1300), bk)); // Unexpected FI
|
||||
CHECK_COMPLETE( rcv.addFrame(gen(0, "", 2, true, 1, 1300), bk));
|
||||
|
||||
ASSERT_TRUE(matchBufferContent(bufmgr.access(gen.bufmgr_key), "345678abcdefgh"));
|
||||
ASSERT_EQ(0x789A, rcv.getLastTransferCrc());
|
||||
ASSERT_GT(TransferReceiver::DefaultTransferInterval, rcv.getInterval());
|
||||
ASSERT_LT(TransferReceiver::MinTransferInterval, rcv.getInterval());
|
||||
ASSERT_EQ(1000, rcv.getLastTransferTimestampMonotonic());
|
||||
ASSERT_FALSE(rcv.isTimedOut(1000));
|
||||
ASSERT_FALSE(rcv.isTimedOut(5000));
|
||||
ASSERT_TRUE(rcv.isTimedOut(60000000));
|
||||
|
||||
/*
|
||||
* Single-frame transfers
|
||||
*/
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "qwe", 0, true, 1, 2000), bk)); // Previous TID
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "qwe", 0, true, 2, 2100), bk)); // Wrong iface
|
||||
CHECK_SINGLE_FRAME(rcv.addFrame(gen(0, "qwe", 0, true, 2, 2200), bk));
|
||||
|
||||
ASSERT_FALSE(bufmgr.access(gen.bufmgr_key)); // Buffer must be removed
|
||||
ASSERT_GT(TransferReceiver::DefaultTransferInterval, rcv.getInterval());
|
||||
ASSERT_EQ(2200, rcv.getLastTransferTimestampMonotonic());
|
||||
|
||||
CHECK_SINGLE_FRAME(rcv.addFrame(gen(0, "", 0, true, 3, 2500), bk));
|
||||
ASSERT_EQ(2500, rcv.getLastTransferTimestampMonotonic());
|
||||
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "", 0, true, 0, 3000), bk)); // Old TID
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "", 0, true, 1, 3100), bk)); // Old TID
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "", 0, true, 3, 3200), bk)); // Old TID
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "", 0, true, 0, 3300), bk)); // Old TID, wrong iface
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "", 0, true, 2, 3400), bk)); // Old TID, wrong iface
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "", 0, true, 3, 3500), bk)); // Old TID, wrong iface
|
||||
CHECK_SINGLE_FRAME(rcv.addFrame(gen(0, "", 0, true, 4, 3600), bk));
|
||||
ASSERT_EQ(3600, rcv.getLastTransferTimestampMonotonic());
|
||||
|
||||
/*
|
||||
* Timeouts
|
||||
*/
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "qwe", 0, true, 1, 100000), bk)); // Wrong iface - ignored
|
||||
CHECK_SINGLE_FRAME(rcv.addFrame(gen(1, "qwe", 0, true, 6, 600000), bk)); // Accepted due to iface timeout
|
||||
ASSERT_EQ(600000, rcv.getLastTransferTimestampMonotonic());
|
||||
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "qwe", 0, true, 7, 600100), bk));// Ignored - old iface 0
|
||||
CHECK_SINGLE_FRAME(rcv.addFrame(gen(1, "qwe", 0, true, 7, 600100), bk));
|
||||
ASSERT_EQ(600100, rcv.getLastTransferTimestampMonotonic());
|
||||
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "qwe", 0, true, 7, 600100), bk)); // Old TID
|
||||
CHECK_SINGLE_FRAME(rcv.addFrame(gen(0, "qwe", 0, true, 7, 100000000), bk));// Accepted - global timeout
|
||||
ASSERT_EQ(100000000, rcv.getLastTransferTimestampMonotonic());
|
||||
|
||||
CHECK_SINGLE_FRAME(rcv.addFrame(gen(0, "qwe", 0, true, 0, 100000100), bk));
|
||||
ASSERT_EQ(100000100, rcv.getLastTransferTimestampMonotonic());
|
||||
|
||||
ASSERT_TRUE(rcv.isTimedOut(900000000));
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "\x78\x56" "345678", 0, false, 0, 900000000), bk));// Global timeout
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "12345678", 0, false, 0, 900000100), bk));// Wrong iface
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "qwe", 1, true, 0, 900000200), bk));// Wrong iface
|
||||
CHECK_COMPLETE( rcv.addFrame(gen(1, "qwe", 1, true, 0, 900000200), bk));
|
||||
ASSERT_EQ(900000000, rcv.getLastTransferTimestampMonotonic());
|
||||
ASSERT_FALSE(rcv.isTimedOut(1000));
|
||||
ASSERT_FALSE(rcv.isTimedOut(900000200));
|
||||
ASSERT_TRUE(rcv.isTimedOut(1000 * 1000000));
|
||||
ASSERT_LT(TransferReceiver::DefaultTransferInterval, rcv.getInterval());
|
||||
ASSERT_LE(TransferReceiver::MinTransferInterval, rcv.getInterval());
|
||||
ASSERT_GE(TransferReceiver::MaxTransferInterval, rcv.getInterval());
|
||||
ASSERT_TRUE(matchBufferContent(bufmgr.access(gen.bufmgr_key), "345678qwe"));
|
||||
ASSERT_EQ(0x5678, rcv.getLastTransferCrc());
|
||||
|
||||
/*
|
||||
* Destruction
|
||||
*/
|
||||
ASSERT_TRUE(bufmgr.access(gen.bufmgr_key));
|
||||
context.receiver.~TransferReceiver(); // TransferReceiver does not own the buffer, it must not be released!
|
||||
ASSERT_TRUE(bufmgr.access(gen.bufmgr_key)); // Making sure that the buffer is still there
|
||||
}
|
||||
|
||||
|
||||
TEST(TransferReceiver, OutOfBufferSpace_32bytes)
|
||||
{
|
||||
Context<32> context;
|
||||
RxFrameGenerator gen(789, uavcan::TransferTypeMessageBroadcast);
|
||||
uavcan::TransferReceiver& rcv = context.receiver;
|
||||
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
|
||||
|
||||
/*
|
||||
* Simple transfer, maximum buffer length
|
||||
*/
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "12345678", 0, false, 1, 100000000), bk)); // 6
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "12345678", 1, false, 1, 100000100), bk)); // 14
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "12345678", 2, false, 1, 100000200), bk)); // 22
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "12345678", 3, false, 1, 100000300), bk)); // 30
|
||||
CHECK_COMPLETE( rcv.addFrame(gen(1, "12", 4, true, 1, 100000400), bk)); // 32
|
||||
|
||||
ASSERT_EQ(100000000, rcv.getLastTransferTimestampMonotonic());
|
||||
ASSERT_TRUE(matchBufferContent(bufmgr.access(gen.bufmgr_key), "34567812345678123456781234567812"));
|
||||
ASSERT_EQ(0x3231, rcv.getLastTransferCrc()); // CRC from "12", which is 0x3231 in little endian
|
||||
|
||||
/*
|
||||
* Transfer longer than available buffer space
|
||||
*/
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "12345678", 0, false, 2, 100001000), bk)); // 6
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "12345678", 1, false, 2, 100001100), bk)); // 14
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "12345678", 2, false, 2, 100001200), bk)); // 22
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "12345678", 3, false, 2, 100001200), bk)); // 30
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "12345678", 4, true, 2, 100001300), bk)); // 38 // EOT, ignored - lost sync
|
||||
|
||||
ASSERT_EQ(100000000, rcv.getLastTransferTimestampMonotonic()); // Timestamp will not be overriden
|
||||
ASSERT_FALSE(bufmgr.access(gen.bufmgr_key)); // Buffer should be removed
|
||||
}
|
||||
|
||||
|
||||
TEST(TransferReceiver, UnterminatedTransfer)
|
||||
{
|
||||
Context<512> context;
|
||||
RxFrameGenerator gen(789, uavcan::TransferTypeMessageBroadcast);
|
||||
uavcan::TransferReceiver& rcv = context.receiver;
|
||||
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
|
||||
|
||||
std::string content;
|
||||
for (int i = 0; i <= uavcan::Frame::MaxIndex; i++)
|
||||
{
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "12345678", i, false, 0, 1000 + i), bk)); // Last one will be dropped
|
||||
content += "12345678";
|
||||
}
|
||||
CHECK_COMPLETE(rcv.addFrame(gen(1, "12345678", uavcan::Frame::MaxIndex, true, 0, 1100), bk));
|
||||
ASSERT_EQ(1000, rcv.getLastTransferTimestampMonotonic());
|
||||
ASSERT_TRUE(matchBufferContent(bufmgr.access(gen.bufmgr_key), std::string(content, 2)));
|
||||
ASSERT_EQ(0x3231, rcv.getLastTransferCrc());
|
||||
}
|
||||
|
||||
|
||||
TEST(TransferReceiver, OutOfOrderFrames)
|
||||
{
|
||||
Context<32> context;
|
||||
RxFrameGenerator gen(789, uavcan::TransferTypeMessageBroadcast);
|
||||
uavcan::TransferReceiver& rcv = context.receiver;
|
||||
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
|
||||
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "12345678", 0, false, 7, 100000000), bk));
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "--------", 3, false, 7, 100000100), bk)); // Out of order
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "--------", 2, true, 7, 100000200), bk)); // Out of order
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "qwertyui", 1, false, 7, 100000300), bk));
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "--------", 4, true, 7, 100000200), bk)); // Out of order
|
||||
CHECK_COMPLETE( rcv.addFrame(gen(1, "abcd", 2, true, 7, 100000400), bk));
|
||||
|
||||
ASSERT_EQ(100000000, rcv.getLastTransferTimestampMonotonic());
|
||||
ASSERT_TRUE(matchBufferContent(bufmgr.access(gen.bufmgr_key), "345678qwertyuiabcd"));
|
||||
ASSERT_EQ(0x3231, rcv.getLastTransferCrc());
|
||||
}
|
||||
|
||||
|
||||
TEST(TransferReceiver, IntervalMeasurement)
|
||||
{
|
||||
Context<32> context;
|
||||
RxFrameGenerator gen(789, uavcan::TransferTypeMessageBroadcast);
|
||||
uavcan::TransferReceiver& rcv = context.receiver;
|
||||
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
|
||||
|
||||
static const int INTERVAL = 1000;
|
||||
uavcan::TransferID tid;
|
||||
uint64_t timestamp = 100000000;
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "12345678", 0, false, tid.get(), timestamp), bk));
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "qwertyui", 1, false, tid.get(), timestamp), bk));
|
||||
CHECK_COMPLETE( rcv.addFrame(gen(1, "abcd", 2, true, tid.get(), timestamp), bk));
|
||||
|
||||
ASSERT_TRUE(matchBufferContent(bufmgr.access(gen.bufmgr_key), "345678qwertyuiabcd"));
|
||||
ASSERT_EQ(0x3231, rcv.getLastTransferCrc());
|
||||
ASSERT_EQ(timestamp, rcv.getLastTransferTimestampMonotonic());
|
||||
|
||||
timestamp += INTERVAL;
|
||||
tid.increment();
|
||||
}
|
||||
|
||||
ASSERT_EQ(INTERVAL, rcv.getInterval());
|
||||
}
|
||||
|
||||
|
||||
TEST(TransferReceiver, Restart)
|
||||
{
|
||||
Context<32> context;
|
||||
RxFrameGenerator gen(789, uavcan::TransferTypeMessageBroadcast);
|
||||
uavcan::TransferReceiver& rcv = context.receiver;
|
||||
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
|
||||
|
||||
/*
|
||||
* This transfer looks complete, but must be ignored because of large delay after the first frame
|
||||
*/
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "--------", 0, false, 0, 100), bk)); // Begin
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "--------", 1, false, 0, 10000100), bk));// Continue 10 sec later, expired
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "--------", 2, true, 0, 10000200), bk));// Ignored
|
||||
|
||||
/*
|
||||
* Begins immediately after, gets an iface timeout but completes OK
|
||||
*/
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "12345678", 0, false, 0, 10000300), bk));// Begin
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "12345678", 1, false, 0, 12000300), bk));// 2 sec later, iface timeout
|
||||
CHECK_COMPLETE( rcv.addFrame(gen(1, "12345678", 2, true, 0, 12000400), bk));// OK nevertheless
|
||||
|
||||
ASSERT_TRUE(matchBufferContent(bufmgr.access(gen.bufmgr_key), "3456781234567812345678"));
|
||||
ASSERT_EQ(0x3231, rcv.getLastTransferCrc());
|
||||
|
||||
/*
|
||||
* Begins OK, gets an iface timeout, switches to another iface
|
||||
*/
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "--------", 0, false, 1, 13000500), bk));// Begin
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "--------", 1, false, 1, 16000500), bk));// 3 sec later, iface timeout
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "--------", 1, false, 1, 16000600), bk));// Same TID, another iface - ignore
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "--------", 1, false, 2, 16000700), bk));// Not first frame - ignore
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "12345678", 0, false, 2, 16000800), bk));// First, another iface - restart
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "--------", 2, true, 1, 16000600), bk));// Old iface - ignore
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "12345678", 1, false, 2, 16000900), bk));// Continuing
|
||||
CHECK_COMPLETE( rcv.addFrame(gen(0, "12345678", 2, true, 2, 16000910), bk));// Done
|
||||
|
||||
ASSERT_TRUE(matchBufferContent(bufmgr.access(gen.bufmgr_key), "3456781234567812345678"));
|
||||
ASSERT_EQ(0x3231, rcv.getLastTransferCrc());
|
||||
}
|
||||
|
||||
|
||||
TEST(TransferReceiver, UtcTransferTimestamping)
|
||||
{
|
||||
Context<32> context;
|
||||
RxFrameGenerator gen(789, uavcan::TransferTypeMessageBroadcast);
|
||||
uavcan::TransferReceiver& rcv = context.receiver;
|
||||
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
|
||||
|
||||
/*
|
||||
* Zero UTC timestamp must be preserved
|
||||
*/
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "12345678", 0, false, 0, 1, 0), bk));
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "qwertyui", 1, false, 0, 2, 0), bk));
|
||||
CHECK_COMPLETE( rcv.addFrame(gen(1, "abcd", 2, true, 0, 3, 0), bk));
|
||||
|
||||
ASSERT_TRUE(matchBufferContent(bufmgr.access(gen.bufmgr_key), "345678qwertyuiabcd"));
|
||||
ASSERT_EQ(1, rcv.getLastTransferTimestampMonotonic());
|
||||
ASSERT_EQ(0, rcv.getLastTransferTimestampUtc());
|
||||
|
||||
/*
|
||||
* Non-zero UTC timestamp
|
||||
*/
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "12345678", 0, false, 1, 4, 123), bk)); // This UTC is going to be preserved
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "qwertyui", 1, false, 1, 5, 0), bk)); // Following are ignored
|
||||
CHECK_COMPLETE( rcv.addFrame(gen(1, "abcd", 2, true, 1, 6, 42), bk));
|
||||
|
||||
ASSERT_TRUE(matchBufferContent(bufmgr.access(gen.bufmgr_key), "345678qwertyuiabcd"));
|
||||
ASSERT_EQ(4, rcv.getLastTransferTimestampMonotonic());
|
||||
ASSERT_EQ(123, rcv.getLastTransferTimestampUtc());
|
||||
|
||||
/*
|
||||
* Single-frame transfers
|
||||
*/
|
||||
CHECK_SINGLE_FRAME(rcv.addFrame(gen(1, "abc", 0, true, 2, 10, 100000000), bk)); // Exact value is irrelevant
|
||||
ASSERT_EQ(10, rcv.getLastTransferTimestampMonotonic());
|
||||
ASSERT_EQ(100000000, rcv.getLastTransferTimestampUtc());
|
||||
|
||||
/*
|
||||
* Restart recovery
|
||||
*/
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "12345678", 0, false, 1, 100000000, 800000000), bk));
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "qwertyui", 1, false, 1, 100000001, 300000000), bk));
|
||||
CHECK_COMPLETE( rcv.addFrame(gen(0, "abcd", 2, true, 1, 100000002, 900000000), bk));
|
||||
|
||||
ASSERT_TRUE(matchBufferContent(bufmgr.access(gen.bufmgr_key), "345678qwertyuiabcd"));
|
||||
ASSERT_EQ(100000000, rcv.getLastTransferTimestampMonotonic());
|
||||
ASSERT_EQ(800000000, rcv.getLastTransferTimestampUtc());
|
||||
}
|
||||
|
||||
|
||||
TEST(TransferReceiver, HeaderParsing)
|
||||
{
|
||||
Context<32> context;
|
||||
RxFrameGenerator gen(789, uavcan::TransferTypeMessageBroadcast);
|
||||
uavcan::TransferReceiver& rcv = context.receiver;
|
||||
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
|
||||
|
||||
static const uavcan::TransferType ADDRESSED_TRANSFER_TYPES[3] =
|
||||
{
|
||||
uavcan::TransferTypeMessageUnicast,
|
||||
uavcan::TransferTypeServiceRequest,
|
||||
uavcan::TransferTypeServiceResponse
|
||||
};
|
||||
|
||||
uavcan::TransferID tid;
|
||||
|
||||
/*
|
||||
* MFT, message broadcasting
|
||||
*/
|
||||
{
|
||||
gen.bufmgr_key =
|
||||
uavcan::TransferBufferManagerKey(gen.bufmgr_key.getNodeID(), uavcan::TransferTypeMessageBroadcast);
|
||||
uavcan::TransferBufferAccessor bk1(context.bufmgr, gen.bufmgr_key);
|
||||
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "12345678", 0, false, tid.get(), 1), bk1));
|
||||
CHECK_COMPLETE( rcv.addFrame(gen(0, "abcd", 1, true, tid.get(), 2), bk1));
|
||||
|
||||
ASSERT_TRUE(matchBufferContent(bufmgr.access(gen.bufmgr_key), "345678abcd"));
|
||||
ASSERT_EQ(0x3231, rcv.getLastTransferCrc());
|
||||
|
||||
tid.increment();
|
||||
bk1.remove();
|
||||
}
|
||||
|
||||
/*
|
||||
* MFT, message unicast, service request/response
|
||||
*/
|
||||
for (int i = 0; i < int(sizeof(ADDRESSED_TRANSFER_TYPES) / sizeof(ADDRESSED_TRANSFER_TYPES[0])); i++)
|
||||
{
|
||||
gen.bufmgr_key =
|
||||
uavcan::TransferBufferManagerKey(gen.bufmgr_key.getNodeID(), ADDRESSED_TRANSFER_TYPES[i]);
|
||||
uavcan::TransferBufferAccessor bk2(context.bufmgr, gen.bufmgr_key);
|
||||
|
||||
const uint64_t ts_monotonic = i + 10;
|
||||
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(0, "1234567", 0, false, tid.get(), ts_monotonic), bk2));
|
||||
CHECK_COMPLETE( rcv.addFrame(gen(0, "abcd", 1, true, tid.get(), ts_monotonic), bk2));
|
||||
|
||||
ASSERT_TRUE(matchBufferContent(bufmgr.access(gen.bufmgr_key), "34567abcd"));
|
||||
ASSERT_EQ(0x3231, rcv.getLastTransferCrc());
|
||||
|
||||
tid.increment();
|
||||
bk2.remove();
|
||||
}
|
||||
|
||||
/*
|
||||
* SFT, message broadcasting
|
||||
*/
|
||||
static const std::string SFT_PAYLOAD_BROADCAST = "12345678";
|
||||
static const std::string SFT_PAYLOAD_UNICAST = "1234567";
|
||||
|
||||
{
|
||||
gen.bufmgr_key =
|
||||
uavcan::TransferBufferManagerKey(gen.bufmgr_key.getNodeID(), uavcan::TransferTypeMessageBroadcast);
|
||||
uavcan::TransferBufferAccessor bk(context.bufmgr, gen.bufmgr_key);
|
||||
|
||||
const uavcan::RxFrame frame = gen(0, SFT_PAYLOAD_BROADCAST, 0, true, tid.get(), 1000);
|
||||
|
||||
CHECK_SINGLE_FRAME(rcv.addFrame(frame, bk));
|
||||
ASSERT_EQ(0x0000, rcv.getLastTransferCrc()); // Default value - zero
|
||||
|
||||
// All bytes are payload, zero overhead
|
||||
ASSERT_TRUE(std::equal(SFT_PAYLOAD_BROADCAST.begin(), SFT_PAYLOAD_BROADCAST.end(), frame.getPayloadPtr()));
|
||||
|
||||
tid.increment();
|
||||
}
|
||||
|
||||
/*
|
||||
* SFT, message unicast, service request/response
|
||||
*/
|
||||
for (int i = 0; i < int(sizeof(ADDRESSED_TRANSFER_TYPES) / sizeof(ADDRESSED_TRANSFER_TYPES[0])); i++)
|
||||
{
|
||||
gen.bufmgr_key =
|
||||
uavcan::TransferBufferManagerKey(gen.bufmgr_key.getNodeID(), ADDRESSED_TRANSFER_TYPES[i]);
|
||||
uavcan::TransferBufferAccessor bk(context.bufmgr, gen.bufmgr_key);
|
||||
|
||||
const uavcan::RxFrame frame = gen(0, SFT_PAYLOAD_UNICAST, 0, true, tid.get(), i + 10000);
|
||||
|
||||
CHECK_SINGLE_FRAME(rcv.addFrame(frame, bk));
|
||||
ASSERT_EQ(0x0000, rcv.getLastTransferCrc()); // Default value - zero
|
||||
|
||||
// First byte must be ignored
|
||||
ASSERT_TRUE(std::equal(SFT_PAYLOAD_UNICAST.begin(), SFT_PAYLOAD_UNICAST.end(), frame.getPayloadPtr()));
|
||||
|
||||
tid.increment();
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <gtest/gtest.h>
|
||||
#include "transfer_test_helpers.hpp"
|
||||
#include "can/iface_mock.hpp"
|
||||
#include <uavcan/internal/transport/transfer_sender.hpp>
|
||||
|
||||
|
||||
static int sendOne(uavcan::TransferSender& sender, const std::string& data,
|
||||
uint64_t monotonic_tx_deadline, uint64_t monotonic_blocking_deadline,
|
||||
uavcan::TransferType transfer_type, uavcan::NodeID dst_node_id)
|
||||
{
|
||||
return sender.send(reinterpret_cast<const uint8_t*>(data.c_str()), data.length(), monotonic_tx_deadline,
|
||||
monotonic_blocking_deadline, transfer_type, dst_node_id);
|
||||
}
|
||||
|
||||
static int sendOne(uavcan::TransferSender& sender, const std::string& data,
|
||||
uint64_t monotonic_tx_deadline, uint64_t monotonic_blocking_deadline,
|
||||
uavcan::TransferType transfer_type, uavcan::NodeID dst_node_id, uavcan::TransferID tid)
|
||||
{
|
||||
return sender.send(reinterpret_cast<const uint8_t*>(data.c_str()), data.length(), monotonic_tx_deadline,
|
||||
monotonic_blocking_deadline, transfer_type, dst_node_id, tid);
|
||||
}
|
||||
|
||||
|
||||
TEST(TransferSender, Basic)
|
||||
{
|
||||
uavcan::PoolManager<1> poolmgr;
|
||||
|
||||
SystemClockMock clockmock(100);
|
||||
CanDriverMock driver(2, clockmock);
|
||||
|
||||
uavcan::OutgoingTransferRegistry<8> out_trans_reg(poolmgr);
|
||||
|
||||
static const uavcan::NodeID TX_NODE_ID(64);
|
||||
static const uavcan::NodeID RX_NODE_ID(65);
|
||||
uavcan::Dispatcher dispatcher_tx(driver, poolmgr, clockmock, out_trans_reg, TX_NODE_ID);
|
||||
uavcan::Dispatcher dispatcher_rx(driver, poolmgr, clockmock, out_trans_reg, RX_NODE_ID);
|
||||
|
||||
/*
|
||||
* Test environment
|
||||
*/
|
||||
static const uavcan::DataTypeDescriptor TYPES[2] =
|
||||
{
|
||||
makeDataType(uavcan::DataTypeKindMessage, 1),
|
||||
makeDataType(uavcan::DataTypeKindService, 1)
|
||||
};
|
||||
|
||||
uavcan::TransferSender senders[2] =
|
||||
{
|
||||
uavcan::TransferSender(dispatcher_tx, TYPES[0], uavcan::CanTxQueue::Volatile),
|
||||
uavcan::TransferSender(dispatcher_tx, TYPES[1], uavcan::CanTxQueue::Persistent)
|
||||
};
|
||||
|
||||
static const std::string DATA[4] =
|
||||
{
|
||||
"Don't panic.",
|
||||
|
||||
"The ships hung in the sky in much the same way that bricks don't.",
|
||||
|
||||
"Would it save you a lot of time if I just gave up and went mad now?",
|
||||
|
||||
"If there's anything more important than my ego around, I want it caught and shot now."
|
||||
};
|
||||
|
||||
/*
|
||||
* Transmission
|
||||
*/
|
||||
static const uint64_t TX_DEADLINE = 1000000;
|
||||
|
||||
sendOne(senders[0], DATA[0], TX_DEADLINE, 0, uavcan::TransferTypeMessageBroadcast, 0);
|
||||
sendOne(senders[0], DATA[1], TX_DEADLINE, 0, uavcan::TransferTypeMessageUnicast, RX_NODE_ID);
|
||||
sendOne(senders[0], "123", TX_DEADLINE, 0, uavcan::TransferTypeMessageBroadcast, 0);
|
||||
sendOne(senders[0], "456", TX_DEADLINE, 0, uavcan::TransferTypeMessageUnicast, RX_NODE_ID);
|
||||
|
||||
sendOne(senders[1], DATA[2], TX_DEADLINE, 0, uavcan::TransferTypeServiceRequest, RX_NODE_ID);
|
||||
sendOne(senders[1], DATA[3], TX_DEADLINE, 0, uavcan::TransferTypeServiceResponse, RX_NODE_ID, 1);
|
||||
sendOne(senders[1], "", TX_DEADLINE, 0, uavcan::TransferTypeServiceRequest, RX_NODE_ID);
|
||||
sendOne(senders[1], "", TX_DEADLINE, 0, uavcan::TransferTypeServiceResponse, RX_NODE_ID, 2);
|
||||
|
||||
static const Transfer TRANSFERS[8] =
|
||||
{
|
||||
Transfer(TX_DEADLINE, 0, uavcan::TransferTypeMessageBroadcast, 0, TX_NODE_ID, 0, DATA[0], TYPES[0]),
|
||||
Transfer(TX_DEADLINE, 0, uavcan::TransferTypeMessageUnicast, 0, TX_NODE_ID, RX_NODE_ID, DATA[1], TYPES[0]),
|
||||
Transfer(TX_DEADLINE, 0, uavcan::TransferTypeMessageBroadcast, 1, TX_NODE_ID, 0, "123", TYPES[0]),
|
||||
Transfer(TX_DEADLINE, 0, uavcan::TransferTypeMessageUnicast, 1, TX_NODE_ID, RX_NODE_ID, "456", TYPES[0]),
|
||||
|
||||
Transfer(TX_DEADLINE, 0, uavcan::TransferTypeServiceRequest, 0, TX_NODE_ID, RX_NODE_ID, DATA[2], TYPES[1]),
|
||||
Transfer(TX_DEADLINE, 0, uavcan::TransferTypeServiceResponse, 1, TX_NODE_ID, RX_NODE_ID, DATA[3], TYPES[1]),
|
||||
Transfer(TX_DEADLINE, 0, uavcan::TransferTypeServiceRequest, 1, TX_NODE_ID, RX_NODE_ID, "", TYPES[1]),
|
||||
Transfer(TX_DEADLINE, 0, uavcan::TransferTypeServiceResponse, 2, TX_NODE_ID, RX_NODE_ID, "", TYPES[1])
|
||||
};
|
||||
|
||||
/*
|
||||
* Receiving on the other side.
|
||||
*/
|
||||
for (int i = 0; i < driver.getNumIfaces(); i++) // Moving the frames from TX to RX side
|
||||
{
|
||||
CanIfaceMock& iface = driver.ifaces.at(i);
|
||||
std::cout << "Num frames: " << iface.tx.size() << std::endl;
|
||||
while (!iface.tx.empty())
|
||||
{
|
||||
CanIfaceMock::FrameWithTime ft = iface.tx.front();
|
||||
iface.tx.pop();
|
||||
iface.rx.push(ft);
|
||||
}
|
||||
}
|
||||
|
||||
TestSubscriber<512, 2, 2> sub_msg(TYPES[0], poolmgr);
|
||||
TestSubscriber<512, 2, 2> sub_srv_req(TYPES[1], poolmgr);
|
||||
TestSubscriber<512, 2, 2> sub_srv_resp(TYPES[1], poolmgr);
|
||||
|
||||
dispatcher_rx.registerMessageListener(&sub_msg);
|
||||
dispatcher_rx.registerServiceRequestListener(&sub_srv_req);
|
||||
dispatcher_rx.registerServiceResponseListener(&sub_srv_resp);
|
||||
|
||||
while (true)
|
||||
{
|
||||
const int res = dispatcher_rx.spin(0);
|
||||
ASSERT_LE(0, res);
|
||||
clockmock.advance(100);
|
||||
if (res == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Validation
|
||||
*/
|
||||
ASSERT_TRUE(sub_msg.matchAndPop(TRANSFERS[0]));
|
||||
ASSERT_TRUE(sub_msg.matchAndPop(TRANSFERS[1]));
|
||||
ASSERT_TRUE(sub_msg.matchAndPop(TRANSFERS[2]));
|
||||
ASSERT_TRUE(sub_msg.matchAndPop(TRANSFERS[3]));
|
||||
|
||||
ASSERT_TRUE(sub_srv_req.matchAndPop(TRANSFERS[4]));
|
||||
ASSERT_TRUE(sub_srv_req.matchAndPop(TRANSFERS[6]));
|
||||
|
||||
ASSERT_TRUE(sub_srv_resp.matchAndPop(TRANSFERS[5]));
|
||||
ASSERT_TRUE(sub_srv_resp.matchAndPop(TRANSFERS[7]));
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "transfer_test_helpers.hpp"
|
||||
|
||||
|
||||
TEST(TransferTestHelpers, Transfer)
|
||||
{
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 8, uavcan::MemPoolBlockSize> pool;
|
||||
uavcan::PoolManager<1> poolmgr;
|
||||
poolmgr.addPool(&pool);
|
||||
|
||||
uavcan::TransferBufferManager<128, 1> mgr(poolmgr);
|
||||
uavcan::TransferBufferAccessor tba(mgr, uavcan::TransferBufferManagerKey(0, uavcan::TransferTypeMessageUnicast));
|
||||
|
||||
uavcan::RxFrame frame(uavcan::Frame(123, uavcan::TransferTypeMessageBroadcast, 1, 0, 0, 0, true), 0, 0, 0);
|
||||
uavcan::MultiFrameIncomingTransfer mfit(10, 1000, frame, tba);
|
||||
|
||||
// Filling the buffer with data
|
||||
static const std::string TEST_DATA = "Kaneda! What do you see? Kaneda! What do you see? Kaneda! Kaneda!!!";
|
||||
ASSERT_TRUE(tba.create());
|
||||
ASSERT_EQ(TEST_DATA.length(),
|
||||
tba.access()->write(0, reinterpret_cast<const uint8_t*>(TEST_DATA.c_str()), TEST_DATA.length()));
|
||||
|
||||
// Reading back
|
||||
const Transfer transfer(mfit, uavcan::DataTypeDescriptor());
|
||||
ASSERT_EQ(TEST_DATA, transfer.payload);
|
||||
}
|
||||
|
||||
|
||||
TEST(TransferTestHelpers, MFTSerialization)
|
||||
{
|
||||
uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "Foo");
|
||||
|
||||
static const std::string DATA = "To go wrong in one's own way is better than to go right in someone else's.";
|
||||
const Transfer transfer(1, 100000, uavcan::TransferTypeMessageUnicast, 2, 42, 127, DATA, type);
|
||||
|
||||
const std::vector<uavcan::RxFrame> ser = serializeTransfer(transfer);
|
||||
|
||||
std::cout << "Serialized transfer:\n";
|
||||
for (std::vector<uavcan::RxFrame>::const_iterator it = ser.begin(); it != ser.end(); ++it)
|
||||
std::cout << "\t" << it->toString() << "\n";
|
||||
|
||||
for (std::vector<uavcan::RxFrame>::const_iterator it = ser.begin(); it != ser.end(); ++it)
|
||||
{
|
||||
std::cout << "\t'";
|
||||
for (int i = 0; i < it->getPayloadLen(); i++)
|
||||
{
|
||||
uint8_t ch = it->getPayloadPtr()[i];
|
||||
if (ch < 0x20 || ch > 0x7E)
|
||||
ch = '.';
|
||||
std::cout << static_cast<char>(ch);
|
||||
}
|
||||
std::cout << "'\n";
|
||||
}
|
||||
std::cout << std::flush;
|
||||
}
|
||||
|
||||
|
||||
TEST(TransferTestHelpers, SFTSerialization)
|
||||
{
|
||||
uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "Foo");
|
||||
|
||||
{
|
||||
const Transfer transfer(1, 100000, uavcan::TransferTypeMessageBroadcast, 7, 42, 0, "Nvrfrget", type);
|
||||
const std::vector<uavcan::RxFrame> ser = serializeTransfer(transfer);
|
||||
ASSERT_EQ(1, ser.size());
|
||||
std::cout << "Serialized transfer:\n\t" << ser[0].toString() << "\n";
|
||||
}
|
||||
{
|
||||
const Transfer transfer(1, 100000, uavcan::TransferTypeServiceRequest, 7, 42, 127, "7-chars", type);
|
||||
const std::vector<uavcan::RxFrame> ser = serializeTransfer(transfer);
|
||||
ASSERT_EQ(1, ser.size());
|
||||
std::cout << "Serialized transfer:\n\t" << ser[0].toString() << "\n";
|
||||
}
|
||||
{
|
||||
const Transfer transfer(1, 100000, uavcan::TransferTypeMessageBroadcast, 7, 42, 0, "", type);
|
||||
const std::vector<uavcan::RxFrame> ser = serializeTransfer(transfer);
|
||||
ASSERT_EQ(1, ser.size());
|
||||
std::cout << "Serialized transfer:\n\t" << ser[0].toString() << "\n";
|
||||
}
|
||||
{
|
||||
const Transfer transfer(1, 100000, uavcan::TransferTypeServiceResponse, 7, 42, 127, "", type);
|
||||
const std::vector<uavcan::RxFrame> ser = serializeTransfer(transfer);
|
||||
ASSERT_EQ(1, ser.size());
|
||||
std::cout << "Serialized transfer:\n\t" << ser[0].toString() << "\n";
|
||||
}
|
||||
}
|
||||
@@ -1,281 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <gtest/gtest.h>
|
||||
#include <uavcan/internal/transport/transfer_listener.hpp>
|
||||
|
||||
/**
|
||||
* UAVCAN transfer representation used in various tests.
|
||||
*/
|
||||
struct Transfer
|
||||
{
|
||||
uint64_t ts_monotonic;
|
||||
uint64_t ts_utc;
|
||||
uavcan::TransferType transfer_type;
|
||||
uavcan::TransferID transfer_id;
|
||||
uavcan::NodeID src_node_id;
|
||||
uavcan::NodeID dst_node_id;
|
||||
uavcan::DataTypeDescriptor data_type;
|
||||
std::string payload;
|
||||
|
||||
Transfer(const uavcan::IncomingTransfer& tr, const uavcan::DataTypeDescriptor& data_type)
|
||||
: ts_monotonic(tr.getMonotonicTimestamp())
|
||||
, ts_utc(tr.getUtcTimestamp())
|
||||
, transfer_type(tr.getTransferType())
|
||||
, transfer_id(tr.getTransferID())
|
||||
, src_node_id(tr.getSrcNodeID())
|
||||
, dst_node_id() // default is invalid
|
||||
, data_type(data_type)
|
||||
{
|
||||
unsigned int offset = 0;
|
||||
while (true)
|
||||
{
|
||||
uint8_t buf[256];
|
||||
int res = tr.read(offset, buf, sizeof(buf));
|
||||
if (res < 0)
|
||||
{
|
||||
std::cout << "IncomingTransferContainer: read failure " << res << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
if (res == 0)
|
||||
break;
|
||||
payload += std::string(reinterpret_cast<const char*>(buf), res);
|
||||
offset += res;
|
||||
}
|
||||
}
|
||||
|
||||
Transfer(uint64_t ts_monotonic, uint64_t ts_utc, uavcan::TransferType transfer_type,
|
||||
uavcan::TransferID transfer_id, uavcan::NodeID src_node_id, uavcan::NodeID dst_node_id,
|
||||
const std::string& payload, const uavcan::DataTypeDescriptor& data_type)
|
||||
: ts_monotonic(ts_monotonic)
|
||||
, ts_utc(ts_utc)
|
||||
, transfer_type(transfer_type)
|
||||
, transfer_id(transfer_id)
|
||||
, src_node_id(src_node_id)
|
||||
, dst_node_id(dst_node_id)
|
||||
, data_type(data_type)
|
||||
, payload(payload)
|
||||
{ }
|
||||
|
||||
bool operator==(const Transfer& rhs) const
|
||||
{
|
||||
return
|
||||
(ts_monotonic == rhs.ts_monotonic) &&
|
||||
((ts_utc && rhs.ts_utc) ? (ts_utc == rhs.ts_utc) : true) &&
|
||||
(transfer_type == rhs.transfer_type) &&
|
||||
(transfer_id == rhs.transfer_id) &&
|
||||
(src_node_id == rhs.src_node_id) &&
|
||||
((dst_node_id.isValid() && rhs.dst_node_id.isValid()) ? (dst_node_id == rhs.dst_node_id) : true) &&
|
||||
(data_type == rhs.data_type) &&
|
||||
(payload == rhs.payload);
|
||||
}
|
||||
|
||||
std::string toString() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "ts_m=" << ts_monotonic
|
||||
<< " ts_utc=" << ts_utc
|
||||
<< " tt=" << transfer_type
|
||||
<< " tid=" << int(transfer_id.get())
|
||||
<< " snid=" << int(src_node_id.get())
|
||||
<< " dnid=" << int(dst_node_id.get())
|
||||
<< " dtid=" << int(data_type.getID())
|
||||
<< "\n\t'" << payload << "'";
|
||||
return os.str();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This subscriber accepts any types of transfers - this makes testing easier.
|
||||
* In reality, uavcan::TransferListener should accept only specific transfer types
|
||||
* which are dispatched/filtered by uavcan::Dispatcher.
|
||||
*/
|
||||
template <unsigned int MAX_BUF_SIZE, unsigned int NUM_STATIC_BUFS, unsigned int NUM_STATIC_RECEIVERS>
|
||||
class TestSubscriber : public uavcan::TransferListener<MAX_BUF_SIZE, NUM_STATIC_BUFS, NUM_STATIC_RECEIVERS>
|
||||
{
|
||||
typedef uavcan::TransferListener<MAX_BUF_SIZE, NUM_STATIC_BUFS, NUM_STATIC_RECEIVERS> Base;
|
||||
|
||||
std::queue<Transfer> transfers_;
|
||||
|
||||
public:
|
||||
TestSubscriber(const uavcan::DataTypeDescriptor& data_type, uavcan::IAllocator& allocator)
|
||||
: Base(data_type, allocator)
|
||||
{ }
|
||||
|
||||
void handleIncomingTransfer(uavcan::IncomingTransfer& transfer)
|
||||
{
|
||||
const Transfer rx(transfer, Base::getDataTypeDescriptor());
|
||||
transfers_.push(rx);
|
||||
std::cout << "Received transfer: " << rx.toString() << std::endl;
|
||||
}
|
||||
|
||||
bool matchAndPop(const Transfer& reference)
|
||||
{
|
||||
if (transfers_.empty())
|
||||
{
|
||||
std::cout << "No received transfers" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const Transfer tr = transfers_.front();
|
||||
transfers_.pop();
|
||||
|
||||
const bool res = (tr == reference);
|
||||
if (!res)
|
||||
{
|
||||
std::cout << "TestSubscriber: Transfer mismatch:\n"
|
||||
<< "Expected: " << reference.toString() << "\n"
|
||||
<< "Received: " << tr.toString() << std::endl;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int getNumReceivedTransfers() const { return transfers_.size(); }
|
||||
bool isEmpty() const { return transfers_.empty(); }
|
||||
};
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
std::vector<uavcan::RxFrame> serializeTransfer(const Transfer& transfer)
|
||||
{
|
||||
bool need_crc = false;
|
||||
switch (transfer.transfer_type)
|
||||
{
|
||||
case uavcan::TransferTypeMessageBroadcast:
|
||||
need_crc = transfer.payload.length() > sizeof(uavcan::CanFrame::data);
|
||||
break;
|
||||
case uavcan::TransferTypeServiceResponse:
|
||||
case uavcan::TransferTypeServiceRequest:
|
||||
case uavcan::TransferTypeMessageUnicast:
|
||||
need_crc = transfer.payload.length() > (sizeof(uavcan::CanFrame::data) - 1);
|
||||
break;
|
||||
default:
|
||||
std::cerr << "X_X" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> raw_payload;
|
||||
if (need_crc)
|
||||
{
|
||||
uavcan::TransferCRC payload_crc = transfer.data_type.getSignature().toTransferCRC();
|
||||
payload_crc.add(reinterpret_cast<const uint8_t*>(transfer.payload.c_str()), transfer.payload.length());
|
||||
// Little endian
|
||||
raw_payload.push_back(payload_crc.get() & 0xFF);
|
||||
raw_payload.push_back((payload_crc.get() >> 8) & 0xFF);
|
||||
}
|
||||
raw_payload.insert(raw_payload.end(), transfer.payload.begin(), transfer.payload.end());
|
||||
|
||||
std::vector<uavcan::RxFrame> output;
|
||||
unsigned int frame_index = 0;
|
||||
unsigned int offset = 0;
|
||||
uint64_t ts_monotonic = transfer.ts_monotonic;
|
||||
uint64_t ts_utc = transfer.ts_utc;
|
||||
|
||||
while (true)
|
||||
{
|
||||
const int bytes_left = raw_payload.size() - offset;
|
||||
EXPECT_TRUE(bytes_left >= 0);
|
||||
|
||||
uavcan::Frame frm(transfer.data_type.getID(), transfer.transfer_type, transfer.src_node_id,
|
||||
transfer.dst_node_id, frame_index, transfer.transfer_id);
|
||||
const int spres = frm.setPayload(&*(raw_payload.begin() + offset), bytes_left);
|
||||
if (spres < 0)
|
||||
{
|
||||
std::cerr << ">_<" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
if (spres == bytes_left)
|
||||
frm.makeLast();
|
||||
|
||||
offset += spres;
|
||||
EXPECT_GE(uavcan::Frame::MaxIndex, frame_index);
|
||||
frame_index++;
|
||||
|
||||
const uavcan::RxFrame rxfrm(frm, ts_monotonic, ts_utc, 0);
|
||||
ts_monotonic += 1;
|
||||
ts_utc += 1;
|
||||
|
||||
output.push_back(rxfrm);
|
||||
if (frm.isLast())
|
||||
break;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
uavcan::DataTypeDescriptor makeDataType(uavcan::DataTypeKind kind, uint16_t id, const char* name = "")
|
||||
{
|
||||
const uavcan::DataTypeSignature signature((uint64_t(kind) << 16) | (id << 8) | (id & 0xFF));
|
||||
return uavcan::DataTypeDescriptor(kind, id, signature, name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class IncomingTransferEmulatorBase
|
||||
{
|
||||
uint64_t ts_;
|
||||
uavcan::TransferID tid_;
|
||||
uavcan::NodeID dst_node_id_;
|
||||
|
||||
public:
|
||||
IncomingTransferEmulatorBase(uavcan::NodeID dst_node_id)
|
||||
: ts_(0)
|
||||
, dst_node_id_(dst_node_id)
|
||||
{ }
|
||||
|
||||
virtual ~IncomingTransferEmulatorBase() { }
|
||||
|
||||
Transfer makeTransfer(uavcan::TransferType transfer_type, uint8_t source_node_id, const std::string& payload,
|
||||
const uavcan::DataTypeDescriptor& type,
|
||||
uavcan::NodeID dst_node_id_override = uavcan::NodeID())
|
||||
{
|
||||
ts_ += 100;
|
||||
const uavcan::NodeID dst_node_id = (transfer_type == uavcan::TransferTypeMessageBroadcast)
|
||||
? uavcan::NodeID::Broadcast
|
||||
: (dst_node_id_override.isValid() ? dst_node_id_override : dst_node_id_);
|
||||
const Transfer tr(ts_, ts_ + 1000000000ul, transfer_type, tid_, source_node_id, dst_node_id, payload, type);
|
||||
tid_.increment();
|
||||
return tr;
|
||||
}
|
||||
|
||||
virtual void sendOneFrame(const uavcan::RxFrame& frame) = 0;
|
||||
|
||||
void send(const std::vector<std::vector<uavcan::RxFrame> >& sers)
|
||||
{
|
||||
unsigned int index = 0;
|
||||
while (true)
|
||||
{
|
||||
// Sending all transfers concurrently
|
||||
bool all_empty = true;
|
||||
for (std::vector<std::vector<uavcan::RxFrame> >::const_iterator it = sers.begin(); it != sers.end(); ++it)
|
||||
{
|
||||
if (it->size() <= index)
|
||||
continue;
|
||||
all_empty = false;
|
||||
std::cout << "Incoming Transfer Emulator: Sending: " << it->at(index).toString() << std::endl;
|
||||
sendOneFrame(it->at(index));
|
||||
}
|
||||
index++;
|
||||
if (all_empty)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void send(const Transfer* transfers, unsigned int num_transfers)
|
||||
{
|
||||
std::vector<std::vector<uavcan::RxFrame> > sers;
|
||||
while (num_transfers--)
|
||||
sers.push_back(serializeTransfer(*transfers++));
|
||||
send(sers);
|
||||
}
|
||||
|
||||
template <int SIZE> void send(const Transfer (&transfers)[SIZE]) { send(transfers, SIZE); }
|
||||
};
|
||||
Reference in New Issue
Block a user