/*
 * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
 */


#include <boost/assign.hpp>

#include "base/task_annotations.h"
#include "net/bgp_af.h"
#include "bgp/bgp_config_ifmap.h"
#include "bgp/bgp_config_parser.h"
#include "bgp/bgp_factory.h"
#include "bgp/bgp_session.h"
#include "bgp/bgp_session_manager.h"
#include "control-node/control_node.h"

#include "bgp_message_test.h"
#include "bgp_server_test_util.h"

using namespace std;
using namespace boost::assign;
using namespace boost::posix_time;

class BgpSessionMock : public BgpSession {
public:
    enum State {
        CONNECT,
        ESTABLISHED,
        CLOSE,
    };

    enum Direction {
        ACTIVE,
        PASSIVE,
    };

    BgpSessionMock(BgpSessionManager *session, State state, Direction direction)
        : BgpSession(session, NULL), state_(state), direction_(direction) {
    }

    ~BgpSessionMock() {
        BGP_DEBUG_UT("Session delete");
    }

    virtual bool Send(const u_int8_t *data, size_t size, size_t *sent) {
        return true;
    }

    virtual bool Connected(Endpoint remote) {
        state_ = BgpSessionMock::ESTABLISHED;
        EventObserver obs = observer();
        if (obs != NULL) {
            obs(this, CONNECT_COMPLETE);
        }
        return true;
    }

    virtual void AsyncReadStart() {
    }

    void Close() {
        state_ = BgpSessionMock::CLOSE;
        BgpSession::Close();
    }

    State state() { return state_; }
    Direction direction() { return direction_; }

private:
    State state_;
    Direction direction_;
};

class BgpSessionManagerMock : public BgpSessionManager {
public:
    BgpSessionManagerMock(EventManager *evm, BgpServer *server)
        : BgpSessionManager(evm, server), active_session_(NULL),
          passive_session_(NULL), duplicate_session_(NULL),
          create_session_fail_(false) {
    }

    const bool create_session_fail() const { return create_session_fail_; }
    void set_create_session_fail(bool flag) { create_session_fail_ = flag; }

    virtual TcpSession *CreateSession() {
        assert(!active_session_);

        // Simulate CreateSession() API failure, such as when the process runs
        // out of file descriptors.
        if (create_session_fail_) return NULL;

        active_session_ =
            new BgpSessionMock(this, BgpSessionMock::CONNECT,
                BgpSessionMock::ACTIVE);
        return active_session_;
    }

    BgpSessionMock *CreatePassiveSession() {
        assert(!passive_session_);
        passive_session_ =
            new BgpSessionMock(this, BgpSessionMock::ESTABLISHED,
                BgpSessionMock::PASSIVE);
        return passive_session_;
    }

    BgpSessionMock *CreateDuplicateSession() {
        assert(passive_session_);
        assert(!duplicate_session_);
        duplicate_session_ =
            new BgpSessionMock(this, BgpSessionMock::ESTABLISHED,
                BgpSessionMock::PASSIVE);
        return duplicate_session_;
    }

    virtual void Connect(TcpSession *session, Endpoint remote) {
    }

    virtual void DeleteSession(TcpSession *session) {
        if (active_session_ == session) {
            active_session_ = NULL;
        } else if (passive_session_ == session) {
            passive_session_ = NULL;
        } else if (duplicate_session_ == session) {
            duplicate_session_ = NULL;
        } else {
            assert(false);
        }
        delete static_cast<BgpSessionMock *>(session);
    }

    BgpSessionMock *active_session() { return active_session_; }
    BgpSessionMock *passive_session() { return passive_session_; }
    BgpSessionMock *duplicate_session() { return duplicate_session_; }

private:
    friend class StateMachineUnitTest;
    BgpSessionMock *active_session_, *passive_session_, *duplicate_session_;
    bool create_session_fail_;
};

class StateMachineUnitTest : public ::testing::Test {
protected:
    StateMachineUnitTest() : server_(&evm_, "A"),
        timer_(TimerManager::CreateTimer(*evm_.io_service(), "Dummy timer")) {
        session_mgr_ =
          static_cast<BgpSessionManagerMock *>(server_.session_manager());
        server_.Configure(GetConfig());
        task_util::WaitForIdle();

        string uuid = BgpConfigParser::session_uuid("A", "B", 1);
        TASK_UTIL_EXPECT_NE(server_.FindPeerByUuid(
                                BgpConfigManager::kMasterInstance, uuid),
                            (BgpPeer *) NULL);
        peer_ = server_.FindPeerByUuid(BgpConfigManager::kMasterInstance, uuid);
        sm_ = peer_->state_machine();
        sm_->set_idle_hold_time(1);

        boost::system::error_code ec;
        lower_id_ = Ip4Address::from_string("191.168.0.2", ec).to_ulong();
        higher_id_ = Ip4Address::from_string("193.168.0.0", ec).to_ulong();
    }

    ~StateMachineUnitTest() {
        task_util::WaitForIdle();
        server_.Shutdown();
        task_util::WaitForIdle();
        TimerManager::DeleteTimer(timer_);
        evm_.Shutdown();
    }

    void StopStateMachine() {
        sm_->work_queue_.set_disable(true);
    }

    void StartStateMachine() {
        sm_->work_queue_.set_disable(false);
    }

    virtual void SetUp() {
    }

    virtual void TearDown() {
    }

    string GetConfig() {
        ostringstream out;
        out <<
        "<config>\
            <bgp-router name=\'A\'>\
                <identifier>192.168.0.1</identifier>\
                <address>127.0.0.1</address>\
                <autonomous-system>10458</autonomous-system>\
                <session to='B'/>\
            </bgp-router>\
            <bgp-router name=\'B\'>\
                <address>127.0.0.1</address>\
                <autonomous-system>10458</autonomous-system>\
            </bgp-router>\
        </config>";
        return out.str();
    }

    bool DummyTimerHandler() {
        BGP_DEBUG_UT("Dummy handler called");
        return false;
    }

    void SetPeerPassive() {
        peer_->passive_ = true;
    }

    void RunToState(StateMachine::State state) {
        timer_->Start(15000,
            boost::bind(&StateMachineUnitTest::DummyTimerHandler, this));
        task_util::WaitForIdle();
        for (int count = 0; count < 100 && sm_->get_state() != state; count++) {
            evm_.RunOnce();
            task_util::WaitForIdle();
        }
        timer_->Cancel();
        VerifyState(state);
    }

    void GetToState(StateMachine::State state) {
        switch (state) {
        case StateMachine::IDLE: {
            EvAdminDown();
            VerifyState(state);
            break;
        }
        case StateMachine::ACTIVE: {
            GetToState(StateMachine::IDLE);
            EvAdminUp();
            task_util::WaitForIdle();
            EvIdleHoldTimerExpired();
            VerifyState(state);
            break;
        }
        case StateMachine::CONNECT: {
            GetToState(StateMachine::ACTIVE);
            EvConnectTimerExpired();
            VerifyState(state);
            break;
        }
        case StateMachine::OPENSENT: {
            GetToState(StateMachine::CONNECT);
            TcpSession::Endpoint endpoint;
            session_mgr_->active_session()->Connected(endpoint);
            VerifyState(state);
            break;
        }
        case StateMachine::OPENCONFIRM: {
            GetToState(StateMachine::OPENSENT);
            BgpProto::OpenMessage *open = new BgpProto::OpenMessage;
            BgpMessageTest::GenerateOpenMessage(open);
            peer_->ResetCapabilities();
            sm_->OnMessage(session_mgr_->active_session(), open);
            VerifyState(StateMachine::OPENCONFIRM);
            break;
        }
        case StateMachine::ESTABLISHED: {
            GetToState(StateMachine::OPENCONFIRM);
            BgpProto::Keepalive *keepalive = new BgpProto::Keepalive;
            sm_->OnMessage(session_mgr_->active_session(), keepalive);
            VerifyState(StateMachine::ESTABLISHED);
            break;
        }
        default: {
            ASSERT_TRUE(false);
            break;
        }
        }
    }

    void VerifyState(StateMachine::State state) {
        task_util::WaitForIdle();
        TaskScheduler::GetInstance()->Stop();

        BGP_DEBUG_UT("VerifyState " << state);
        TASK_UTIL_EXPECT_EQ(state, sm_->get_state());

        switch (state) {
        case StateMachine::IDLE:
            TASK_UTIL_EXPECT_TRUE(!ConnectTimerRunning());
            TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
            TASK_UTIL_EXPECT_TRUE(!HoldTimerRunning());
            if (peer_->IsAdminDown()) {
                TASK_UTIL_EXPECT_TRUE(!IdleHoldTimerRunning());
            } else {
                TASK_UTIL_EXPECT_TRUE(IdleHoldTimerRunning());
            }
            TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
            TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
            TASK_UTIL_EXPECT_TRUE(!peer_->KeepaliveTimerRunning());
            TASK_UTIL_EXPECT_TRUE(peer_->session() == NULL);
            break;

        case StateMachine::ACTIVE:
            if (peer_->IsPassive()) {
                TASK_UTIL_EXPECT_FALSE(ConnectTimerRunning());
            } else {
                TASK_UTIL_EXPECT_TRUE(
                    ConnectTimerRunning() != OpenTimerRunning());
            }
            TASK_UTIL_EXPECT_TRUE(!HoldTimerRunning());
            TASK_UTIL_EXPECT_TRUE(!IdleHoldTimerRunning());
            TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
            TASK_UTIL_EXPECT_TRUE((sm_->passive_session() != NULL) ==
                                  OpenTimerRunning());
            TASK_UTIL_EXPECT_TRUE(!peer_->KeepaliveTimerRunning());
            TASK_UTIL_EXPECT_TRUE(peer_->session() == NULL);
            break;

        case StateMachine::CONNECT:
            TASK_UTIL_EXPECT_TRUE(ConnectTimerRunning());
            TASK_UTIL_EXPECT_TRUE(!HoldTimerRunning());
            TASK_UTIL_EXPECT_TRUE(!IdleHoldTimerRunning());
            if (!session_mgr_->create_session_fail()) {
                TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
            } else {
                TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
            }
            TASK_UTIL_EXPECT_TRUE((sm_->passive_session() != NULL) ==
                                  OpenTimerRunning());
            TASK_UTIL_EXPECT_TRUE(!peer_->KeepaliveTimerRunning());
            TASK_UTIL_EXPECT_TRUE(peer_->session() == NULL);
            break;

        case StateMachine::OPENSENT:
            TASK_UTIL_EXPECT_TRUE(!ConnectTimerRunning());
            TASK_UTIL_EXPECT_TRUE(HoldTimerRunning());
            TASK_UTIL_EXPECT_TRUE(!IdleHoldTimerRunning());
            TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL ||
                                  sm_->passive_session() != NULL);
            TASK_UTIL_EXPECT_TRUE((sm_->passive_session() != NULL) ||
                                  !OpenTimerRunning());
            TASK_UTIL_EXPECT_TRUE(!peer_->KeepaliveTimerRunning());
            TASK_UTIL_EXPECT_TRUE(peer_->session() == NULL);
            break;

        case StateMachine::OPENCONFIRM:
            TASK_UTIL_EXPECT_TRUE(!ConnectTimerRunning());
            TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
            TASK_UTIL_EXPECT_TRUE(HoldTimerRunning());
            TASK_UTIL_EXPECT_TRUE(!IdleHoldTimerRunning());
            TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
            TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
            TASK_UTIL_EXPECT_TRUE(peer_->KeepaliveTimerRunning());
            TASK_UTIL_EXPECT_TRUE(peer_->session() != NULL);
            break;

        case StateMachine::ESTABLISHED:
            TASK_UTIL_EXPECT_TRUE(!ConnectTimerRunning());
            TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
            TASK_UTIL_EXPECT_TRUE(HoldTimerRunning());
            TASK_UTIL_EXPECT_TRUE(!IdleHoldTimerRunning());
            TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
            TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
            TASK_UTIL_EXPECT_TRUE(peer_->KeepaliveTimerRunning());
            TASK_UTIL_EXPECT_TRUE(peer_->session() != NULL);
            break;

        default:
            ASSERT_TRUE(false);
            break;
        }

