blob: b8b871f3786c267386e21cdb123ca4b456ecf949 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include "tools/battor_agent/battor_agent.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "tools/battor_agent/battor_protocol_types.h"
using namespace testing;
using std::vector;
namespace battor {
namespace {
BattOrControlMessageAck kInitAck{BATTOR_CONTROL_MESSAGE_TYPE_INIT, 0};
BattOrControlMessageAck kSetGainAck{BATTOR_CONTROL_MESSAGE_TYPE_SET_GAIN, 0};
BattOrControlMessageAck kStartTracingAck{
BATTOR_CONTROL_MESSAGE_TYPE_START_SAMPLING_SD, 0};
const char kClockSyncId[] = "MY_MARKER";
// Creates a byte vector copy of the specified object.
template <typename T>
std::unique_ptr<std::vector<char>> ToCharVector(const T& object) {
return std::unique_ptr<std::vector<char>>(new std::vector<char>(
reinterpret_cast<const char*>(&object),
reinterpret_cast<const char*>(&object) + sizeof(T)));
}
MATCHER_P2(
BufferEq,
expected_buffer,
expected_buffer_size,
"Makes sure that the argument has the same contents as the buffer.") {
return memcmp(reinterpret_cast<const void*>(arg),
reinterpret_cast<const void*>(expected_buffer),
expected_buffer_size) == 0;
}
std::unique_ptr<vector<char>> CreateFrame(const BattOrFrameHeader& frame_header,
const RawBattOrSample* samples,
const size_t& num_samples) {
std::unique_ptr<vector<char>> bytes(new vector<char>(
sizeof(BattOrFrameHeader) + sizeof(RawBattOrSample) * num_samples));
memcpy(bytes->data(), &frame_header, sizeof(BattOrFrameHeader));
memcpy(bytes->data() + sizeof(BattOrFrameHeader), samples,
sizeof(RawBattOrSample) * num_samples);
return bytes;
}
class MockBattOrConnection : public BattOrConnection {
public:
MockBattOrConnection(BattOrConnection::Listener* listener)
: BattOrConnection(listener) {}
~MockBattOrConnection() override {}
MOCK_METHOD0(Open, void());
MOCK_METHOD0(Close, void());
MOCK_METHOD3(SendBytes,
void(BattOrMessageType type,
const void* buffer,
size_t bytes_to_send));
MOCK_METHOD1(ReadMessage, void(BattOrMessageType type));
MOCK_METHOD0(CancelReadMessage, void());
MOCK_METHOD0(Flush, void());
private:
DISALLOW_COPY_AND_ASSIGN(MockBattOrConnection);
};
} // namespace
// TestableBattOrAgent uses a fake BattOrConnection to be testable.
class TestableBattOrAgent : public BattOrAgent {
public:
TestableBattOrAgent(BattOrAgent::Listener* listener)
: BattOrAgent("/dev/test", listener, nullptr, nullptr) {
connection_ =
std::unique_ptr<BattOrConnection>(new MockBattOrConnection(this));
}
MockBattOrConnection* GetConnection() {
return static_cast<MockBattOrConnection*>(connection_.get());
}
void OnActionTimeout() override {}
};
// BattOrAgentTest provides a BattOrAgent and captures the results of its
// tracing commands.
class BattOrAgentTest : public testing::Test, public BattOrAgent::Listener {
public:
BattOrAgentTest()
: task_runner_(new base::TestSimpleTaskRunner()),
thread_task_runner_handle_(task_runner_) {}
void OnStartTracingComplete(BattOrError error) override {
is_command_complete_ = true;
command_error_ = error;
}
void OnStopTracingComplete(const std::string& trace,
BattOrError error) override {
is_command_complete_ = true;
command_error_ = error;
trace_ = trace;
}
void OnRecordClockSyncMarkerComplete(BattOrError error) override {
is_command_complete_ = true;
command_error_ = error;
}
void OnGetFirmwareGitHashComplete(const std::string& firmware_git_hash,
BattOrError error) override {
is_command_complete_ = true;
command_error_ = error;
firmware_git_hash_ = firmware_git_hash;
}
void OnBytesSent(bool success) {
agent_->OnBytesSent(success);
task_runner_->RunUntilIdle();
}
void OnMessageRead(bool success,
BattOrMessageType type,
std::unique_ptr<std::vector<char>> bytes) {
agent_->OnMessageRead(success, type, std::move(bytes));
task_runner_->RunUntilIdle();
}
protected:
void SetUp() override {
agent_.reset(new TestableBattOrAgent(this));
task_runner_->ClearPendingTasks();
is_command_complete_ = false;
command_error_ = BATTOR_ERROR_NONE;
}
// Possible states that the BattOrAgent can be in.
enum class BattOrAgentState {
// States required to connect to a BattOr.
CONNECTED,
// States required to StartTracing.
INIT_SENT,
INIT_ACKED,
SET_GAIN_SENT,
GAIN_ACKED,
START_TRACING_SENT,
START_TRACING_COMPLETE,
// States required to StopTracing.
EEPROM_REQUEST_SENT,
EEPROM_RECEIVED,
SAMPLES_REQUEST_SENT,
CALIBRATION_FRAME_RECEIVED,
// States required to RecordClockSyncMarker.
CURRENT_SAMPLE_REQUEST_SENT,
RECORD_CLOCK_SYNC_MARKER_COMPLETE,
// States required to GetFirmwareGitHash.
GIT_FIRMWARE_HASH_REQUEST_SENT,
READ_GIT_HASH_RECEIVED,
};
// Runs BattOrAgent::StartTracing until it reaches the specified state by
// feeding it the callbacks it needs to progress.
void RunStartTracingTo(BattOrAgentState end_state) {
is_command_complete_ = false;
GetAgent()->StartTracing();
GetTaskRunner()->RunUntilIdle();
GetAgent()->OnConnectionOpened(true);
GetTaskRunner()->RunUntilIdle();
if (end_state == BattOrAgentState::CONNECTED)
return;
OnBytesSent(true);
if (end_state == BattOrAgentState::INIT_SENT)
return;
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kInitAck));
if (end_state == BattOrAgentState::INIT_ACKED)
return;
OnBytesSent(true);
if (end_state == BattOrAgentState::SET_GAIN_SENT)
return;
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kSetGainAck));
if (end_state == BattOrAgentState::GAIN_ACKED)
return;
OnBytesSent(true);
if (end_state == BattOrAgentState::START_TRACING_SENT)
return;
// Make sure that we're actually forwarding to a state in the start tracing
// state machine.
DCHECK(end_state == BattOrAgentState::START_TRACING_COMPLETE);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kStartTracingAck));
}
// Runs BattOrAgent::StopTracing until it reaches the specified state by
// feeding it the callbacks it needs to progress.
void RunStopTracingTo(BattOrAgentState end_state) {
is_command_complete_ = false;
GetAgent()->StopTracing();
GetTaskRunner()->RunUntilIdle();
GetAgent()->OnConnectionOpened(true);
GetTaskRunner()->RunUntilIdle();
if (end_state == BattOrAgentState::CONNECTED)
return;
OnBytesSent(true);
if (end_state == BattOrAgentState::EEPROM_REQUEST_SENT)
return;
BattOrEEPROM eeprom;
eeprom.r1 = 1;
eeprom.r2 = 1;
eeprom.r3 = 1;
eeprom.low_gain = 1;
eeprom.low_gain_correction_offset = 0;
eeprom.low_gain_correction_factor = 1;
eeprom.sd_sample_rate = 1000;
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK, ToCharVector(eeprom));
if (end_state == BattOrAgentState::EEPROM_RECEIVED)
return;
OnBytesSent(true);
if (end_state == BattOrAgentState::SAMPLES_REQUEST_SENT)
return;
DCHECK(end_state == BattOrAgentState::CALIBRATION_FRAME_RECEIVED);
BattOrFrameHeader cal_frame_header{0, sizeof(RawBattOrSample)};
RawBattOrSample cal_frame[] = {RawBattOrSample{1, 1}};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(cal_frame_header, cal_frame, 1));
}
// Runs BattOrAgent::RecordClockSyncMarker until it reaches the specified
// state by feeding it the callbacks it needs to progress.
void RunRecordClockSyncMarkerTo(BattOrAgentState end_state) {
is_command_complete_ = false;
GetAgent()->RecordClockSyncMarker(kClockSyncId);
GetTaskRunner()->RunUntilIdle();
GetAgent()->OnConnectionOpened(true);
GetTaskRunner()->RunUntilIdle();
if (end_state == BattOrAgentState::CONNECTED)
return;
OnBytesSent(true);
if (end_state == BattOrAgentState::CURRENT_SAMPLE_REQUEST_SENT)
return;
DCHECK(end_state == BattOrAgentState::RECORD_CLOCK_SYNC_MARKER_COMPLETE);
uint32_t current_sample = 1;
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(current_sample));
}
// Runs BattOrAgent::GetFirmwareGitHash until it reaches the specified
// state by feeding it the callbacks it needs to progress.
void RunGetFirmwareGitHashTo(BattOrAgentState end_state) {
is_command_complete_ = false;
GetAgent()->GetFirmwareGitHash();
GetTaskRunner()->RunUntilIdle();
GetAgent()->OnConnectionOpened(true);
GetTaskRunner()->RunUntilIdle();
if (end_state == BattOrAgentState::CONNECTED)
return;
OnBytesSent(true);
if (end_state == BattOrAgentState::INIT_SENT)
return;
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kInitAck));
if (end_state == BattOrAgentState::INIT_ACKED)
return;
OnBytesSent(true);
if (end_state == BattOrAgentState::GIT_FIRMWARE_HASH_REQUEST_SENT)
return;
DCHECK(end_state == BattOrAgentState::READ_GIT_HASH_RECEIVED);
std::unique_ptr<std::vector<char>> firmware_git_hash_vector(
new std::vector<char>{'G', 'I', 'T', 'H', 'A', 'S', 'H'});
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
std::move(firmware_git_hash_vector));
}
TestableBattOrAgent* GetAgent() { return agent_.get(); }
scoped_refptr<base::TestSimpleTaskRunner> GetTaskRunner() {
return task_runner_;
}
bool IsCommandComplete() { return is_command_complete_; }
BattOrError GetCommandError() { return command_error_; }
std::string GetTrace() { return trace_; }
std::string GetGitHash() { return firmware_git_hash_; }
private:
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
// Needed to support ThreadTaskRunnerHandle::Get() in code under test.
base::ThreadTaskRunnerHandle thread_task_runner_handle_;
std::unique_ptr<TestableBattOrAgent> agent_;
bool is_command_complete_;
BattOrError command_error_;
std::string trace_;
std::string firmware_git_hash_;
};
TEST_F(BattOrAgentTest, StartTracing) {
testing::InSequence s;
EXPECT_CALL(*GetAgent()->GetConnection(), Open());
EXPECT_CALL(*GetAgent()->GetConnection(), Flush());
BattOrControlMessage init_msg{BATTOR_CONTROL_MESSAGE_TYPE_INIT, 0, 0};
EXPECT_CALL(
*GetAgent()->GetConnection(),
SendBytes(BATTOR_MESSAGE_TYPE_CONTROL,
BufferEq(&init_msg, sizeof(init_msg)), sizeof(init_msg)));
EXPECT_CALL(*GetAgent()->GetConnection(),
ReadMessage(BATTOR_MESSAGE_TYPE_CONTROL_ACK));
BattOrControlMessage set_gain_msg{BATTOR_CONTROL_MESSAGE_TYPE_SET_GAIN,
BATTOR_GAIN_LOW, 0};
EXPECT_CALL(*GetAgent()->GetConnection(),
SendBytes(BATTOR_MESSAGE_TYPE_CONTROL,
BufferEq(&set_gain_msg, sizeof(set_gain_msg)),
sizeof(set_gain_msg)));
EXPECT_CALL(*GetAgent()->GetConnection(),
ReadMessage(BATTOR_MESSAGE_TYPE_CONTROL_ACK));
BattOrControlMessage start_tracing_msg{
BATTOR_CONTROL_MESSAGE_TYPE_START_SAMPLING_SD, 0, 0};
EXPECT_CALL(*GetAgent()->GetConnection(),
SendBytes(BATTOR_MESSAGE_TYPE_CONTROL,
BufferEq(&start_tracing_msg, sizeof(start_tracing_msg)),
sizeof(start_tracing_msg)));
EXPECT_CALL(*GetAgent()->GetConnection(),
ReadMessage(BATTOR_MESSAGE_TYPE_CONTROL_ACK));
RunStartTracingTo(BattOrAgentState::START_TRACING_COMPLETE);
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
}
TEST_F(BattOrAgentTest, StartTracingFailsWithoutConnection) {
GetAgent()->StartTracing();
GetTaskRunner()->RunUntilIdle();
GetAgent()->OnConnectionOpened(false);
GetTaskRunner()->RunUntilIdle();
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_CONNECTION_FAILED, GetCommandError());
}
TEST_F(BattOrAgentTest, StartTracingFailsIfInitSendFails) {
RunStartTracingTo(BattOrAgentState::CONNECTED);
OnBytesSent(false);
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_SEND_ERROR, GetCommandError());
}
TEST_F(BattOrAgentTest, StartTracingFailsIfInitAckReadFails) {
RunStartTracingTo(BattOrAgentState::INIT_SENT);
for (int i = 0; i < 21; i++) {
OnMessageRead(false, BATTOR_MESSAGE_TYPE_CONTROL_ACK, nullptr);
// Bytes will be sent because INIT will be retried.
OnBytesSent(true);
}
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_TOO_MANY_INIT_RETRIES, GetCommandError());
}
TEST_F(BattOrAgentTest, StartTracingFailsIfInitWrongAckRead) {
RunStartTracingTo(BattOrAgentState::INIT_SENT);
for (int i = 0; i < 21; i++) {
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kStartTracingAck));
// Bytes will be sent because INIT will be retried.
OnBytesSent(true);
}
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_TOO_MANY_INIT_RETRIES, GetCommandError());
}
TEST_F(BattOrAgentTest, StartTracingFailsIfSetGainSendFails) {
RunStartTracingTo(BattOrAgentState::INIT_SENT);
OnBytesSent(false);
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_SEND_ERROR, GetCommandError());
}
TEST_F(BattOrAgentTest, StartTracingFailsIfSetGainAckReadFails) {
RunStartTracingTo(BattOrAgentState::SET_GAIN_SENT);
OnMessageRead(false, BATTOR_MESSAGE_TYPE_CONTROL_ACK, nullptr);
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_RECEIVE_ERROR, GetCommandError());
}
TEST_F(BattOrAgentTest, StartTracingFailsIfSetGainWrongAckRead) {
RunStartTracingTo(BattOrAgentState::SET_GAIN_SENT);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kStartTracingAck));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_UNEXPECTED_MESSAGE, GetCommandError());
}
TEST_F(BattOrAgentTest, StartTracingFailsIfStartTracingSendFails) {
RunStartTracingTo(BattOrAgentState::INIT_SENT);
OnBytesSent(false);
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_SEND_ERROR, GetCommandError());
}
TEST_F(BattOrAgentTest, StartTracingSucceedsAfterRetriesIfWrongAckRead) {
RunStartTracingTo(BattOrAgentState::CONNECTED);
for (int i = 0; i < 4; i++) {
// Go through the correct init sequence, but give the wrong ack to
// START_TRACING.
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kInitAck));
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kSetGainAck));
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kInitAck));
}
// On the last attempt, give the correct ack to START_TRACING.
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK, ToCharVector(kInitAck));
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kSetGainAck));
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kStartTracingAck));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
}
TEST_F(BattOrAgentTest, StartTracingSucceedsAfterRetriesWithReadFailure) {
RunStartTracingTo(BattOrAgentState::CONNECTED);
for (int i = 0; i < 4; i++) {
// Go through the correct init sequence, but indicate that we failed to read
// the START_TRACING ack.
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kInitAck));
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kSetGainAck));
OnBytesSent(true);
OnMessageRead(false, BATTOR_MESSAGE_TYPE_CONTROL_ACK, nullptr);
}
// On the last attempt, give the correct ack to START_TRACING.
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK, ToCharVector(kInitAck));
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kSetGainAck));
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kStartTracingAck));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
}
TEST_F(BattOrAgentTest, StartTracingFailsIfStartTracingWrongAckReadTooMuch) {
RunStartTracingTo(BattOrAgentState::CONNECTED);
for (int i = 0; i < 5; i++) {
// Go through the correct init sequence, but give the wrong ack to
// START_TRACING.
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kInitAck));
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kSetGainAck));
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kInitAck));
}
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_TOO_MANY_START_TRACING_RETRIES, GetCommandError());
}
TEST_F(BattOrAgentTest, StartTracingSucceedsWithOneInitFailure) {
RunStartTracingTo(BattOrAgentState::INIT_SENT);
// Send some samples instead of an INIT ACK. This will force an INIT retry.
BattOrFrameHeader frame_header{1, 3 * sizeof(RawBattOrSample)};
RawBattOrSample frame[] = {
RawBattOrSample{1, 1}, RawBattOrSample{2, 2}, RawBattOrSample{3, 3},
};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(frame_header, frame, 3));
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK, ToCharVector(kInitAck));
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kSetGainAck));
OnBytesSent(true);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kStartTracingAck));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracing) {
testing::InSequence s;
EXPECT_CALL(*GetAgent()->GetConnection(), Open());
BattOrControlMessage request_eeprom_msg{
BATTOR_CONTROL_MESSAGE_TYPE_READ_EEPROM, sizeof(BattOrEEPROM), 0};
EXPECT_CALL(
*GetAgent()->GetConnection(),
SendBytes(BATTOR_MESSAGE_TYPE_CONTROL,
BufferEq(&request_eeprom_msg, sizeof(request_eeprom_msg)),
sizeof(request_eeprom_msg)));
EXPECT_CALL(*GetAgent()->GetConnection(),
ReadMessage(BATTOR_MESSAGE_TYPE_CONTROL_ACK));
BattOrControlMessage request_samples_msg{
BATTOR_CONTROL_MESSAGE_TYPE_READ_SD_UART, 0, 0};
EXPECT_CALL(
*GetAgent()->GetConnection(),
SendBytes(BATTOR_MESSAGE_TYPE_CONTROL,
BufferEq(&request_samples_msg, sizeof(request_samples_msg)),
sizeof(request_samples_msg)));
// We send the agent four frames: a calibration frame, and two real frames,
// and one zero-length frame to indicate that we're done.
EXPECT_CALL(*GetAgent()->GetConnection(),
ReadMessage(BATTOR_MESSAGE_TYPE_SAMPLES))
.Times(4);
RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT);
// Send the calibration frame.
BattOrFrameHeader cal_frame_header{0, 2 * sizeof(RawBattOrSample)};
RawBattOrSample cal_frame[] = {
RawBattOrSample{1, 1}, RawBattOrSample{2, 2},
};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(cal_frame_header, cal_frame, 2));
// Send the two real data frames.
BattOrFrameHeader frame_header1{1, 3 * sizeof(RawBattOrSample)};
RawBattOrSample frame1[] = {
RawBattOrSample{1, 1}, RawBattOrSample{2, 2}, RawBattOrSample{3, 3},
};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(frame_header1, frame1, 3));
BattOrFrameHeader frame_header2{2, 1 * sizeof(RawBattOrSample)};
RawBattOrSample frame2[] = {RawBattOrSample{1, 1}};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(frame_header2, frame2, 1));
// Send an empty last frame to indicate that we're done.
BattOrFrameHeader frame_header3{3, 0 * sizeof(RawBattOrSample)};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(frame_header3, nullptr, 0));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
EXPECT_EQ(
"# BattOr\n# voltage_range [-2401.8, 2398.2] mV\n# "
"current_range [-1200.9, 1199.1] mA\n"
"# sample_rate 1000 Hz, gain 1.0x\n"
"0.00 -0.3 -0.6\n1.00 0.3 0.6\n2.00 0.9 1.8\n3.00 -0.3 -0.6\n",
GetTrace());
}
TEST_F(BattOrAgentTest, StopTracingFailsWithoutConnection) {
GetAgent()->StopTracing();
GetTaskRunner()->RunUntilIdle();
GetAgent()->OnConnectionOpened(false);
GetTaskRunner()->RunUntilIdle();
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_CONNECTION_FAILED, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracingFailsIfEEPROMRequestSendFails) {
RunStopTracingTo(BattOrAgentState::CONNECTED);
OnBytesSent(false);
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_SEND_ERROR, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracingRetriesEEPROMRead) {
RunStopTracingTo(BattOrAgentState::EEPROM_REQUEST_SENT);
OnMessageRead(false, BATTOR_MESSAGE_TYPE_CONTROL_ACK, nullptr);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(BattOrEEPROM()));
// Give confirmation that the samples request was sent.
OnBytesSent(true);
BattOrFrameHeader cal_frame_header{0, sizeof(RawBattOrSample)};
RawBattOrSample cal_frame[] = {RawBattOrSample{1, 1}};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(cal_frame_header, cal_frame, 1));
BattOrFrameHeader frame_header{1, 0};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(frame_header, nullptr, 0));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracingFailsIfEEPROMReadFails) {
RunStopTracingTo(BattOrAgentState::EEPROM_REQUEST_SENT);
for (int i = 0; i < 21; i++) {
OnMessageRead(false, BATTOR_MESSAGE_TYPE_CONTROL_ACK, nullptr);
}
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_RECEIVE_ERROR, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracingFailsIfEEPROMWrongAckRead) {
RunStopTracingTo(BattOrAgentState::EEPROM_REQUEST_SENT);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK, ToCharVector(kInitAck));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_UNEXPECTED_MESSAGE, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracingFailsIfRequestSamplesFails) {
RunStopTracingTo(BattOrAgentState::EEPROM_RECEIVED);
OnBytesSent(false);
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_SEND_ERROR, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracingSucceedsWithOneCalibrationFrameReadFailure) {
RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT);
// Make a read fail in order to make sure that the agent will retry.
OnMessageRead(false, BATTOR_MESSAGE_TYPE_SAMPLES, nullptr);
BattOrFrameHeader cal_frame_header{0, sizeof(RawBattOrSample)};
RawBattOrSample cal_frame[] = {RawBattOrSample{1, 1}};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(cal_frame_header, cal_frame, 1));
BattOrFrameHeader frame_header{1, 0};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(frame_header, nullptr, 0));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracingFailsWithManyCalibrationFrameReadFailures) {
RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT);
// We attempt the read a max of 20 times: send that many failures.
for (int i = 0; i < 21; i++) {
OnMessageRead(false, BATTOR_MESSAGE_TYPE_SAMPLES, nullptr);
}
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_RECEIVE_ERROR, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracingSucceedsWithOneDataFrameReadFailure) {
RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED);
// Make a read fail in order to make sure that the agent will retry.
OnMessageRead(false, BATTOR_MESSAGE_TYPE_SAMPLES, nullptr);
BattOrFrameHeader frame_header{1, 0};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(frame_header, nullptr, 0));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracingFailsWithManyDataFrameReadFailures) {
RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED);
// We attempt the read a max of 20 times: send that many failures.
for (int i = 0; i < 21; i++) {
OnMessageRead(false, BATTOR_MESSAGE_TYPE_SAMPLES, nullptr);
}
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_RECEIVE_ERROR, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracingRetriesResetEachFrame) {
RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED);
// Send 11 failures on two different reads: because the retry count should
// reset after a successful read, this should still be okay.
for (int i = 0; i < 11; i++) {
OnMessageRead(false, BATTOR_MESSAGE_TYPE_SAMPLES, nullptr);
}
BattOrFrameHeader frame_header1{1, 1 * sizeof(RawBattOrSample)};
RawBattOrSample frame1[] = {RawBattOrSample{1, 1}};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(frame_header1, frame1, 1));
for (int i = 0; i < 11; i++) {
OnMessageRead(false, BATTOR_MESSAGE_TYPE_SAMPLES, nullptr);
}
BattOrFrameHeader frame_header2{2, 0};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(frame_header2, nullptr, 0));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracingFailsIfSamplesReadHasWrongType) {
RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT);
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK, ToCharVector(kInitAck));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_UNEXPECTED_MESSAGE, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracingFailsIfCalibrationFrameHasWrongLength) {
RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT);
// Send a calibration frame with a mismatch between the frame length in the
// header and the actual frame length.
BattOrFrameHeader cal_frame_header{0, 1 * sizeof(RawBattOrSample)};
RawBattOrSample cal_frame[] = {
RawBattOrSample{1, 1}, RawBattOrSample{2, 2},
};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(cal_frame_header, cal_frame, 2));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_UNEXPECTED_MESSAGE, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracingFailsIfDataFrameHasWrongLength) {
RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT);
BattOrFrameHeader cal_frame_header{0, 1 * sizeof(RawBattOrSample)};
RawBattOrSample cal_frame[] = {
RawBattOrSample{1, 1},
};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(cal_frame_header, cal_frame, 1));
// Send a data frame with a mismatch between the frame length in the
// header and the actual frame length.
BattOrFrameHeader frame_header{1, 2 * sizeof(RawBattOrSample)};
RawBattOrSample frame[] = {RawBattOrSample{1, 1}};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(frame_header, frame, 1));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_UNEXPECTED_MESSAGE, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracingFailsIfCalibrationFrameMissingByte) {
RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT);
BattOrFrameHeader cal_frame_header{0, 2 * sizeof(RawBattOrSample)};
RawBattOrSample cal_frame[] = {
RawBattOrSample{1, 1}, RawBattOrSample{2, 2},
};
// Remove the last byte from the frame to make it invalid.
std::unique_ptr<vector<char>> cal_frame_bytes =
CreateFrame(cal_frame_header, cal_frame, 2);
cal_frame_bytes->pop_back();
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES, std::move(cal_frame_bytes));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_UNEXPECTED_MESSAGE, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracingFailsIfDataFrameMissingByte) {
RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT);
BattOrFrameHeader cal_frame_header{0, 1 * sizeof(RawBattOrSample)};
RawBattOrSample cal_frame[] = {
RawBattOrSample{1, 1},
};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(cal_frame_header, cal_frame, 1));
BattOrFrameHeader frame_header{1, 1 * sizeof(RawBattOrSample)};
RawBattOrSample frame[] = {RawBattOrSample{1, 1}};
// Remove the last byte from the frame to make it invalid.
std::unique_ptr<vector<char>> frame_bytes =
CreateFrame(frame_header, frame, 1);
frame_bytes->pop_back();
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES, std::move(frame_bytes));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_UNEXPECTED_MESSAGE, GetCommandError());
}
TEST_F(BattOrAgentTest, StopTracingFailsIfFrameArrivesOutOfOrder) {
RunStopTracingTo(BattOrAgentState::CALIBRATION_FRAME_RECEIVED);
BattOrFrameHeader frame_header{1, 1 * sizeof(RawBattOrSample)};
RawBattOrSample frame[] = {RawBattOrSample{1, 1}};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(frame_header, frame, 1));
// Skip frame with sequence number 2.
frame_header = BattOrFrameHeader{3, 1 * sizeof(RawBattOrSample)};
frame[0] = RawBattOrSample{1, 1};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(frame_header, frame, 1));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_UNEXPECTED_MESSAGE, GetCommandError());
}
TEST_F(BattOrAgentTest, RecordClockSyncMarker) {
testing::InSequence s;
EXPECT_CALL(*GetAgent()->GetConnection(), Open());
BattOrControlMessage request_current_sample_msg{
BATTOR_CONTROL_MESSAGE_TYPE_READ_SAMPLE_COUNT, 0, 0};
EXPECT_CALL(*GetAgent()->GetConnection(),
SendBytes(BATTOR_MESSAGE_TYPE_CONTROL,
BufferEq(&request_current_sample_msg,
sizeof(request_current_sample_msg)),
sizeof(request_current_sample_msg)));
EXPECT_CALL(*GetAgent()->GetConnection(),
ReadMessage(BATTOR_MESSAGE_TYPE_CONTROL_ACK));
RunRecordClockSyncMarkerTo(
BattOrAgentState::RECORD_CLOCK_SYNC_MARKER_COMPLETE);
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
}
TEST_F(BattOrAgentTest, RecordClockSyncMarkerPrintsInStopTracingResult) {
// Record a clock sync marker that says CLOCK_SYNC_ID happened at sample #2.
RunRecordClockSyncMarkerTo(BattOrAgentState::CURRENT_SAMPLE_REQUEST_SENT);
uint32_t current_sample = 1;
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(current_sample));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
RunStopTracingTo(BattOrAgentState::SAMPLES_REQUEST_SENT);
// Now run StopTracing, and make sure that CLOCK_SYNC_ID gets printed out with
// sample #2 (including calibration frame samples).
BattOrFrameHeader cal_frame_header{0, 1 * sizeof(RawBattOrSample)};
RawBattOrSample cal_frame[] = {RawBattOrSample{1, 1}};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(cal_frame_header, cal_frame, 1));
BattOrFrameHeader frame_header1{1, 2 * sizeof(RawBattOrSample)};
RawBattOrSample frame1[] = {RawBattOrSample{1, 1}, RawBattOrSample{2, 2}};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(frame_header1, frame1, 2));
BattOrFrameHeader frame_header2{2, 0};
OnMessageRead(true, BATTOR_MESSAGE_TYPE_SAMPLES,
CreateFrame(frame_header2, {}, 0));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
EXPECT_EQ(
"# BattOr\n# voltage_range [-2401.2, 2398.8] mV\n# "
"current_range [-1200.6, 1199.4] mA\n"
"# sample_rate 1000 Hz, gain 1.0x\n"
"0.00 0.0 0.0 <MY_MARKER>\n"
"1.00 0.6 1.2\n",
GetTrace());
}
TEST_F(BattOrAgentTest, RecordClockSyncMarkerFailsWithoutConnection) {
GetAgent()->RecordClockSyncMarker("my_marker");
GetTaskRunner()->RunUntilIdle();
GetAgent()->OnConnectionOpened(false);
GetTaskRunner()->RunUntilIdle();
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_CONNECTION_FAILED, GetCommandError());
}
TEST_F(BattOrAgentTest, RecordClockSyncMarkerFailsIfSampleRequestSendFails) {
RunRecordClockSyncMarkerTo(BattOrAgentState::CONNECTED);
OnBytesSent(false);
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_SEND_ERROR, GetCommandError());
}
TEST_F(BattOrAgentTest, RecordClockSyncMarkerRetriesCurrentSampleRead) {
RunRecordClockSyncMarkerTo(BattOrAgentState::CURRENT_SAMPLE_REQUEST_SENT);
OnMessageRead(false, BATTOR_MESSAGE_TYPE_CONTROL_ACK, nullptr);
uint32_t current_sample = 1;
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(current_sample));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
}
TEST_F(BattOrAgentTest,
RecordClockSyncMarkerFailsIfCurrentSampleReadHasWrongType) {
RunRecordClockSyncMarkerTo(BattOrAgentState::CURRENT_SAMPLE_REQUEST_SENT);
uint32_t current_sample = 1;
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL,
ToCharVector(current_sample));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_UNEXPECTED_MESSAGE, GetCommandError());
}
TEST_F(BattOrAgentTest, GetFirmwareGitHash) {
RunGetFirmwareGitHashTo(BattOrAgentState::READ_GIT_HASH_RECEIVED);
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_NONE, GetCommandError());
EXPECT_EQ("GITHASH", GetGitHash());
}
TEST_F(BattOrAgentTest, GetFirmwareGitHashFailsWithoutConnection) {
GetAgent()->GetFirmwareGitHash();
GetTaskRunner()->RunUntilIdle();
GetAgent()->OnConnectionOpened(false);
GetTaskRunner()->RunUntilIdle();
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_CONNECTION_FAILED, GetCommandError());
}
TEST_F(BattOrAgentTest, GetFirmwareGitHashFailsIfReadHasWrongType) {
RunGetFirmwareGitHashTo(BattOrAgentState::GIT_FIRMWARE_HASH_REQUEST_SENT);
uint32_t current_sample = 1;
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL,
ToCharVector(current_sample));
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_UNEXPECTED_MESSAGE, GetCommandError());
}
TEST_F(BattOrAgentTest, GetFirmwareGitHashFailsIfInitSendFails) {
RunGetFirmwareGitHashTo(BattOrAgentState::CONNECTED);
OnBytesSent(false);
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_SEND_ERROR, GetCommandError());
}
TEST_F(BattOrAgentTest, GetFirmwareGitHashFailsIfInitAckReadFails) {
RunGetFirmwareGitHashTo(BattOrAgentState::INIT_SENT);
for (int i =0; i < 21; i++) {
OnMessageRead(false, BATTOR_MESSAGE_TYPE_CONTROL_ACK, nullptr);
// Bytes will be sent because INIT will be retried.
OnBytesSent(true);
}
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_TOO_MANY_INIT_RETRIES, GetCommandError());
}
TEST_F(BattOrAgentTest, GetFirmwareGithashFailsIfInitWrongAckRead) {
RunGetFirmwareGitHashTo(BattOrAgentState::INIT_SENT);
for (int i = 0; i < 21; i++) {
OnMessageRead(true, BATTOR_MESSAGE_TYPE_CONTROL_ACK,
ToCharVector(kStartTracingAck));
// Bytes will be sent because INIT will be retried.
OnBytesSent(true);
}
EXPECT_TRUE(IsCommandComplete());
EXPECT_EQ(BATTOR_ERROR_TOO_MANY_INIT_RETRIES, GetCommandError());
}
} // namespace battor