diff options
author | Ciro Nishiguchi <ciro.nishiguchi@ni.com> | 2019-03-27 17:40:37 -0500 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2019-11-26 11:49:18 -0800 |
commit | 888d710cf51b7fa886b0c83c999128160f2ba176 (patch) | |
tree | 3b689c851422928de6704f766c7d0d25eac5673f /host/tests | |
parent | 9c2d90292b9a613fb290c5a04e99014d1a9118ab (diff) | |
download | uhd-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.txt | 1 | ||||
-rw-r--r-- | host/tests/common/mock_link.hpp | 256 | ||||
-rw-r--r-- | host/tests/link_test.cpp | 162 |
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); + } + } +} |