aboutsummaryrefslogtreecommitdiffstats
path: root/host/tests
diff options
context:
space:
mode:
authorCiro Nishiguchi <ciro.nishiguchi@ni.com>2019-03-27 17:40:37 -0500
committerMartin Braun <martin.braun@ettus.com>2019-11-26 11:49:18 -0800
commit888d710cf51b7fa886b0c83c999128160f2ba176 (patch)
tree3b689c851422928de6704f766c7d0d25eac5673f /host/tests
parent9c2d90292b9a613fb290c5a04e99014d1a9118ab (diff)
downloaduhd-888d710cf51b7fa886b0c83c999128160f2ba176.tar.gz
uhd-888d710cf51b7fa886b0c83c999128160f2ba176.tar.bz2
uhd-888d710cf51b7fa886b0c83c999128160f2ba176.zip
tests: add mock transport implementation and test
Add test for transports using mock transports
Diffstat (limited to 'host/tests')
-rw-r--r--host/tests/CMakeLists.txt1
-rw-r--r--host/tests/common/mock_link.hpp256
-rw-r--r--host/tests/link_test.cpp162
3 files changed, 419 insertions, 0 deletions
diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt
index c918cc6bb..431c380c3 100644
--- a/host/tests/CMakeLists.txt
+++ b/host/tests/CMakeLists.txt
@@ -53,6 +53,7 @@ set(test_sources
expert_test.cpp
fe_conn_test.cpp
rfnoc_node_test.cpp
+ link_test.cpp
)
set(benchmark_sources
diff --git a/host/tests/common/mock_link.hpp b/host/tests/common/mock_link.hpp
new file mode 100644
index 000000000..34ea15540
--- /dev/null
+++ b/host/tests/common/mock_link.hpp
@@ -0,0 +1,256 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_MOCK_LINK_HPP
+#define INCLUDED_MOCK_LINK_HPP
+
+#include <uhd/exception.hpp>
+#include <uhdlib/transport/link_base.hpp>
+#include <boost/shared_array.hpp>
+#include <list>
+#include <utility>
+#include <vector>
+
+namespace uhd { namespace transport {
+
+/*!
+ * Frame buffer for mock link.
+ */
+class mock_frame_buff : public frame_buff
+{
+public:
+ mock_frame_buff() {}
+
+ void set_mem(boost::shared_array<uint8_t> mem)
+ {
+ _mem = mem;
+ _data = _mem.get();
+ }
+
+ boost::shared_array<uint8_t> get_mem() const
+ {
+ return _mem;
+ }
+
+private:
+ boost::shared_array<uint8_t> _mem;
+};
+
+/*!
+ * Test link that allows the contents of packets sent to be inspected
+ * by test code.
+ */
+class mock_send_link : public send_link_base<mock_send_link>
+{
+public:
+ using sptr = std::shared_ptr<mock_send_link>;
+
+ using base_t = send_link_base<mock_send_link>;
+
+ /*!
+ * Parameters for link creation.
+ */
+ struct link_params
+ {
+ size_t frame_size;
+ size_t num_frames;
+ };
+
+ /*!
+ * Constructor for mock_send_link. If reuse_send_memory is set to
+ * false, the link creates a new memory buffer for every packet, which
+ * can be read using the pop_send_packet method. If it is set to true, the
+ * same array is used for all frame buffers. The latter is useful when a
+ * test requires the same buffer contents to be used repeatedly.
+ *
+ * \param params specifies the number of frames and frame size for this
+ * link.
+ * \param reuse_send_memory configures the link to use the same buffer
+ * memory region for all frame buffers.
+ */
+ mock_send_link(const link_params& params, const bool reuse_send_memory = false)
+ : base_t(params.num_frames, params.frame_size)
+ , _reuse_send_memory(reuse_send_memory)
+ {
+ _buffs.resize(params.num_frames);
+
+ for (auto& buff : _buffs) {
+ base_t::preload_free_buff(&buff);
+ }
+
+ if (_reuse_send_memory) {
+ // If reusing memory, all buffers will use the same memory region,
+ // just preconfigure them here.
+ _tx_mems.push_back(
+ boost::shared_array<uint8_t>(new uint8_t[params.frame_size]));
+
+ for (auto& buff : _buffs) {
+ buff.set_mem(_tx_mems.back());
+ }
+ }
+ }
+
+ /*!
+ * Retrieve the contents of a packet sent by the link. The link
+ * stores packets in a queue in the order they were sent.
+ */
+ std::pair<boost::shared_array<uint8_t>, size_t> pop_send_packet()
+ {
+ UHD_ASSERT_THROW(!_reuse_send_memory);
+ UHD_ASSERT_THROW(!_tx_mems.empty());
+ UHD_ASSERT_THROW(!_tx_lens.empty());
+
+ auto buff = _tx_mems.front();
+ auto len = _tx_lens.front();
+
+ _tx_mems.pop_front();
+ _tx_lens.pop_front();
+ return std::make_pair(buff, len);
+ }
+
+ /*!
+ * Configures the link to simulate a timeout in the next call to
+ * get_send_buff.
+ */
+ void set_simulate_io_timeout(const bool simulate_io_timeout)
+ {
+ _simulate_io_timeout = simulate_io_timeout;
+ }
+
+private:
+ // Friend declaration to allow base class to call private methods
+ friend base_t;
+
+ // Method called by send_link_base
+ bool get_send_buff_derived(frame_buff& buff, int32_t /*timeout*/)
+ {
+ if (_simulate_io_timeout) {
+ return false;
+ }
+
+ if (!_reuse_send_memory) {
+ const size_t size = base_t::get_send_frame_size();
+ auto mem = boost::shared_array<uint8_t>(new uint8_t[size]);
+
+ auto* buff_ptr = static_cast<mock_frame_buff*>(&buff);
+ buff_ptr->set_mem(mem);
+ }
+ return true;
+ }
+
+ // Method called by send_link_base
+ void release_send_buff_derived(frame_buff& buff)
+ {
+ if (!_reuse_send_memory) {
+ auto* buff_ptr = static_cast<mock_frame_buff*>(&buff);
+ _tx_mems.push_back(buff_ptr->get_mem());
+ _tx_lens.push_back(buff_ptr->packet_size());
+ buff_ptr->set_mem(boost::shared_array<uint8_t>());
+ }
+ }
+
+ std::vector<mock_frame_buff> _buffs;
+ std::list<boost::shared_array<uint8_t>> _tx_mems;
+ std::list<size_t> _tx_lens;
+ bool _reuse_send_memory;
+
+ bool _simulate_io_timeout = false;
+};
+
+/*!
+ * Test link that allows the caller to enqueue buffers to be returned
+ * by the get_recv_buff method.
+ */
+class mock_recv_link : public recv_link_base<mock_recv_link>
+{
+public:
+ using sptr = std::shared_ptr<mock_recv_link>;
+
+ using base_t = recv_link_base<mock_recv_link>;
+
+ /*!
+ * Parameters for link creation.
+ */
+ struct link_params
+ {
+ size_t frame_size;
+ size_t num_frames;
+ };
+
+ /*!
+ * Constructor for mock_recv_link. If reuse_recv_memory is set to
+ * false, the link pops an array pushed by push_back_recv_packet
+ * in every get_recv_buff call. If it is set to true, the same array
+ * is used for all frame buffers. The latter is useful when a test requires
+ * the same buffer contents to be used repeatedly.
+ *
+ * \param params specifies the number of frames and frame size for this
+ * link.
+ * \param reuse_recv_memory configures the link to use the same buffer
+ * memory region for all frame buffers.
+ */
+ mock_recv_link(const link_params& params, const bool reuse_recv_memory = false)
+ : base_t(params.num_frames, params.frame_size)
+ , _reuse_recv_memory(reuse_recv_memory)
+ {
+ _buffs.resize(params.num_frames);
+
+ for (auto& buff : _buffs) {
+ base_t::preload_free_buff(&buff);
+ }
+ }
+
+ /*!
+ * Queues a data array to be returned by a future call to get_recv_buff.
+ * If reuse_recv_memory is set to false, push a buffer for each call to
+ * get_recv_buff. If it is set to true, push a single buffer to be used
+ * for all packets.
+ */
+ void push_back_recv_packet(const boost::shared_array<uint8_t> data, const size_t len)
+ {
+ _rx_mems.push_back(data);
+ _rx_lens.push_back(len);
+ }
+
+private:
+ // Friend declaration to allow base class to call private methods
+ friend base_t;
+
+ // Method called by recv_link_base
+ size_t get_recv_buff_derived(frame_buff& buff, int32_t)
+ {
+ if (_rx_mems.empty()) {
+ return 0; // timeout
+ }
+
+ auto* buff_ptr = static_cast<mock_frame_buff*>(&buff);
+ buff_ptr->set_mem(_rx_mems.front());
+ buff_ptr->set_packet_size(_rx_lens.front());
+
+ if (not _reuse_recv_memory) {
+ _rx_mems.pop_front();
+ _rx_lens.pop_front();
+ }
+
+ return buff_ptr->packet_size();
+ }
+
+ // Method called by recv_link_base
+ void release_recv_buff_derived(frame_buff& buff)
+ {
+ auto* buff_ptr = static_cast<mock_frame_buff*>(&buff);
+ buff_ptr->set_mem(boost::shared_array<uint8_t>());
+ }
+
+ std::vector<mock_frame_buff> _buffs;
+ std::list<boost::shared_array<uint8_t>> _rx_mems;
+ std::list<size_t> _rx_lens;
+ bool _reuse_recv_memory;
+};
+
+}} // namespace uhd::transport
+
+#endif /*INCLUDED_MOCK_LINK_HPP*/
diff --git a/host/tests/link_test.cpp b/host/tests/link_test.cpp
new file mode 100644
index 000000000..bcd3c3860
--- /dev/null
+++ b/host/tests/link_test.cpp
@@ -0,0 +1,162 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "common/mock_link.hpp"
+#include <boost/test/unit_test.hpp>
+
+using namespace uhd::transport;
+
+BOOST_AUTO_TEST_CASE(test_send_get_release)
+{
+ // Just call get_send_buff, release_send_buff, and pop_send_packet
+ // from a link containing a single frame_buff.
+ const size_t num_frames = 1;
+ const int32_t timeout_ms = 1;
+
+ const mock_send_link::link_params params = {1000, num_frames};
+
+ auto xport = std::make_shared<mock_send_link>(params);
+
+ // Check sizes
+ BOOST_CHECK_EQUAL(xport->get_num_send_frames(), params.num_frames);
+ BOOST_CHECK_EQUAL(xport->get_send_frame_size(), params.frame_size);
+
+ // Call get and release a few times and check packet contents
+ for (size_t i = 0; i < 5; i++) {
+ auto buff = xport->get_send_buff(timeout_ms);
+ BOOST_CHECK(buff);
+
+ auto* ptr = static_cast<uint8_t*>(buff->data());
+ ptr[0] = i;
+
+ buff->set_packet_size(sizeof(uint8_t));
+ xport->release_send_buff(std::move(buff));
+ BOOST_CHECK(!buff);
+
+ auto packet = xport->pop_send_packet();
+ BOOST_CHECK_EQUAL(packet.first[0], i);
+ BOOST_CHECK_EQUAL(packet.second, sizeof(uint8_t));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_send_timeout)
+{
+ // Test that the link returns timeouts correctly and continues to
+ // operate properly after a timeout condition.
+ const size_t num_frames = 10;
+ const int32_t timeout_ms = 1;
+
+ const mock_send_link::link_params params = {1000, num_frames};
+
+ auto xport = std::make_shared<mock_send_link>(params);
+
+ // Repeat the following a few times to check buffers are not lost
+ for (size_t i = 0; i < 3; i++) {
+ // Cause a timeout by simulating an I/O delay in the underlying
+ // link implementation.
+ std::vector<frame_buff::uptr> buffs;
+
+ for (size_t j = 0; j < num_frames / 2; j++) {
+ buffs.push_back(xport->get_send_buff(timeout_ms));
+ BOOST_CHECK(buffs.back());
+ }
+
+ // Simulate a timeout
+ xport->set_simulate_io_timeout(true);
+ BOOST_CHECK(!xport->get_send_buff(timeout_ms));
+ xport->set_simulate_io_timeout(false);
+
+ for (size_t j = 0; j < num_frames / 2; j++) {
+ buffs.push_back(xport->get_send_buff(timeout_ms));
+ BOOST_CHECK(buffs.back());
+ }
+
+ for (auto& buff : buffs) {
+ buff->set_packet_size(params.frame_size);
+ xport->release_send_buff(std::move(buff));
+ BOOST_CHECK(!buff);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_recv_get_release)
+{
+ // Just call push_recv_packet, get_recv_buff, and release_recv_buff
+ // from a link containing a single frame_buff.
+ const size_t num_frames = 1;
+ const int32_t timeout_ms = 1;
+
+ const mock_recv_link::link_params params = {1000, num_frames};
+
+ auto xport = std::make_shared<mock_recv_link>(params);
+
+ // Check sizes
+ BOOST_CHECK_EQUAL(xport->get_num_recv_frames(), params.num_frames);
+ BOOST_CHECK_EQUAL(xport->get_recv_frame_size(), params.frame_size);
+
+ // Call get and release a few times and check packet contents
+ for (size_t i = 0; i < 5; i++) {
+ size_t packet_size = sizeof(uint8_t);
+ auto packet_data = boost::shared_array<uint8_t>(new uint8_t[packet_size]);
+ packet_data[0] = i;
+ xport->push_back_recv_packet(packet_data, packet_size);
+
+ auto buff = xport->get_recv_buff(timeout_ms);
+ BOOST_CHECK(buff);
+ BOOST_CHECK(buff->data());
+
+ auto* ptr = static_cast<uint8_t*>(buff->data());
+ ptr[0] = i;
+
+ xport->release_recv_buff(std::move(buff));
+ BOOST_CHECK(!buff);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_recv_timeout)
+{
+ // Test that the link returns timeouts correctly and continues to
+ // operate properly after a timeout condition.
+ const size_t num_frames = 10;
+ const int32_t timeout_ms = 1;
+
+ const mock_recv_link::link_params params = {1000, num_frames};
+
+ auto xport = std::make_shared<mock_recv_link>(params);
+
+ // Repeat the following a few times to check buffers are not lost
+ for (size_t i = 0; i < 3; i++) {
+ // Cause a timeout by simulating an I/O delay in the underlying
+ // link implementation.
+ std::vector<frame_buff::uptr> buffs;
+
+ for (size_t i = 0; i < num_frames / 2; i++) {
+ size_t packet_size = sizeof(uint8_t);
+ auto packet_data = boost::shared_array<uint8_t>(new uint8_t[packet_size]);
+ xport->push_back_recv_packet(packet_data, packet_size);
+
+ buffs.push_back(xport->get_recv_buff(timeout_ms));
+ BOOST_CHECK(buffs.back());
+ }
+
+ // Simulate a timeout by getting a buffer without queueing a data array
+ BOOST_CHECK(!xport->get_recv_buff(timeout_ms));
+
+ for (size_t i = 0; i < num_frames / 2; i++) {
+ size_t packet_size = sizeof(uint8_t);
+ auto packet_data = boost::shared_array<uint8_t>(new uint8_t[packet_size]);
+ xport->push_back_recv_packet(packet_data, packet_size);
+
+ buffs.push_back(xport->get_recv_buff(timeout_ms));
+ BOOST_CHECK(buffs.back());
+ }
+
+ for (auto& buff : buffs) {
+ xport->release_recv_buff(std::move(buff));
+ BOOST_CHECK(!buff);
+ }
+ }
+}