#include <limits.h>
#include "gtest/gtest.h"

#include "Plugins/Process/Linux/ThreadStateCoordinator.h"

using namespace lldb_private;

namespace
{
    const ThreadStateCoordinator::ThreadIDSet EMPTY_THREAD_ID_SET;

    void
    NOPLogger (const char *format, va_list args)
    {
        // Do nothing.
    }

    void
    StdoutLogger (const char *format, va_list args)
    {
        // Print to stdout.
        vprintf (format, args);
        printf ("\n");
    }

    // These are single-line macros so that IDE integration of gtest results puts
    // the error markers on the correct failure point within the gtest.

#define ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS() ASSERT_EQ (ThreadStateCoordinator::eventLoopResultContinue, m_coordinator.ProcessNextEvent ()); \
if (HasError ()) { printf ("unexpected error in processing of event, error: %s\n", m_error_string.c_str ()); } \
ASSERT_EQ (false, HasError ());

#define ASSERT_PROCESS_NEXT_EVENT_FAILS() ASSERT_EQ (ThreadStateCoordinator::eventLoopResultContinue, m_coordinator.ProcessNextEvent ()); \
ASSERT_EQ (true, HasError ());

    class ThreadStateCoordinatorTest: public ::testing::Test
    {
    protected:
        // Constants.
        const lldb::tid_t TRIGGERING_TID      = 4105;
        const lldb::tid_t PENDING_STOP_TID    = 3;
        const lldb::tid_t PENDING_STOP_TID_02 = 29016;
        const lldb::tid_t NEW_THREAD_TID      = 1234;

        // Member variables.
        bool m_error_called = false;
        std::string m_error_string;

        ThreadStateCoordinator m_coordinator;

        bool m_deferred_notification_called;
        lldb::tid_t m_deferred_notification_tid;

        ThreadStateCoordinator::ThreadIDSet m_requested_stop_tids;

        // Constructors.
        ThreadStateCoordinatorTest () :
        m_error_called (false),
        m_error_string (),
        m_coordinator (StdoutLogger),
        m_deferred_notification_called (false),
        m_deferred_notification_tid (0),
        m_requested_stop_tids ()
        {
        }

        // Member functions.

        // Error handling.
        void
        SetErrorString (const std::string &error_string)
        {
            m_error_called = true;
            m_error_string = error_string;
            printf ("received error: %s (test might be expecting)\n", error_string.c_str ());
        }

        ThreadStateCoordinator::ErrorFunction
        GetErrorFunction ()
        {
            using namespace std::placeholders;
            return std::bind(&ThreadStateCoordinatorTest::SetErrorString, this, _1);
        }

        bool
        HasError () const
        {
            return m_error_called;
        }

        // Deferred notificaton reception.
        void
        DeferredStopNotificationHandler (lldb::tid_t triggered_tid)
        {
            m_deferred_notification_called = true;
            m_deferred_notification_tid = triggered_tid;
        }

        ThreadStateCoordinator::ThreadIDFunction
        GetDeferredStopNotificationFunction ()
        {
            using namespace std::placeholders;
            return std::bind(&ThreadStateCoordinatorTest::DeferredStopNotificationHandler, this, _1);
        }

        bool
        DidFireDeferredNotification () const
        {
            return m_deferred_notification_called;
        }

        lldb::tid_t
        GetDeferredNotificationTID () const
        {
            return m_deferred_notification_tid;
        }

        // Stop request call reception.
        void
        ThreadStopRequested (lldb::tid_t stop_tid)
        {
            m_requested_stop_tids.insert (stop_tid);
        }

        ThreadStateCoordinator::ThreadIDFunction
        GetStopRequestFunction ()
        {
            using namespace std::placeholders;
            return std::bind(&ThreadStateCoordinatorTest::ThreadStopRequested, this, _1);
        }

        ThreadStateCoordinator::ThreadIDSet::size_type
        GetRequestedStopCount () const
        {
            return m_requested_stop_tids.size();
        }

        bool
        DidRequestStopForTid (lldb::tid_t tid)
        {
            return m_requested_stop_tids.find (tid) != m_requested_stop_tids.end ();
        }

        // Test state initialization helpers.
        void
        SetupKnownRunningThread (lldb::tid_t tid)
        {
            NotifyThreadCreate (tid, false);
            ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
        }

        void
        SetupKnownStoppedThread (lldb::tid_t tid)
        {
            NotifyThreadCreate (tid, true);
            ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
        }