        TaskScheduler::GetInstance()->Start();
    }

    void VerifyDirection(BgpSessionMock::Direction direction) {
        BgpSession *session = sm_->peer()->session();
        BgpSessionMock *mock_session = dynamic_cast<BgpSessionMock *>(session);
        ASSERT_TRUE(mock_session != NULL);
        TASK_UTIL_EXPECT_TRUE(mock_session->direction() == direction);
    }

    BgpSessionMock *GetSession(BgpSessionMock *session) {
        if (session) return session;
        session = session_mgr_->active_session();
        if (!session || session->state() != BgpSessionMock::ESTABLISHED) {
            if (!session_mgr_->passive_session())
                EvTcpPassiveOpen();
            session = session_mgr_->passive_session();
        }
        return session;
    }

    void EvStart() {
        sm_->reset_idle_hold_time();
        sm_->Initialize();
    }
    void EvStop() {
        sm_->Shutdown(BgpProto::Notification::AdminShutdown);
    }
    void EvAdminUp() {
        ConcurrencyScope scope("bgp::Config");
        peer_->SetAdminState(false);
        sm_->set_idle_hold_time(1);
    }
    void EvAdminDown() {
        ConcurrencyScope scope("bgp::Config");
        peer_->SetAdminState(true);
        sm_->set_idle_hold_time(1);
    }
    void EvConnectTimerExpired() {
        sm_->FireConnectTimer();
    }
    void EvIdleHoldTimerExpired() {
        sm_->FireIdleHoldTimer();
    }
    void EvOpenTimerExpired() {
        sm_->FireOpenTimer();
    }
    void EvTcpPassiveOpen() {
        ConcurrencyScope scope("bgp::Config");
        StopStateMachine();
        BgpSessionMock *session = session_mgr_->CreatePassiveSession();
        TcpSession::Endpoint endpoint;
        peer_->AcceptSession(session);
        StartStateMachine();
    }
    void EvTcpDuplicatePassiveOpen() {
        ConcurrencyScope scope("bgp::Config");
        StopStateMachine();
        BgpSessionMock *session = session_mgr_->CreateDuplicateSession();
        TcpSession::Endpoint endpoint;
        peer_->AcceptSession(session);
        StartStateMachine();
    }
    void EvOpenTimerExpiredPassive() {
        EvTcpPassiveOpen();
        task_util::WaitForIdle();
        sm_->FireOpenTimer();
        task_util::WaitForIdle();
    }
    void EvHoldTimerExpired() {
        sm_->FireHoldTimer();
    }
    void EvTcpConnected() {
        sm_->OnSessionEvent(session_mgr_->active_session(),
            TcpSession::CONNECT_COMPLETE);
    }
    void EvTcpConnectFail() {
        sm_->OnSessionEvent(session_mgr_->active_session(),
            TcpSession::CONNECT_FAILED);
    }
    void EvTcpClose(BgpSessionMock *session = NULL) {
        if (!session) session = session_mgr_->active_session();
        sm_->OnSessionEvent(session, TcpSession::CLOSE);
    }
    void EvBgpHeaderError(BgpSessionMock *session = NULL) {
        session = GetSession(session);
        ParseErrorContext context;
        context.error_code = BgpProto::Notification::MsgHdrErr;
        sm_->OnMessageError(session, &context);
    }
    void EvBgpOpen(BgpSessionMock *session = NULL) {
        session = GetSession(session);
        BgpProto::OpenMessage *open = new BgpProto::OpenMessage;
        BgpMessageTest::GenerateOpenMessage(open);
        peer_->ResetCapabilities();
        sm_->OnMessage(session, open);
    }
    void EvBgpOpenCustom(BgpSessionMock *session,
        uint32_t identifier = 1, int holdtime = 30) {
        BgpProto::OpenMessage *open = new BgpProto::OpenMessage;
        BgpMessageTest::GenerateOpenMessage(open);
        open->identifier = identifier;
        open->holdtime = holdtime;
        peer_->ResetCapabilities();
        sm_->OnMessage(session, open);
    }
    void EvBgpOpenError(BgpSessionMock *session = NULL) {
        session = GetSession(session);
        ParseErrorContext context;
        context.error_code = BgpProto::Notification::OpenMsgErr;
        sm_->OnMessageError(session, &context);
    }
    void EvBgpKeepalive(BgpSessionMock *session = NULL) {
        session = GetSession(session);
        BgpProto::Keepalive *keepalive = new BgpProto::Keepalive;
        sm_->OnMessage(session, keepalive);
    }
    void EvBgpNotification(BgpSessionMock *session = NULL) {
        session = GetSession(session);
        BgpProto::Notification *notification = new BgpProto::Notification;
        sm_->OnMessage(session, notification);
    }
    void EvBgpUpdate(BgpSessionMock *session = NULL) {
        session = GetSession(session);
        BgpProto::Update *update = new BgpProto::Update;
        BgpMessageTest::GenerateEmptyUpdateMessage(update);
        sm_->OnMessage(session, update);
    }
    void EvBgpUpdateError(BgpSessionMock *session = NULL) {
        session = GetSession(session);
        ParseErrorContext context;
        context.error_code = BgpProto::Notification::UpdateMsgErr;
        sm_->OnMessageError(session, &context);
    }

    bool ConnectTimerRunning() { return sm_->connect_timer_->running(); }
    bool OpenTimerRunning() { return sm_->open_timer_->running(); }
    bool HoldTimerRunning() { return sm_->hold_timer_->running(); }
    bool IdleHoldTimerRunning() { return sm_->idle_hold_timer_->running(); }

    EventManager evm_;
    BgpServerTest server_;
    BgpSessionManagerMock *session_mgr_;
    BgpPeer *peer_;
    StateMachine *sm_;
    Timer *timer_;
    uint32_t lower_id_, higher_id_;
};

typedef boost::function<void(void)> EvGen;
struct EvGenComp {
    bool operator()(const EvGen &lhs, const EvGen &rhs) const {
        return &lhs < &rhs;
    }
};

