| // Copyright 2023 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <algorithm> |
| #include <memory> |
| #include <vector> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "libec/ec_command.h" |
| #include "libec/i2c_passthru_command.h" |
| |
| namespace ec { |
| |
| namespace { |
| |
| using ::testing::ElementsAreArray; |
| using ::testing::NiceMock; |
| using ::testing::Return; |
| |
| constexpr uint8_t kI2cBus = 5; |
| constexpr uint8_t kI2cAddr = 0x30; |
| constexpr size_t kI2cReadLen = 16; |
| constexpr size_t kI2cResponseHeaderSize = 2; |
| |
| TEST(I2cPassthruCommand, I2cPassthruCommandWriteSuccess) { |
| const std::vector<uint8_t> kData{0xaa, 0xbb, 0xcc, 0xdd}; |
| struct ec_params_i2c_passthru_msg expected_write_info { |
| .addr_flags = kI2cAddr, .len = static_cast<uint16_t>(kData.size()) |
| }; |
| auto* ptr = reinterpret_cast<uint8_t*>(&expected_write_info); |
| std::vector<uint8_t> expected_msg_and_payload( |
| ptr, ptr + sizeof(expected_write_info)); |
| expected_msg_and_payload.insert(expected_msg_and_payload.end(), kData.begin(), |
| kData.end()); |
| |
| auto cmd = I2cPassthruCommand::Create(kI2cBus, kI2cAddr, kData, 0); |
| EXPECT_NE(cmd, nullptr); |
| EXPECT_EQ(cmd->Command(), EC_CMD_I2C_PASSTHRU); |
| EXPECT_EQ(cmd->Version(), 0); |
| EXPECT_EQ(cmd->Req()->req.port, kI2cBus); |
| EXPECT_EQ(cmd->Req()->req.num_msgs, 1); |
| EXPECT_THAT(expected_msg_and_payload, |
| ElementsAreArray(cmd->Req()->msg_and_payload.begin(), |
| expected_msg_and_payload.size())); |
| EXPECT_EQ(cmd->RespSize(), kI2cResponseHeaderSize); |
| } |
| |
| TEST(I2cPassthruCommand, I2cPassthruCommandWriteFailure) { |
| const std::vector<uint8_t> kData(kMaxPacketSize + 1, 0xaa); |
| auto cmd = I2cPassthruCommand::Create(kI2cBus, kI2cAddr, kData, 0); |
| EXPECT_EQ(cmd, nullptr); |
| } |
| |
| TEST(I2cPassthruCommand, I2cPassthruCommandReadSuccess) { |
| struct ec_params_i2c_passthru_msg expected_read_info { |
| .addr_flags = kI2cAddr | EC_I2C_FLAG_READ, .len = kI2cReadLen |
| }; |
| auto* ptr = reinterpret_cast<uint8_t*>(&expected_read_info); |
| std::vector<uint8_t> expected_msg_and_payload( |
| ptr, ptr + sizeof(expected_read_info)); |
| |
| auto cmd = I2cPassthruCommand::Create(kI2cBus, kI2cAddr, {}, kI2cReadLen); |
| EXPECT_NE(cmd, nullptr); |
| EXPECT_EQ(cmd->Command(), EC_CMD_I2C_PASSTHRU); |
| EXPECT_EQ(cmd->Version(), 0); |
| EXPECT_EQ(cmd->Req()->req.port, kI2cBus); |
| EXPECT_EQ(cmd->Req()->req.num_msgs, 1); |
| EXPECT_THAT(expected_msg_and_payload, |
| ElementsAreArray(cmd->Req()->msg_and_payload.begin(), |
| expected_msg_and_payload.size())); |
| EXPECT_EQ(cmd->RespSize(), kI2cResponseHeaderSize + kI2cReadLen); |
| } |
| |
| TEST(I2cPassthruCommand, I2cPassthruCommandReadFail) { |
| auto cmd = I2cPassthruCommand::Create(kI2cBus, kI2cAddr, {}, |
| i2c_passthru::kResponseDataMaxSize + 1); |
| EXPECT_EQ(cmd, nullptr); |
| } |
| |
| TEST(I2cPassthruCommand, I2cPassthruCommandWriteAndRead) { |
| const std::vector<uint8_t> kData{0xaa, 0xbb, 0xcc, 0xdd}; |
| struct ec_params_i2c_passthru_msg expected_write_info { |
| .addr_flags = kI2cAddr, .len = static_cast<uint16_t>(kData.size()) |
| }; |
| struct ec_params_i2c_passthru_msg expected_read_info { |
| .addr_flags = kI2cAddr | EC_I2C_FLAG_READ, .len = kI2cReadLen |
| }; |
| auto* write_info_ptr = reinterpret_cast<uint8_t*>(&expected_write_info); |
| auto* read_info_ptr = reinterpret_cast<uint8_t*>(&expected_read_info); |
| |
| std::vector<uint8_t> expected_msg_and_payload( |
| write_info_ptr, write_info_ptr + sizeof(expected_write_info)); |
| expected_msg_and_payload.insert(expected_msg_and_payload.end(), read_info_ptr, |
| read_info_ptr + sizeof(expected_read_info)); |
| expected_msg_and_payload.insert(expected_msg_and_payload.end(), kData.begin(), |
| kData.end()); |
| |
| auto cmd = I2cPassthruCommand::Create(kI2cBus, kI2cAddr, kData, kI2cReadLen); |
| EXPECT_NE(cmd, nullptr); |
| EXPECT_EQ(cmd->Command(), EC_CMD_I2C_PASSTHRU); |
| EXPECT_EQ(cmd->Version(), 0); |
| EXPECT_EQ(cmd->Req()->req.port, kI2cBus); |
| EXPECT_EQ(cmd->Req()->req.num_msgs, 2); |
| EXPECT_THAT(expected_msg_and_payload, |
| ElementsAreArray(cmd->Req()->msg_and_payload.begin(), |
| expected_msg_and_payload.size())); |
| EXPECT_EQ(cmd->RespSize(), kI2cResponseHeaderSize + kI2cReadLen); |
| } |
| |
| TEST(I2cPassthruCommand, I2cPassthruCommandNoOp) { |
| auto cmd = I2cPassthruCommand::Create(kI2cBus, kI2cAddr, {}, 0); |
| EXPECT_NE(cmd, nullptr); |
| EXPECT_EQ(cmd->Command(), EC_CMD_I2C_PASSTHRU); |
| EXPECT_EQ(cmd->Version(), 0); |
| EXPECT_EQ(cmd->Req()->req.port, kI2cBus); |
| EXPECT_EQ(cmd->Req()->req.num_msgs, 0); |
| EXPECT_EQ(cmd->RespSize(), kI2cResponseHeaderSize); |
| } |
| |
| // Mock the underlying EcCommand to test. |
| class I2cPassthruCommandTest : public testing::Test { |
| public: |
| class MockI2cPassthruCommand : public I2cPassthruCommand { |
| public: |
| MOCK_METHOD(struct i2c_passthru::Response*, Resp, (), (const, override)); |
| MOCK_METHOD(uint32_t, RespSize, (), (const, override)); |
| }; |
| }; |
| |
| TEST_F(I2cPassthruCommandTest, I2cPassthruCommandResponseSucceed) { |
| const std::vector<uint8_t> kData{0xaa, 0xbb, 0xcc, 0xdd}; |
| i2c_passthru::Response response{.resp = {.i2c_status = 0, .num_msgs = 1}}; |
| std::copy(kData.begin(), kData.end(), response.data.begin()); |
| |
| auto mock_cmd = |
| I2cPassthruCommand::Create<NiceMock<MockI2cPassthruCommand>>(0, 0, {}, 0); |
| ON_CALL(*mock_cmd, Resp).WillByDefault(Return(&response)); |
| ON_CALL(*mock_cmd, RespSize) |
| .WillByDefault(Return(kI2cResponseHeaderSize + kData.size())); |
| EXPECT_EQ(mock_cmd->I2cStatus(), 0); |
| EXPECT_THAT(mock_cmd->RespData(), ElementsAreArray(kData)); |
| } |
| |
| TEST_F(I2cPassthruCommandTest, I2cPassthruCommandResponseFailed) { |
| auto mock_cmd = |
| I2cPassthruCommand::Create<NiceMock<MockI2cPassthruCommand>>(0, 0, {}, 0); |
| |
| i2c_passthru::Response response{ |
| .resp = {.i2c_status = EC_I2C_STATUS_NAK, .num_msgs = 0}, .data = {}}; |
| |
| ON_CALL(*mock_cmd, Resp).WillByDefault(Return(&response)); |
| ON_CALL(*mock_cmd, RespSize).WillByDefault(Return(kI2cResponseHeaderSize)); |
| EXPECT_EQ(mock_cmd->I2cStatus(), EC_I2C_STATUS_NAK); |
| EXPECT_TRUE(mock_cmd->RespData().empty()); |
| } |
| |
| } // namespace |
| |
| } // namespace ec |