From 575423c1ffff7152fdaec8099977fae2f9c926e2 Mon Sep 17 00:00:00 2001 From: Longxiang Lyu <35479537+lolyu@users.noreply.github.com> Date: Tue, 6 Aug 2024 19:43:11 +0800 Subject: [PATCH] Fix state machine handler race conditions introduced by `strand::wrap` (#257) Approach What is the motivation for this PR? As the subject. Work item tracking Microsoft ADO (number only): 28585413 How did you do it? Post the handler directly to handler instead of calling it with strand::wrap How did you verify/test it? UT Any platform specific information? Documentation --- src/MuxPort.cpp | 60 ++-- src/MuxPort.h | 9 + test/FakeLinkManagerStateMachine.cpp | 121 ++++++++ test/FakeLinkManagerStateMachine.h | 90 ++++++ test/FakeMuxPort.cpp | 36 ++- test/FakeMuxPort.h | 8 +- ...inkManagerStateMachineActiveActiveTest.cpp | 9 +- test/MuxPortTest.cpp | 290 ++++++++++++++++++ test/MuxPortTest.h | 56 ++++ test/subdir.mk | 12 +- 10 files changed, 635 insertions(+), 56 deletions(-) create mode 100644 test/FakeLinkManagerStateMachine.cpp create mode 100644 test/FakeLinkManagerStateMachine.h create mode 100644 test/MuxPortTest.cpp create mode 100644 test/MuxPortTest.h diff --git a/src/MuxPort.cpp b/src/MuxPort.cpp index a9961dff..5d6f53b1 100644 --- a/src/MuxPort.cpp +++ b/src/MuxPort.cpp @@ -96,12 +96,11 @@ void MuxPort::handleBladeIpv4AddressUpdate(boost::asio::ip::address address) { MUXLOGDEBUG(boost::format("port: %s") % mMuxPortConfig.getPortName()); - boost::asio::io_service &ioService = mStrand.context(); - ioService.post(mStrand.wrap(boost::bind( + boost::asio::post(mStrand, boost::bind( &link_manager::LinkManagerStateMachineBase::handleSwssBladeIpv4AddressUpdate, mLinkManagerStateMachinePtr.get(), address - ))); + )); } // @@ -113,12 +112,11 @@ void MuxPort::handleSoCIpv4AddressUpdate(boost::asio::ip::address address) { MUXLOGDEBUG(boost::format("port: %s") % mMuxPortConfig.getPortName()); - boost::asio::io_service &ioService = mStrand.context(); - ioService.post(mStrand.wrap(boost::bind( + boost::asio::post(mStrand, boost::bind( &link_manager::LinkManagerStateMachineBase::handleSwssSoCIpv4AddressUpdate, mLinkManagerStateMachinePtr.get(), address - ))); + )); } // @@ -135,12 +133,11 @@ void MuxPort::handleLinkState(const std::string &linkState) label = link_state::LinkState::Label::Up; } - boost::asio::io_service &ioService = mStrand.context(); - ioService.post(mStrand.wrap(boost::bind( + boost::asio::post(mStrand, boost::bind( &link_manager::LinkManagerStateMachineBase::handleSwssLinkStateNotification, mLinkManagerStateMachinePtr.get(), label - ))); + )); } // @@ -157,12 +154,11 @@ void MuxPort::handlePeerLinkState(const std::string &linkState) label = link_state::LinkState::Label::Down; } - boost::asio::io_service &ioService = mStrand.context(); - ioService.post(mStrand.wrap(boost::bind( + boost::asio::post(mStrand, boost::bind( &link_manager::LinkManagerStateMachineBase::handlePeerLinkStateNotification, mLinkManagerStateMachinePtr.get(), label - ))); + )); } // @@ -174,12 +170,11 @@ void MuxPort::handleGetServerMacAddress(const std::array */ void setComponentInitState(uint8_t component) {mLinkManagerStateMachinePtr->setComponentInitState(component);}; + /** + *@method resetLinkManagerStateMachinePtr + * + *@brief reset the LinkManagerStateMachinePtr (used during unit test) + * + *@return none + */ + void resetLinkManagerStateMachinePtr(link_manager::LinkManagerStateMachineBase *stateMachinePtr) { mLinkManagerStateMachinePtr.reset(stateMachinePtr); }; + private: std::shared_ptr mDbInterfacePtr = nullptr; common::MuxPortConfig mMuxPortConfig; diff --git a/test/FakeLinkManagerStateMachine.cpp b/test/FakeLinkManagerStateMachine.cpp new file mode 100644 index 00000000..544dcaaa --- /dev/null +++ b/test/FakeLinkManagerStateMachine.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2021 (c) Microsoft 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 +#include +#include + +#include "FakeLinkManagerStateMachine.h" +#include "common/MuxLogger.h" + +namespace test +{ + +FakeLinkManagerStateMachine::FakeLinkManagerStateMachine( + mux::MuxPort *muxPortPtr, + boost::asio::io_service::strand &strand, + common::MuxPortConfig &muxPortConfig +) : + link_manager::LinkManagerStateMachineBase( + muxPortPtr, + strand, + muxPortConfig, + {link_prober::LinkProberState::Label::Wait, + mux_state::MuxState::Label::Wait, + link_state::LinkState::Label::Down} + ) +{ + assert(muxPortPtr != nullptr); +} + +void FakeLinkManagerStateMachine::handleSwssBladeIpv4AddressUpdate(boost::asio::ip::address address) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + mLastSwssBladeIpv4Address = address; + ++mSwssBladeIpv4AddressUpdateHandlerCalled; +} + +void FakeLinkManagerStateMachine::handleSwssSoCIpv4AddressUpdate(boost::asio::ip::address address) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + mLastSwssSoCIpv4Address = address; + ++mSwssSoCIpv4AddressUpdateHandlerCalled; +} + +void FakeLinkManagerStateMachine::handleGetServerMacAddressNotification(std::array address) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + mLastServerMacAddress = address; + ++mGetServerMacAddressNotificationHandlerCalled; +} + +void FakeLinkManagerStateMachine::handleGetMuxStateNotification(mux_state::MuxState::Label label) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + mLastGetMuxState = label; + ++mGetMuxStateNotificationHandlerCalled; +} + +void FakeLinkManagerStateMachine::handleProbeMuxStateNotification(mux_state::MuxState::Label label) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + mLastProbeMuxState = label; + ++mProbeMuxStateNotificationHandlerCalled; +} + +void FakeLinkManagerStateMachine::handleMuxStateNotification(mux_state::MuxState::Label label) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + mLastMuxStateNotification = label; + ++mMuxStateNotificationHandlerCalled; +} + +void FakeLinkManagerStateMachine::handleSwssLinkStateNotification(const link_state::LinkState::Label label) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + mLastSwssLinkState = label; + ++mSwssLinkStateNotificationHandlerCalled; +} + +void FakeLinkManagerStateMachine::handlePeerLinkStateNotification(const link_state::LinkState::Label label) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + mLastPeerLinkState = label; + ++mPeerLinkStateNotificationHandlerCalled; +} + +void FakeLinkManagerStateMachine::handlePeerMuxStateNotification(mux_state::MuxState::Label label) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + mLastPeerMuxState = label; + ++mPeerMuxStateNotificationHandlerCalled; +} + +void FakeLinkManagerStateMachine::handleMuxConfigNotification(const common::MuxPortConfig::Mode mode) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + mLastMuxConfig = mode; + ++mMuxConfigNotificationHandlerCalled; +} + +void FakeLinkManagerStateMachine::handleDefaultRouteStateNotification(const DefaultRoute routeState) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + mLastDefaultRouteState = routeState; + ++mDefaultRouteStateNotificationHandlerCalled; +} + +} /* namespace test */ diff --git a/test/FakeLinkManagerStateMachine.h b/test/FakeLinkManagerStateMachine.h new file mode 100644 index 00000000..059b0d08 --- /dev/null +++ b/test/FakeLinkManagerStateMachine.h @@ -0,0 +1,90 @@ +/* + * Copyright 2021 (c) Microsoft 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. + */ + +#ifndef FAKELINKMANAGERSTATEMACHINE_H_ +#define FAKELINKMANAGERSTATEMACHINE_H_ + +#include "link_manager/LinkManagerStateMachineBase.h" + +namespace test +{ + +class FakeLinkManagerStateMachine : public link_manager::LinkManagerStateMachineBase +{ +public: + FakeLinkManagerStateMachine( + mux::MuxPort *muxPortPtr, + boost::asio::io_service::strand &strand, + common::MuxPortConfig &muxPortConfig + ); + + ~FakeLinkManagerStateMachine() = default; + + void setLabel(Label label) override {}; + void initializeTransitionFunctionTable() override {}; + void handleStateChange(link_manager::LinkProberEvent& event, link_prober::LinkProberState::Label state) override {}; + void handleStateChange(link_manager::MuxStateEvent& event, mux_state::MuxState::Label state) override {}; + void handleStateChange(link_manager::LinkStateEvent& event, link_state::LinkState::Label state) override {}; + void handleSwssBladeIpv4AddressUpdate(boost::asio::ip::address address) override; + void handleSwssSoCIpv4AddressUpdate(boost::asio::ip::address address) override; + void handleGetServerMacAddressNotification(std::array address) override; + void handleGetMuxStateNotification(mux_state::MuxState::Label label) override; + void handleProbeMuxStateNotification(mux_state::MuxState::Label label) override; + void handleMuxStateNotification(mux_state::MuxState::Label label) override; + void handleSwssLinkStateNotification(const link_state::LinkState::Label label) override; + void handlePeerLinkStateNotification(const link_state::LinkState::Label label) override; + void handlePeerMuxStateNotification(mux_state::MuxState::Label label) override; + void handleMuxConfigNotification(const common::MuxPortConfig::Mode mode) override; + void handleDefaultRouteStateNotification(const DefaultRoute routeState) override; + +public: + boost::asio::ip::address mLastSwssBladeIpv4Address; + uint32_t mSwssBladeIpv4AddressUpdateHandlerCalled = 0; + + boost::asio::ip::address mLastSwssSoCIpv4Address; + uint32_t mSwssSoCIpv4AddressUpdateHandlerCalled = 0; + + std::array mLastServerMacAddress; + uint32_t mGetServerMacAddressNotificationHandlerCalled = 0; + + mux_state::MuxState::Label mLastGetMuxState; + uint32_t mGetMuxStateNotificationHandlerCalled = 0; + + mux_state::MuxState::Label mLastProbeMuxState; + uint32_t mProbeMuxStateNotificationHandlerCalled = 0; + + mux_state::MuxState::Label mLastMuxStateNotification; + uint32_t mMuxStateNotificationHandlerCalled = 0; + + link_state::LinkState::Label mLastSwssLinkState; + uint32_t mSwssLinkStateNotificationHandlerCalled = 0; + + link_state::LinkState::Label mLastPeerLinkState; + uint32_t mPeerLinkStateNotificationHandlerCalled = 0; + + mux_state::MuxState::Label mLastPeerMuxState; + uint32_t mPeerMuxStateNotificationHandlerCalled = 0; + + common::MuxPortConfig::Mode mLastMuxConfig; + uint32_t mMuxConfigNotificationHandlerCalled = 0; + + DefaultRoute mLastDefaultRouteState; + uint32_t mDefaultRouteStateNotificationHandlerCalled = 0; +}; + +} /* namespace test */ + +#endif /* FAKELINKMANAGERSTATEMACHINE_H_ */ diff --git a/test/FakeMuxPort.cpp b/test/FakeMuxPort.cpp index 7757003f..59d65b7f 100644 --- a/test/FakeMuxPort.cpp +++ b/test/FakeMuxPort.cpp @@ -26,6 +26,7 @@ #include "common/MuxLogger.h" #include "FakeMuxPort.h" #include "FakeLinkProber.h" +#include "FakeLinkManagerStateMachine.h" namespace test { @@ -36,7 +37,8 @@ FakeMuxPort::FakeMuxPort( std::string &portName, uint16_t serverId, boost::asio::io_service &ioService, - common::MuxPortConfig::PortCableType portCableType + common::MuxPortConfig::PortCableType portCableType, + bool useFakeLinkManagerStateMachine ) : mux::MuxPort( dbInterface, @@ -57,15 +59,29 @@ FakeMuxPort::FakeMuxPort( ) { mMuxPortConfig.setMode(common::MuxPortConfig::Mode::Auto); - switch (portCableType) { - case common::MuxPortConfig::PortCableType::ActiveStandby: - initLinkProberActiveStandby(); - break; - case common::MuxPortConfig::PortCableType::ActiveActive: - initLinkProberActiveActive(); - break; - default: - break; + if (useFakeLinkManagerStateMachine) + { + resetLinkManagerStateMachinePtr( + new FakeLinkManagerStateMachine( + this, + mStrand, + mMuxPortConfig + ) + ); + mFakeLinkManagerStateMachinePtr = std::dynamic_pointer_cast(getLinkManagerStateMachinePtr()); + } + else + { + switch (portCableType) { + case common::MuxPortConfig::PortCableType::ActiveStandby: + initLinkProberActiveStandby(); + break; + case common::MuxPortConfig::PortCableType::ActiveActive: + initLinkProberActiveActive(); + break; + default: + break; + } } } diff --git a/test/FakeMuxPort.h b/test/FakeMuxPort.h index 07e5a0be..6772dc30 100644 --- a/test/FakeMuxPort.h +++ b/test/FakeMuxPort.h @@ -32,8 +32,9 @@ #include "MuxPort.h" #include "FakeDbInterface.h" #include "FakeLinkProber.h" +#include "FakeLinkManagerStateMachine.h" -namespace test +namespace test { class FakeMuxPort : public ::mux::MuxPort { @@ -44,7 +45,8 @@ class FakeMuxPort : public ::mux::MuxPort { std::string& portName, uint16_t serverId, boost::asio::io_service& ioService, - common::MuxPortConfig::PortCableType portCableType = common::MuxPortConfig::PortCableType::ActiveStandby); + common::MuxPortConfig::PortCableType portCableType = common::MuxPortConfig::PortCableType::ActiveStandby, + bool useFakeLinkManagerStateMachine = false); virtual ~FakeMuxPort() = default; void activateStateMachine(); @@ -59,6 +61,7 @@ class FakeMuxPort : public ::mux::MuxPort { std::shared_ptr getActiveActiveStateMachinePtr() { return mActiveActiveStateMachinePtr; } std::shared_ptr getActiveStandbyStateMachinePtr() { return mActiveStandbyStateMachinePtr; } + std::shared_ptr getFakeLinkManagerStateMachinePtr() { return mFakeLinkManagerStateMachinePtr; }; const link_manager::LinkManagerStateMachineBase::CompositeState& getCompositeState() { return getLinkManagerStateMachinePtr()->getCompositeState(); }; link_prober::LinkProberStateMachineBase* getLinkProberStateMachinePtr() { return getLinkManagerStateMachinePtr()->getLinkProberStateMachinePtr().get(); }; mux_state::MuxStateMachine& getMuxStateMachine() { return getLinkManagerStateMachinePtr()->getMuxStateMachine(); }; @@ -76,6 +79,7 @@ class FakeMuxPort : public ::mux::MuxPort { std::shared_ptr mActiveActiveStateMachinePtr; std::shared_ptr mActiveStandbyStateMachinePtr; + std::shared_ptr mFakeLinkManagerStateMachinePtr = nullptr; std::shared_ptr mFakeLinkProber; }; diff --git a/test/LinkManagerStateMachineActiveActiveTest.cpp b/test/LinkManagerStateMachineActiveActiveTest.cpp index ee96d672..683206d1 100644 --- a/test/LinkManagerStateMachineActiveActiveTest.cpp +++ b/test/LinkManagerStateMachineActiveActiveTest.cpp @@ -193,10 +193,8 @@ void LinkManagerStateMachineActiveActiveTest::handleMuxState(std::string state, void LinkManagerStateMachineActiveActiveTest::handlePeerMuxState(std::string state, uint32_t count) { - for (uint8_t i = 0; i < mPositiveUpdateCount; i++) { - mFakeMuxPort.handlePeerMuxState(state); - runIoService(count); - } + mFakeMuxPort.handlePeerMuxState(state); + runIoService(count); } void LinkManagerStateMachineActiveActiveTest::handleProbeMuxState(std::string state, uint32_t count) @@ -405,6 +403,7 @@ TEST_F(LinkManagerStateMachineActiveActiveTest, MuxActiveLinkProberPeerUnknown) VALIDATE_PEER_STATE(PeerWait, Wait); postPeerLinkProberEvent(link_prober::LinkProberState::PeerActive, 1); + runIoService(1); handlePeerMuxState("active", 1); VALIDATE_PEER_STATE(PeerActive, Active); @@ -413,7 +412,7 @@ TEST_F(LinkManagerStateMachineActiveActiveTest, MuxActiveLinkProberPeerUnknown) EXPECT_EQ(mDbInterfacePtr->mSetPeerMuxStateInvokeCount, 1); EXPECT_EQ(mDbInterfacePtr->mLastSetPeerMuxState, mux_state::MuxState::Label::Standby); - handlePeerMuxState("standby", 1); + handlePeerMuxState("standby", 2); VALIDATE_PEER_STATE(PeerUnknown, Standby); EXPECT_EQ(mFakeMuxPort.mFakeLinkProber->mSendPeerProbeCommand, 1); } diff --git a/test/MuxPortTest.cpp b/test/MuxPortTest.cpp new file mode 100644 index 00000000..14aba4c4 --- /dev/null +++ b/test/MuxPortTest.cpp @@ -0,0 +1,290 @@ +/* + * Copyright 2021 (c) Microsoft 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 "common/MuxLogger.h" + +#include "MuxPortTest.h" + +namespace test +{ + +MuxPortTest::MuxPortTest() : + mDbInterfacePtr(std::make_shared(&mIoService)), + mFakeMuxPort( + mDbInterfacePtr, + mMuxConfig, + mPortName, + mServerId, + mIoService, + common::MuxPortConfig::PortCableType::ActiveStandby, + true + ) +{ +} + +void MuxPortTest::runIoServiceThreaded(uint32_t count) +{ + mWork = std::make_unique(mIoService); + for (uint8_t i = 0; i < count; i++) { + mThreadGroup.create_thread(boost::bind(&boost::asio::io_service::run, &mIoService)); + } +} + +void MuxPortTest::stopIoServiceThreaded() +{ + mWork.reset(); + mIoService.stop(); + mThreadGroup.join_all(); +} + +void MuxPortTest::SetUp() +{ + runIoServiceThreaded(3); +} + +void MuxPortTest::TearDown() +{ + stopIoServiceThreaded(); +} + +TEST_F(MuxPortTest, TestSwssBladeIpv4AddressUpdateHandler) +{ + auto fakeLinkManagerStateMachinePtr = mFakeMuxPort.getFakeLinkManagerStateMachinePtr(); + auto first_ip = boost::asio::ip::address::from_string("192.168.0.100"); + auto second_ip = boost::asio::ip::address::from_string("192.168.0.101"); + for (uint i = 0, called = 0; i < 10000; ++i) + { + MUXLOGDEBUG(boost::format("Iteration %d") % i); + mFakeMuxPort.handleBladeIpv4AddressUpdate(first_ip); + mFakeMuxPort.handleBladeIpv4AddressUpdate(second_ip); + called += 2; + + while (fakeLinkManagerStateMachinePtr->mSwssBladeIpv4AddressUpdateHandlerCalled < called) + { + usleep(1000); + } + + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mLastSwssBladeIpv4Address, second_ip); + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mSwssBladeIpv4AddressUpdateHandlerCalled, called); + } +} + +TEST_F(MuxPortTest, TestSocIpv4AddressUpdateHandler) +{ + auto fakeLinkManagerStateMachinePtr = mFakeMuxPort.getFakeLinkManagerStateMachinePtr(); + auto first_ip = boost::asio::ip::address::from_string("192.168.0.100"); + auto second_ip = boost::asio::ip::address::from_string("192.168.0.101"); + for (uint i = 0, called = 0; i < 10000; ++i) + { + MUXLOGDEBUG(boost::format("Iteration %d") % i); + mFakeMuxPort.handleSoCIpv4AddressUpdate(first_ip); + mFakeMuxPort.handleSoCIpv4AddressUpdate(second_ip); + called += 2; + + while (fakeLinkManagerStateMachinePtr->mSwssSoCIpv4AddressUpdateHandlerCalled < called) + { + usleep(1000); + } + + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mLastSwssSoCIpv4Address, second_ip); + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mSwssSoCIpv4AddressUpdateHandlerCalled, called); + } +} + +TEST_F(MuxPortTest, TestGetServerMacAddressHandler) +{ + auto fakeLinkManagerStateMachinePtr = mFakeMuxPort.getFakeLinkManagerStateMachinePtr(); + std::array first_mac = {0, 'b', 2, 'd', 4, 'f'}; + std::array second_mac = {0, 'b', 2, 'd', 8, 'f'}; + for (uint i = 0, called = 0; i < 10000; ++i) + { + MUXLOGDEBUG(boost::format("Iteration %d") % i); + mFakeMuxPort.handleGetServerMacAddress(first_mac); + mFakeMuxPort.handleGetServerMacAddress(second_mac); + called += 2; + + while (fakeLinkManagerStateMachinePtr->mGetServerMacAddressNotificationHandlerCalled < called) + { + usleep(1000); + } + + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mLastServerMacAddress, second_mac); + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mGetServerMacAddressNotificationHandlerCalled, called); + } +} + +TEST_F(MuxPortTest, TestGetMuxStateNotificationHandler) +{ + auto fakeLinkManagerStateMachinePtr = mFakeMuxPort.getFakeLinkManagerStateMachinePtr(); + for (uint i = 0, called = 0; i < 10000; ++i) + { + MUXLOGDEBUG(boost::format("Iteration %d") % i); + mFakeMuxPort.handleGetMuxState("active"); + mFakeMuxPort.handleGetMuxState("standby"); + called += 2; + + while (fakeLinkManagerStateMachinePtr->mGetMuxStateNotificationHandlerCalled < called) + { + usleep(1000); + } + + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mLastGetMuxState, mux_state::MuxState::Label::Standby); + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mGetMuxStateNotificationHandlerCalled, called); + } +} + +TEST_F(MuxPortTest, TestProbeMuxStateNotificationHandler) +{ + auto fakeLinkManagerStateMachinePtr = mFakeMuxPort.getFakeLinkManagerStateMachinePtr(); + for (uint i = 0, called = 0; i < 10000; ++i) + { + MUXLOGDEBUG(boost::format("Iteration %d") % i); + mFakeMuxPort.handleProbeMuxState("active"); + mFakeMuxPort.handleProbeMuxState("standby"); + called += 2; + + while (fakeLinkManagerStateMachinePtr->mProbeMuxStateNotificationHandlerCalled < called) + { + usleep(1000); + } + + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mLastProbeMuxState, mux_state::MuxState::Label::Standby); + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mProbeMuxStateNotificationHandlerCalled, called); + } +} + +TEST_F(MuxPortTest, TestMuxStateNotificationHandler) +{ + auto fakeLinkManagerStateMachinePtr = mFakeMuxPort.getFakeLinkManagerStateMachinePtr(); + for (uint i = 0, called = 0; i < 10000; ++i) + { + MUXLOGDEBUG(boost::format("Iteration %d") % i); + mFakeMuxPort.handleMuxState("active"); + mFakeMuxPort.handleMuxState("standby"); + called += 2; + + while (fakeLinkManagerStateMachinePtr->mMuxStateNotificationHandlerCalled < called) + { + usleep(1000); + } + + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mLastMuxStateNotification, mux_state::MuxState::Label::Standby); + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mMuxStateNotificationHandlerCalled, called); + } +} + +TEST_F(MuxPortTest, TestSwssLinkStateNotificationHandler) +{ + auto fakeLinkManagerStateMachinePtr = mFakeMuxPort.getFakeLinkManagerStateMachinePtr(); + for (uint i = 0, called = 0; i < 10000; ++i) + { + MUXLOGDEBUG(boost::format("Iteration %d") % i); + mFakeMuxPort.handleLinkState("up"); + mFakeMuxPort.handleLinkState("down"); + called += 2; + + while (fakeLinkManagerStateMachinePtr->mSwssLinkStateNotificationHandlerCalled < called) + { + usleep(1000); + } + + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mLastSwssLinkState, link_state::LinkState::Label::Down); + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mSwssLinkStateNotificationHandlerCalled, called); + } +} + +TEST_F(MuxPortTest, TestPeerLinkStateNotificationHandler) +{ + auto fakeLinkManagerStateMachinePtr = mFakeMuxPort.getFakeLinkManagerStateMachinePtr(); + for (uint i = 0, called = 0; i < 10000; ++i) + { + MUXLOGDEBUG(boost::format("Iteration %d") % i); + mFakeMuxPort.handlePeerLinkState("up"); + mFakeMuxPort.handlePeerLinkState("down"); + called += 2; + + while (fakeLinkManagerStateMachinePtr->mPeerLinkStateNotificationHandlerCalled < called) + { + usleep(1000); + } + + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mLastPeerLinkState, link_state::LinkState::Label::Down); + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mPeerLinkStateNotificationHandlerCalled, called); + } +} + +TEST_F(MuxPortTest, TestPeerMuxStateNotificationHandler) +{ + auto fakeLinkManagerStateMachinePtr = mFakeMuxPort.getFakeLinkManagerStateMachinePtr(); + for (uint i = 0, called = 0; i < 10000; ++i) + { + MUXLOGDEBUG(boost::format("Iteration %d") % i); + mFakeMuxPort.handlePeerMuxState("active"); + mFakeMuxPort.handlePeerMuxState("standby"); + called += 2; + + while (fakeLinkManagerStateMachinePtr->mPeerMuxStateNotificationHandlerCalled < called) + { + usleep(1000); + } + + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mLastPeerMuxState, mux_state::MuxState::Label::Standby); + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mPeerMuxStateNotificationHandlerCalled, called); + } +} + +TEST_F(MuxPortTest, TestMuxConfigNotificationHandler) +{ + auto fakeLinkManagerStateMachinePtr = mFakeMuxPort.getFakeLinkManagerStateMachinePtr(); + for (uint i = 0, called = 0; i < 10000; ++i) + { + MUXLOGDEBUG(boost::format("Iteration %d") % i); + mFakeMuxPort.handleMuxConfig("active"); + mFakeMuxPort.handleMuxConfig("standby"); + called += 2; + + while (fakeLinkManagerStateMachinePtr->mMuxConfigNotificationHandlerCalled < called) + { + usleep(1000); + } + + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mLastMuxConfig, common::MuxPortConfig::Mode::Standby); + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mMuxConfigNotificationHandlerCalled, called); + } +} + +TEST_F(MuxPortTest, TestDefaultRouteStateNotificationHandler) +{ + mMuxConfig.enableDefaultRouteFeature(true); + auto fakeLinkManagerStateMachinePtr = mFakeMuxPort.getFakeLinkManagerStateMachinePtr(); + for (uint i = 0, called = 0; i < 10000; ++i) + { + MUXLOGDEBUG(boost::format("Iteration %d") % i); + mFakeMuxPort.handleDefaultRouteState("na"); + mFakeMuxPort.handleDefaultRouteState("ok"); + called += 2; + + while (fakeLinkManagerStateMachinePtr->mDefaultRouteStateNotificationHandlerCalled < called) + { + usleep(1000); + } + + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mLastDefaultRouteState, link_manager::LinkManagerStateMachineBase::DefaultRoute::OK); + EXPECT_EQ(fakeLinkManagerStateMachinePtr->mDefaultRouteStateNotificationHandlerCalled, called); + } +} + +} /* namespace test */ diff --git a/test/MuxPortTest.h b/test/MuxPortTest.h new file mode 100644 index 00000000..b21df48d --- /dev/null +++ b/test/MuxPortTest.h @@ -0,0 +1,56 @@ +/* + * Copyright 2021 (c) Microsoft 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. + */ + +#ifndef MUXPORTTEST_H_ +#define MUXPORTTEST_H_ + +#include +#include "gtest/gtest.h" + +#include "FakeMuxPort.h" +#include "FakeLinkManagerStateMachine.h" + +namespace test +{ + +class MuxPortTest: public testing::Test +{ +public: + MuxPortTest(); + virtual ~MuxPortTest() = default; + + void runIoServiceThreaded(uint32_t count = 3); + void stopIoServiceThreaded(); + + void SetUp() override; + + void TearDown() override; + +public: + boost::asio::io_service mIoService; + std::unique_ptr mWork; + boost::thread_group mThreadGroup; + common::MuxConfig mMuxConfig; + std::shared_ptr mDbInterfacePtr; + std::string mPortName = "EtherTest01"; + uint16_t mServerId = 01; + FakeMuxPort mFakeMuxPort; +}; + + +} /* namespace test */ + +#endif /* MUXPORTTEST_H_ */ diff --git a/test/subdir.mk b/test/subdir.mk index b1aa7282..325e4e16 100644 --- a/test/subdir.mk +++ b/test/subdir.mk @@ -10,7 +10,9 @@ CPP_SRCS += \ ./test/MockLinkManagerStateMachine.cpp \ ./test/MockLinkProberTest.cpp \ ./test/LinkMgrdTestMain.cpp \ - ./test/MuxLoggerTest.cpp + ./test/MuxLoggerTest.cpp \ + ./test/FakeLinkManagerStateMachine.cpp \ + ./test/MuxPortTest.cpp OBJS_LINKMGRD_TEST += \ ./test/FakeDbInterface.o \ @@ -23,7 +25,9 @@ OBJS_LINKMGRD_TEST += \ ./test/MockLinkManagerStateMachine.o \ ./test/MockLinkProberTest.o \ ./test/LinkMgrdTestMain.o \ - ./test/MuxLoggerTest.o + ./test/MuxLoggerTest.o \ + ./test/FakeLinkManagerStateMachine.o \ + ./test/MuxPortTest.o CPP_DEPS += \ ./test/FakeDbInterface.d \ @@ -36,7 +40,9 @@ CPP_DEPS += \ ./test/MockLinkManagerStateMachine.d \ ./test/MockLinkProberTest.d \ ./test/LinkMgrdTestMain.d \ - ./test/MuxLoggerTest.d + ./test/MuxLoggerTest.d \ + ./test/FakeLinkManagerStateMachine.d \ + ./test/MuxPortTest.d # Each subdirectory must supply rules for building sources it contributes test/%.o: test/%.cpp