/** * Copyright © 2020 IBM Corporation * * 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 "action_environment.hpp" #include "action_error.hpp" #include "device.hpp" #include "i2c_compare_bytes_action.hpp" #include "i2c_interface.hpp" #include "id_map.hpp" #include "mock_services.hpp" #include "mocked_i2c_interface.hpp" #include #include #include #include #include #include #include #include using namespace phosphor::power::regulators; using ::testing::NotNull; using ::testing::Return; using ::testing::SetArrayArgument; using ::testing::Throw; using ::testing::TypedEq; // Test for I2CCompareBytesAction(uint8_t reg, // const std::vector& values) TEST(I2CCompareBytesActionTests, Constructor1) { // Test where works try { std::vector values{0x56, 0x14, 0xDA}; I2CCompareBytesAction action{0x7C, values}; EXPECT_EQ(action.getRegister(), 0x7C); EXPECT_EQ(action.getValues().size(), 3); EXPECT_EQ(action.getValues()[0], 0x56); EXPECT_EQ(action.getValues()[1], 0x14); EXPECT_EQ(action.getValues()[2], 0xDA); EXPECT_EQ(action.getMasks().size(), 3); EXPECT_EQ(action.getMasks()[0], 0xFF); EXPECT_EQ(action.getMasks()[1], 0xFF); EXPECT_EQ(action.getMasks()[2], 0xFF); } catch (...) { ADD_FAILURE() << "Should not have caught exception."; } // Test where fails: Values vector is empty try { std::vector values{}; I2CCompareBytesAction action{0x7C, values}; ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Values vector is empty"); } catch (...) { ADD_FAILURE() << "Should not have caught exception."; } } // Test for I2CCompareBytesAction(uint8_t reg, // const std::vector& values, // const std::vector& masks) TEST(I2CCompareBytesActionTests, Constructor2) { // Test where works try { std::vector values{0x56, 0x14}; std::vector masks{0x7E, 0x3C}; I2CCompareBytesAction action{0xA0, values, masks}; EXPECT_EQ(action.getRegister(), 0xA0); EXPECT_EQ(action.getValues().size(), 2); EXPECT_EQ(action.getValues()[0], 0x56); EXPECT_EQ(action.getValues()[1], 0x14); EXPECT_EQ(action.getMasks().size(), 2); EXPECT_EQ(action.getMasks()[0], 0x7E); EXPECT_EQ(action.getMasks()[1], 0x3C); } catch (...) { ADD_FAILURE() << "Should not have caught exception."; } // Test where fails: Values vector is empty try { std::vector values{}; std::vector masks{}; I2CCompareBytesAction action{0xA0, values, masks}; ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Values vector is empty"); } catch (...) { ADD_FAILURE() << "Should not have caught exception."; } // Test where fails: Masks vector different size than values vector try { std::vector values{0x56, 0x14, 0xFE}; std::vector masks{0x7E, 0x3C}; I2CCompareBytesAction action{0x7C, values, masks}; ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Masks vector has invalid size"); } catch (...) { ADD_FAILURE() << "Should not have caught exception."; } } TEST(I2CCompareBytesActionTests, Execute) { // Test where works: Equal: Mask specified try { // Create mock I2CInterface: read() returns values 0xD7, 0x96 std::unique_ptr i2cInterface = std::make_unique(); uint8_t actualValues[] = {0xD7, 0x96}; EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); EXPECT_CALL(*i2cInterface, read(0xA0, TypedEq(2), NotNull(), i2c::I2CInterface::Mode::I2C)) .Times(1) .WillOnce(SetArrayArgument<2>(actualValues, actualValues + 2)); // Create Device, IDMap, MockServices, and ActionEnvironment Device device{ "reg1", true, "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1", std::move(i2cInterface)}; IDMap idMap{}; idMap.addDevice(device); MockServices services{}; ActionEnvironment env{idMap, "reg1", services}; // Actual values: 0xD7 = 1101 0111 0x96 = 1001 0110 // Masks : 0x7E = 0111 1110 0x3C = 0011 1100 // Results : 0x56 = 0101 0110 0x14 = 0001 0100 const std::vector values{0x56, 0x14}; const std::vector masks{0x7E, 0x3C}; I2CCompareBytesAction action{0xA0, values, masks}; EXPECT_EQ(action.execute(env), true); } catch (...) { ADD_FAILURE() << "Should not have caught exception."; } // Test where works: Equal: Mask not specified try { // Create mock I2CInterface: read() returns values 0x56, 0x14, 0xDA std::unique_ptr i2cInterface = std::make_unique(); uint8_t actualValues[] = {0x56, 0x14, 0xDA}; EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); EXPECT_CALL(*i2cInterface, read(0x7C, TypedEq(3), NotNull(), i2c::I2CInterface::Mode::I2C)) .Times(1) .WillOnce(SetArrayArgument<2>(actualValues, actualValues + 3)); // Create Device, IDMap, MockServices, and ActionEnvironment Device device{ "reg1", true, "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1", std::move(i2cInterface)}; IDMap idMap{}; idMap.addDevice(device); MockServices services{}; ActionEnvironment env{idMap, "reg1", services}; std::vector values{0x56, 0x14, 0xDA}; I2CCompareBytesAction action{0x7C, values}; EXPECT_EQ(action.execute(env), true); } catch (...) { ADD_FAILURE() << "Should not have caught exception."; } // Test where works: Not equal: Mask specified try { // Create mock I2CInterface: read() returns values 0xD7, 0x96 std::unique_ptr i2cInterface = std::make_unique(); uint8_t actualValues[] = {0xD7, 0x96}; EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); EXPECT_CALL(*i2cInterface, read(0xA0, TypedEq(2), NotNull(), i2c::I2CInterface::Mode::I2C)) .Times(1) .WillOnce(SetArrayArgument<2>(actualValues, actualValues + 2)); // Create Device, IDMap, MockServices, and ActionEnvironment Device device{ "reg1", true, "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1", std::move(i2cInterface)}; IDMap idMap{}; idMap.addDevice(device); MockServices services{}; ActionEnvironment env{idMap, "reg1", services}; // Actual values: 0xD7 = 1101 0111 0x96 = 1001 0110 // Masks : 0x7E = 0111 1110 0x3C = 0011 1100 // Results : 0x56 = 0101 0110 0x14 = 0001 0100 const std::vector values{0x56, 0x13}; const std::vector masks{0x7E, 0x3C}; I2CCompareBytesAction action{0xA0, values, masks}; EXPECT_EQ(action.execute(env), false); } catch (...) { ADD_FAILURE() << "Should not have caught exception."; } // Test where works: Not equal: Mask not specified try { // Create mock I2CInterface: read() returns values 0x56, 0x14, 0xDA std::unique_ptr i2cInterface = std::make_unique(); uint8_t actualValues[] = {0x56, 0x14, 0xDA}; EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); EXPECT_CALL(*i2cInterface, read(0x7C, TypedEq(3), NotNull(), i2c::I2CInterface::Mode::I2C)) .Times(1) .WillOnce(SetArrayArgument<2>(actualValues, actualValues + 3)); // Create Device, IDMap, MockServices, and ActionEnvironment Device device{ "reg1", true, "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1", std::move(i2cInterface)}; IDMap idMap{}; idMap.addDevice(device); MockServices services{}; ActionEnvironment env{idMap, "reg1", services}; std::vector values{0x56, 0x14, 0xDB}; I2CCompareBytesAction action{0x7C, values}; EXPECT_EQ(action.execute(env), false); } catch (...) { ADD_FAILURE() << "Should not have caught exception."; } // Test where works: Single byte try { // Create mock I2CInterface: read() returns value 0xD7 std::unique_ptr i2cInterface = std::make_unique(); uint8_t actualValues[] = {0xD7}; EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); EXPECT_CALL(*i2cInterface, read(0xA0, TypedEq(1), NotNull(), i2c::I2CInterface::Mode::I2C)) .Times(1) .WillOnce(SetArrayArgument<2>(actualValues, actualValues + 1)); // Create Device, IDMap, MockServices, and ActionEnvironment Device device{ "reg1", true, "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1", std::move(i2cInterface)}; IDMap idMap{}; idMap.addDevice(device); MockServices services{}; ActionEnvironment env{idMap, "reg1", services}; // Actual values: 0xD7 = 1101 0111 // Masks : 0x7E = 0111 1110 // Results : 0x56 = 0101 0110 std::vector values{0x56}; std::vector masks{0x7E}; I2CCompareBytesAction action{0xA0, values, masks}; EXPECT_EQ(action.execute(env), true); } catch (...) { ADD_FAILURE() << "Should not have caught exception."; } // Test where fails: Getting I2CInterface fails try { // Create IDMap, MockServices, and ActionEnvironment IDMap idMap{}; MockServices services{}; ActionEnvironment env{idMap, "reg1", services}; std::vector values{0x56, 0x14, 0xDB}; I2CCompareBytesAction action{0x7C, values}; action.execute(env); ADD_FAILURE() << "Should not have reached this line."; } catch (const std::invalid_argument& e) { EXPECT_STREQ(e.what(), "Unable to find device with ID \"reg1\""); } catch (...) { ADD_FAILURE() << "Should not have caught exception."; } // Test where fails: Reading bytes fails try { // Create mock I2CInterface: read() throws an I2CException std::unique_ptr i2cInterface = std::make_unique(); EXPECT_CALL(*i2cInterface, isOpen).Times(1).WillOnce(Return(true)); EXPECT_CALL(*i2cInterface, read(0x7C, TypedEq(2), NotNull(), i2c::I2CInterface::Mode::I2C)) .Times(1) .WillOnce(Throw(i2c::I2CException{"Failed to read i2c block data", "/dev/i2c-1", 0x70})); // Create Device, IDMap, MockServices, and ActionEnvironment Device device{ "reg1", true, "/xyz/openbmc_project/inventory/system/chassis/motherboard/reg1", std::move(i2cInterface)}; IDMap idMap{}; idMap.addDevice(device); MockServices services{}; ActionEnvironment env{idMap, "reg1", services}; std::vector values{0x56, 0x14}; I2CCompareBytesAction action{0x7C, values}; action.execute(env); ADD_FAILURE() << "Should not have reached this line."; } catch (const ActionError& e) { EXPECT_STREQ(e.what(), "ActionError: i2c_compare_bytes: { register: " "0x7C, values: [ 0x56, 0x14 ], masks: [ 0xFF, 0xFF ] }"); try { // Re-throw inner I2CException std::rethrow_if_nested(e); ADD_FAILURE() << "Should not have reached this line."; } catch (const i2c::I2CException& ie) { EXPECT_STREQ(ie.what(), "I2CException: Failed to read i2c block data: bus " "/dev/i2c-1, addr 0x70"); } catch (...) { ADD_FAILURE() << "Should not have caught exception."; } } catch (...) { ADD_FAILURE() << "Should not have caught exception."; } } TEST(I2CCompareBytesActionTests, GetRegister) { std::vector values{0x56, 0x14}; I2CCompareBytesAction action{0xA0, values}; EXPECT_EQ(action.getRegister(), 0xA0); } TEST(I2CCompareBytesActionTests, GetValues) { std::vector values{0x56, 0x14}; std::vector masks{0x7E, 0x3C}; I2CCompareBytesAction action{0xA0, values, masks}; EXPECT_EQ(action.getValues().size(), 2); EXPECT_EQ(action.getValues()[0], 0x56); EXPECT_EQ(action.getValues()[1], 0x14); } TEST(I2CCompareBytesActionTests, GetMasks) { // Test where masks were not specified { std::vector values{0x56, 0x14, 0xDA}; I2CCompareBytesAction action{0x7C, values}; EXPECT_EQ(action.getMasks().size(), 3); EXPECT_EQ(action.getMasks()[0], 0xFF); EXPECT_EQ(action.getMasks()[1], 0xFF); EXPECT_EQ(action.getMasks()[2], 0xFF); } // Test where masks were specified { std::vector values{0x56, 0x14}; std::vector masks{0x7E, 0x3C}; I2CCompareBytesAction action{0xA0, values, masks}; EXPECT_EQ(action.getMasks().size(), 2); EXPECT_EQ(action.getMasks()[0], 0x7E); EXPECT_EQ(action.getMasks()[1], 0x3C); } } TEST(I2CCompareBytesActionTests, ToString) { std::vector values{0x56, 0x14}; std::vector masks{0x7E, 0x3C}; I2CCompareBytesAction action{0xA0, values, masks}; EXPECT_EQ(action.toString(), "i2c_compare_bytes: { register: 0xA0, values: " "[ 0x56, 0x14 ], masks: [ 0x7E, 0x3C ] }"); }