1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <vector>
18
19 #include <gmock/gmock.h>
20
21 #include <application.h>
22 #include <nos/transport.h>
23
24 #include "crc16.h"
25
26 using ::testing::_;
27 using ::testing::Args;
28 using ::testing::DoAll;
29 using ::testing::Eq;
30 using ::testing::ElementsAreArray;
31 using ::testing::InSequence;
32 using ::testing::IsNull;
33 using ::testing::Return;
34 using ::testing::SetArrayArgument;
35 using ::testing::StrictMock;
36
37 namespace {
38
39 struct Device {
40 virtual int Read(uint32_t command, uint8_t* buf, uint32_t len) = 0;
41 virtual int Write(uint32_t command, const uint8_t* buf, uint32_t len) = 0;
42 virtual int WaitForInterrupt(int msecs) = 0;
43 virtual int Reset() = 0;
44 };
45
46 struct MockDevice : public Device {
47 MOCK_METHOD3(Read, int(uint32_t command, uint8_t* buf, uint32_t len));
48 MOCK_METHOD3(Write, int(uint32_t command, const uint8_t* buf, uint32_t len));
49 MOCK_METHOD1(WaitForInterrupt, int(int msecs));
50 MOCK_METHOD0(Reset, int());
51 };
52
53 // We want to closely examine the interactions with the device to make it a
54 // strict mock
55 using CtxType = StrictMock<MockDevice>;
56
57 // Forward calls onto the mock
read_datagram(void * ctx,uint32_t command,uint8_t * buf,uint32_t len)58 int read_datagram(void* ctx, uint32_t command, uint8_t* buf, uint32_t len) {
59 return reinterpret_cast<CtxType*>(ctx)->Read(command, buf, len);
60 }
write_datagram(void * ctx,uint32_t command,const uint8_t * buf,uint32_t len)61 int write_datagram(void* ctx, uint32_t command, const uint8_t* buf, uint32_t len) {
62 return reinterpret_cast<CtxType*>(ctx)->Write(command, buf, len);
63 }
wait_for_interrupt(void * ctx,int msecs)64 int wait_for_interrupt(void* ctx, int msecs) {
65 return reinterpret_cast<CtxType*>(ctx)->WaitForInterrupt(msecs);
66 }
reset(void * ctx)67 int reset(void* ctx) {
68 return reinterpret_cast<CtxType*>(ctx)->Reset();
69 }
close_device(void * ctx)70 void close_device(void* ctx) {
71 delete reinterpret_cast<CtxType*>(ctx);
72 }
73
74 // Implement the datagram API that calls a mock.
75 extern "C" {
nos_device_open(const char * device_name,struct nos_device * dev)76 int nos_device_open(const char* device_name, struct nos_device* dev) {
77 EXPECT_THAT(device_name, IsNull());
78 dev->ctx = new CtxType;
79 dev->ops.read = read_datagram;
80 dev->ops.write = write_datagram;
81 dev->ops.wait_for_interrupt = wait_for_interrupt;
82 dev->ops.reset = reset;
83 dev->ops.close = close_device;
84 return 0;
85 }
86 }
87
88 // Test fixture that sets up the mocked device.
89 struct TransportTest : public ::testing::Test {
SetUp__anonf0c384500111::TransportTest90 virtual void SetUp() override {
91 nos_device_open(nullptr, &dev_);
92 mock_dev_ = reinterpret_cast<CtxType*>(dev_.ctx);
93 }
TearDown__anonf0c384500111::TransportTest94 virtual void TearDown() override {
95 dev_.ops.close(dev_.ctx);
96 }
97
dev__anonf0c384500111::TransportTest98 nos_device* dev() { return &dev_; }
mock_dev__anonf0c384500111::TransportTest99 CtxType& mock_dev() { return *mock_dev_; }
100
101 private:
102 nos_device dev_;
103 CtxType* mock_dev_;
104 };
105
command_crc(uint32_t command,const uint8_t * args,uint16_t args_len,const transport_command_info * command_info)106 uint16_t command_crc(uint32_t command, const uint8_t* args, uint16_t args_len,
107 const transport_command_info* command_info) {
108 uint16_t crc = crc16(&args_len, sizeof(args_len));
109 crc = crc16_update(args, args_len, crc);
110 crc = crc16_update(&command, sizeof(command), crc);
111 crc = crc16_update(command_info, sizeof(*command_info), crc);
112 return htole16(crc);
113 }
114
115 } // namespace
116
117 /* Actions to return mock data */
118
119 #define READ_UNSET 0xdf
120
ACTION(ReadStatusV0_Idle)121 ACTION(ReadStatusV0_Idle) {
122 transport_status* status = (transport_status*)arg1;
123 memset(status, READ_UNSET, sizeof(*status));
124 status->status = APP_STATUS_IDLE;
125 status->reply_len = 0;
126 }
127
ACTION(ReadStatusV1_Idle)128 ACTION(ReadStatusV1_Idle) {
129 transport_status* status = (transport_status*)arg1;
130 memset(status, READ_UNSET, sizeof(*status));
131 status->status = APP_STATUS_IDLE;
132 status->reply_len = 0;
133 status->length = sizeof(transport_status);
134 status->version = TRANSPORT_V1;
135 status->flags = 0;
136 status->crc = STATUS_CRC_FOR_IDLE;
137 status->reply_crc = 0;
138 }
139
ACTION(ReadStatusV1_IdleWithBadCrc)140 ACTION(ReadStatusV1_IdleWithBadCrc) {
141 transport_status* status = (transport_status*)arg1;
142 memset(status, READ_UNSET, sizeof(*status));
143 status->status = APP_STATUS_IDLE;
144 status->reply_len = 0;
145 status->length = sizeof(transport_status);
146 status->version = TRANSPORT_V1;
147 status->flags = 0;
148 status->crc = STATUS_CRC_FOR_IDLE + 1; // <- wrong!
149 status->reply_crc = 0;
150 }
151
ACTION(ReadStatusV1_Working)152 ACTION(ReadStatusV1_Working) {
153 transport_status* status = (transport_status*)arg1;
154 memset(status, READ_UNSET, sizeof(*status));
155 status->status = APP_STATUS_IDLE;
156 status->reply_len = 0;
157 status->length = sizeof(transport_status);
158 status->version = TRANSPORT_V1;
159 status->flags = STATUS_FLAG_WORKING;
160 status->crc = STATUS_CRC_FOR_WORKING;
161 status->reply_crc = 0;
162 }
163
ACTION_P(ReadStatusV0_DoneWithData,reply_len)164 ACTION_P(ReadStatusV0_DoneWithData, reply_len) {
165 transport_status* status = (transport_status*)arg1;
166 memset(status, READ_UNSET, sizeof(*status));
167 status->status = APP_STATUS_DONE | APP_SUCCESS;
168 status->reply_len = reply_len;
169 }
170
ACTION_P2(ReadStatusV1_DoneWithData,reply,reply_len)171 ACTION_P2(ReadStatusV1_DoneWithData, reply, reply_len) {
172 transport_status* status = (transport_status*)arg1;
173 memset(status, READ_UNSET, sizeof(*status));
174 status->status = APP_STATUS_DONE | APP_SUCCESS;
175 status->reply_len = reply_len;
176 status->length = sizeof(transport_status);
177 status->version = TRANSPORT_V1;
178 status->flags = 0;
179 status->reply_crc = crc16(reply, reply_len);
180 status->crc = 0;
181 status->crc = crc16(status, status->length);
182 }
183
ACTION(ReadStatusV1_BadCrc)184 ACTION(ReadStatusV1_BadCrc) {
185 transport_status* status = (transport_status*)arg1;
186 memset(status, READ_UNSET, sizeof(*status));
187 status->status = APP_STATUS_DONE | APP_ERROR_CHECKSUM;
188 status->reply_len = 0;
189 status->length = sizeof(transport_status);
190 status->version = TRANSPORT_V1;
191 status->flags = 0;
192 status->crc = 0x92c0;
193 status->reply_crc = 0;
194 }
195
ACTION(ReadStatusV42_Working)196 ACTION(ReadStatusV42_Working) {
197 memset(arg1, 0xb3, STATUS_MAX_LENGTH);
198 transport_status* status = (transport_status*)arg1;
199 status->status = APP_STATUS_IDLE;
200 status->reply_len = 0;
201 status->length = STATUS_MAX_LENGTH;
202 status->version = 42;
203 status->flags = STATUS_FLAG_WORKING;
204 status->crc = 0xf781;
205 status->reply_crc = 0;
206 }
207
ACTION_P3(ReadData,len,data,size)208 ACTION_P3(ReadData, len, data, size) {
209 memset(arg1, READ_UNSET, len);
210 memcpy(arg1, data, size);
211 }
212
213 /* Helper macros to expect datagram calls */
214
215 #define EXPECT_GET_STATUS_V0_IDLE(app_id) do { \
216 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
217 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
218 .WillOnce(DoAll(ReadStatusV0_Idle(), Return(0))); \
219 } while (0)
220
221 #define EXPECT_GET_STATUS_IDLE(app_id) do { \
222 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
223 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
224 .WillOnce(DoAll(ReadStatusV1_Idle(), Return(0))); \
225 } while (0)
226
227 #define EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id) do { \
228 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
229 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
230 .WillOnce(DoAll(ReadStatusV1_IdleWithBadCrc(), Return(0))); \
231 } while (0)
232
233 #define EXPECT_GET_STATUS_BAD_CRC(app_id) do { \
234 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
235 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
236 .WillOnce(DoAll(ReadStatusV1_BadCrc(), Return(0))); \
237 } while (0)
238
239 #define EXPECT_GET_STATUS_WORKING(app_id) do { \
240 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
241 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
242 .WillOnce(DoAll(ReadStatusV1_Working(), Return(0))); \
243 } while (0)
244
245 #define EXPECT_GET_STATUS_V0_DONE(app_id) do { \
246 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
247 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
248 .WillOnce(DoAll(ReadStatusV0_DoneWithData(0), Return(0))); \
249 } while (0)
250
251 #define EXPECT_GET_STATUS_V0_DONE_WITH_DATA(app_id, reply_len) do { \
252 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
253 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
254 .WillOnce(DoAll(ReadStatusV0_DoneWithData((reply_len)), Return(0))); \
255 } while (0)
256
257 #define EXPECT_GET_STATUS_DONE(app_id) do { \
258 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
259 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
260 .WillOnce(DoAll(ReadStatusV1_DoneWithData(nullptr, 0), Return(0))); \
261 } while (0)
262
263 #define EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, reply, reply_len) do { \
264 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
265 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
266 .WillOnce(DoAll(ReadStatusV1_DoneWithData((reply), (reply_len)), Return(0))); \
267 } while (0)
268
269 #define EXPECT_GET_STATUS_DONE_BAD_CRC(app_id, reply, reply_len) do { \
270 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
271 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
272 .WillOnce(DoAll(ReadStatusV1_DoneBadCrc((reply), (reply_len)), Return(0))); \
273 } while (0)
274
275 #define EXPECT_SEND_DATA(app_id, args, args_len) do { \
276 const uint32_t command = CMD_ID((app_id)) | CMD_IS_DATA | CMD_TRANSPORT | CMD_PARAM((args_len)); \
277 EXPECT_CALL(mock_dev(), Write(command, _, (args_len))) \
278 .With(Args<1,2>(ElementsAreArray((uint8_t*)(args), (args_len)))) \
279 .WillOnce(Return(0)); \
280 } while (0)
281
282 #define EXPECT_GO_COMMAND(app_id, param, args, args_len, reply_len) do { \
283 const uint32_t command = CMD_ID((app_id)) | CMD_PARAM((param)); \
284 transport_command_info command_info = {}; \
285 command_info.length = sizeof(command_info); \
286 command_info.version = htole16(TRANSPORT_V1); \
287 command_info.reply_len_hint = htole16((reply_len)); \
288 command_info.crc = command_crc(command, (args), (args_len), &command_info); \
289 EXPECT_CALL(mock_dev(), Write(command, _, command_info.length)) \
290 .With(Args<1,2>(ElementsAreArray((uint8_t*)&command_info, command_info.length))) \
291 .WillOnce(Return(0)); \
292 } while (0)
293
294 #define EXPECT_RECV_DATA(app_id, len, reply, reply_len) do { \
295 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_IS_DATA | CMD_TRANSPORT; \
296 EXPECT_CALL(mock_dev(), Read(command, _, (reply_len))) \
297 .WillOnce(DoAll(ReadData((len), (reply), (reply_len)), Return(0))); \
298 } while (0)
299
300 #define EXPECT_RECV_MORE_DATA(app_id, len, reply, reply_len) do { \
301 const uint32_t command = \
302 CMD_ID((app_id)) | CMD_IS_READ | CMD_IS_DATA | CMD_MORE_TO_COME | CMD_TRANSPORT; \
303 EXPECT_CALL(mock_dev(), Read(command, _, (reply_len))) \
304 .WillOnce(DoAll(ReadData((len), (reply), (reply_len)), Return(0))); \
305 } while (0)
306
307 #define EXPECT_CLEAR_STATUS(app_id) do { \
308 const uint32_t command = CMD_ID((app_id)) | CMD_TRANSPORT; \
309 EXPECT_CALL(mock_dev(), Write(command, _, 0)) \
310 .WillOnce(Return(0)); \
311 } while (0)
312
313 /* Protocol tests */
314
TEST_F(TransportTest,WorkingAppIsBusy)315 TEST_F(TransportTest, WorkingAppIsBusy) {
316 const uint8_t app_id = 213;
317 EXPECT_GET_STATUS_WORKING(app_id);
318
319 const uint16_t param = 2;
320 uint32_t reply_len = 0;
321 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, &reply_len);
322 EXPECT_THAT(res, Eq(APP_ERROR_BUSY));
323 }
324
TEST_F(TransportTest,WorkingIsForwardCompatible)325 TEST_F(TransportTest, WorkingIsForwardCompatible) {
326 const uint8_t app_id = 25;
327 const uint32_t command = CMD_ID(app_id) | CMD_IS_READ | CMD_TRANSPORT;
328 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH))
329 .WillOnce(DoAll(ReadStatusV42_Working(), Return(0)));
330
331 const uint16_t param = 2;
332 uint32_t reply_len = 0;
333 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, &reply_len);
334 EXPECT_THAT(res, Eq(APP_ERROR_BUSY));
335 }
336
TEST_F(TransportTest,SuccessIfStatusNotClear)337 TEST_F(TransportTest, SuccessIfStatusNotClear) {
338 const uint8_t app_id = 12;
339 const uint16_t param = 2;
340 const uint8_t args[] = {1, 2, 3};
341 const uint16_t args_len = 3;
342
343 InSequence please;
344 EXPECT_GET_STATUS_BAD_CRC(app_id);
345 // Try and reset
346 EXPECT_CLEAR_STATUS(app_id);
347 // Try again
348 EXPECT_GET_STATUS_IDLE(app_id);
349 EXPECT_SEND_DATA(app_id, args, args_len);
350 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
351 EXPECT_GET_STATUS_WORKING(app_id);
352 EXPECT_GET_STATUS_DONE(app_id);
353 EXPECT_CLEAR_STATUS(app_id);
354
355 uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
356 EXPECT_THAT(res, Eq(APP_SUCCESS));
357 }
358
TEST_F(TransportTest,StatusCrcError)359 TEST_F(TransportTest, StatusCrcError) {
360 const uint8_t app_id = 53;
361 const uint16_t param = 192;
362
363 InSequence please;
364 // Try 5 times
365 EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
366 EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
367 EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
368 EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
369 EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
370
371 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, nullptr);
372 EXPECT_THAT(res, Eq(APP_ERROR_IO));
373 }
374
TEST_F(TransportTest,FailToClearStatus)375 TEST_F(TransportTest, FailToClearStatus) {
376 const uint8_t app_id = 12;
377 const uint16_t param = 2;
378 const uint8_t args[] = {1, 2, 3};
379 const uint16_t args_len = 3;
380
381 InSequence please;
382 EXPECT_GET_STATUS_BAD_CRC(app_id);
383 // Try and reset
384 EXPECT_CLEAR_STATUS(app_id);
385 // No luck
386 EXPECT_GET_STATUS_BAD_CRC(app_id);
387
388 uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
389 EXPECT_THAT(res, Eq(APP_ERROR_IO));
390 }
391
TEST_F(TransportTest,FailToClearStatusAfterStatusCrcError)392 TEST_F(TransportTest, FailToClearStatusAfterStatusCrcError) {
393 const uint8_t app_id = 53;
394 const uint16_t param = 192;
395
396 InSequence please;
397 // Try 5 times
398 EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
399 EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
400 EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
401 EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
402 EXPECT_GET_STATUS_BAD_CRC(app_id);
403 // Try and reset
404 EXPECT_CLEAR_STATUS(app_id);
405 // No luck
406 EXPECT_GET_STATUS_BAD_CRC(app_id);
407
408 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, nullptr);
409 EXPECT_THAT(res, Eq(APP_ERROR_IO));
410 }
411
TEST_F(TransportTest,RequestCrcError)412 TEST_F(TransportTest, RequestCrcError) {
413 const uint8_t app_id = 58;
414 const uint16_t param = 93;
415 const uint8_t args[] = {4, 24, 183, 255, 219};
416 const uint16_t args_len = 5;
417
418 InSequence please;
419 // Should try 5 times
420 EXPECT_GET_STATUS_IDLE(app_id);
421 EXPECT_SEND_DATA(app_id, args, args_len);
422 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
423 EXPECT_GET_STATUS_BAD_CRC(app_id);
424 // 4 more
425 EXPECT_GET_STATUS_IDLE(app_id);
426 EXPECT_SEND_DATA(app_id, args, args_len);
427 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
428 EXPECT_GET_STATUS_BAD_CRC(app_id);
429 // 3 more
430 EXPECT_GET_STATUS_IDLE(app_id);
431 EXPECT_SEND_DATA(app_id, args, args_len);
432 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
433 EXPECT_GET_STATUS_BAD_CRC(app_id);
434 // 2 more
435 EXPECT_GET_STATUS_IDLE(app_id);
436 EXPECT_SEND_DATA(app_id, args, args_len);
437 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
438 EXPECT_GET_STATUS_BAD_CRC(app_id);
439 // last one
440 EXPECT_GET_STATUS_IDLE(app_id);
441 EXPECT_SEND_DATA(app_id, args, args_len);
442 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
443 EXPECT_GET_STATUS_BAD_CRC(app_id);
444 // Clean up
445 EXPECT_CLEAR_STATUS(app_id);
446
447 uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
448 EXPECT_THAT(res, Eq(APP_ERROR_IO));
449 }
450
TEST_F(TransportTest,SuccessAfterRequestCrcError)451 TEST_F(TransportTest, SuccessAfterRequestCrcError) {
452 const uint8_t app_id = 255;
453 const uint16_t param = 163;
454 const uint8_t args[] = {42, 89, 125, 0, 83, 92, 80};
455 const uint16_t args_len = 7;
456
457 InSequence please;
458 // First request is CRC error
459 EXPECT_GET_STATUS_IDLE(app_id);
460 EXPECT_SEND_DATA(app_id, args, args_len);
461 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
462 EXPECT_GET_STATUS_BAD_CRC(app_id);
463 // The retry succeeds
464 EXPECT_GET_STATUS_IDLE(app_id);
465 EXPECT_SEND_DATA(app_id, args, args_len);
466 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
467 EXPECT_GET_STATUS_WORKING(app_id);
468 EXPECT_GET_STATUS_DONE(app_id);
469 EXPECT_CLEAR_STATUS(app_id);
470
471 uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
472 EXPECT_THAT(res, Eq(APP_SUCCESS));
473 }
474
TEST_F(TransportTest,SuccessWithoutReply)475 TEST_F(TransportTest, SuccessWithoutReply) {
476 const uint8_t app_id = 12;
477 const uint16_t param = 2;
478 const uint8_t args[] = {1, 2, 3};
479 const uint16_t args_len = 3;
480
481 InSequence please;
482 EXPECT_GET_STATUS_IDLE(app_id);
483 EXPECT_SEND_DATA(app_id, args, args_len);
484 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
485 EXPECT_GET_STATUS_WORKING(app_id);
486 EXPECT_GET_STATUS_DONE(app_id);
487 EXPECT_CLEAR_STATUS(app_id);
488
489 uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
490 EXPECT_THAT(res, Eq(APP_SUCCESS));
491 }
492
TEST_F(TransportTest,DetectAppAbort)493 TEST_F(TransportTest, DetectAppAbort) {
494 const uint8_t app_id = 25;
495 const uint16_t param = 252;
496 const uint8_t args[] = {17, 27, 43, 193};
497 const uint16_t args_len = 4;
498
499 InSequence please;
500 EXPECT_GET_STATUS_IDLE(app_id);
501 EXPECT_SEND_DATA(app_id, args, args_len);
502 EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
503 EXPECT_GET_STATUS_WORKING(app_id);
504 EXPECT_GET_STATUS_WORKING(app_id);
505 EXPECT_GET_STATUS_WORKING(app_id);
506 // It just stopped working
507 EXPECT_GET_STATUS_IDLE(app_id);
508 // It's probably already clear but just making sure
509 EXPECT_CLEAR_STATUS(app_id);
510
511 uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
512 EXPECT_THAT(res, Eq(APP_ERROR_INTERNAL));
513 }
514
TEST_F(TransportTest,SuccessWithReply)515 TEST_F(TransportTest, SuccessWithReply) {
516 const uint8_t app_id = 165;
517 const uint16_t param = 16;
518 const uint8_t data[] = {5, 6, 7, 8};
519 uint8_t reply[4];
520 uint32_t reply_len = 4;
521
522 InSequence please;
523 EXPECT_GET_STATUS_IDLE(app_id);
524 EXPECT_SEND_DATA(app_id, nullptr, 0);
525 EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len);
526 EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, data, sizeof(data));
527 EXPECT_RECV_DATA(app_id, reply_len, data, sizeof(data));
528 EXPECT_CLEAR_STATUS(app_id);
529
530 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply, &reply_len);
531 EXPECT_THAT(res, Eq(APP_SUCCESS));
532 EXPECT_THAT(reply_len, Eq(4));
533 EXPECT_THAT(reply, ElementsAreArray(data, sizeof(data)));
534 }
535
TEST_F(TransportTest,SuccessWithReplyInMultipleDatagrams)536 TEST_F(TransportTest, SuccessWithReplyInMultipleDatagrams) {
537 const uint8_t app_id = 165;
538 const uint16_t param = 16;
539 std::vector<uint8_t> data(MAX_DEVICE_TRANSFER + 24, 0xea);
540 std::vector<uint8_t> reply(data.size());
541 uint32_t reply_len = reply.size();
542
543 InSequence please;
544 EXPECT_GET_STATUS_IDLE(app_id);
545 EXPECT_SEND_DATA(app_id, nullptr, 0);
546 EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len);
547 EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, data.data(), data.size());
548 EXPECT_RECV_DATA(app_id, reply_len, data.data(), MAX_DEVICE_TRANSFER);
549 EXPECT_RECV_MORE_DATA(app_id, 24, data.data() + MAX_DEVICE_TRANSFER, 24);
550 EXPECT_CLEAR_STATUS(app_id);
551
552 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply.data(), &reply_len);
553 EXPECT_THAT(res, Eq(APP_SUCCESS));
554 EXPECT_THAT(reply_len, Eq(MAX_DEVICE_TRANSFER + 24));
555 EXPECT_THAT(reply, ElementsAreArray(data));
556 }
557
TEST_F(TransportTest,ReplyCrcError)558 TEST_F(TransportTest, ReplyCrcError) {
559 const uint8_t app_id = 5;
560 const uint16_t param = 0;
561 const uint8_t data[] = {1, 1, 2, 3, 5, 7};
562 const uint8_t wrong_data[] = {3, 1, 2, 3, 5, 7};
563 uint8_t reply[6];
564 uint32_t reply_len = 6;
565
566 InSequence please;
567 EXPECT_GET_STATUS_IDLE(app_id);
568 EXPECT_SEND_DATA(app_id, nullptr, 0);
569 EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len);
570 EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, data, sizeof(data));
571 // Try 5 times to read data
572 EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
573 EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
574 EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
575 EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
576 EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
577
578 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply, &reply_len);
579 EXPECT_THAT(res, Eq(APP_ERROR_IO));
580 }
581
TEST_F(TransportTest,SuccessAfterReplyCrcError)582 TEST_F(TransportTest, SuccessAfterReplyCrcError) {
583 const uint8_t app_id = 5;
584 const uint16_t param = 0;
585 const uint8_t data[] = {2, 4, 9, 16};
586 const uint8_t wrong_data[] = {2, 4, 9, 48};
587 uint8_t reply[4];
588 uint32_t reply_len = 4;
589
590 InSequence please;
591 EXPECT_GET_STATUS_IDLE(app_id);
592 EXPECT_SEND_DATA(app_id, nullptr, 0);
593 EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len);
594 EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, data, sizeof(data));
595 // Retry due to crc error
596 EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
597 EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
598 EXPECT_RECV_DATA(app_id, reply_len, data, sizeof(data));
599 EXPECT_CLEAR_STATUS(app_id);
600
601 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply, &reply_len);
602 EXPECT_THAT(res, Eq(APP_SUCCESS));
603 EXPECT_THAT(reply_len, Eq(4));
604 EXPECT_THAT(reply, ElementsAreArray(data, sizeof(data)));
605 }
606
TEST_F(TransportTest,V0SuccessWithoutReply)607 TEST_F(TransportTest, V0SuccessWithoutReply) {
608 const uint8_t app_id = 6;
609 const uint16_t param = 92;
610
611 InSequence please;
612 EXPECT_GET_STATUS_V0_IDLE(app_id);
613 EXPECT_SEND_DATA(app_id, nullptr, 0);
614 EXPECT_GO_COMMAND(app_id, param, nullptr, 0, 0);
615 EXPECT_GET_STATUS_V0_DONE(app_id);
616 EXPECT_CLEAR_STATUS(app_id);
617
618 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, nullptr);
619 EXPECT_THAT(res, Eq(APP_SUCCESS));
620 }
621
TEST_F(TransportTest,V0SuccessWithReply)622 TEST_F(TransportTest, V0SuccessWithReply) {
623 const uint8_t app_id = 0;
624 const uint16_t param = 18;
625 const uint8_t data[] = {15, 20, 25, 30, 35, 40};
626 uint8_t reply[6];
627 uint32_t reply_len = 6;
628
629 InSequence please;
630 EXPECT_GET_STATUS_V0_IDLE(app_id);
631 EXPECT_SEND_DATA(app_id, nullptr, 0);
632 EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len);
633 EXPECT_GET_STATUS_V0_DONE_WITH_DATA(app_id, sizeof(data));
634 EXPECT_RECV_DATA(app_id, reply_len, data, sizeof(data));
635 EXPECT_CLEAR_STATUS(app_id);
636
637 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply, &reply_len);
638 EXPECT_THAT(res, Eq(APP_SUCCESS));
639 EXPECT_THAT(reply_len, Eq(6));
640 EXPECT_THAT(reply, ElementsAreArray(data, sizeof(data)));
641 }
642
TEST_F(TransportTest,ErrorIfArgsLenButNotArgs)643 TEST_F(TransportTest, ErrorIfArgsLenButNotArgs) {
644 uint8_t reply[] = {1, 2, 3};
645 uint32_t reply_len = 0;
646 uint32_t status = nos_call_application(dev(), 1, 2, nullptr, 5, reply, &reply_len);
647 EXPECT_THAT(status, Eq(APP_ERROR_IO));
648 }
649
main(int argc,char ** argv)650 int main(int argc, char** argv) {
651 ::testing::InitGoogleTest(&argc, argv);
652 return RUN_ALL_TESTS();
653 }
654
655 #ifdef TEST_TIMEOUT
TEST_F(TransportTest,Timeout)656 TEST_F(TransportTest, Timeout) {
657 const uint8_t app_id = 49;
658 const uint16_t param = 64;
659
660 InSequence please;
661 EXPECT_GET_STATUS_IDLE(app_id);
662 EXPECT_SEND_DATA(app_id, nullptr, 0);
663 EXPECT_GO_COMMAND(app_id, param, nullptr, 0, 0);
664
665 // Keep saying we're working on it
666 const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT;
667 EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH))
668 .WillRepeatedly(DoAll(ReadStatusV1_Working(), Return(0)));
669
670 // We'll still try and clean up
671 EXPECT_CLEAR_STATUS(app_id);
672
673 uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, nullptr);
674 EXPECT_THAT(res, Eq(APP_ERROR_TIMEOUT));
675 }
676 #endif
677