        // Convenience wrappers for ThreadStateCoordinator, using defaults for expected arguments
        // that plug into the test case handlers.
        void
        CallAfterThreadsStop (lldb::tid_t deferred_tid,
                              const ThreadStateCoordinator::ThreadIDSet &pending_stop_wait_tids)
        {
            m_coordinator.CallAfterThreadsStop (deferred_tid,
                                                pending_stop_wait_tids,
                                                GetStopRequestFunction (),
                                                GetDeferredStopNotificationFunction (),
                                                GetErrorFunction ());
        }

        void
        CallAfterRunningThreadsStop (lldb::tid_t deferred_tid)
        {
            m_coordinator.CallAfterRunningThreadsStop (deferred_tid,
                                                       GetStopRequestFunction (),
                                                       GetDeferredStopNotificationFunction (),
                                                       GetErrorFunction ());
        }

        void
        NotifyThreadCreate (lldb::tid_t stopped_tid, bool thread_is_stopped)
        {
            m_coordinator.NotifyThreadCreate (stopped_tid, thread_is_stopped, GetErrorFunction ());
        }

        void
        NotifyThreadStop (lldb::tid_t stopped_tid)
        {
            m_coordinator.NotifyThreadStop (stopped_tid, GetErrorFunction ());
        }

        void
        NotifyThreadDeath (lldb::tid_t tid)
        {
            m_coordinator.NotifyThreadDeath (tid, GetErrorFunction ());
        }
};
}

TEST_F (ThreadStateCoordinatorTest, StopCoordinatorWorksNoPriorEvents)
{
    m_coordinator.StopCoordinator ();
    ASSERT_EQ (ThreadStateCoordinator::eventLoopResultStop, m_coordinator.ProcessNextEvent ());
    ASSERT_EQ (false, HasError ());
}

TEST_F (ThreadStateCoordinatorTest, NotifyThreadCreateSignalsErrorOnAlreadyKnownThread)
{
    // Let the coordinator know about our thread.
    SetupKnownStoppedThread (TRIGGERING_TID);

    // Notify the thread was created - again.
    NotifyThreadCreate (TRIGGERING_TID, true);

    // This should error out.
    ASSERT_PROCESS_NEXT_EVENT_FAILS ();
}


TEST_F (ThreadStateCoordinatorTest, NotifyThreadDeathSignalsErrorOnUnknownThread)
{
    const lldb::tid_t UNKNOWN_TID = 678;

    // Notify an unknown thread has died.
    NotifyThreadDeath (UNKNOWN_TID);

    // This should error out.
    ASSERT_PROCESS_NEXT_EVENT_FAILS ();
}

TEST_F (ThreadStateCoordinatorTest, NotifyThreadStopSignalsErrorOnUnknownThread)
{
    const lldb::tid_t UNKNOWN_TID = 678;

    // Notify an unknown thread has stopped.
    NotifyThreadStop (UNKNOWN_TID);
    ASSERT_PROCESS_NEXT_EVENT_FAILS ();
}

TEST_F (ThreadStateCoordinatorTest, CallAfterTheadsStopSignalsErrorOnUnknownDeferredThread)
{
    const lldb::tid_t UNKNOWN_TRIGGER_TID = 678;

    // Defer notify for an unknown thread.
    CallAfterThreadsStop (UNKNOWN_TRIGGER_TID,
                          EMPTY_THREAD_ID_SET);

    // Shouldn't have fired yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Event should fail because trigger tid is unknown.
    ASSERT_PROCESS_NEXT_EVENT_FAILS ();

    // Shouldn't have fired due to error.
    ASSERT_EQ (false, DidFireDeferredNotification ());
}

TEST_F (ThreadStateCoordinatorTest, CallAfterTheadsStopSignalsErrorOnUnknownPendingStopThread)
{
    // Let the coordinator know about our thread.
    SetupKnownStoppedThread (TRIGGERING_TID);

    // Defer notify for an unknown thread.
    const lldb::tid_t UNKNOWN_PENDING_STOP_TID = 7890;
    ThreadStateCoordinator::ThreadIDSet pending_stop_tids { UNKNOWN_PENDING_STOP_TID };

    CallAfterThreadsStop (TRIGGERING_TID,
                          pending_stop_tids);

    // Shouldn't have fired yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Event should fail because trigger tid is unknown.
    ASSERT_PROCESS_NEXT_EVENT_FAILS ();

    // Shouldn't have triggered deferred notification due to error.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Shouldn't have triggered stop request due to unknown tid.
    ASSERT_EQ (0, GetRequestedStopCount ());
}

TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenNoPendingStops)
{
    // Let the coordinator know about our thread.
    SetupKnownStoppedThread (TRIGGERING_TID);

    // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
    CallAfterThreadsStop (TRIGGERING_TID,
                          EMPTY_THREAD_ID_SET);

    // Notification trigger shouldn't go off yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Process next event.  This will pick up the call after threads stop event.
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // Now the trigger should have fired, since there were no threads that needed to first stop.
    ASSERT_EQ (true, DidFireDeferredNotification ());
    ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
}

TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenOnePendingStop)
{
    // Let the coordinator know about our thread.
    SetupKnownStoppedThread (TRIGGERING_TID);

    // Let the coordinator know about a currently-running thread we'll wait on.
    SetupKnownRunningThread (PENDING_STOP_TID);

    ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID };

    // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
    CallAfterThreadsStop (TRIGGERING_TID,
                          pending_stop_tids);

    // Neither trigger should have gone off yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());
    ASSERT_EQ (false, DidRequestStopForTid (PENDING_STOP_TID));

    // Process next event.
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // Now the request thread stop should have been called for the pending stop.
    ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));

    // But we still shouldn't have the deferred signal call go off yet.  Need to wait for the stop to be reported.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Now report the that the pending stop occurred.
    NotifyThreadStop (PENDING_STOP_TID);

    // Shouldn't take effect until after next processing step.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Process next event.
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // Deferred signal notification should have fired now.
    ASSERT_EQ (true, DidFireDeferredNotification ());
    ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
}

TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenTwoPendingStops)
{
    // Setup threads.
    SetupKnownStoppedThread (TRIGGERING_TID);
    SetupKnownRunningThread (PENDING_STOP_TID);
    SetupKnownRunningThread (PENDING_STOP_TID_02);

    ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID, PENDING_STOP_TID_02 };

    // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
    CallAfterThreadsStop (TRIGGERING_TID,
                          pending_stop_tids);

    // Neither trigger should have gone off yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());
    ASSERT_EQ (0, GetRequestedStopCount ());

    // Process next event.
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // Now the request thread stops should have been called for the pending stop tids.
    ASSERT_EQ (2, GetRequestedStopCount ());
    ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
    ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID_02));

    // But we still shouldn't have the deferred signal call go off yet.  Need to wait for the stop to be reported.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Report the that the first pending stop occurred.
    NotifyThreadStop (PENDING_STOP_TID);
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // Shouldn't take effect until after both pending threads are notified.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Report the that the first pending stop occurred.
    NotifyThreadStop (PENDING_STOP_TID_02);
    ASSERT_EQ (false, DidFireDeferredNotification ());
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // Deferred signal notification should have fired now.
    ASSERT_EQ (true, DidFireDeferredNotification ());
    ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
}

TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenPendingAlreadyStopped)
{
    // Setup threads.
    SetupKnownStoppedThread (TRIGGERING_TID);
    SetupKnownRunningThread (PENDING_STOP_TID);

    ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID };

    // Tell m_coordinator the pending stop tid is already stopped.
    NotifyThreadStop (PENDING_STOP_TID);
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
    CallAfterThreadsStop (TRIGGERING_TID,
                          pending_stop_tids);

    // Neither trigger should have gone off yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());
    ASSERT_EQ (false, DidRequestStopForTid (PENDING_STOP_TID));

    // Process next event.
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // The pending stop should *not* fire because the m_coordinator knows it has already stopped.
    ASSERT_EQ (false, DidRequestStopForTid (PENDING_STOP_TID));

    // The deferred signal notification should have fired since all requirements were met.
    ASSERT_EQ (true, DidFireDeferredNotification ());
    ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
}

TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenTwoPendingOneAlreadyStopped)
{
    SetupKnownStoppedThread (TRIGGERING_TID);
    SetupKnownRunningThread (PENDING_STOP_TID);
    SetupKnownRunningThread (PENDING_STOP_TID_02);

    ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID, PENDING_STOP_TID_02 };

    // Tell coordinator the pending stop tid is already stopped.
    NotifyThreadStop (PENDING_STOP_TID);
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
    CallAfterThreadsStop (TRIGGERING_TID,
                          pending_stop_tids);

    // Neither trigger should have gone off yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());
    ASSERT_EQ (0, GetRequestedStopCount ());

    // Process next event.
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // The pending stop should only fire for one of the threads, the one that wasn't already stopped.
    ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID_02));
    ASSERT_EQ (false, DidRequestStopForTid (PENDING_STOP_TID));

    // The deferred signal notification should not yet have fired since all pending thread stops have not yet occurred.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Notify final thread has stopped.
    NotifyThreadStop (PENDING_STOP_TID_02);
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // The deferred signal notification should have fired since all requirements were met.
    ASSERT_EQ (true, DidFireDeferredNotification ());
    ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
}

TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenOnePendingThreadDies)
{
    SetupKnownStoppedThread (TRIGGERING_TID);
    SetupKnownRunningThread (PENDING_STOP_TID);

    ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID };

    // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
    CallAfterThreadsStop (TRIGGERING_TID,
                          pending_stop_tids);

    // Neither trigger should have gone off yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());
    ASSERT_EQ (0, GetRequestedStopCount ());

    // Process next event.
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // Now the request thread stop should have been called for the pending stop.
    ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));

    // But we still shouldn't have the deferred signal call go off yet.  Need to wait for the death to be reported.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Now report the that the thread with pending stop dies.
    NotifyThreadDeath (PENDING_STOP_TID);

    // Shouldn't take effect until after next processing step.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Process next event.
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // Deferred signal notification should have fired now.
    ASSERT_EQ (true, DidFireDeferredNotification ());
    ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
}

TEST_F (ThreadStateCoordinatorTest, ExistingPendingNotificationRequiresStopFromNewThread)
{
    SetupKnownStoppedThread (TRIGGERING_TID);
    SetupKnownRunningThread (PENDING_STOP_TID);

    ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID };

    // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
    CallAfterThreadsStop (TRIGGERING_TID,
                          pending_stop_tids);

    // Neither trigger should have gone off yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());
    ASSERT_EQ (0, GetRequestedStopCount ());

    // Process next event.
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // Now the request thread stop should have been called for the pending stop.
    ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));

    // But we still shouldn't have the deferred signal call go off yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Indicate a new thread has just been created.
    SetupKnownRunningThread (NEW_THREAD_TID);

    // We should have just received a stop request for the new thread id.
    ASSERT_EQ (2, GetRequestedStopCount ());
    ASSERT_EQ (true, DidRequestStopForTid (NEW_THREAD_TID));

    // Now report the original pending tid stopped.  This should no longer
    // trigger the pending notification because we should now require the
    // new thread to stop too.
    NotifyThreadStop (PENDING_STOP_TID);
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Now notify the new thread stopped.
    NotifyThreadStop (NEW_THREAD_TID);
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // Deferred signal notification should have fired now.
    ASSERT_EQ (true, DidFireDeferredNotification ());
    ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
}

TEST_F (ThreadStateCoordinatorTest, DeferredNotificationRemovedByResetForExec)
{
    SetupKnownStoppedThread (TRIGGERING_TID);

    // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
    CallAfterThreadsStop (TRIGGERING_TID,
                          EMPTY_THREAD_ID_SET);

    // Notification trigger shouldn't go off yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Now indicate an exec occurred, which will invalidate all state about the process and threads.
    m_coordinator.ResetForExec ();

    // Verify the deferred stop notification does *not* fire with the next
    // process.  It will handle the reset and not the deferred signaling, which
    // should now be removed.
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
    ASSERT_EQ (false, DidFireDeferredNotification ());
}

TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeSignalsErrorOnUnknownThread)
{
    const lldb::tid_t UNKNOWN_TID = 411;

    // Request a resume.
    lldb::tid_t resumed_tid = 0;
    int resume_call_count = 0;

    m_coordinator.RequestThreadResume (UNKNOWN_TID,
                                       [&](lldb::tid_t tid)
                                       {
                                           ++resume_call_count;
                                           resumed_tid = tid;
                                       },
                                       GetErrorFunction ());
    // Shouldn't be called yet.
    ASSERT_EQ (0, resume_call_count);

    // Process next event.  This should fail since the coordinator doesn't know about the thread.
    ASSERT_PROCESS_NEXT_EVENT_FAILS ();
    ASSERT_EQ (0, resume_call_count);
}

TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeCallsCallbackWhenThreadIsStopped)
{
    // Initialize thread to be in stopped state.
    SetupKnownStoppedThread (NEW_THREAD_TID);

    // Request a resume.
    lldb::tid_t resumed_tid = 0;
    int resume_call_count = 0;

    m_coordinator.RequestThreadResume (NEW_THREAD_TID,
                                      [&](lldb::tid_t tid)
                                      {
                                          ++resume_call_count;
                                          resumed_tid = tid;
                                      },
                                      GetErrorFunction ());
    // Shouldn't be called yet.
    ASSERT_EQ (0, resume_call_count);

    // Process next event.  After that, the resume request call should have fired.
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
    ASSERT_EQ (1, resume_call_count);
    ASSERT_EQ (NEW_THREAD_TID, resumed_tid);
}

TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeSkipsCallbackOnSecondResumeAttempt)
{
    // Initialize thread to be in stopped state.
    SetupKnownStoppedThread (NEW_THREAD_TID);

    // Request a resume.
    lldb::tid_t resumed_tid = 0;
    int resume_call_count = 0;

    m_coordinator.RequestThreadResume (NEW_THREAD_TID,
                                       [&](lldb::tid_t tid)
                                       {
                                           ++resume_call_count;
                                           resumed_tid = tid;
                                       },
                                       GetErrorFunction ());
    // Shouldn't be called yet.
    ASSERT_EQ (0, resume_call_count);

    // Process next event.  After that, the resume request call should have fired.
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
    ASSERT_EQ (1, resume_call_count);
    ASSERT_EQ (NEW_THREAD_TID, resumed_tid);

    // Make a second resume request.
    const int initial_resume_call_count = resume_call_count;
    m_coordinator.RequestThreadResume (NEW_THREAD_TID,
                                       [&](lldb::tid_t tid)
                                       {
                                           ++resume_call_count;
                                           resumed_tid = tid;
                                       },
                                       GetErrorFunction ());

    // Process next event.  This should fail since the thread should already be running.
    ASSERT_PROCESS_NEXT_EVENT_FAILS ();

    // And the resume count should not have increased.
    ASSERT_EQ (initial_resume_call_count, resume_call_count);
}

TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeSignalsErrorOnAlreadyRunningThread)
{
    const lldb::tid_t TEST_TID = 1234;
    SetupKnownRunningThread (NEW_THREAD_TID);

    // Request a resume.
    lldb::tid_t resumed_tid = 0;
    int resume_call_count = 0;

    m_coordinator.RequestThreadResume (TEST_TID,
                                     [&](lldb::tid_t tid)
                                     {
                                         ++resume_call_count;
                                         resumed_tid = tid;
                                     },
                                     GetErrorFunction ());

    // Shouldn't be called yet.
    ASSERT_EQ (0, resume_call_count);

    // Process next event.  Should be an error.
    ASSERT_PROCESS_NEXT_EVENT_FAILS ();

    // The resume request should not have gone off because we think it is already running.
    ASSERT_EQ (0, resume_call_count);
}

TEST_F (ThreadStateCoordinatorTest, ResumedThreadAlreadyMarkedDoesNotHoldUpPendingStopNotification)
{
    // We're going to test this scenario:
    //   * Deferred notification waiting on two threads, A and B.  A and B currently running.
    //   * Thread A stops.
    //   * Thread A resumes.
    //   * Thread B stops.
    //
    //   Here we could have forced A to stop again (after the Thread A resumes) because we had a pending stop nofication awaiting
    //   all those threads to stop.  However, we are going to explicitly not try to restop A - somehow
    //   that seems wrong and possibly buggy since for that to happen, we would have intentionally called
    //   a resume after the stop.  Instead, we'll just log and indicate this looks suspicous.  We can revisit
    //   that decision after we see if/when that happens.
    const lldb::tid_t PENDING_TID_A = 2;
    const lldb::tid_t PENDING_TID_B = 89;

    SetupKnownStoppedThread (TRIGGERING_TID);
    SetupKnownRunningThread (PENDING_TID_A);
    SetupKnownRunningThread (PENDING_TID_B);

    ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_TID_A, PENDING_TID_B };

    // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
    CallAfterThreadsStop (TRIGGERING_TID,
                          pending_stop_tids);

    // Neither trigger should have gone off yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());
    ASSERT_EQ (0, GetRequestedStopCount ());

    // Execute CallAfterThreadsStop.
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // Both TID A and TID B should have had stop requests made.
    ASSERT_EQ (2, GetRequestedStopCount ());
    ASSERT_EQ (true, DidRequestStopForTid (PENDING_TID_A));
    ASSERT_EQ (true, DidRequestStopForTid (PENDING_TID_B));

    // But we still shouldn't have the deferred signal call go off yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Report thread A stopped.
    NotifyThreadStop (PENDING_TID_A);
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Now report thread A is resuming.  Ensure the resume is called.
    bool resume_called = false;
    m_coordinator.RequestThreadResume (PENDING_TID_A,
                                     [&](lldb::tid_t tid) { resume_called = true; },
                                     GetErrorFunction ());
    ASSERT_EQ (false, resume_called);
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
    ASSERT_EQ (true, resume_called);

    // Report thread B stopped.
    NotifyThreadStop (PENDING_TID_B);
    ASSERT_EQ (false, DidFireDeferredNotification ());
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // After notifying thread b stopped, we now have thread a resumed but thread b stopped.
    // However, since thread a had stopped, we now have had both requirements stopped at some point.
    // For now we'll expect this will fire the pending deferred stop notification.
    ASSERT_EQ (true, DidFireDeferredNotification ());
    ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
}

