| #include "MCTPEndpoint.hpp" |
| #include "MCTPReactor.hpp" |
| #include "Utils.hpp" |
| |
| #include <cstdint> |
| #include <functional> |
| #include <memory> |
| #include <string> |
| #include <system_error> |
| #include <vector> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| class MockMCTPDevice : public MCTPDevice |
| { |
| public: |
| ~MockMCTPDevice() override = default; |
| |
| MOCK_METHOD(void, setup, |
| (std::function<void(const std::error_code& ec, |
| const std::shared_ptr<MCTPEndpoint>& ep)> && |
| added), |
| (override)); |
| MOCK_METHOD(void, remove, (), (override)); |
| MOCK_METHOD(std::string, describe, (), (const, override)); |
| }; |
| |
| class MockMCTPEndpoint : public MCTPEndpoint |
| { |
| public: |
| ~MockMCTPEndpoint() override = default; |
| |
| MOCK_METHOD(int, network, (), (const, override)); |
| MOCK_METHOD(uint8_t, eid, (), (const, override)); |
| MOCK_METHOD(void, subscribe, |
| (Event && degraded, Event&& available, Event&& removed), |
| (override)); |
| MOCK_METHOD(void, remove, (), (override)); |
| MOCK_METHOD(std::string, describe, (), (const, override)); |
| MOCK_METHOD(std::shared_ptr<MCTPDevice>, device, (), (const, override)); |
| }; |
| |
| class MockAssociationServer : public AssociationServer |
| { |
| public: |
| ~MockAssociationServer() override = default; |
| |
| MOCK_METHOD(void, associate, |
| (const std::string& path, |
| const std::vector<Association>& associations), |
| (override)); |
| MOCK_METHOD(void, disassociate, (const std::string& path), (override)); |
| }; |
| |
| class MCTPReactorFixture : public testing::Test |
| { |
| protected: |
| void SetUp() override |
| { |
| reactor = std::make_shared<MCTPReactor>(assoc); |
| device = std::make_shared<MockMCTPDevice>(); |
| EXPECT_CALL(*device, describe()) |
| .WillRepeatedly(testing::Return("mock device")); |
| |
| endpoint = std::make_shared<MockMCTPEndpoint>(); |
| EXPECT_CALL(*endpoint, device()) |
| .WillRepeatedly(testing::Return(device)); |
| EXPECT_CALL(*endpoint, describe()) |
| .WillRepeatedly(testing::Return("mock endpoint")); |
| EXPECT_CALL(*endpoint, eid()).WillRepeatedly(testing::Return(9)); |
| EXPECT_CALL(*endpoint, network()).WillRepeatedly(testing::Return(1)); |
| } |
| |
| void TearDown() override |
| { |
| // https://stackoverflow.com/a/10289205 |
| EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(endpoint.get())); |
| EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(device.get())); |
| } |
| |
| MockAssociationServer assoc; |
| std::shared_ptr<MCTPReactor> reactor; |
| std::shared_ptr<MockMCTPDevice> device; |
| std::shared_ptr<MockMCTPEndpoint> endpoint; |
| }; |
| |
| TEST_F(MCTPReactorFixture, manageNullDevice) |
| { |
| reactor->manageMCTPDevice("/test", {}); |
| reactor->unmanageMCTPDevice("/test"); |
| } |
| |
| TEST_F(MCTPReactorFixture, manageMockDeviceSetupFailure) |
| { |
| EXPECT_CALL(*device, remove()); |
| EXPECT_CALL(*device, setup(testing::_)) |
| .WillOnce(testing::InvokeArgument<0>( |
| std::make_error_code(std::errc::permission_denied), endpoint)); |
| |
| reactor->manageMCTPDevice("/test", device); |
| reactor->unmanageMCTPDevice("/test"); |
| } |
| |
| TEST_F(MCTPReactorFixture, manageMockDevice) |
| { |
| std::function<void(const std::shared_ptr<MCTPEndpoint>& ep)> removeHandler; |
| |
| std::vector<Association> requiredAssociation{ |
| {"configured_by", "configures", "/test"}}; |
| EXPECT_CALL(assoc, associate("/xyz/openbmc_project/mctp/1/9", |
| requiredAssociation)); |
| EXPECT_CALL(assoc, disassociate("/xyz/openbmc_project/mctp/1/9")); |
| |
| EXPECT_CALL(*endpoint, remove()).WillOnce(testing::Invoke([&]() { |
| removeHandler(endpoint); |
| })); |
| EXPECT_CALL(*endpoint, subscribe(testing::_, testing::_, testing::_)) |
| .WillOnce(testing::SaveArg<2>(&removeHandler)); |
| |
| EXPECT_CALL(*device, remove()).WillOnce(testing::Invoke([&]() { |
| endpoint->remove(); |
| })); |
| EXPECT_CALL(*device, setup(testing::_)) |
| .WillOnce(testing::InvokeArgument<0>(std::error_code(), endpoint)); |
| |
| reactor->manageMCTPDevice("/test", device); |
| reactor->unmanageMCTPDevice("/test"); |
| } |
| |
| TEST_F(MCTPReactorFixture, manageMockDeviceDeferredSetup) |
| { |
| std::function<void(const std::shared_ptr<MCTPEndpoint>& ep)> removeHandler; |
| |
| std::vector<Association> requiredAssociation{ |
| {"configured_by", "configures", "/test"}}; |
| EXPECT_CALL(assoc, associate("/xyz/openbmc_project/mctp/1/9", |
| requiredAssociation)); |
| EXPECT_CALL(assoc, disassociate("/xyz/openbmc_project/mctp/1/9")); |
| |
| EXPECT_CALL(*endpoint, remove()).WillOnce(testing::Invoke([&]() { |
| removeHandler(endpoint); |
| })); |
| EXPECT_CALL(*endpoint, subscribe(testing::_, testing::_, testing::_)) |
| .WillOnce(testing::SaveArg<2>(&removeHandler)); |
| |
| EXPECT_CALL(*device, remove()).WillOnce(testing::Invoke([&]() { |
| endpoint->remove(); |
| })); |
| EXPECT_CALL(*device, setup(testing::_)) |
| .WillOnce(testing::InvokeArgument<0>( |
| std::make_error_code(std::errc::permission_denied), endpoint)) |
| .WillOnce(testing::InvokeArgument<0>(std::error_code(), endpoint)); |
| |
| reactor->manageMCTPDevice("/test", device); |
| reactor->tick(); |
| reactor->unmanageMCTPDevice("/test"); |
| } |
| |
| TEST_F(MCTPReactorFixture, manageMockDeviceRemoved) |
| { |
| std::function<void(const std::shared_ptr<MCTPEndpoint>& ep)> removeHandler; |
| |
| std::vector<Association> requiredAssociation{ |
| {"configured_by", "configures", "/test"}}; |
| EXPECT_CALL(assoc, |
| associate("/xyz/openbmc_project/mctp/1/9", requiredAssociation)) |
| .Times(2); |
| EXPECT_CALL(assoc, disassociate("/xyz/openbmc_project/mctp/1/9")).Times(2); |
| |
| EXPECT_CALL(*endpoint, remove()).WillOnce(testing::Invoke([&]() { |
| removeHandler(endpoint); |
| })); |
| EXPECT_CALL(*endpoint, subscribe(testing::_, testing::_, testing::_)) |
| .Times(2) |
| .WillRepeatedly(testing::SaveArg<2>(&removeHandler)); |
| |
| EXPECT_CALL(*device, remove()).WillOnce(testing::Invoke([&]() { |
| endpoint->remove(); |
| })); |
| EXPECT_CALL(*device, setup(testing::_)) |
| .Times(2) |
| .WillRepeatedly( |
| testing::InvokeArgument<0>(std::error_code(), endpoint)); |
| |
| reactor->manageMCTPDevice("/test", device); |
| removeHandler(endpoint); |
| reactor->tick(); |
| reactor->unmanageMCTPDevice("/test"); |
| } |
| |
| TEST(MCTPReactor, replaceConfiguration) |
| { |
| MockAssociationServer assoc{}; |
| auto reactor = std::make_shared<MCTPReactor>(assoc); |
| std::function<void(const std::shared_ptr<MCTPEndpoint>& ep)> removeHandler; |
| |
| std::vector<Association> requiredAssociation{ |
| {"configured_by", "configures", "/test"}}; |
| |
| EXPECT_CALL(assoc, |
| associate("/xyz/openbmc_project/mctp/1/9", requiredAssociation)) |
| .Times(2); |
| EXPECT_CALL(assoc, disassociate("/xyz/openbmc_project/mctp/1/9")).Times(2); |
| |
| auto endpoint = std::make_shared<MockMCTPEndpoint>(); |
| EXPECT_CALL(*endpoint, describe()) |
| .WillRepeatedly(testing::Return("mock endpoint")); |
| EXPECT_CALL(*endpoint, eid()).WillRepeatedly(testing::Return(9)); |
| EXPECT_CALL(*endpoint, network()).WillRepeatedly(testing::Return(1)); |
| EXPECT_CALL(*endpoint, remove()) |
| .Times(2) |
| .WillRepeatedly(testing::Invoke([&]() { removeHandler(endpoint); })); |
| EXPECT_CALL(*endpoint, subscribe(testing::_, testing::_, testing::_)) |
| .Times(2) |
| .WillRepeatedly(testing::SaveArg<2>(&removeHandler)); |
| |
| auto initial = std::make_shared<MockMCTPDevice>(); |
| EXPECT_CALL(*initial, describe()) |
| .WillRepeatedly(testing::Return("mock device: initial")); |
| EXPECT_CALL(*initial, setup(testing::_)) |
| .WillOnce(testing::InvokeArgument<0>(std::error_code(), endpoint)); |
| EXPECT_CALL(*initial, remove()).WillOnce(testing::Invoke([&]() { |
| endpoint->remove(); |
| })); |
| |
| auto replacement = std::make_shared<MockMCTPDevice>(); |
| EXPECT_CALL(*replacement, describe()) |
| .WillRepeatedly(testing::Return("mock device: replacement")); |
| EXPECT_CALL(*replacement, setup(testing::_)) |
| .WillOnce(testing::InvokeArgument<0>(std::error_code(), endpoint)); |
| EXPECT_CALL(*replacement, remove()).WillOnce(testing::Invoke([&]() { |
| endpoint->remove(); |
| })); |
| |
| EXPECT_CALL(*endpoint, device()) |
| .WillOnce(testing::Return(initial)) |
| .WillOnce(testing::Return(initial)) |
| .WillOnce(testing::Return(replacement)) |
| .WillOnce(testing::Return(replacement)); |
| |
| reactor->manageMCTPDevice("/test", initial); |
| reactor->manageMCTPDevice("/test", replacement); |
| reactor->tick(); |
| reactor->unmanageMCTPDevice("/test"); |
| |
| EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(initial.get())); |
| EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(replacement.get())); |
| EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(endpoint.get())); |
| } |