TEST_F(StateMachineUnitTest, Matrix) {
    boost::asio::io_service::work work(*evm_.io_service());
    typedef map<EvGen, StateMachine::State, EvGenComp> Transitions;

#define TRANSITION(F, E) \
    ((EvGen) boost::bind(&StateMachineUnitTest_Matrix_Test::F, this), E)
#define TRANSITION2(F, E) \
    ((EvGen) boost::bind(&StateMachineUnitTest_Matrix_Test::F, this, \
            (BgpSessionMock *) NULL), E)

    Transitions idle = map_list_of
            TRANSITION(EvTcpPassiveOpen, StateMachine::IDLE)
            TRANSITION(EvAdminUp, StateMachine::ACTIVE);

    Transitions active = map_list_of
            TRANSITION(EvStop, StateMachine::IDLE)
            TRANSITION(EvAdminDown, StateMachine::IDLE)
            TRANSITION(EvHoldTimerExpired, StateMachine::ACTIVE)
            TRANSITION2(EvBgpNotification, StateMachine::IDLE)
            TRANSITION2(EvBgpKeepalive, StateMachine::IDLE)
            TRANSITION2(EvBgpUpdate, StateMachine::IDLE)
            TRANSITION2(EvBgpOpenError, StateMachine::IDLE)
            TRANSITION2(EvBgpHeaderError, StateMachine::IDLE)
            TRANSITION2(EvBgpUpdateError, StateMachine::IDLE)
            TRANSITION(EvConnectTimerExpired, StateMachine::CONNECT)
            TRANSITION(EvOpenTimerExpired, StateMachine::ACTIVE)
            TRANSITION(EvOpenTimerExpiredPassive, StateMachine::OPENSENT)
            TRANSITION(EvTcpPassiveOpen, StateMachine::ACTIVE)
            TRANSITION2(EvTcpClose, StateMachine::ACTIVE)
            TRANSITION2(EvBgpOpen, StateMachine::OPENCONFIRM);

    Transitions connect = map_list_of
            TRANSITION(EvStop, StateMachine::IDLE)
            TRANSITION(EvAdminDown, StateMachine::IDLE)
            TRANSITION(EvConnectTimerExpired, StateMachine::ACTIVE)
            TRANSITION(EvOpenTimerExpiredPassive, StateMachine::OPENSENT)
            TRANSITION(EvTcpConnected, StateMachine::OPENSENT)
            TRANSITION(EvTcpConnectFail, StateMachine::ACTIVE)
            TRANSITION(EvTcpPassiveOpen, StateMachine::CONNECT)
            TRANSITION2(EvTcpClose, StateMachine::ACTIVE)
            TRANSITION2(EvBgpOpen, StateMachine::OPENCONFIRM)
            TRANSITION2(EvBgpHeaderError, StateMachine::IDLE)
            TRANSITION2(EvBgpOpenError, StateMachine::IDLE)
            TRANSITION2(EvBgpUpdateError, StateMachine::IDLE)
            TRANSITION2(EvBgpNotification, StateMachine::IDLE)
            TRANSITION(EvHoldTimerExpired, StateMachine::CONNECT)
            TRANSITION2(EvBgpKeepalive, StateMachine::IDLE)
            TRANSITION2(EvBgpUpdate, StateMachine::IDLE);

    Transitions opensent = map_list_of
            TRANSITION(EvStop, StateMachine::IDLE)
            TRANSITION(EvAdminDown, StateMachine::IDLE)
            TRANSITION(EvTcpPassiveOpen, StateMachine::OPENSENT)
            TRANSITION2(EvTcpClose, StateMachine::ACTIVE)
            TRANSITION2(EvBgpOpen, StateMachine::OPENCONFIRM)
            TRANSITION2(EvBgpHeaderError, StateMachine::IDLE)
            TRANSITION2(EvBgpOpenError, StateMachine::IDLE)
            TRANSITION2(EvBgpUpdateError, StateMachine::IDLE)
            TRANSITION(EvHoldTimerExpired, StateMachine::IDLE)
            TRANSITION2(EvBgpKeepalive, StateMachine::IDLE)
            TRANSITION2(EvBgpNotification, StateMachine::IDLE)
            TRANSITION(EvConnectTimerExpired, StateMachine::OPENSENT)
            TRANSITION(EvOpenTimerExpiredPassive, StateMachine::OPENSENT)
            TRANSITION2(EvBgpUpdate, StateMachine::IDLE);

    Transitions openconfirm = map_list_of
            TRANSITION(EvStop, StateMachine::IDLE)
            TRANSITION(EvAdminDown, StateMachine::IDLE)
            TRANSITION(EvTcpPassiveOpen, StateMachine::OPENCONFIRM)
            TRANSITION2(EvBgpOpen, StateMachine::IDLE)
            TRANSITION(EvHoldTimerExpired, StateMachine::IDLE)
            TRANSITION2(EvBgpKeepalive, StateMachine::ESTABLISHED)
            TRANSITION2(EvTcpClose, StateMachine::IDLE)
            TRANSITION2(EvBgpHeaderError, StateMachine::IDLE)
            TRANSITION2(EvBgpOpenError, StateMachine::IDLE)
            TRANSITION2(EvBgpUpdateError, StateMachine::IDLE)
            TRANSITION2(EvBgpNotification, StateMachine::IDLE)
            TRANSITION(EvConnectTimerExpired, StateMachine::OPENCONFIRM)
            TRANSITION(EvOpenTimerExpiredPassive, StateMachine::OPENCONFIRM)
            TRANSITION2(EvBgpUpdate, StateMachine::IDLE);

    Transitions established = map_list_of
            TRANSITION(EvStop, StateMachine::IDLE)
            TRANSITION(EvAdminDown, StateMachine::IDLE)
            TRANSITION2(EvBgpOpen, StateMachine::IDLE)
            TRANSITION(EvHoldTimerExpired, StateMachine::IDLE)
            TRANSITION2(EvTcpClose, StateMachine::IDLE)
            TRANSITION2(EvBgpKeepalive, StateMachine::ESTABLISHED)
            TRANSITION2(EvBgpHeaderError, StateMachine::IDLE)
            TRANSITION2(EvBgpOpenError, StateMachine::IDLE)
            TRANSITION2(EvBgpUpdateError, StateMachine::IDLE)
            TRANSITION2(EvBgpNotification, StateMachine::IDLE)
            TRANSITION(EvConnectTimerExpired, StateMachine::ESTABLISHED)
            TRANSITION(EvOpenTimerExpired, StateMachine::ESTABLISHED)
            TRANSITION(EvTcpPassiveOpen, StateMachine::IDLE)
            TRANSITION2(EvBgpUpdate, StateMachine::ESTABLISHED);

    Transitions matrix[] =
        { idle, active, connect, opensent, openconfirm, established };

    for (int k = StateMachine::IDLE; k <= StateMachine::ESTABLISHED; k++) {
        StateMachine::State i = static_cast<StateMachine::State> (k);
        int count = 0;
        for (Transitions::iterator j = matrix[i].begin();
                j != matrix[i].end(); j++) {
            BGP_DEBUG_UT("Starting test " << count++ << " in state " << i
                    << " expecting " << j->second << " *********************");
            GetToState(i);
            j->first();
            RunToState(j->second);

            // If the final state is IDLE, and the peer is not AdminDown,
            // we expect to go to ACTIVE state after the idle hold timer
            // expires.
            if (j->second == StateMachine::IDLE) {
                if (sm_->peer()->IsAdminDown()) {
                    TASK_UTIL_EXPECT_TRUE(!IdleHoldTimerRunning());
                } else {
                    RunToState(StateMachine::ACTIVE);
                }
            }
            sm_->set_idle_hold_time(1);
        }
    }
}

//
// Simplest sequence of events to go to Established.
//
TEST_F(StateMachineUnitTest, Basic) {
    GetToState(StateMachine::ACTIVE);
    EvConnectTimerExpired();
    VerifyState(StateMachine::CONNECT);
    BgpSessionMock *session = session_mgr_->active_session();
    TcpSession::Endpoint endpoint;
    session->Connected(endpoint);
    VerifyState(StateMachine::OPENSENT);
    EvBgpOpen();
    VerifyState(StateMachine::OPENCONFIRM);
    EvBgpKeepalive();
    VerifyState(StateMachine::ESTABLISHED);
}

//
// Simulate EvTcpConnectFail and EvTcpClose.
//
TEST_F(StateMachineUnitTest, ConnectionErrors) {
    boost::asio::io_service::work work(*evm_.io_service());
    GetToState(StateMachine::CONNECT);
    EvTcpConnectFail();
    VerifyState(StateMachine::ACTIVE);
    EvConnectTimerExpired();
    VerifyState(StateMachine::CONNECT);
    BgpSessionMock *session = session_mgr_->active_session();
    TcpSession::Endpoint endpoint;
    session->Connected(endpoint);
    VerifyState(StateMachine::OPENSENT);
    EvTcpClose();
    VerifyState(StateMachine::ACTIVE);
}

//
// Simulate CreateSession failure and verify that we cycle through the
// Connect and Active states till CreateSession eventually succeeds.
//
TEST_F(StateMachineUnitTest, CreateSessionFail) {

    // Mark CreateSession() to fail.
    session_mgr_->set_create_session_fail(true);
    GetToState(StateMachine::ACTIVE);

    // Feed a few EvConnectTimerExpired and go through Active/Connect.
    for (int i = 0; i < 4; i++) {
        EvConnectTimerExpired();
        VerifyState(StateMachine::CONNECT);
        EvConnectTimerExpired();
        VerifyState(StateMachine::ACTIVE);
    }

    // Restore CreateSession() to succeed.
    session_mgr_->set_create_session_fail(false);

    // Make sure we can progress beyond Connect.
    EvConnectTimerExpired();
    VerifyState(StateMachine::CONNECT);
    BgpSessionMock *session = session_mgr_->active_session();
    TcpSession::Endpoint endpoint;
    session->Connected(endpoint);
    VerifyState(StateMachine::OPENSENT);
}

//
// First get to Active state and then inject EvConnectTimerExpired and
// EvTcpConnectFail multiple times to cycle through Connect and Active
// states.
//
// Verify that the connect time backs off as expected.
//
TEST_F(StateMachineUnitTest, ConnectTimerBackoff) {
    GetToState(StateMachine::ACTIVE);
    for (int idx = 0; idx < 10; idx++) {
        int connect_time = sm_->GetConnectTime();
        if (idx == 0) {
            TASK_UTIL_EXPECT_EQ(0, connect_time);
        } else if (idx <= 5) {
            TASK_UTIL_EXPECT_EQ(1 << (idx -1), connect_time);
        } else {
            TASK_UTIL_EXPECT_EQ(StateMachine::kConnectInterval, connect_time);
        }
        EvConnectTimerExpired();
        VerifyState(StateMachine::CONNECT);
        EvTcpConnectFail();
        VerifyState(StateMachine::ACTIVE);
    }
    EvConnectTimerExpired();
    VerifyState(StateMachine::CONNECT);
    BgpSessionMock *session = session_mgr_->active_session();
    TcpSession::Endpoint endpoint;
    session->Connected(endpoint);
    VerifyState(StateMachine::OPENSENT);
}

class StateMachinePassivePeerTest : public StateMachineUnitTest {
protected:
    virtual void SetUp() {
        SetPeerPassive();
        GetToState(StateMachine::IDLE);
    }

    virtual void TearDown() {
    }
};

// Old State: Idle
// Event:     EvStart + EvTcpPassiveOpen + EvBgpOpen + EvBgpKeepalive
// New State: Active
// Intent:    Simplest sequence of events to go to Established for passive peer.
TEST_F(StateMachinePassivePeerTest, Basic) {
    EvStart();
    VerifyState(StateMachine::ACTIVE);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::ACTIVE);
    EvBgpOpen();
    VerifyState(StateMachine::OPENCONFIRM);
    EvBgpKeepalive();
    VerifyState(StateMachine::ESTABLISHED);
}

