blob: abdd9e9d2dedb9fde511febee66df35772ab9dd6 [file] [log] [blame]
#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()));
}