677 lines
23 KiB
C++
677 lines
23 KiB
C++
/*
|
|
* Copyright (C) 2018 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <vector>
|
|
|
|
#include <gmock/gmock.h>
|
|
|
|
#include <application.h>
|
|
#include <nos/transport.h>
|
|
|
|
#include "crc16.h"
|
|
|
|
using ::testing::_;
|
|
using ::testing::Args;
|
|
using ::testing::DoAll;
|
|
using ::testing::Eq;
|
|
using ::testing::ElementsAreArray;
|
|
using ::testing::InSequence;
|
|
using ::testing::IsNull;
|
|
using ::testing::Return;
|
|
using ::testing::SetArrayArgument;
|
|
using ::testing::StrictMock;
|
|
|
|
namespace {
|
|
|
|
struct Device {
|
|
virtual int Read(uint32_t command, uint8_t* buf, uint32_t len) = 0;
|
|
virtual int Write(uint32_t command, const uint8_t* buf, uint32_t len) = 0;
|
|
virtual int WaitForInterrupt(int msecs) = 0;
|
|
virtual int Reset() = 0;
|
|
};
|
|
|
|
struct MockDevice : public Device {
|
|
MOCK_METHOD3(Read, int(uint32_t command, uint8_t* buf, uint32_t len));
|
|
MOCK_METHOD3(Write, int(uint32_t command, const uint8_t* buf, uint32_t len));
|
|
MOCK_METHOD1(WaitForInterrupt, int(int msecs));
|
|
MOCK_METHOD0(Reset, int());
|
|
};
|
|
|
|
// We want to closely examine the interactions with the device to make it a
|
|
// strict mock
|
|
using CtxType = StrictMock<MockDevice>;
|
|
|
|
// Forward calls onto the mock
|
|
int read_datagram(void* ctx, uint32_t command, uint8_t* buf, uint32_t len) {
|
|
return reinterpret_cast<CtxType*>(ctx)->Read(command, buf, len);
|
|
}
|
|
int write_datagram(void* ctx, uint32_t command, const uint8_t* buf, uint32_t len) {
|
|
return reinterpret_cast<CtxType*>(ctx)->Write(command, buf, len);
|
|
}
|
|
int wait_for_interrupt(void* ctx, int msecs) {
|
|
return reinterpret_cast<CtxType*>(ctx)->WaitForInterrupt(msecs);
|
|
}
|
|
int reset(void* ctx) {
|
|
return reinterpret_cast<CtxType*>(ctx)->Reset();
|
|
}
|
|
void close_device(void* ctx) {
|
|
delete reinterpret_cast<CtxType*>(ctx);
|
|
}
|
|
|
|
// Implement the datagram API that calls a mock.
|
|
extern "C" {
|
|
int nos_device_open(const char* device_name, struct nos_device* dev) {
|
|
EXPECT_THAT(device_name, IsNull());
|
|
dev->ctx = new CtxType;
|
|
dev->ops.read = read_datagram;
|
|
dev->ops.write = write_datagram;
|
|
dev->ops.wait_for_interrupt = wait_for_interrupt;
|
|
dev->ops.reset = reset;
|
|
dev->ops.close = close_device;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Test fixture that sets up the mocked device.
|
|
struct TransportTest : public ::testing::Test {
|
|
virtual void SetUp() override {
|
|
nos_device_open(nullptr, &dev_);
|
|
mock_dev_ = reinterpret_cast<CtxType*>(dev_.ctx);
|
|
}
|
|
virtual void TearDown() override {
|
|
dev_.ops.close(dev_.ctx);
|
|
}
|
|
|
|
nos_device* dev() { return &dev_; }
|
|
CtxType& mock_dev() { return *mock_dev_; }
|
|
|
|
private:
|
|
nos_device dev_;
|
|
CtxType* mock_dev_;
|
|
};
|
|
|
|
uint16_t command_crc(uint32_t command, const uint8_t* args, uint16_t args_len,
|
|
const transport_command_info* command_info) {
|
|
uint16_t crc = crc16(&args_len, sizeof(args_len));
|
|
crc = crc16_update(args, args_len, crc);
|
|
crc = crc16_update(&command, sizeof(command), crc);
|
|
crc = crc16_update(command_info, sizeof(*command_info), crc);
|
|
return htole16(crc);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
/* Actions to return mock data */
|
|
|
|
#define READ_UNSET 0xdf
|
|
|
|
ACTION(ReadStatusV0_Idle) {
|
|
transport_status* status = (transport_status*)arg1;
|
|
memset(status, READ_UNSET, sizeof(*status));
|
|
status->status = APP_STATUS_IDLE;
|
|
status->reply_len = 0;
|
|
}
|
|
|
|
ACTION(ReadStatusV1_Idle) {
|
|
transport_status* status = (transport_status*)arg1;
|
|
memset(status, READ_UNSET, sizeof(*status));
|
|
status->status = APP_STATUS_IDLE;
|
|
status->reply_len = 0;
|
|
status->length = sizeof(transport_status);
|
|
status->version = TRANSPORT_V1;
|
|
status->flags = 0;
|
|
status->crc = STATUS_CRC_FOR_IDLE;
|
|
status->reply_crc = 0;
|
|
}
|
|
|
|
ACTION(ReadStatusV1_IdleWithBadCrc) {
|
|
transport_status* status = (transport_status*)arg1;
|
|
memset(status, READ_UNSET, sizeof(*status));
|
|
status->status = APP_STATUS_IDLE;
|
|
status->reply_len = 0;
|
|
status->length = sizeof(transport_status);
|
|
status->version = TRANSPORT_V1;
|
|
status->flags = 0;
|
|
status->crc = STATUS_CRC_FOR_IDLE + 1; // <- wrong!
|
|
status->reply_crc = 0;
|
|
}
|
|
|
|
ACTION(ReadStatusV1_Working) {
|
|
transport_status* status = (transport_status*)arg1;
|
|
memset(status, READ_UNSET, sizeof(*status));
|
|
status->status = APP_STATUS_IDLE;
|
|
status->reply_len = 0;
|
|
status->length = sizeof(transport_status);
|
|
status->version = TRANSPORT_V1;
|
|
status->flags = STATUS_FLAG_WORKING;
|
|
status->crc = STATUS_CRC_FOR_WORKING;
|
|
status->reply_crc = 0;
|
|
}
|
|
|
|
ACTION_P(ReadStatusV0_DoneWithData, reply_len) {
|
|
transport_status* status = (transport_status*)arg1;
|
|
memset(status, READ_UNSET, sizeof(*status));
|
|
status->status = APP_STATUS_DONE | APP_SUCCESS;
|
|
status->reply_len = reply_len;
|
|
}
|
|
|
|
ACTION_P2(ReadStatusV1_DoneWithData, reply, reply_len) {
|
|
transport_status* status = (transport_status*)arg1;
|
|
memset(status, READ_UNSET, sizeof(*status));
|
|
status->status = APP_STATUS_DONE | APP_SUCCESS;
|
|
status->reply_len = reply_len;
|
|
status->length = sizeof(transport_status);
|
|
status->version = TRANSPORT_V1;
|
|
status->flags = 0;
|
|
status->reply_crc = crc16(reply, reply_len);
|
|
status->crc = 0;
|
|
status->crc = crc16(status, status->length);
|
|
}
|
|
|
|
ACTION(ReadStatusV1_BadCrc) {
|
|
transport_status* status = (transport_status*)arg1;
|
|
memset(status, READ_UNSET, sizeof(*status));
|
|
status->status = APP_STATUS_DONE | APP_ERROR_CHECKSUM;
|
|
status->reply_len = 0;
|
|
status->length = sizeof(transport_status);
|
|
status->version = TRANSPORT_V1;
|
|
status->flags = 0;
|
|
status->crc = 0x92c0;
|
|
status->reply_crc = 0;
|
|
}
|
|
|
|
ACTION(ReadStatusV42_Working) {
|
|
memset(arg1, 0xb3, STATUS_MAX_LENGTH);
|
|
transport_status* status = (transport_status*)arg1;
|
|
status->status = APP_STATUS_IDLE;
|
|
status->reply_len = 0;
|
|
status->length = STATUS_MAX_LENGTH;
|
|
status->version = 42;
|
|
status->flags = STATUS_FLAG_WORKING;
|
|
status->crc = 0xf781;
|
|
status->reply_crc = 0;
|
|
}
|
|
|
|
ACTION_P3(ReadData, len, data, size) {
|
|
memset(arg1, READ_UNSET, len);
|
|
memcpy(arg1, data, size);
|
|
}
|
|
|
|
/* Helper macros to expect datagram calls */
|
|
|
|
#define EXPECT_GET_STATUS_V0_IDLE(app_id) do { \
|
|
const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
|
|
EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
|
|
.WillOnce(DoAll(ReadStatusV0_Idle(), Return(0))); \
|
|
} while (0)
|
|
|
|
#define EXPECT_GET_STATUS_IDLE(app_id) do { \
|
|
const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
|
|
EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
|
|
.WillOnce(DoAll(ReadStatusV1_Idle(), Return(0))); \
|
|
} while (0)
|
|
|
|
#define EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id) do { \
|
|
const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
|
|
EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
|
|
.WillOnce(DoAll(ReadStatusV1_IdleWithBadCrc(), Return(0))); \
|
|
} while (0)
|
|
|
|
#define EXPECT_GET_STATUS_BAD_CRC(app_id) do { \
|
|
const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
|
|
EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
|
|
.WillOnce(DoAll(ReadStatusV1_BadCrc(), Return(0))); \
|
|
} while (0)
|
|
|
|
#define EXPECT_GET_STATUS_WORKING(app_id) do { \
|
|
const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
|
|
EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
|
|
.WillOnce(DoAll(ReadStatusV1_Working(), Return(0))); \
|
|
} while (0)
|
|
|
|
#define EXPECT_GET_STATUS_V0_DONE(app_id) do { \
|
|
const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
|
|
EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
|
|
.WillOnce(DoAll(ReadStatusV0_DoneWithData(0), Return(0))); \
|
|
} while (0)
|
|
|
|
#define EXPECT_GET_STATUS_V0_DONE_WITH_DATA(app_id, reply_len) do { \
|
|
const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
|
|
EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
|
|
.WillOnce(DoAll(ReadStatusV0_DoneWithData((reply_len)), Return(0))); \
|
|
} while (0)
|
|
|
|
#define EXPECT_GET_STATUS_DONE(app_id) do { \
|
|
const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
|
|
EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
|
|
.WillOnce(DoAll(ReadStatusV1_DoneWithData(nullptr, 0), Return(0))); \
|
|
} while (0)
|
|
|
|
#define EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, reply, reply_len) do { \
|
|
const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
|
|
EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
|
|
.WillOnce(DoAll(ReadStatusV1_DoneWithData((reply), (reply_len)), Return(0))); \
|
|
} while (0)
|
|
|
|
#define EXPECT_GET_STATUS_DONE_BAD_CRC(app_id, reply, reply_len) do { \
|
|
const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT; \
|
|
EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH)) \
|
|
.WillOnce(DoAll(ReadStatusV1_DoneBadCrc((reply), (reply_len)), Return(0))); \
|
|
} while (0)
|
|
|
|
#define EXPECT_SEND_DATA(app_id, args, args_len) do { \
|
|
const uint32_t command = CMD_ID((app_id)) | CMD_IS_DATA | CMD_TRANSPORT | CMD_PARAM((args_len)); \
|
|
EXPECT_CALL(mock_dev(), Write(command, _, (args_len))) \
|
|
.With(Args<1,2>(ElementsAreArray((uint8_t*)(args), (args_len)))) \
|
|
.WillOnce(Return(0)); \
|
|
} while (0)
|
|
|
|
#define EXPECT_GO_COMMAND(app_id, param, args, args_len, reply_len) do { \
|
|
const uint32_t command = CMD_ID((app_id)) | CMD_PARAM((param)); \
|
|
transport_command_info command_info = {}; \
|
|
command_info.length = sizeof(command_info); \
|
|
command_info.version = htole16(TRANSPORT_V1); \
|
|
command_info.reply_len_hint = htole16((reply_len)); \
|
|
command_info.crc = command_crc(command, (args), (args_len), &command_info); \
|
|
EXPECT_CALL(mock_dev(), Write(command, _, command_info.length)) \
|
|
.With(Args<1,2>(ElementsAreArray((uint8_t*)&command_info, command_info.length))) \
|
|
.WillOnce(Return(0)); \
|
|
} while (0)
|
|
|
|
#define EXPECT_RECV_DATA(app_id, len, reply, reply_len) do { \
|
|
const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_IS_DATA | CMD_TRANSPORT; \
|
|
EXPECT_CALL(mock_dev(), Read(command, _, (reply_len))) \
|
|
.WillOnce(DoAll(ReadData((len), (reply), (reply_len)), Return(0))); \
|
|
} while (0)
|
|
|
|
#define EXPECT_RECV_MORE_DATA(app_id, len, reply, reply_len) do { \
|
|
const uint32_t command = \
|
|
CMD_ID((app_id)) | CMD_IS_READ | CMD_IS_DATA | CMD_MORE_TO_COME | CMD_TRANSPORT; \
|
|
EXPECT_CALL(mock_dev(), Read(command, _, (reply_len))) \
|
|
.WillOnce(DoAll(ReadData((len), (reply), (reply_len)), Return(0))); \
|
|
} while (0)
|
|
|
|
#define EXPECT_CLEAR_STATUS(app_id) do { \
|
|
const uint32_t command = CMD_ID((app_id)) | CMD_TRANSPORT; \
|
|
EXPECT_CALL(mock_dev(), Write(command, _, 0)) \
|
|
.WillOnce(Return(0)); \
|
|
} while (0)
|
|
|
|
/* Protocol tests */
|
|
|
|
TEST_F(TransportTest, WorkingAppIsBusy) {
|
|
const uint8_t app_id = 213;
|
|
EXPECT_GET_STATUS_WORKING(app_id);
|
|
|
|
const uint16_t param = 2;
|
|
uint32_t reply_len = 0;
|
|
uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, &reply_len);
|
|
EXPECT_THAT(res, Eq(APP_ERROR_BUSY));
|
|
}
|
|
|
|
TEST_F(TransportTest, WorkingIsForwardCompatible) {
|
|
const uint8_t app_id = 25;
|
|
const uint32_t command = CMD_ID(app_id) | CMD_IS_READ | CMD_TRANSPORT;
|
|
EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH))
|
|
.WillOnce(DoAll(ReadStatusV42_Working(), Return(0)));
|
|
|
|
const uint16_t param = 2;
|
|
uint32_t reply_len = 0;
|
|
uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, &reply_len);
|
|
EXPECT_THAT(res, Eq(APP_ERROR_BUSY));
|
|
}
|
|
|
|
TEST_F(TransportTest, SuccessIfStatusNotClear) {
|
|
const uint8_t app_id = 12;
|
|
const uint16_t param = 2;
|
|
const uint8_t args[] = {1, 2, 3};
|
|
const uint16_t args_len = 3;
|
|
|
|
InSequence please;
|
|
EXPECT_GET_STATUS_BAD_CRC(app_id);
|
|
// Try and reset
|
|
EXPECT_CLEAR_STATUS(app_id);
|
|
// Try again
|
|
EXPECT_GET_STATUS_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, args, args_len);
|
|
EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
|
|
EXPECT_GET_STATUS_WORKING(app_id);
|
|
EXPECT_GET_STATUS_DONE(app_id);
|
|
EXPECT_CLEAR_STATUS(app_id);
|
|
|
|
uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
|
|
EXPECT_THAT(res, Eq(APP_SUCCESS));
|
|
}
|
|
|
|
TEST_F(TransportTest, StatusCrcError) {
|
|
const uint8_t app_id = 53;
|
|
const uint16_t param = 192;
|
|
|
|
InSequence please;
|
|
// Try 5 times
|
|
EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
|
|
EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
|
|
EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
|
|
EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
|
|
EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
|
|
|
|
uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, nullptr);
|
|
EXPECT_THAT(res, Eq(APP_ERROR_IO));
|
|
}
|
|
|
|
TEST_F(TransportTest, FailToClearStatus) {
|
|
const uint8_t app_id = 12;
|
|
const uint16_t param = 2;
|
|
const uint8_t args[] = {1, 2, 3};
|
|
const uint16_t args_len = 3;
|
|
|
|
InSequence please;
|
|
EXPECT_GET_STATUS_BAD_CRC(app_id);
|
|
// Try and reset
|
|
EXPECT_CLEAR_STATUS(app_id);
|
|
// No luck
|
|
EXPECT_GET_STATUS_BAD_CRC(app_id);
|
|
|
|
uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
|
|
EXPECT_THAT(res, Eq(APP_ERROR_IO));
|
|
}
|
|
|
|
TEST_F(TransportTest, FailToClearStatusAfterStatusCrcError) {
|
|
const uint8_t app_id = 53;
|
|
const uint16_t param = 192;
|
|
|
|
InSequence please;
|
|
// Try 5 times
|
|
EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
|
|
EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
|
|
EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
|
|
EXPECT_GET_STATUS_IDLE_WITH_BAD_CRC(app_id);
|
|
EXPECT_GET_STATUS_BAD_CRC(app_id);
|
|
// Try and reset
|
|
EXPECT_CLEAR_STATUS(app_id);
|
|
// No luck
|
|
EXPECT_GET_STATUS_BAD_CRC(app_id);
|
|
|
|
uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, nullptr);
|
|
EXPECT_THAT(res, Eq(APP_ERROR_IO));
|
|
}
|
|
|
|
TEST_F(TransportTest, RequestCrcError) {
|
|
const uint8_t app_id = 58;
|
|
const uint16_t param = 93;
|
|
const uint8_t args[] = {4, 24, 183, 255, 219};
|
|
const uint16_t args_len = 5;
|
|
|
|
InSequence please;
|
|
// Should try 5 times
|
|
EXPECT_GET_STATUS_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, args, args_len);
|
|
EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
|
|
EXPECT_GET_STATUS_BAD_CRC(app_id);
|
|
// 4 more
|
|
EXPECT_GET_STATUS_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, args, args_len);
|
|
EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
|
|
EXPECT_GET_STATUS_BAD_CRC(app_id);
|
|
// 3 more
|
|
EXPECT_GET_STATUS_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, args, args_len);
|
|
EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
|
|
EXPECT_GET_STATUS_BAD_CRC(app_id);
|
|
// 2 more
|
|
EXPECT_GET_STATUS_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, args, args_len);
|
|
EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
|
|
EXPECT_GET_STATUS_BAD_CRC(app_id);
|
|
// last one
|
|
EXPECT_GET_STATUS_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, args, args_len);
|
|
EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
|
|
EXPECT_GET_STATUS_BAD_CRC(app_id);
|
|
// Clean up
|
|
EXPECT_CLEAR_STATUS(app_id);
|
|
|
|
uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
|
|
EXPECT_THAT(res, Eq(APP_ERROR_IO));
|
|
}
|
|
|
|
TEST_F(TransportTest, SuccessAfterRequestCrcError) {
|
|
const uint8_t app_id = 255;
|
|
const uint16_t param = 163;
|
|
const uint8_t args[] = {42, 89, 125, 0, 83, 92, 80};
|
|
const uint16_t args_len = 7;
|
|
|
|
InSequence please;
|
|
// First request is CRC error
|
|
EXPECT_GET_STATUS_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, args, args_len);
|
|
EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
|
|
EXPECT_GET_STATUS_BAD_CRC(app_id);
|
|
// The retry succeeds
|
|
EXPECT_GET_STATUS_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, args, args_len);
|
|
EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
|
|
EXPECT_GET_STATUS_WORKING(app_id);
|
|
EXPECT_GET_STATUS_DONE(app_id);
|
|
EXPECT_CLEAR_STATUS(app_id);
|
|
|
|
uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
|
|
EXPECT_THAT(res, Eq(APP_SUCCESS));
|
|
}
|
|
|
|
TEST_F(TransportTest, SuccessWithoutReply) {
|
|
const uint8_t app_id = 12;
|
|
const uint16_t param = 2;
|
|
const uint8_t args[] = {1, 2, 3};
|
|
const uint16_t args_len = 3;
|
|
|
|
InSequence please;
|
|
EXPECT_GET_STATUS_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, args, args_len);
|
|
EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
|
|
EXPECT_GET_STATUS_WORKING(app_id);
|
|
EXPECT_GET_STATUS_DONE(app_id);
|
|
EXPECT_CLEAR_STATUS(app_id);
|
|
|
|
uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
|
|
EXPECT_THAT(res, Eq(APP_SUCCESS));
|
|
}
|
|
|
|
TEST_F(TransportTest, DetectAppAbort) {
|
|
const uint8_t app_id = 25;
|
|
const uint16_t param = 252;
|
|
const uint8_t args[] = {17, 27, 43, 193};
|
|
const uint16_t args_len = 4;
|
|
|
|
InSequence please;
|
|
EXPECT_GET_STATUS_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, args, args_len);
|
|
EXPECT_GO_COMMAND(app_id, param, args, args_len, 0);
|
|
EXPECT_GET_STATUS_WORKING(app_id);
|
|
EXPECT_GET_STATUS_WORKING(app_id);
|
|
EXPECT_GET_STATUS_WORKING(app_id);
|
|
// It just stopped working
|
|
EXPECT_GET_STATUS_IDLE(app_id);
|
|
// It's probably already clear but just making sure
|
|
EXPECT_CLEAR_STATUS(app_id);
|
|
|
|
uint32_t res = nos_call_application(dev(), app_id, param, args, args_len, nullptr, nullptr);
|
|
EXPECT_THAT(res, Eq(APP_ERROR_INTERNAL));
|
|
}
|
|
|
|
TEST_F(TransportTest, SuccessWithReply) {
|
|
const uint8_t app_id = 165;
|
|
const uint16_t param = 16;
|
|
const uint8_t data[] = {5, 6, 7, 8};
|
|
uint8_t reply[4];
|
|
uint32_t reply_len = 4;
|
|
|
|
InSequence please;
|
|
EXPECT_GET_STATUS_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, nullptr, 0);
|
|
EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len);
|
|
EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, data, sizeof(data));
|
|
EXPECT_RECV_DATA(app_id, reply_len, data, sizeof(data));
|
|
EXPECT_CLEAR_STATUS(app_id);
|
|
|
|
uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply, &reply_len);
|
|
EXPECT_THAT(res, Eq(APP_SUCCESS));
|
|
EXPECT_THAT(reply_len, Eq(4));
|
|
EXPECT_THAT(reply, ElementsAreArray(data, sizeof(data)));
|
|
}
|
|
|
|
TEST_F(TransportTest, SuccessWithReplyInMultipleDatagrams) {
|
|
const uint8_t app_id = 165;
|
|
const uint16_t param = 16;
|
|
std::vector<uint8_t> data(MAX_DEVICE_TRANSFER + 24, 0xea);
|
|
std::vector<uint8_t> reply(data.size());
|
|
uint32_t reply_len = reply.size();
|
|
|
|
InSequence please;
|
|
EXPECT_GET_STATUS_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, nullptr, 0);
|
|
EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len);
|
|
EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, data.data(), data.size());
|
|
EXPECT_RECV_DATA(app_id, reply_len, data.data(), MAX_DEVICE_TRANSFER);
|
|
EXPECT_RECV_MORE_DATA(app_id, 24, data.data() + MAX_DEVICE_TRANSFER, 24);
|
|
EXPECT_CLEAR_STATUS(app_id);
|
|
|
|
uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply.data(), &reply_len);
|
|
EXPECT_THAT(res, Eq(APP_SUCCESS));
|
|
EXPECT_THAT(reply_len, Eq(MAX_DEVICE_TRANSFER + 24));
|
|
EXPECT_THAT(reply, ElementsAreArray(data));
|
|
}
|
|
|
|
TEST_F(TransportTest, ReplyCrcError) {
|
|
const uint8_t app_id = 5;
|
|
const uint16_t param = 0;
|
|
const uint8_t data[] = {1, 1, 2, 3, 5, 7};
|
|
const uint8_t wrong_data[] = {3, 1, 2, 3, 5, 7};
|
|
uint8_t reply[6];
|
|
uint32_t reply_len = 6;
|
|
|
|
InSequence please;
|
|
EXPECT_GET_STATUS_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, nullptr, 0);
|
|
EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len);
|
|
EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, data, sizeof(data));
|
|
// Try 5 times to read data
|
|
EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
|
|
EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
|
|
EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
|
|
EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
|
|
EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
|
|
|
|
uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply, &reply_len);
|
|
EXPECT_THAT(res, Eq(APP_ERROR_IO));
|
|
}
|
|
|
|
TEST_F(TransportTest, SuccessAfterReplyCrcError) {
|
|
const uint8_t app_id = 5;
|
|
const uint16_t param = 0;
|
|
const uint8_t data[] = {2, 4, 9, 16};
|
|
const uint8_t wrong_data[] = {2, 4, 9, 48};
|
|
uint8_t reply[4];
|
|
uint32_t reply_len = 4;
|
|
|
|
InSequence please;
|
|
EXPECT_GET_STATUS_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, nullptr, 0);
|
|
EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len);
|
|
EXPECT_GET_STATUS_DONE_WITH_DATA(app_id, data, sizeof(data));
|
|
// Retry due to crc error
|
|
EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
|
|
EXPECT_RECV_DATA(app_id, reply_len, wrong_data, sizeof(wrong_data));
|
|
EXPECT_RECV_DATA(app_id, reply_len, data, sizeof(data));
|
|
EXPECT_CLEAR_STATUS(app_id);
|
|
|
|
uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply, &reply_len);
|
|
EXPECT_THAT(res, Eq(APP_SUCCESS));
|
|
EXPECT_THAT(reply_len, Eq(4));
|
|
EXPECT_THAT(reply, ElementsAreArray(data, sizeof(data)));
|
|
}
|
|
|
|
TEST_F(TransportTest, V0SuccessWithoutReply) {
|
|
const uint8_t app_id = 6;
|
|
const uint16_t param = 92;
|
|
|
|
InSequence please;
|
|
EXPECT_GET_STATUS_V0_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, nullptr, 0);
|
|
EXPECT_GO_COMMAND(app_id, param, nullptr, 0, 0);
|
|
EXPECT_GET_STATUS_V0_DONE(app_id);
|
|
EXPECT_CLEAR_STATUS(app_id);
|
|
|
|
uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, nullptr);
|
|
EXPECT_THAT(res, Eq(APP_SUCCESS));
|
|
}
|
|
|
|
TEST_F(TransportTest, V0SuccessWithReply) {
|
|
const uint8_t app_id = 0;
|
|
const uint16_t param = 18;
|
|
const uint8_t data[] = {15, 20, 25, 30, 35, 40};
|
|
uint8_t reply[6];
|
|
uint32_t reply_len = 6;
|
|
|
|
InSequence please;
|
|
EXPECT_GET_STATUS_V0_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, nullptr, 0);
|
|
EXPECT_GO_COMMAND(app_id, param, nullptr, 0, reply_len);
|
|
EXPECT_GET_STATUS_V0_DONE_WITH_DATA(app_id, sizeof(data));
|
|
EXPECT_RECV_DATA(app_id, reply_len, data, sizeof(data));
|
|
EXPECT_CLEAR_STATUS(app_id);
|
|
|
|
uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, reply, &reply_len);
|
|
EXPECT_THAT(res, Eq(APP_SUCCESS));
|
|
EXPECT_THAT(reply_len, Eq(6));
|
|
EXPECT_THAT(reply, ElementsAreArray(data, sizeof(data)));
|
|
}
|
|
|
|
TEST_F(TransportTest, ErrorIfArgsLenButNotArgs) {
|
|
uint8_t reply[] = {1, 2, 3};
|
|
uint32_t reply_len = 0;
|
|
uint32_t status = nos_call_application(dev(), 1, 2, nullptr, 5, reply, &reply_len);
|
|
EXPECT_THAT(status, Eq(APP_ERROR_IO));
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|
|
|
|
#ifdef TEST_TIMEOUT
|
|
TEST_F(TransportTest, Timeout) {
|
|
const uint8_t app_id = 49;
|
|
const uint16_t param = 64;
|
|
|
|
InSequence please;
|
|
EXPECT_GET_STATUS_IDLE(app_id);
|
|
EXPECT_SEND_DATA(app_id, nullptr, 0);
|
|
EXPECT_GO_COMMAND(app_id, param, nullptr, 0, 0);
|
|
|
|
// Keep saying we're working on it
|
|
const uint32_t command = CMD_ID((app_id)) | CMD_IS_READ | CMD_TRANSPORT;
|
|
EXPECT_CALL(mock_dev(), Read(command, _, STATUS_MAX_LENGTH))
|
|
.WillRepeatedly(DoAll(ReadStatusV1_Working(), Return(0)));
|
|
|
|
// We'll still try and clean up
|
|
EXPECT_CLEAR_STATUS(app_id);
|
|
|
|
uint32_t res = nos_call_application(dev(), app_id, param, nullptr, 0, nullptr, nullptr);
|
|
EXPECT_THAT(res, Eq(APP_ERROR_TIMEOUT));
|
|
}
|
|
#endif
|