// Old State: Idle
// Event:     EvStart
// New State: Active
// Intent:    Connect timer should not be started as peer is passive.
TEST_F(StateMachinePassivePeerTest, Start) {
    EvStart();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_FALSE(ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: Idle
// Event:     EvStart + EvConnectTimerExpired
// New State: Active
// Intent:    Connect timer expiration should be ignored as peer is passive.
TEST_F(StateMachinePassivePeerTest, StartThenConnectTimerExpired) {
    EvStart();
    VerifyState(StateMachine::ACTIVE);
    EvConnectTimerExpired();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_FALSE(ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: Idle
// Event:     EvStart + EvTcpPassiveOpen.
// New State: Active
TEST_F(StateMachinePassivePeerTest, StartThenTcpPassiveOpen) {
    EvStart();
    VerifyState(StateMachine::ACTIVE);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_FALSE(ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

class StateMachineIdleTest : public StateMachineUnitTest {
protected:
    virtual void SetUp() {
        GetToState(StateMachine::IDLE);
    }

    virtual void TearDown() {
    }
};

// Old State: Idle
// Event:     EvTcpPassiveOpen
// New State: Idle
TEST_F(StateMachineIdleTest, TcpPassiveOpen) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::IDLE);
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);

    // Also verify that we can proceed after this event
    GetToState(StateMachine::ACTIVE);
}

// Old State: Idle
// Event:     EvAdminUp + EvAdminDown + EvIdleHoldTimerExpired
// New State: Idle
// Intent:    IdleHoldTimer expires right after an AdminDown is enqueued.
//            We shouldn't proceed beyond the Idle state if this happens.
// Other:     Need to make sure that IdleHoldTimer is running when we call
//            EvIdleHoldTimerExpired, otherwise no event will be enqueued.
TEST_F(StateMachineIdleTest, AdminUpThenAdminDownThenIdleHoldTimerExpired) {
    TaskScheduler::GetInstance()->Stop();
    EvAdminUp();
    sm_->set_idle_hold_time(StateMachine::kMaxIdleHoldTime);
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::IDLE);
    TaskScheduler::GetInstance()->Stop();
    EvAdminDown();
    EvIdleHoldTimerExpired();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::IDLE);
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: Idle
// Event:     EvTcpPassiveOpen + EvBgpOpen
// New State: Idle
TEST_F(StateMachineIdleTest, TcpPassiveOpenThenBgpOpen) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    BgpSessionMock *session = session_mgr_->passive_session();
    EvBgpOpenCustom(session, lower_id_);
    TaskScheduler::GetInstance()->Start();

    VerifyState(StateMachine::IDLE);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->active_session() == NULL);

    // Also verify that we can proceed after this event
    GetToState(StateMachine::ACTIVE);
}

// Old State: Idle
// Event:     EvTcpPassiveOpen + EvStart + EvBgpOpen
// New State: Active
// Messages:  None
// Other:     Open message is ignored since there's no passive session
//            when we process it. The passive session gets closed since
//            we are in IDLE state when we process it.
TEST_F(StateMachineIdleTest, TcpPassiveOpenThenStartThenBgpOpen) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvStart();
    BgpSessionMock *session = session_mgr_->passive_session();
    EvBgpOpenCustom(session, lower_id_);
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() == NULL);
}

// Old State: Idle
// Event:     EvTcpPassiveOpen + EvStart + EvConnectTimerExpired + EvBgpOpen
// New State: Connect
// Messages:  None
// Other:     Open message is ignored since there's no passive session
//            when we process it. The passive session gets closed since
//            we are in IDLE state when we process it.
TEST_F(StateMachineIdleTest, TcpPassiveOpenThenConnectTimerExpiredThenBgpOpen) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvStart();
    EvConnectTimerExpired();
    BgpSessionMock *session = session_mgr_->passive_session();
    EvBgpOpenCustom(session, lower_id_);
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::CONNECT);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() == NULL);
}

class StateMachineActiveTest : public StateMachineUnitTest {
protected:
    virtual void SetUp() {
        GetToState(StateMachine::ACTIVE);
    }

    virtual void TearDown() {
    }
};

// Old State: Active
// Event:     EvTcpPassiveOpen.
// New State: Active
// Timers:    Open timer should be running since we have a passive session.
//            Connect timer must have been cancelled on seeing passive session.
// Messages:  None.
TEST_F(StateMachineActiveTest, TcpPassiveOpen) {
    EvTcpPassiveOpen();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(!ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: Active
// Event:     EvTcpPassiveOpen + EvTcpPassiveOpen.
// New State: Active
// Timers:    Open timer should be running since we have a passive session.
//            Connect timer must have been cancelled on seeing passive session.
// Messages:  None.
// Other:     Original passive session should get closed and deleted, while
//            the duplicate passive session becomes the new passive session.
TEST_F(StateMachineActiveTest, DuplicateTcpPassiveOpen) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvTcpDuplicatePassiveOpen();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(!ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->duplicate_session() != NULL);
}

// Old State: Active
// Event:     EvTcpPassiveOpen + EvTcpPassiveOpen + BgpOpen
// New State: Active
// Timers:    Open timer should be running since we have a passive session.
//            Connect timer must have been cancelled on seeing passive session.
// Messages:  None.
// Intent:    BgpOpen should be ignored since it's received on stale session.
// Other:     Original passive session should get closed and deleted, while
//            the duplicate passive session becomes the new passive session.
TEST_F(StateMachineActiveTest, DuplicateTcpPassiveOpenThenStaleBgpOpen) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvTcpDuplicatePassiveOpen();
    EvBgpOpen(session_mgr_->passive_session());
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(!ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->duplicate_session() != NULL);
}

// Old State: Active
// Event:     EvTcpClose on the passive session.
// New State: Active
// Timers:    Open timer should not be running since passive session got closed.
//            Connect timer should have been started again.
// Messages:  None.
TEST_F(StateMachineActiveTest, TcpPassiveClose) {
    EvTcpPassiveOpen();
    EvTcpClose(session_mgr_->passive_session());
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: Active
// Event:     EvOpenTimerExpired
// New State: OpenSent
// Timers:    Open timer must not be running since it just expired.
// Messages:  Open message sent on passive session.
TEST_F(StateMachineActiveTest, OpenTimerExpiredPassive) {
    EvOpenTimerExpiredPassive();
    RunToState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: Active
// Event:     EvTcpPassiveOpen + EvConnectTimerExpired
// New State: Active
// Timers:    Open timer should be running since we have a passive session.
//            Connect timer must not be running since it just expired.
// Messages:  None.
//
// Note:      The Connect timer has expired before it can be cancelled while
//            processing EvTcpPassiveOpen.
TEST_F(StateMachineActiveTest, TcpPassiveOpenThenConnectTimerExpired) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvConnectTimerExpired();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(!ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Parameterize id (higher/lower)

class StateMachineActiveParamTest1 :
    public StateMachineActiveTest,
    public ::testing::WithParamInterface<bool> {
protected:
    virtual void SetUp() {
        id_ = GetParam() ? lower_id_ : higher_id_;
        StateMachineActiveTest::SetUp();
    }

    virtual void TearDown() {
        StateMachineActiveTest::TearDown();
    }

    int id_;
};

// Old State: Active
// Event:     EvTcpPassiveOpen + EvBgpOpen (from higher or lower id)
// New State: OpenConfirm
// Messages:  Open message sent on passive session.
TEST_P(StateMachineActiveParamTest1, TcpPassiveOpenThenBgpOpen) {
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), id_);
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::PASSIVE);
}

INSTANTIATE_TEST_CASE_P(One, StateMachineActiveParamTest1, ::testing::Bool());

// Parameterize id (higher/lower) and holdtime (higher/lower)

typedef std::tr1::tuple<bool, bool> ActiveTestParams2;

class StateMachineActiveParamTest2 :
    public StateMachineActiveTest,
    public ::testing::WithParamInterface<ActiveTestParams2> {
protected:
    virtual void SetUp() {
        id_ = tr1::get<0>(GetParam()) ? lower_id_ : higher_id_;
        if (tr1::get<1>(GetParam())) {
            holdtime_ = StateMachine::kHoldTime - 1;
        } else {
            holdtime_ = StateMachine::kHoldTime + 1;
        }
        StateMachineActiveTest::SetUp();
    }

    virtual void TearDown() {
        StateMachineActiveTest::TearDown();
    }

    uint32_t id_;
    int holdtime_;
};

// Old State: Active
// Event:     EvTcpPassiveOpen + EvBgpOpen (with higher or lower holdtime)
// New State: OpenConfirm
// Messages:  Open message sent on passive session.
TEST_P(StateMachineActiveParamTest2, BgpHoldtimeNegotiation) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), id_, holdtime_);
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::PASSIVE);
    TASK_UTIL_EXPECT_EQ(min(holdtime_, StateMachine::kHoldTime),
        sm_->hold_time());
}

INSTANTIATE_TEST_CASE_P(One, StateMachineActiveParamTest2,
    ::testing::Combine(::testing::Bool(), ::testing::Bool()));

class StateMachineConnectTest : public StateMachineUnitTest {
protected:
    virtual void SetUp() {
        GetToState(StateMachine::CONNECT);
    }

    virtual void TearDown() {
    }
};

// Old State: Connect
// Event:     EvTcpConnected
// New State: OpenSent
// Timers:    Open timer must not be running since there's no passive session.
// Messages:  Open message sent on active session.
TEST_F(StateMachineConnectTest, TcpConnected) {
    EvTcpConnected();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: Connect
// Event:     EvTcpPassiveOpen
// New State: Connect
// Timers:    Open timer must not running since there's a passive session.
// Messages:  None.
TEST_F(StateMachineConnectTest, TcpPassiveOpen) {
    EvTcpPassiveOpen();
    VerifyState(StateMachine::CONNECT);
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: Connect
// Event:     EvTcpPassiveOpen + EvTcpPassiveOpen.
// New State: Connect
// Timers:    Open timer should be running since we have a passive session.
// Messages:  None.
// Other:     Original passive session should get closed and deleted, while
//            the duplicate passive session becomes the new passive session.
TEST_F(StateMachineConnectTest, DuplicateTcpPassiveOpen) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvTcpDuplicatePassiveOpen();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::CONNECT);
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->duplicate_session() != NULL);
}

// Old State: Connect
// Event:     EvTcpPassiveOpen + EvTcpPassiveOpen + BgpOpen
// New State: Connect
// Timers:    Open timer should be running since we have a passive session.
// Messages:  None.
// Intent:    BgpOpen should be ignored since it's received on stale session.
// Other:     Original passive session should get closed and deleted, while
//            the duplicate passive session becomes the new passive session.
TEST_F(StateMachineConnectTest, DuplicateTcpPassiveOpenThenStaleBgpOpen) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvTcpDuplicatePassiveOpen();
    EvBgpOpen(session_mgr_->passive_session());
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::CONNECT);
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->duplicate_session() != NULL);
}

