blob: 9fba333019c999feeeb0effc8c2ac80a97091b60 [file] [log] [blame]
Alexei Frolov22ee1142022-02-03 13:59:01 -08001// Copyright 2022 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
15#include "pw_transfer/transfer_thread.h"
16
17#include "gtest/gtest.h"
18#include "pw_assert/check.h"
19#include "pw_bytes/array.h"
20#include "pw_rpc/raw/client_testing.h"
21#include "pw_rpc/raw/test_method_context.h"
Dennis Kormalev801d66a2022-06-24 18:46:32 +000022#include "pw_rpc/test_helpers.h"
Alexei Frolov22ee1142022-02-03 13:59:01 -080023#include "pw_thread/thread.h"
24#include "pw_thread_stl/options.h"
25#include "pw_transfer/handler.h"
26#include "pw_transfer/transfer.h"
27#include "pw_transfer/transfer.raw_rpc.pb.h"
28#include "pw_transfer_private/chunk_testing.h"
29
30namespace pw::transfer::test {
31namespace {
32
33using internal::Chunk;
34
35// TODO(frolv): Have a generic way to obtain a thread for testing on any system.
36thread::Options& TransferThreadOptions() {
37 static thread::stl::Options options;
38 return options;
39}
40
41class TransferThreadTest : public ::testing::Test {
42 public:
43 TransferThreadTest()
Wyatt Hepler9639d1c2022-02-08 22:47:28 -080044 : ctx_(transfer_thread_, 512),
Alexei Frolov7278d992022-03-09 09:05:42 -080045 max_parameters_(chunk_buffer_.size(),
46 chunk_buffer_.size(),
47 cfg::kDefaultExtendWindowDivisor),
Wyatt Hepler9639d1c2022-02-08 22:47:28 -080048 transfer_thread_(chunk_buffer_, encode_buffer_),
49 system_thread_(TransferThreadOptions(), transfer_thread_) {}
Alexei Frolov22ee1142022-02-03 13:59:01 -080050
Ted Pudlik26c7a132022-06-29 21:10:34 +000051 ~TransferThreadTest() override {
Alexei Frolov22ee1142022-02-03 13:59:01 -080052 transfer_thread_.Terminate();
53 system_thread_.join();
54 }
55
56 protected:
Wyatt Hepler9639d1c2022-02-08 22:47:28 -080057 PW_RAW_TEST_METHOD_CONTEXT(TransferService, Read) ctx_;
58
Alexei Frolov22ee1142022-02-03 13:59:01 -080059 std::array<std::byte, 64> chunk_buffer_;
60 std::array<std::byte, 64> encode_buffer_;
Alexei Frolov22ee1142022-02-03 13:59:01 -080061
62 rpc::RawClientTestContext<> rpc_client_context_;
Wyatt Hepler9639d1c2022-02-08 22:47:28 -080063 internal::TransferParameters max_parameters_;
64
65 transfer::Thread<1, 1> transfer_thread_;
66
67 thread::Thread system_thread_;
Alexei Frolov22ee1142022-02-03 13:59:01 -080068};
69
70class SimpleReadTransfer final : public ReadOnlyHandler {
71 public:
Alexei Frolov5ceb57e2022-03-28 11:21:03 -070072 SimpleReadTransfer(uint32_t session_id, ConstByteSpan data)
73 : ReadOnlyHandler(session_id),
Alexei Frolov22ee1142022-02-03 13:59:01 -080074 prepare_read_called(false),
75 finalize_read_called(false),
76 finalize_read_status(Status::Unknown()),
77 reader_(data) {}
78
79 Status PrepareRead() final {
80 PW_CHECK_OK(reader_.Seek(0));
81 set_reader(reader_);
82 prepare_read_called = true;
83 return OkStatus();
84 }
85
86 void FinalizeRead(Status status) final {
87 finalize_read_called = true;
88 finalize_read_status = status;
89 }
90
91 bool prepare_read_called;
92 bool finalize_read_called;
93 Status finalize_read_status;
94
95 private:
96 stream::MemoryReader reader_;
97};
98
99constexpr auto kData = bytes::Initialized<32>([](size_t i) { return i; });
100
101TEST_F(TransferThreadTest, AddTransferHandler) {
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800102 auto reader_writer = ctx_.reader_writer();
Alexei Frolov22ee1142022-02-03 13:59:01 -0800103 transfer_thread_.SetServerReadStream(reader_writer);
104
105 SimpleReadTransfer handler(3, kData);
106 transfer_thread_.AddTransferHandler(handler);
107
108 transfer_thread_.StartServerTransfer(internal::TransferType::kTransmit,
109 3,
110 3,
111 max_parameters_,
112 std::chrono::seconds(2),
113 0);
114
115 transfer_thread_.WaitUntilEventIsProcessed();
116
117 EXPECT_TRUE(handler.prepare_read_called);
Erik Gillingfaf42d62022-04-26 22:34:13 +0000118
119 transfer_thread_.RemoveTransferHandler(handler);
Alexei Frolov22ee1142022-02-03 13:59:01 -0800120}
121
122TEST_F(TransferThreadTest, RemoveTransferHandler) {
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800123 auto reader_writer = ctx_.reader_writer();
Alexei Frolov22ee1142022-02-03 13:59:01 -0800124 transfer_thread_.SetServerReadStream(reader_writer);
125
126 SimpleReadTransfer handler(3, kData);
127 transfer_thread_.AddTransferHandler(handler);
128 transfer_thread_.RemoveTransferHandler(handler);
129
130 transfer_thread_.StartServerTransfer(internal::TransferType::kTransmit,
131 3,
132 3,
133 max_parameters_,
134 std::chrono::seconds(2),
135 0);
136
137 transfer_thread_.WaitUntilEventIsProcessed();
138
139 EXPECT_FALSE(handler.prepare_read_called);
140
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800141 ASSERT_EQ(ctx_.total_responses(), 1u);
142 auto chunk = DecodeChunk(ctx_.response());
Alexei Frolov42efd502022-04-26 18:45:35 -0700143 EXPECT_EQ(chunk.session_id(), 3u);
144 ASSERT_TRUE(chunk.status().has_value());
145 EXPECT_EQ(chunk.status().value(), Status::NotFound());
Erik Gillingfaf42d62022-04-26 22:34:13 +0000146
147 transfer_thread_.RemoveTransferHandler(handler);
Alexei Frolov22ee1142022-02-03 13:59:01 -0800148}
149
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800150TEST_F(TransferThreadTest, ProcessChunk_SendsWindow) {
151 auto reader_writer = ctx_.reader_writer();
152 transfer_thread_.SetServerReadStream(reader_writer);
Alexei Frolov22ee1142022-02-03 13:59:01 -0800153
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800154 SimpleReadTransfer handler(3, kData);
155 transfer_thread_.AddTransferHandler(handler);
Alexei Frolov22ee1142022-02-03 13:59:01 -0800156
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800157 transfer_thread_.StartServerTransfer(internal::TransferType::kTransmit,
158 3,
159 3,
160 max_parameters_,
161 std::chrono::seconds(2),
162 0);
Alexei Frolov22ee1142022-02-03 13:59:01 -0800163
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800164 rpc::test::WaitForPackets(ctx_.output(), 2, [this] {
Alexei Frolov22ee1142022-02-03 13:59:01 -0800165 transfer_thread_.ProcessServerChunk(
Alexei Frolov42efd502022-04-26 18:45:35 -0700166 EncodeChunk(Chunk(internal::ProtocolVersion::kLegacy,
167 Chunk::Type::kParametersRetransmit)
168 .set_session_id(3)
169 .set_window_end_offset(16)
170 .set_max_chunk_size_bytes(8)
171 .set_offset(0)));
Alexei Frolov22ee1142022-02-03 13:59:01 -0800172 });
173
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800174 ASSERT_EQ(ctx_.total_responses(), 2u);
175 auto chunk = DecodeChunk(ctx_.responses()[0]);
Alexei Frolov42efd502022-04-26 18:45:35 -0700176 EXPECT_EQ(chunk.session_id(), 3u);
177 EXPECT_EQ(chunk.offset(), 0u);
178 EXPECT_EQ(chunk.payload().size(), 8u);
179 EXPECT_EQ(
180 std::memcmp(chunk.payload().data(), kData.data(), chunk.payload().size()),
181 0);
Alexei Frolov22ee1142022-02-03 13:59:01 -0800182
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800183 chunk = DecodeChunk(ctx_.responses()[1]);
Alexei Frolov42efd502022-04-26 18:45:35 -0700184 EXPECT_EQ(chunk.session_id(), 3u);
185 EXPECT_EQ(chunk.offset(), 8u);
186 EXPECT_EQ(chunk.payload().size(), 8u);
187 EXPECT_EQ(
188 std::memcmp(
189 chunk.payload().data(), kData.data() + 8, chunk.payload().size()),
190 0);
Erik Gillingfaf42d62022-04-26 22:34:13 +0000191
192 transfer_thread_.RemoveTransferHandler(handler);
Alexei Frolov22ee1142022-02-03 13:59:01 -0800193}
194
Alexei Frolov9af45f52022-08-03 18:01:22 +0000195TEST_F(TransferThreadTest, StartTransferExhausted_Server) {
196 auto reader_writer = ctx_.reader_writer();
197 transfer_thread_.SetServerReadStream(reader_writer);
198
199 SimpleReadTransfer handler3(3, kData);
200 SimpleReadTransfer handler4(4, kData);
201 transfer_thread_.AddTransferHandler(handler3);
202 transfer_thread_.AddTransferHandler(handler4);
203
204 transfer_thread_.StartServerTransfer(internal::TransferType::kTransmit,
205 3,
206 3,
207 max_parameters_,
208 std::chrono::seconds(2),
209 0);
210 transfer_thread_.WaitUntilEventIsProcessed();
211
212 // First transfer starts correctly.
213 EXPECT_TRUE(handler3.prepare_read_called);
214 EXPECT_FALSE(handler4.prepare_read_called);
215
216 // Try to start a simultaneous transfer to resource 4, for which the thread
217 // does not have an available context.
218 transfer_thread_.StartServerTransfer(internal::TransferType::kTransmit,
219 4,
220 4,
221 max_parameters_,
222 std::chrono::seconds(2),
223 0);
224 transfer_thread_.WaitUntilEventIsProcessed();
225
226 EXPECT_FALSE(handler4.prepare_read_called);
227
228 ASSERT_EQ(ctx_.total_responses(), 1u);
229 auto chunk = DecodeChunk(ctx_.response());
230 EXPECT_EQ(chunk.session_id(), 4u);
231 ASSERT_TRUE(chunk.status().has_value());
232 EXPECT_EQ(chunk.status().value(), Status::ResourceExhausted());
233
234 transfer_thread_.RemoveTransferHandler(handler3);
235 transfer_thread_.RemoveTransferHandler(handler4);
236}
237
238TEST_F(TransferThreadTest, StartTransferExhausted_Client) {
239 rpc::RawClientReaderWriter read_stream = pw_rpc::raw::Transfer::Read(
240 rpc_client_context_.client(), rpc_client_context_.channel().id());
241 transfer_thread_.SetClientReadStream(read_stream);
242
243 Status status3 = Status::Unknown();
244 Status status4 = Status::Unknown();
245
246 stream::MemoryWriterBuffer<16> buffer3;
247 stream::MemoryWriterBuffer<16> buffer4;
248
249 transfer_thread_.StartClientTransfer(
250 internal::TransferType::kReceive,
251 3,
252 3,
253 &buffer3,
254 max_parameters_,
255 [&status3](Status status) { status3 = status; },
256 std::chrono::seconds(2),
257 0);
258 transfer_thread_.WaitUntilEventIsProcessed();
259
260 EXPECT_EQ(status3, Status::Unknown());
261 EXPECT_EQ(status4, Status::Unknown());
262
263 // Try to start a simultaneous transfer to resource 4, for which the thread
264 // does not have an available context.
265 transfer_thread_.StartClientTransfer(
266 internal::TransferType::kReceive,
267 4,
268 4,
269 &buffer4,
270 max_parameters_,
271 [&status4](Status status) { status4 = status; },
272 std::chrono::seconds(2),
273 0);
274 transfer_thread_.WaitUntilEventIsProcessed();
275
276 EXPECT_EQ(status3, Status::Unknown());
277 EXPECT_EQ(status4, Status::ResourceExhausted());
278}
279
Alexei Frolov22ee1142022-02-03 13:59:01 -0800280} // namespace
281} // namespace pw::transfer::test