TEST_F (ThreadStateCoordinatorTest, CallAfterRunningThreadsStopFiresWhenNoRunningThreads)
{
    // Let the coordinator know about our thread.
    SetupKnownStoppedThread (TRIGGERING_TID);

    // Notify we have a trigger that needs to be fired when all running threads have stopped.
    CallAfterRunningThreadsStop (TRIGGERING_TID);

    // Notification trigger shouldn't go off yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Process next event.  This will pick up the call after threads stop event.
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // Now the trigger should have fired, since there were no threads that needed to first stop.
    ASSERT_EQ (true, DidFireDeferredNotification ());
    ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());

    // And no stop requests should have been made.
    ASSERT_EQ (0, GetRequestedStopCount ());
}

TEST_F (ThreadStateCoordinatorTest, CallAfterRunningThreadsStopRequestsTwoPendingStops)
{
    // Let the coordinator know about our threads.
    SetupKnownStoppedThread (TRIGGERING_TID);
    SetupKnownRunningThread (PENDING_STOP_TID);
    SetupKnownRunningThread (PENDING_STOP_TID_02);

    // Notify we have a trigger that needs to be fired when all running threads have stopped.
    CallAfterRunningThreadsStop (TRIGGERING_TID);

    // Notification trigger shouldn't go off yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Process next event.  This will pick up the call after threads stop event.
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // We should have two stop requests for the two threads currently running.
    ASSERT_EQ (2, GetRequestedStopCount ());
    ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
    ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID_02));

    // But the deferred stop notification should not have fired yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Now notify the two threads stopped.
    NotifyThreadStop (PENDING_STOP_TID);
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();
    ASSERT_EQ (false, DidFireDeferredNotification ());

    NotifyThreadStop (PENDING_STOP_TID_02);
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // Now the trigger should have fired, since there were no threads that needed to first stop.
    ASSERT_EQ (true, DidFireDeferredNotification ());
    ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
}

TEST_F (ThreadStateCoordinatorTest, CallAfterRunningThreadsStopRequestsStopTwoOtherThreadsOneRunning)
{
    // Let the coordinator know about our threads.  PENDING_STOP_TID_02 will already be stopped.
    SetupKnownStoppedThread (TRIGGERING_TID);
    SetupKnownRunningThread (PENDING_STOP_TID);
    SetupKnownStoppedThread (PENDING_STOP_TID_02);

    // Notify we have a trigger that needs to be fired when all running threads have stopped.
    CallAfterRunningThreadsStop (TRIGGERING_TID);

    // Notification trigger shouldn't go off yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Process next event.  This will pick up the call after threads stop event.
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // We should have two stop requests for the two threads currently running.
    ASSERT_EQ (1, GetRequestedStopCount ());
    ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));

    // But the deferred stop notification should not have fired yet.
    ASSERT_EQ (false, DidFireDeferredNotification ());

    // Now notify the two threads stopped.
    NotifyThreadStop (PENDING_STOP_TID);
    ASSERT_PROCESS_NEXT_EVENT_SUCCEEDS ();

    // Now the trigger should have fired, since there were no threads that needed to first stop.
    ASSERT_EQ (true, DidFireDeferredNotification ());
    ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
}