// Old State: Connect
// Event:     EvConnectTimerExpired
// New State: Active
// Timers:    Connect timer must be running since we want an active session.
// Messages:  None.
TEST_F(StateMachineConnectTest, ConnectTimerExpired1) {
    EvConnectTimerExpired();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: Connect
// Event:     EvTcpPassiveOpen + EvConnectTimerExpired
// New State: Active
// Timers:    Open timer must be running since we have a passive session.
//            Connect timer must not be started since there's a passive session.
// Messages:  None.
//
// Note:      The Connect timer has expired before it can be cancelled while
//            processing EvTcpPassiveOpen.
TEST_F(StateMachineConnectTest, ConnectTimerExpired2) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvConnectTimerExpired();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(!ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: Connect
// Event:     EvTcpConnectFail
// New State: Active
// Timers:    Connect timer must be running since we want an active session.
// Messages:  None.
TEST_F(StateMachineConnectTest, TcpConnectFail) {
    EvTcpConnectFail();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: Connect
// Event:     EvTcpClose
// New State: Active
// Timers:    Connect timer must be running since we want an active session.
// Messages:  None.
TEST_F(StateMachineConnectTest, TcpClose) {
    EvTcpConnectFail();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: Connect
// Event:     EvTcpClose
// New State: Active
// Timers:    Connect timer must not be running since we have passive session.
// Messages:  None.
TEST_F(StateMachineConnectTest, TcpPassiveOpenThenActiveTcpClose) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvTcpClose();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(!ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: Connect
// Event:     EvTcpPassiveOpen + EvTcpConnectFail
// New State: Active
// Timers:    Open timer must be running since we have a passive session.
//            Connect timer must not be started since there's a passive session.
// Messages:  None.
TEST_F(StateMachineConnectTest, TcpPassiveOpenThenTcpConnectFail) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvTcpConnectFail();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(!ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: Connect
// Event:     EvTcpPassiveOpen + EvTcpClose
// New State: Connect
// Timers:    Open timer must not be running since there's no passive session.
//            Connect timer must be running since we want an active session.
// Messages:  None.
TEST_F(StateMachineConnectTest, TcpPassiveOpenThenTcpPassiveClose) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvTcpClose(session_mgr_->passive_session());
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::CONNECT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: Connect
// Event:     EvTcpPassiveOpen + EvOpenTimerExpired
// New State: OpenSent
// Timers:    Open timer must not be running since it just expired.
// Messages:  Open message sent on passive session.
TEST_F(StateMachineConnectTest, TcpPassiveOpenThenOpenTimerExpired) {
    EvOpenTimerExpiredPassive();
    RunToState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: Connect
// Event:     EvTcpConnected + EvConnectTimerExpired
// New State: OpenSent
// Messages:  Open message sent on active session.
// Other:     The Connect timer has expired before it can be cancelled while
//            processing EvTcpConnected.
TEST_F(StateMachineConnectTest, TcpConnectedThenConnectTimerExpired) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpConnected();
    EvConnectTimerExpired();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() == NULL);
}

// Old State: Connect
// Event:     EvConnectTimerExpired + EvTcpConnected
// New State: Active
// Messages:  None.
// Other:     The Connect timer expired before we see EvTcpConnected, thus
//            the session would already have been deleted when we process
//            EvTcpConnected.
TEST_F(StateMachineConnectTest, ConnectTimerExpiredThenTcpConnected) {
    TaskScheduler::GetInstance()->Stop();
    EvConnectTimerExpired();
    EvTcpConnected();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->active_session() == NULL);
}

// Old State: Connect
// Event:     EvConnectTimerExpired + EvTcpConnectFail
// New State: Active
// Messages:  None.
// Other:     The Connect timer expired before we see EvTcpConnectFail, thus
//            the session would already have been deleted when we process
//            EvTcpConnectFail.
TEST_F(StateMachineConnectTest, ConnectTimerExpiredThenTcpConnectFail) {
    TaskScheduler::GetInstance()->Stop();
    EvConnectTimerExpired();
    EvTcpConnectFail();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->active_session() == NULL);
}

// Parameterize id (higher/lower)

class StateMachineConnectParamTest1 :
    public StateMachineConnectTest,
    public ::testing::WithParamInterface<bool> {
protected:
    virtual void SetUp() {
        id_ = GetParam() ? lower_id_ : higher_id_;
        StateMachineConnectTest::SetUp();
    }

    virtual void TearDown() {
        StateMachineConnectTest::TearDown();
    }

    int id_;
};

// Old State: Connect
// Event:     EvTcpPassiveOpen + EvBgpOpen (from higher or lower id)
// New State: OpenConfirm
// Messages:  Open message sent on passive session.
TEST_P(StateMachineConnectParamTest1, TcpPassiveOpenThenBgpOpen) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), id_);
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::PASSIVE);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->active_session() == NULL);
}

// Old State: Connect
// Event:     EvTcpPassiveOpen + EvBgpOpen (from higher or lower id) +
//            EvTcpConnected
// New State: OpenConfirm
// Messages:  Open message sent on passive session.
// Other:     The active session would have been closed before we process
//            EvTcpConnected.
TEST_P(StateMachineConnectParamTest1, BgpPassiveOpenThenTcpConnected) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), id_);
    EvTcpConnected();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::PASSIVE);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->active_session() == NULL);
}

// Old State: Connect
// Event:     EvTcpPassiveOpen + EvBgpOpen (from higher or lower id) +
//            EvTcpConnectFail
// New State: OpenConfirm
// Messages:  Open message sent on passive session.
// Other:     The active session would have been closed before we process
//            EvTcpConnectFail.
TEST_P(StateMachineConnectParamTest1, BgpPassiveOpenThenTcpConnectFail) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), id_);
    EvTcpConnectFail();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::PASSIVE);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->active_session() == NULL);
}

// Old State: Connect
// Event:     EvTcpPassiveOpen + EvBgpOpen (from higher or lower id) +
//            EvConnectTimerExpired
// New State: OpenConfirm
// Messages:  Open message sent on passive session.
// Other:     The active session would have been closed before we process
//            EvConnectTimerExpired.
TEST_P(StateMachineConnectParamTest1, BgpPassiveOpenThenConnectTimerExpired) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), id_);
    EvConnectTimerExpired();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::PASSIVE);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->active_session() == NULL);
}

INSTANTIATE_TEST_CASE_P(One, StateMachineConnectParamTest1, ::testing::Bool());

// Parameterize id (higher/lower) and holdtime (higher/lower)

typedef std::tr1::tuple<bool, bool> ConnectTestParams2;

class StateMachineConnectParamTest2 :
    public StateMachineConnectTest,
    public ::testing::WithParamInterface<ConnectTestParams2> {
protected:
    virtual void SetUp() {
        id_ = tr1::get<0>(GetParam()) ? lower_id_ : higher_id_;
        if (tr1::get<1>(GetParam())) {
            holdtime_ = StateMachine::kHoldTime - 1;
        } else {
            holdtime_ = StateMachine::kHoldTime + 1;
        }
        StateMachineConnectTest::SetUp();
    }

    virtual void TearDown() {
        StateMachineConnectTest::TearDown();
    }

    uint32_t id_;
    int holdtime_;
};

// Old State: Connect
// Event:     EvTcpPassiveOpen + EvBgpOpen (with higher or lower holdtime)
// New State: OpenConfirm
// Messages:  Open message sent on passive session.
TEST_P(StateMachineConnectParamTest2, BgpHoldtimeNegotiation) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), id_, holdtime_);
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::PASSIVE);
    TASK_UTIL_EXPECT_EQ(min(holdtime_, StateMachine::kHoldTime),
        sm_->hold_time());
}

INSTANTIATE_TEST_CASE_P(One, StateMachineConnectParamTest2,
    ::testing::Combine(::testing::Bool(), ::testing::Bool()));

class StateMachineOpenSentTest : public StateMachineUnitTest {
protected:
    virtual void SetUp() {
    }

    virtual void TearDown() {
    }
};

// Old State: OpenSent
// Event:     EvTcpPassiveOpen
// New State: OpenSent
// Timers:    Open timer must be running since there's a passive session.
// Messages:  None.
TEST_F(StateMachineOpenSentTest, TcpPassiveOpen) {
    GetToState(StateMachine::OPENSENT);
    EvTcpPassiveOpen();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvTcpPassiveOpen.
// New State: OpenSent
// Timers:    Open timer should be running since we have a passive session.
// Messages:  None.
// Other:     Original passive session should get closed and deleted, while
//            the duplicate passive session becomes the new passive session.
TEST_F(StateMachineOpenSentTest, DuplicateTcpPassiveOpen1) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvTcpDuplicatePassiveOpen();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->duplicate_session() != NULL);
}

// Old State: OpenSent (via passive session)
// Event:     EvTcpPassiveOpen
// New State: Active
// Timers:    Open timer should be running since we have a passive session.
//            Connect timer must not be started since there's a passive session.
// Messages:  None.
// Other:     Original passive session should get closed and deleted, while
//            the duplicate passive session becomes the new passive session.
TEST_F(StateMachineOpenSentTest, DuplicateTcpPassiveOpen2) {
    GetToState(StateMachine::ACTIVE);
    EvOpenTimerExpiredPassive();
    VerifyState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpDuplicatePassiveOpen();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(!ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->duplicate_session() != NULL);
}

