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