blob: 7890041ba5f247a5fb420adbc377a2da24f9736c [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"
22#include "pw_rpc/thread_testing.h"
23#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
Wyatt Hepler70a62102022-02-11 15:14:35 -080035PW_MODIFY_DIAGNOSTICS_PUSH();
36PW_MODIFY_DIAGNOSTIC(ignored, "-Wmissing-field-initializers");
37
Alexei Frolov22ee1142022-02-03 13:59:01 -080038// TODO(frolv): Have a generic way to obtain a thread for testing on any system.
39thread::Options& TransferThreadOptions() {
40 static thread::stl::Options options;
41 return options;
42}
43
44class TransferThreadTest : public ::testing::Test {
45 public:
46 TransferThreadTest()
Wyatt Hepler9639d1c2022-02-08 22:47:28 -080047 : ctx_(transfer_thread_, 512),
Alexei Frolov7278d992022-03-09 09:05:42 -080048 max_parameters_(chunk_buffer_.size(),
49 chunk_buffer_.size(),
50 cfg::kDefaultExtendWindowDivisor),
Wyatt Hepler9639d1c2022-02-08 22:47:28 -080051 transfer_thread_(chunk_buffer_, encode_buffer_),
52 system_thread_(TransferThreadOptions(), transfer_thread_) {}
Alexei Frolov22ee1142022-02-03 13:59:01 -080053
54 ~TransferThreadTest() {
55 transfer_thread_.Terminate();
56 system_thread_.join();
57 }
58
59 protected:
Wyatt Hepler9639d1c2022-02-08 22:47:28 -080060 PW_RAW_TEST_METHOD_CONTEXT(TransferService, Read) ctx_;
61
Alexei Frolov22ee1142022-02-03 13:59:01 -080062 std::array<std::byte, 64> chunk_buffer_;
63 std::array<std::byte, 64> encode_buffer_;
Alexei Frolov22ee1142022-02-03 13:59:01 -080064
65 rpc::RawClientTestContext<> rpc_client_context_;
Wyatt Hepler9639d1c2022-02-08 22:47:28 -080066 internal::TransferParameters max_parameters_;
67
68 transfer::Thread<1, 1> transfer_thread_;
69
70 thread::Thread system_thread_;
Alexei Frolov22ee1142022-02-03 13:59:01 -080071};
72
73class SimpleReadTransfer final : public ReadOnlyHandler {
74 public:
75 SimpleReadTransfer(uint32_t transfer_id, ConstByteSpan data)
76 : ReadOnlyHandler(transfer_id),
77 prepare_read_called(false),
78 finalize_read_called(false),
79 finalize_read_status(Status::Unknown()),
80 reader_(data) {}
81
82 Status PrepareRead() final {
83 PW_CHECK_OK(reader_.Seek(0));
84 set_reader(reader_);
85 prepare_read_called = true;
86 return OkStatus();
87 }
88
89 void FinalizeRead(Status status) final {
90 finalize_read_called = true;
91 finalize_read_status = status;
92 }
93
94 bool prepare_read_called;
95 bool finalize_read_called;
96 Status finalize_read_status;
97
98 private:
99 stream::MemoryReader reader_;
100};
101
102constexpr auto kData = bytes::Initialized<32>([](size_t i) { return i; });
103
104TEST_F(TransferThreadTest, AddTransferHandler) {
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800105 auto reader_writer = ctx_.reader_writer();
Alexei Frolov22ee1142022-02-03 13:59:01 -0800106 transfer_thread_.SetServerReadStream(reader_writer);
107
108 SimpleReadTransfer handler(3, kData);
109 transfer_thread_.AddTransferHandler(handler);
110
111 transfer_thread_.StartServerTransfer(internal::TransferType::kTransmit,
112 3,
113 3,
114 max_parameters_,
115 std::chrono::seconds(2),
116 0);
117
118 transfer_thread_.WaitUntilEventIsProcessed();
119
120 EXPECT_TRUE(handler.prepare_read_called);
121}
122
123TEST_F(TransferThreadTest, RemoveTransferHandler) {
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800124 auto reader_writer = ctx_.reader_writer();
Alexei Frolov22ee1142022-02-03 13:59:01 -0800125 transfer_thread_.SetServerReadStream(reader_writer);
126
127 SimpleReadTransfer handler(3, kData);
128 transfer_thread_.AddTransferHandler(handler);
129 transfer_thread_.RemoveTransferHandler(handler);
130
131 transfer_thread_.StartServerTransfer(internal::TransferType::kTransmit,
132 3,
133 3,
134 max_parameters_,
135 std::chrono::seconds(2),
136 0);
137
138 transfer_thread_.WaitUntilEventIsProcessed();
139
140 EXPECT_FALSE(handler.prepare_read_called);
141
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800142 ASSERT_EQ(ctx_.total_responses(), 1u);
143 auto chunk = DecodeChunk(ctx_.response());
Alexei Frolov22ee1142022-02-03 13:59:01 -0800144 EXPECT_EQ(chunk.transfer_id, 3u);
145 ASSERT_TRUE(chunk.status.has_value());
146 EXPECT_EQ(chunk.status.value(), Status::NotFound());
147}
148
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800149TEST_F(TransferThreadTest, ProcessChunk_SendsWindow) {
150 auto reader_writer = ctx_.reader_writer();
151 transfer_thread_.SetServerReadStream(reader_writer);
Alexei Frolov22ee1142022-02-03 13:59:01 -0800152
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800153 SimpleReadTransfer handler(3, kData);
154 transfer_thread_.AddTransferHandler(handler);
Alexei Frolov22ee1142022-02-03 13:59:01 -0800155
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800156 transfer_thread_.StartServerTransfer(internal::TransferType::kTransmit,
157 3,
158 3,
159 max_parameters_,
160 std::chrono::seconds(2),
161 0);
Alexei Frolov22ee1142022-02-03 13:59:01 -0800162
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800163 rpc::test::WaitForPackets(ctx_.output(), 2, [this] {
Alexei Frolov22ee1142022-02-03 13:59:01 -0800164 // Malformed transfer parameters chunk without a pending_bytes field.
165 transfer_thread_.ProcessServerChunk(
166 EncodeChunk({.transfer_id = 3,
167 .window_end_offset = 16,
168 .pending_bytes = 16,
169 .max_chunk_size_bytes = 8,
170 .offset = 0,
171 .type = Chunk::Type::kParametersRetransmit}));
172 });
173
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800174 ASSERT_EQ(ctx_.total_responses(), 2u);
175 auto chunk = DecodeChunk(ctx_.responses()[0]);
Alexei Frolov22ee1142022-02-03 13:59:01 -0800176 EXPECT_EQ(chunk.transfer_id, 3u);
177 EXPECT_EQ(chunk.offset, 0u);
178 EXPECT_EQ(chunk.data.size(), 8u);
179 EXPECT_EQ(std::memcmp(chunk.data.data(), kData.data(), chunk.data.size()), 0);
180
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800181 chunk = DecodeChunk(ctx_.responses()[1]);
Alexei Frolov22ee1142022-02-03 13:59:01 -0800182 EXPECT_EQ(chunk.transfer_id, 3u);
183 EXPECT_EQ(chunk.offset, 8u);
184 EXPECT_EQ(chunk.data.size(), 8u);
185 EXPECT_EQ(std::memcmp(chunk.data.data(), kData.data() + 8, chunk.data.size()),
186 0);
187}
188
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800189TEST_F(TransferThreadTest, ProcessChunk_Malformed) {
190 auto reader_writer = ctx_.reader_writer();
191 transfer_thread_.SetServerReadStream(reader_writer);
Alexei Frolov22ee1142022-02-03 13:59:01 -0800192
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800193 SimpleReadTransfer handler(3, kData);
194 transfer_thread_.AddTransferHandler(handler);
Alexei Frolov22ee1142022-02-03 13:59:01 -0800195
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800196 rpc::test::WaitForPackets(ctx_.output(), 1, [this] {
Alexei Frolov22ee1142022-02-03 13:59:01 -0800197 transfer_thread_.StartServerTransfer(internal::TransferType::kTransmit,
198 3,
199 3,
200 max_parameters_,
201 std::chrono::seconds(2),
202 0);
203
204 // Malformed transfer parameters chunk without a pending_bytes field.
205 transfer_thread_.ProcessServerChunk(EncodeChunk({.transfer_id = 3}));
206 });
Wyatt Hepler9639d1c2022-02-08 22:47:28 -0800207
208 ASSERT_EQ(ctx_.total_responses(), 1u);
209 auto chunk = DecodeChunk(ctx_.response());
Alexei Frolov22ee1142022-02-03 13:59:01 -0800210 EXPECT_EQ(chunk.transfer_id, 3u);
211 ASSERT_TRUE(chunk.status.has_value());
212 EXPECT_EQ(chunk.status.value(), Status::InvalidArgument());
213}
214
Wyatt Hepler70a62102022-02-11 15:14:35 -0800215PW_MODIFY_DIAGNOSTICS_POP();
216
Alexei Frolov22ee1142022-02-03 13:59:01 -0800217} // namespace
218} // namespace pw::transfer::test