// Old State: OpenSent (via active and passive session)
// Event:     EvTcpPassiveOpen
// New State: OpenSent
// Timers:    Open timer should be running since we have a passive session.
//            Connect timer must not be started since there's a passive session.
// Messages:  None.
// Other:     Original passive session should get closed and deleted, while
//            the duplicate passive session becomes the new passive session.
TEST_F(StateMachineOpenSentTest, DuplicateTcpPassiveOpen3) {
    GetToState(StateMachine::OPENSENT);
    EvOpenTimerExpiredPassive();
    VerifyState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpDuplicatePassiveOpen();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->duplicate_session() != NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvOpenTimerExpired
// New State: OpenSent
// Timers:    Open timer must not be running since it just expired.
// Messages:  Open message sent on passive session.
TEST_F(StateMachineOpenSentTest, TcpPassiveOpenThenOpenTimerExpired) {
    GetToState(StateMachine::OPENSENT);
    EvOpenTimerExpiredPassive();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpNotification (passive)
// New State: OpenSent
// Timers:    Open timer must not be running since there's no passive session.
// Messages:  None.
TEST_F(StateMachineOpenSentTest, Notification1a) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpNotification(session_mgr_->passive_session());
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent (via active and passive sessions)
// Event:     EvBgpNotification (passive)
// New State: OpenSent
// Timers:    Open timer must not be running since there's no passive session.
// Messages:  Open message sent on passive session.
TEST_F(StateMachineOpenSentTest, Notification1b) {
    GetToState(StateMachine::OPENSENT);
    EvOpenTimerExpiredPassive();
    VerifyState(StateMachine::OPENSENT);
    EvBgpNotification(session_mgr_->passive_session());
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpNotification (active)
// New State: Active
// Timers:    Open timer must be running since there's a passive session.
// Messages:  None.
TEST_F(StateMachineOpenSentTest, Notification2a) {
    GetToState(StateMachine::OPENSENT);
    EvTcpPassiveOpen();
    EvBgpNotification(session_mgr_->active_session());
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: OpenSent (via active and passive sessions)
// Event:     EvBgpNotification (active)
// New State: OpenSent
// Timers:    Open timer must not be running since it already expired.
// Messages:  None.
TEST_F(StateMachineOpenSentTest, Notification2b) {
    GetToState(StateMachine::OPENSENT);
    EvOpenTimerExpiredPassive();
    VerifyState(StateMachine::OPENSENT);
    EvBgpNotification(session_mgr_->active_session());
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: OpenSent
// Event:     EvBgpNotification (on active session)
// New State: Idle
// Messages:  None.
TEST_F(StateMachineOpenSentTest, Notification3a) {
    GetToState(StateMachine::OPENSENT);
    EvBgpNotification(session_mgr_->active_session());
    VerifyState(StateMachine::IDLE);
}

// Old State: OpenSent (via passive session)
// Event:     EvBgpNotification (active)
// New State: Idle
// Messages:  None.
TEST_F(StateMachineOpenSentTest, Notification3b) {
    GetToState(StateMachine::ACTIVE);
    EvOpenTimerExpiredPassive();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    EvBgpNotification(session_mgr_->active_session());
    VerifyState(StateMachine::IDLE);
}

// Old State: OpenSent
// Event:     EvTcpClose (on active session)
// New State: Active
// Timers:    Connect timer must be running since there's no passive session.
// Messages:  None.
TEST_F(StateMachineOpenSentTest, TcpActiveClose1) {
    GetToState(StateMachine::OPENSENT);
    EvTcpClose(session_mgr_->active_session());
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvTcpClose (active)
// New State: Active
// Timers:    Connect timer must not be running since there's a passive session.
//            Open timer must be running since there's a passive session.
// Messages:  None.
TEST_F(StateMachineOpenSentTest, TcpActiveClose2) {
    GetToState(StateMachine::OPENSENT);
    EvTcpPassiveOpen();
    EvTcpClose(session_mgr_->active_session());
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(!ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: OpenSent (via active and passive sessions)
// Event:     EvTcpClose (active)
// New State: OpenSent
// Timers:    Open timer must not be running since it already expired.
// Messages:  Open message sent on passive session.
TEST_F(StateMachineOpenSentTest, TcpActiveClose3) {
    GetToState(StateMachine::OPENSENT);
    EvOpenTimerExpiredPassive();
    VerifyState(StateMachine::OPENSENT);
    EvTcpClose(session_mgr_->active_session());
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvTcpClose (passive)
// New State: OpenSent
// Timers:    Open timer must not be running since there's no passive session.
// Messages:  None.
TEST_F(StateMachineOpenSentTest, TcpPassiveClose1) {
    GetToState(StateMachine::OPENSENT);
    EvTcpPassiveOpen();
    EvTcpClose(session_mgr_->passive_session());
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent (via passive session)
// Event:     EvTcpClose (passive)
// New State: Active
// Timers:    Open timer must not be running since there's no passive session.
//            Connect timer must be running since there's no passive session.
// Messages:  None.
TEST_F(StateMachineOpenSentTest, TcpPassiveClose2) {
    GetToState(StateMachine::ACTIVE);
    EvOpenTimerExpiredPassive();
    VerifyState(StateMachine::OPENSENT);
    EvTcpClose(session_mgr_->passive_session());
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent (via active and passive sessions)
// Event:     EvTcpClose (passive)
// New State: OpenSent
// Timers:    Open timer must not be running since there's no passive session.
// Messages:  None.
TEST_F(StateMachineOpenSentTest, TcpPassiveClose3) {
    GetToState(StateMachine::OPENSENT);
    EvOpenTimerExpiredPassive();
    VerifyState(StateMachine::OPENSENT);
    EvTcpClose(session_mgr_->passive_session());
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpOpen (active with higher id)
// New State: OpenSent
// Timers:    Open timer must not be running since it was cancelled.
// Messages:  Open message sent on passive session.
TEST_F(StateMachineOpenSentTest, Collision1a) {
    GetToState(StateMachine::OPENSENT);
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->active_session(), higher_id_);
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: OpenSent (via active and passive sessions)
// Event:     EvBgpOpen (active with higher id)
// New State: OpenSent
// Timers:    Open timer must not be running since it already expired.
// Messages:  None.
TEST_F(StateMachineOpenSentTest, Collision1b) {
    GetToState(StateMachine::OPENSENT);
    EvOpenTimerExpiredPassive();
    VerifyState(StateMachine::OPENSENT);
    EvBgpOpenCustom(session_mgr_->active_session(), higher_id_);
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpOpen (passive with higher id)
// New State: OpenConfirm
// Messages:  Open message sent on passive session.
//            Keepalive message sent on passive session.
TEST_F(StateMachineOpenSentTest, Collision1c) {
    GetToState(StateMachine::OPENSENT);
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), higher_id_);
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::PASSIVE);
}

// Old State: OpenSent (via active and passive sessions)
// Event:     EvBgpOpen (passive with higher id)
// New State: OpenConfirm
// Messages:  Keepalive message sent on passive session.
TEST_F(StateMachineOpenSentTest, Collision1d) {
    GetToState(StateMachine::OPENSENT);
    EvOpenTimerExpiredPassive();
    VerifyState(StateMachine::OPENSENT);
    EvBgpOpenCustom(session_mgr_->passive_session(), higher_id_);
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::PASSIVE);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpOpen (passive with lower id)
// New State: OpenSent
// Timers:    Open timer must not be running since there's no passive session.
// Messages:  None.
TEST_F(StateMachineOpenSentTest, Collision2a) {
    GetToState(StateMachine::OPENSENT);
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), lower_id_);
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent (via active and passive sessions)
// Event:     EvBgpOpen (passive with lower id)
// New State: OpenSent
// Timers:    Open timer must not be running since it already expired.
// Messages:  None.
TEST_F(StateMachineOpenSentTest, Collision2b) {
    GetToState(StateMachine::OPENSENT);
    EvOpenTimerExpiredPassive();
    VerifyState(StateMachine::OPENSENT);
    EvBgpOpenCustom(session_mgr_->passive_session(), lower_id_);
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpOpen (active with lower id)
// New State: OpenConfirm
// Messages:  Keepalive message sent on active session.
TEST_F(StateMachineOpenSentTest, Collision2c) {
    GetToState(StateMachine::OPENSENT);
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->active_session(), lower_id_);
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::ACTIVE);
}

// Old State: OpenSent (via active and passive sessions)
// Event:     EvBgpOpen (active with lower id)
// New State: OpenConfirm
// Messages:  Keepalive message sent on active session.
TEST_F(StateMachineOpenSentTest, Collision2d) {
    GetToState(StateMachine::OPENSENT);
    EvOpenTimerExpiredPassive();
    VerifyState(StateMachine::OPENSENT);
    EvBgpOpenCustom(session_mgr_->active_session(), lower_id_);
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::ACTIVE);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpOpen (passive with duplicate id)
// New State: Idle
// Messages:  Notification message sent on passive session.
TEST_F(StateMachineOpenSentTest, Collision3a) {
    GetToState(StateMachine::OPENSENT);
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(),
        server_.bgp_identifier());
    VerifyState(StateMachine::IDLE);
}

// Old State: OpenSent (via active and passive sessions)
// Event:     EvBgpOpen (passive with duplicate id)
// New State: Idle
// Messages:  Notification message sent on passive session.
TEST_F(StateMachineOpenSentTest, Collision3b) {
    GetToState(StateMachine::OPENSENT);
    EvOpenTimerExpiredPassive();
    EvBgpOpenCustom(session_mgr_->passive_session(),
        server_.bgp_identifier());
    VerifyState(StateMachine::IDLE);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpOpen (active with duplicate id)
// New State: Idle
// Messages:  Notification message sent on active session.
TEST_F(StateMachineOpenSentTest, Collision3c) {
    GetToState(StateMachine::OPENSENT);
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->active_session(),
        server_.bgp_identifier());
    VerifyState(StateMachine::IDLE);
}

// Old State: OpenSent (via active and passive sessions)
// Event:     EvBgpOpen (active with duplicate id)
// New State: Idle
// Messages:  Notification message sent on active session.
TEST_F(StateMachineOpenSentTest, Collision3d) {
    GetToState(StateMachine::OPENSENT);
    EvOpenTimerExpiredPassive();
    EvBgpOpenCustom(session_mgr_->active_session(),
        server_.bgp_identifier());
    VerifyState(StateMachine::IDLE);
}

// Old State: OpenSent
// Event:     EvBgpOpenError (active) + EvBgpKeepalive (active)
// New State: Idle
// Messages:  Notification message sent on active session.
TEST_F(StateMachineOpenSentTest, BgpOpenErrorThenKeepalive) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvBgpOpenError(session_mgr_->active_session());
    EvBgpKeepalive(session_mgr_->active_session());
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::IDLE);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpOpen (passive with lower id) +
//            EvBgpKeepalive (passive)
// New State: OpenSent
// Timers:    Open timer must not be running since there's no passive session.
// Messages:  Notification message sent on passive session.
TEST_F(StateMachineOpenSentTest, CollisionThenKeepalive) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), lower_id_);
    EvBgpKeepalive(session_mgr_->passive_session());
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + 2 * EvBgpOpen (passive with lower id)
// New State: OpenSent
// Timers:    Open timer must not be running since there's no passive session.
// Messages:  Notification message sent on passive session.
TEST_F(StateMachineOpenSentTest, CollisionThenBgpOpen) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), lower_id_);
    EvBgpOpenCustom(session_mgr_->passive_session(), lower_id_);
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpOpen (passive with lower id) +
//            EvBgpNotification (passive)
// New State: OpenSent
// Timers:    Open timer must not be running since there's no passive session.
// Messages:  Notification message sent on passive session.
TEST_F(StateMachineOpenSentTest, CollisionThenNotification1) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), lower_id_);
    EvBgpNotification(session_mgr_->passive_session());
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpOpen (passive with lower id) +
//            EvBgpNotification (active)
// New State: Active
// Timers:    Open timer must not be running since there's no passive session.
//            Connect timer must be running since there's no passive session.
// Messages:  Notification message sent on passive session.
TEST_F(StateMachineOpenSentTest, CollisionThenNotification2) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), lower_id_);
    EvBgpNotification(session_mgr_->active_session());
    TaskScheduler::GetInstance()->Start();
    RunToState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(ConnectTimerRunning());
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpOpen (passive with lower id) +
//            EvTcpClose (passive)
// New State: OpenSent
// Timers:    Open timer must not be running since there's no passive session.
// Messages:  Notification message sent on passive session.
TEST_F(StateMachineOpenSentTest, CollisionThenClose1a) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), lower_id_);
    EvTcpClose(session_mgr_->passive_session());
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpOpen (active with lower id) +
//            EvTcpClose (passive)
// New State: OpenConfirm
// Messages:  Notification message sent on passive session.
//            Keepalive message sent on active session.
TEST_F(StateMachineOpenSentTest, CollisionThenClose1b) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->active_session(), lower_id_);
    EvTcpClose(session_mgr_->passive_session());
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::ACTIVE);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpOpen (passive with higher id) +
//            EvTcpClose (active)
// New State: OpenConfirm
// Messages:  Keepalive message sent on passive session.
TEST_F(StateMachineOpenSentTest, CollisionThenClose2a) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), higher_id_);
    EvTcpClose(session_mgr_->active_session());
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::PASSIVE);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpOpen (active with higher id) +
//            EvTcpClose (active)
// New State: OpenSent
// Timers:    Open timer must not be running since it must be cancelled.
// Messages:  Open message sent on passive session.
TEST_F(StateMachineOpenSentTest, CollisionThenClose2b) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->active_session(), higher_id_);
    EvTcpClose(session_mgr_->active_session());
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpNotification (active) +
//            EvTcpClose (active)
// New State: Active
// Timers:    Open timer must be running since there's a passive session.
TEST_F(StateMachineOpenSentTest, ActiveNotificationThenClose) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpNotification(session_mgr_->active_session());
    EvTcpClose(session_mgr_->active_session());
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() != NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpNotification (passive) +
//            EvTcpClose (passive)
// New State: OpenSent
// Timers:    Open timer must not be running since there's no passive session.
TEST_F(StateMachineOpenSentTest, PassiveNotificationThenClose) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpNotification(session_mgr_->passive_session());
    EvTcpClose(session_mgr_->passive_session());
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpOpen (passive with lower id) +
//            EvOpenTimerExpired
// New State: OpenSent
// Timers:    Open timer must not be running since there's no passive session.
TEST_F(StateMachineOpenSentTest, CollisionThenOpenTimerExpired) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), lower_id_);
    EvOpenTimerExpired();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvBgpNotification (passive) +
