• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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