blob: 6f6deee3ac08b9b3b8c0747363d8b00fc109cd09 [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
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()
44 : transfer_thread_(chunk_buffer_, encode_buffer_),
45 system_thread_(TransferThreadOptions(), transfer_thread_),
46 max_parameters_(chunk_buffer_.size(), chunk_buffer_.size()) {}
47
48 ~TransferThreadTest() {
49 transfer_thread_.Terminate();
50 system_thread_.join();
51 }
52
53 protected:
54 transfer::Thread<1, 1> transfer_thread_;
55 thread::Thread system_thread_;
56 std::array<std::byte, 64> chunk_buffer_;
57 std::array<std::byte, 64> encode_buffer_;
58 internal::TransferParameters max_parameters_;
59
60 rpc::RawClientTestContext<> rpc_client_context_;
61};
62
63class SimpleReadTransfer final : public ReadOnlyHandler {
64 public:
65 SimpleReadTransfer(uint32_t transfer_id, ConstByteSpan data)
66 : ReadOnlyHandler(transfer_id),
67 prepare_read_called(false),
68 finalize_read_called(false),
69 finalize_read_status(Status::Unknown()),
70 reader_(data) {}
71
72 Status PrepareRead() final {
73 PW_CHECK_OK(reader_.Seek(0));
74 set_reader(reader_);
75 prepare_read_called = true;
76 return OkStatus();
77 }
78
79 void FinalizeRead(Status status) final {
80 finalize_read_called = true;
81 finalize_read_status = status;
82 }
83
84 bool prepare_read_called;
85 bool finalize_read_called;
86 Status finalize_read_status;
87
88 private:
89 stream::MemoryReader reader_;
90};
91
92constexpr auto kData = bytes::Initialized<32>([](size_t i) { return i; });
93
94TEST_F(TransferThreadTest, AddTransferHandler) {
95 PW_RAW_TEST_METHOD_CONTEXT(TransferService, Read) ctx(transfer_thread_, 512);
96
97 auto reader_writer = ctx.reader_writer();
98 transfer_thread_.SetServerReadStream(reader_writer);
99
100 SimpleReadTransfer handler(3, kData);
101 transfer_thread_.AddTransferHandler(handler);
102
103 transfer_thread_.StartServerTransfer(internal::TransferType::kTransmit,
104 3,
105 3,
106 max_parameters_,
107 std::chrono::seconds(2),
108 0);
109
110 transfer_thread_.WaitUntilEventIsProcessed();
111
112 EXPECT_TRUE(handler.prepare_read_called);
113}
114
115TEST_F(TransferThreadTest, RemoveTransferHandler) {
116 PW_RAW_TEST_METHOD_CONTEXT(TransferService, Read) ctx(transfer_thread_, 512);
117
118 auto reader_writer = ctx.reader_writer();
119 transfer_thread_.SetServerReadStream(reader_writer);
120
121 SimpleReadTransfer handler(3, kData);
122 transfer_thread_.AddTransferHandler(handler);
123 transfer_thread_.RemoveTransferHandler(handler);
124
125 transfer_thread_.StartServerTransfer(internal::TransferType::kTransmit,
126 3,
127 3,
128 max_parameters_,
129 std::chrono::seconds(2),
130 0);
131
132 transfer_thread_.WaitUntilEventIsProcessed();
133
134 EXPECT_FALSE(handler.prepare_read_called);
135
136 ASSERT_EQ(ctx.total_responses(), 1u);
137 auto chunk = DecodeChunk(ctx.response());
138 EXPECT_EQ(chunk.transfer_id, 3u);
139 ASSERT_TRUE(chunk.status.has_value());
140 EXPECT_EQ(chunk.status.value(), Status::NotFound());
141}
142
143// TODO(hepler): Re-enable once ASAN issue is fixed.
144TEST_F(TransferThreadTest, DISABLED_ProcessChunk_SendsWindow) {
145 PW_RAW_TEST_METHOD_CONTEXT(TransferService, Read) ctx(transfer_thread_, 512);
146
147 auto reader_writer = ctx.reader_writer();
148
149 rpc::test::WaitForPackets(ctx.output(), 2, [&] {
150 transfer_thread_.SetServerReadStream(reader_writer);
151
152 SimpleReadTransfer handler(3, kData);
153 transfer_thread_.AddTransferHandler(handler);
154
155 transfer_thread_.StartServerTransfer(internal::TransferType::kTransmit,
156 3,
157 3,
158 max_parameters_,
159 std::chrono::seconds(2),
160 0);
161
162 // Malformed transfer parameters chunk without a pending_bytes field.
163 transfer_thread_.ProcessServerChunk(
164 EncodeChunk({.transfer_id = 3,
165 .window_end_offset = 16,
166 .pending_bytes = 16,
167 .max_chunk_size_bytes = 8,
168 .offset = 0,
169 .type = Chunk::Type::kParametersRetransmit}));
170 });
171
172 ASSERT_EQ(ctx.total_responses(), 2u);
173 auto chunk = DecodeChunk(ctx.responses()[0]);
174 EXPECT_EQ(chunk.transfer_id, 3u);
175 EXPECT_EQ(chunk.offset, 0u);
176 EXPECT_EQ(chunk.data.size(), 8u);
177 EXPECT_EQ(std::memcmp(chunk.data.data(), kData.data(), chunk.data.size()), 0);
178
179 chunk = DecodeChunk(ctx.responses()[1]);
180 EXPECT_EQ(chunk.transfer_id, 3u);
181 EXPECT_EQ(chunk.offset, 8u);
182 EXPECT_EQ(chunk.data.size(), 8u);
183 EXPECT_EQ(std::memcmp(chunk.data.data(), kData.data() + 8, chunk.data.size()),
184 0);
185}
186
187// TODO(hepler): Re-enable once ASAN issue is fixed.
188TEST_F(TransferThreadTest, DISABLED_ProcessChunk_Malformed) {
189 PW_RAW_TEST_METHOD_CONTEXT(TransferService, Read) ctx(transfer_thread_, 512);
190
191 auto reader_writer = ctx.reader_writer();
192 rpc::test::WaitForPackets(ctx.output(), 1, [&] {
193 transfer_thread_.SetServerReadStream(reader_writer);
194
195 SimpleReadTransfer handler(3, kData);
196 transfer_thread_.AddTransferHandler(handler);
197
198 transfer_thread_.StartServerTransfer(internal::TransferType::kTransmit,
199 3,
200 3,
201 max_parameters_,
202 std::chrono::seconds(2),
203 0);
204
205 // Malformed transfer parameters chunk without a pending_bytes field.
206 transfer_thread_.ProcessServerChunk(EncodeChunk({.transfer_id = 3}));
207 });
208 ASSERT_EQ(ctx.total_responses(), 1u);
209 auto chunk = DecodeChunk(ctx.response());
210 EXPECT_EQ(chunk.transfer_id, 3u);
211 ASSERT_TRUE(chunk.status.has_value());
212 EXPECT_EQ(chunk.status.value(), Status::InvalidArgument());
213}
214
215} // namespace
216} // namespace pw::transfer::test