//            EvOpenTimerExpired
// New State: OpenSent
// Timers:    Open timer must not be running since there's no passive session.
TEST_F(StateMachineOpenSentTest, NotificationThenOpenTimerExpired) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvBgpNotification(session_mgr_->passive_session());
    EvOpenTimerExpired();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent
// Event:     EvTcpPassiveOpen + EvTcpClose (passive) + EvOpenTimerExpired
// New State: OpenSent
// Timers:    Open timer must not be running since there's no passive session.
TEST_F(StateMachineOpenSentTest, TcpCloseThenOpenTimerExpired) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    EvTcpClose(session_mgr_->passive_session());
    EvOpenTimerExpired();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENSENT);
    TASK_UTIL_EXPECT_TRUE(!OpenTimerRunning());
    TASK_UTIL_EXPECT_TRUE(sm_->active_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(sm_->passive_session() == NULL);
}

// Old State: OpenSent
// Event:     EvTcpClose + EvAdminDown
// New State: Idle
// Timers:    IdleHold timer must not be running as it should get cancelled
//            when processing EvStop.
TEST_F(StateMachineOpenSentTest, TcpCloseThenAdminDown) {
    GetToState(StateMachine::OPENSENT);
    sm_->set_idle_hold_time(StateMachine::kMaxIdleHoldTime);
    EvTcpClose(session_mgr_->active_session());
    task_util::WaitForIdle();
    EvAdminDown();
    VerifyState(StateMachine::IDLE);
}

// Old State: OpenSent
// Event:     EvHoldTimerExpired + EvBgpOpen (via active session)
// New State: Idle
// Timers:    IdleHold timer must be running.
// Messages:  Notification message sent on active session.
TEST_F(StateMachineOpenSentTest, HoldTimerExpiredThenBgpOpen1) {
    GetToState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvHoldTimerExpired();
    EvBgpOpen();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::IDLE);
}

// Old State: OpenSent (via passive session)
// Event:     EvHoldTimerExpired + EvBgpOpen (via passive session)
// New State: Idle
// Timers:    IdleHold timer must be running.
// Messages:  Notification message sent on passive session.
TEST_F(StateMachineOpenSentTest, HoldTimerExpiredThenBgpOpen2) {
    GetToState(StateMachine::ACTIVE);
    EvOpenTimerExpiredPassive();
    VerifyState(StateMachine::OPENSENT);
    TaskScheduler::GetInstance()->Stop();
    EvHoldTimerExpired();
    EvBgpOpen(session_mgr_->passive_session());
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::IDLE);
}

// Old State: OpenSent (via active and passive sessions)
// Event:     EvHoldTimerExpired + EvBgpOpen (via active session)
// New State: Idle
// Timers:    IdleHold timer must be running.
// Messages:  Notification message sent on active session.
TEST_F(StateMachineOpenSentTest, HoldTimerExpiredThenBgpOpen3a) {
    GetToState(StateMachine::OPENSENT);
    EvOpenTimerExpiredPassive();
    TaskScheduler::GetInstance()->Stop();
    EvHoldTimerExpired();
    EvBgpOpen(session_mgr_->active_session());
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::IDLE);
}

// Old State: OpenSent (via active and passive sessions)
// Event:     EvHoldTimerExpired + EvBgpOpen (via passive session)
// New State: Idle
// Timers:    IdleHold timer must be running.
// Messages:  Notification message sent on active session.
TEST_F(StateMachineOpenSentTest, HoldTimerExpiredThenBgpOpen3b) {
    GetToState(StateMachine::OPENSENT);
    EvOpenTimerExpiredPassive();
    TaskScheduler::GetInstance()->Stop();
    EvHoldTimerExpired();
    EvBgpOpen(session_mgr_->passive_session());
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::IDLE);
}

// Parameterize id (higher/lower)

class StateMachineOpenSentParamTest1 :
    public StateMachineOpenSentTest,
    public ::testing::WithParamInterface<bool> {
protected:
    virtual void SetUp() {
        id_ = GetParam() ? lower_id_ : higher_id_;
        StateMachineOpenSentTest::SetUp();
    }

    virtual void TearDown() {
        StateMachineOpenSentTest::TearDown();
    }

    int id_;
};

// Old State: OpenSent
// Event:     EvBgpOpen (active with higher or lower id)
// New State: OpenConfirm
// Messages:  Keepalive message sent on active session.
TEST_P(StateMachineOpenSentParamTest1, BgpOpenActive) {
    GetToState(StateMachine::OPENSENT);
    EvBgpOpenCustom(session_mgr_->active_session(), id_);
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::ACTIVE);
}

// Old State: OpenSent (via passive session)
// Event:     EvBgpOpen (passive with higher or lower id)
// New State: OpenConfirm
// Messages:  Keepalive message sent on passive session.
TEST_P(StateMachineOpenSentParamTest1, BgpOpenPassive) {
    GetToState(StateMachine::ACTIVE);
    EvOpenTimerExpiredPassive();
    EvBgpOpenCustom(session_mgr_->passive_session(), id_);
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::PASSIVE);
}

INSTANTIATE_TEST_CASE_P(One, StateMachineOpenSentParamTest1, ::testing::Bool());

// Parameterize holdtime (higher/lower)

class StateMachineOpenSentParamTest2 :
    public StateMachineOpenSentTest,
    public ::testing::WithParamInterface<bool> {
protected:
    virtual void SetUp() {
        if (GetParam()) {
            holdtime_ = StateMachine::kHoldTime - 1;
        } else {
            holdtime_ = StateMachine::kHoldTime + 1;
        }
        StateMachineOpenSentTest::SetUp();
    }

    virtual void TearDown() {
        StateMachineOpenSentTest::TearDown();
    }

    int holdtime_;
};

// Old State: OpenSent (via active and passive session)
// Event:     EvBgpOpen (via active session)
// New State: OpenConfirm
TEST_P(StateMachineOpenSentParamTest2, BgpHoldtimeNegotiation1) {
    GetToState(StateMachine::OPENSENT);
    EvOpenTimerExpiredPassive();
    EvBgpOpenCustom(session_mgr_->active_session(), lower_id_, holdtime_);
    VerifyState(StateMachine::OPENCONFIRM);
    TASK_UTIL_EXPECT_EQ(min(holdtime_, StateMachine::kHoldTime),
        sm_->hold_time());
}

