aboutsummaryrefslogtreecommitdiffstats
path: root/host/tests/common/mock_link.hpp
blob: b2c5be974ee45a8bf5182a80ea6040d2df29c99f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
//
// 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());
            }
        }
    }

    /*!
     * Return the number of packets stored in the mock link.
     */
    size_t get_num_packets() const
    {
        return _tx_mems.size();
    }

    /*!
     * 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;
    }

    adapter_id_t get_send_adapter_id() const override
    {
        return NULL_ADAPTER_ID;
    }

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

    adapter_id_t get_recv_adapter_id() const override
    {
        return NULL_ADAPTER_ID;
    }

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*/