// Old State: OpenSent (via active and passive session)
// Event:     EvBgpOpen (via active session)
// New State: OpenConfirm
TEST_P(StateMachineOpenSentParamTest2, BgpHoldtimeNegotiation2) {
    GetToState(StateMachine::OPENSENT);
    EvOpenTimerExpiredPassive();
    EvBgpOpenCustom(session_mgr_->passive_session(), higher_id_, holdtime_);
    VerifyState(StateMachine::OPENCONFIRM);
    TASK_UTIL_EXPECT_EQ(min(holdtime_, StateMachine::kHoldTime),
        sm_->hold_time());
}

INSTANTIATE_TEST_CASE_P(One, StateMachineOpenSentParamTest2, ::testing::Bool());

// Parameterize id (higher/lower) and holdtime (higher/lower)

typedef std::tr1::tuple<bool, bool> OpenSentTestParams3;

class StateMachineOpenSentParamTest3 :
    public StateMachineOpenSentTest,
    public ::testing::WithParamInterface<OpenSentTestParams3> {
protected:
    virtual void SetUp() {
        id_ = tr1::get<0>(GetParam()) ? lower_id_ : higher_id_;
        if (tr1::get<1>(GetParam())) {
            holdtime_ = StateMachine::kHoldTime - 1;
        } else {
            holdtime_ = StateMachine::kHoldTime + 1;
        }
        StateMachineOpenSentTest::SetUp();
    }

    virtual void TearDown() {
        StateMachineOpenSentTest::TearDown();
    }

    uint32_t id_;
    int holdtime_;
};

// Old State: OpenSent (via active session)
// Event:     EvBgpOpen (higher/lower id and higher/lower holdtime)
// New State: OpenConfirm
TEST_P(StateMachineOpenSentParamTest3, BgpHoldtimeNegotiation1) {
    GetToState(StateMachine::OPENSENT);
    EvBgpOpenCustom(session_mgr_->active_session(), id_, holdtime_);
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::ACTIVE);
    TASK_UTIL_EXPECT_EQ(min(holdtime_, StateMachine::kHoldTime),
        sm_->hold_time());
}

// Old State: OpenSent (via passive session)
// Event:     EvBgpOpen (higher/lower id and higher/lower holdtime)
// New State: OpenConfirm
TEST_P(StateMachineOpenSentParamTest3, BgpHoldtimeNegotiation2) {
    GetToState(StateMachine::ACTIVE);
    EvOpenTimerExpiredPassive();
    VerifyState(StateMachine::OPENSENT);
    EvBgpOpenCustom(session_mgr_->passive_session(), id_, holdtime_);
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::PASSIVE);
    TASK_UTIL_EXPECT_EQ(min(holdtime_, StateMachine::kHoldTime),
        sm_->hold_time());
}

INSTANTIATE_TEST_CASE_P(One, StateMachineOpenSentParamTest3,
    ::testing::Combine(::testing::Bool(), ::testing::Bool()));

class StateMachineOpenConfirmTest : public StateMachineUnitTest {
protected:
    virtual void SetUp() {
    }

    virtual void TearDown() {
    }
};

// Old State: OpenConfirm (via active session)
// Event:     EvBgpNotification (active)
// New State: Idle
TEST_F(StateMachineOpenConfirmTest, Notification) {
    GetToState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::ACTIVE);
    EvBgpNotification(session_mgr_->active_session());
    VerifyState(StateMachine::IDLE);
}

// Old State: OpenConfirm (via active session)
// Event:     EvTcpPassiveOpen
// New State: OpenConfirm
// Messages:  Notification message sent on passive session.
TEST_F(StateMachineOpenConfirmTest, TcpPassiveOpen) {
    GetToState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::ACTIVE);
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::ACTIVE);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() == NULL);
}

// Old State: OpenConfirm (via active session)
// Event:     EvBgpKeepalive (active)
// New State: Established
TEST_F(StateMachineOpenConfirmTest, BgpKeepalive) {
    GetToState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::ACTIVE);
    EvBgpKeepalive(session_mgr_->active_session());
    VerifyState(StateMachine::ESTABLISHED);
    VerifyDirection(BgpSessionMock::ACTIVE);
}

// Old State: OpenConfirm (via active session)
// Event:     EvBgpNotification (active) + EvHoldTimerExpired
// New State: Idle
TEST_F(StateMachineOpenConfirmTest, NotificationThenHoldTimerExpired) {
    GetToState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::ACTIVE);
    TaskScheduler::GetInstance()->Stop();
    EvBgpNotification(session_mgr_->active_session());
    EvHoldTimerExpired();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::IDLE);
}

// Old State: OpenConfirm (via active session)
// Event:     EvTcpClose (active) + EvHoldTimerExpired
// New State: Idle
TEST_F(StateMachineOpenConfirmTest, TcpCloseThenHoldTimerExpired) {
    GetToState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::ACTIVE);
    TaskScheduler::GetInstance()->Stop();
    EvTcpClose(session_mgr_->active_session());
    EvHoldTimerExpired();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::IDLE);
}

// Old State: OpenConfirm
// Event:     EvTcpClose + EvAdminDown
// New State: Idle
// Timers:    IdleHold timer must not be running as it should get cancelled
//            when processing EvStop.
TEST_F(StateMachineOpenConfirmTest, TcpCloseThenAdminDown) {
    GetToState(StateMachine::OPENCONFIRM);
    sm_->set_idle_hold_time(StateMachine::kMaxIdleHoldTime);
    EvTcpClose(session_mgr_->active_session());
    task_util::WaitForIdle();
    EvAdminDown();
    VerifyState(StateMachine::IDLE);
}

// Parameterize id (higher/lower)

class StateMachineOpenConfirmParamTest1 :
    public StateMachineOpenConfirmTest,
    public ::testing::WithParamInterface<bool> {
protected:
    virtual void SetUp() {
        id_ = GetParam() ? lower_id_ : higher_id_;
        StateMachineOpenConfirmTest::SetUp();
    }

    virtual void TearDown() {
        StateMachineOpenConfirmTest::TearDown();
    }

    int id_;
};

// Old State: OpenConfirm (via passive session)
// Event:     EvBgpNotification (passive)
// New State: Idle
TEST_P(StateMachineOpenConfirmParamTest1, Notification) {
    GetToState(StateMachine::ACTIVE);
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), id_);
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::PASSIVE);
    EvBgpNotification(session_mgr_->passive_session());
    VerifyState(StateMachine::IDLE);
}

// Old State: OpenConfirm (via passive session)
// Event:     EvTcpPassiveOpen (duplicate)
// New State: OpenConfirm
TEST_P(StateMachineOpenConfirmParamTest1, TcpPassiveOpen) {
    GetToState(StateMachine::ACTIVE);
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), id_);
    VerifyState(StateMachine::OPENCONFIRM);
    TaskScheduler::GetInstance()->Stop();
    EvTcpDuplicatePassiveOpen();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::OPENCONFIRM);
    VerifyDirection(BgpSessionMock::PASSIVE);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() != NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->duplicate_session() == NULL);
}

// Old State: OpenConfirm (via passive session)
// Event:     EvBgpKeepalive (passive)
// New State: Established
TEST_P(StateMachineOpenConfirmParamTest1, BgpKeepalive) {
    GetToState(StateMachine::ACTIVE);
    EvTcpPassiveOpen();
    EvBgpOpenCustom(session_mgr_->passive_session(), id_);
    VerifyState(StateMachine::OPENCONFIRM);
    EvBgpKeepalive(session_mgr_->passive_session());
    VerifyState(StateMachine::ESTABLISHED);
    VerifyDirection(BgpSessionMock::PASSIVE);
}

INSTANTIATE_TEST_CASE_P(One, StateMachineOpenConfirmParamTest1,
    ::testing::Bool());


class StateMachineEstablishedTest : public StateMachineUnitTest {
protected:
    virtual void SetUp() {
        GetToState(StateMachine::ESTABLISHED);
        VerifyDirection(BgpSessionMock::ACTIVE);
    }

    virtual void TearDown() {
    }
};

// Old State: Established
// Event:     EvTcpPassiveOpen
// New State: Idle
TEST_F(StateMachineEstablishedTest, TcpPassiveOpen) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    TaskScheduler::GetInstance()->Start();
    VerifyState(StateMachine::IDLE);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->active_session() == NULL);
}

// Old State: Established
// Event:     EvTcpPassiveOpen + EvBgpOpen (on passive session)
// New State: Idle
TEST_F(StateMachineEstablishedTest, TcpPassiveOpenThenBgpOpen) {
    TaskScheduler::GetInstance()->Stop();
    EvTcpPassiveOpen();
    BgpSessionMock *session = session_mgr_->passive_session();
    EvBgpOpenCustom(session, lower_id_);
    TaskScheduler::GetInstance()->Start();
    task_util::WaitForIdle();
    VerifyState(StateMachine::IDLE);
}

// Old State: Established
// Event:     EvBgpOpen (on active session)
// New State: Idle
TEST_F(StateMachineEstablishedTest, BgpOpen) {
    TaskScheduler::GetInstance()->Stop();
    EvBgpOpen(session_mgr_->active_session());
    TaskScheduler::GetInstance()->Start();
    task_util::WaitForIdle();
    VerifyState(StateMachine::IDLE);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->passive_session() == NULL);
    TASK_UTIL_EXPECT_TRUE(session_mgr_->active_session() == NULL);
}

// Old State: Established
// Event:     EvTcpClose + EvAdminDown
// New State: Idle
// Timers:    IdleHold timer must not be running as it should get cancelled
//            when processing EvStop.
TEST_F(StateMachineEstablishedTest, TcpCloseThenAdminDown) {
    sm_->set_idle_hold_time(StateMachine::kMaxIdleHoldTime);
    EvTcpClose(session_mgr_->active_session());
    task_util::WaitForIdle();
    EvAdminDown();
    VerifyState(StateMachine::IDLE);
}

int main(int argc, char **argv) {
    bgp_log_test::init();
    ControlNode::SetDefaultSchedulingPolicy();
    BgpObjectFactory::Register<BgpConfigManager>(
        boost::factory<BgpIfmapConfigManager *>());
    BgpObjectFactory::Register<BgpSessionManager>(
        boost::factory<BgpSessionManagerMock *>());
    ::testing::InitGoogleTest(&argc, argv);
    int result = RUN_ALL_TESTS();
    TaskScheduler::GetInstance()->Terminate();
    return result;
}
