From 272d6fd063bcf56da102dcbe3a34e81042a38207 Mon Sep 17 00:00:00 2001
From: Vivien Nicolas <vnicolas@apple.com>
Date: Tue, 25 Jul 2023 09:35:39 +0200
Subject: [PATCH] [chiptool.py] Ensure async report that came in before the
 test got a chance to listen for a report are dispatched properly

---
 .../interactive/InteractiveCommands.cpp       | 45 +++++++++++++++++--
 1 file changed, 41 insertions(+), 4 deletions(-)

diff --git a/examples/chip-tool/commands/interactive/InteractiveCommands.cpp b/examples/chip-tool/commands/interactive/InteractiveCommands.cpp
index 6dae09ce392206..d2752a3fffac5a 100644
--- a/examples/chip-tool/commands/interactive/InteractiveCommands.cpp
+++ b/examples/chip-tool/commands/interactive/InteractiveCommands.cpp
@@ -67,6 +67,7 @@ struct InteractiveServerResult
     bool mIsAsyncReport = false;
     uint16_t mTimeout   = 0;
     int mStatus         = EXIT_SUCCESS;
+    std::vector<std::string> mPendingResults;
     std::vector<std::string> mResults;
     std::vector<InteractiveServerResultLog> mLogs;
 
@@ -100,9 +101,20 @@ struct InteractiveServerResult
         mIsAsyncReport = isAsyncReport;
         mTimeout       = timeout;
 
-        if (mIsAsyncReport && mTimeout)
+        if (mIsAsyncReport)
         {
-            chip::DeviceLayer::PlatformMgr().ScheduleWork(StartAsyncTimeout, reinterpret_cast<intptr_t>(this));
+            if (mTimeout)
+            {
+                chip::DeviceLayer::PlatformMgr().ScheduleWork(StartAsyncTimeout, reinterpret_cast<intptr_t>(this));
+            }
+
+            if (!mPendingResults.empty())
+            {
+                mResults = std::move(mPendingResults);
+            }
+
+            // Reset the pending results.
+            mPendingResults.clear();
         }
     }
 
@@ -119,6 +131,7 @@ struct InteractiveServerResult
         mIsAsyncReport = false;
         mTimeout       = 0;
         mStatus        = EXIT_SUCCESS;
+        mPendingResults.clear();
         mResults.clear();
         mLogs.clear();
     }
@@ -158,11 +171,26 @@ struct InteractiveServerResult
     void MaybeAddResult(const char * result)
     {
         auto lock = ScopedLock(mMutex);
-        VerifyOrReturn(mEnabled);
+        // If we are not waiting for a result it could be that a report came in *before* the command to wait for
+        // it has happened. The result is stored until the next command. If the next command is an async
+        // command the pending result would be promote to a valid result so it could be dispatched properly.
+        if (!mEnabled)
+        {
+            mPendingResults.push_back(result);
+            return;
+        }
 
         mResults.push_back(result);
     }
 
+    bool HasResults()
+    {
+        auto lock = ScopedLock(mMutex);
+        VerifyOrReturnValue(mEnabled, false);
+
+        return !mResults.empty();
+    }
+
     std::string AsJsonString()
     {
         auto lock = ScopedLock(mMutex);
@@ -308,7 +336,16 @@ bool InteractiveServerCommand::OnWebSocketMessageReceived(char * msg)
     }
 
     gInteractiveServerResult.Setup(isAsyncReport, timeout);
-    VerifyOrReturnValue(!isAsyncReport, true);
+
+    if (isAsyncReport)
+    {
+        if (gInteractiveServerResult.HasResults())
+        {
+            mWebSocketServer.Send(gInteractiveServerResult.AsJsonString().c_str());
+            gInteractiveServerResult.Reset();
+        }
+        return true;
+    }
 
     auto shouldStop = ParseCommand(msg, &gInteractiveServerResult.mStatus);
     mWebSocketServer.Send(gInteractiveServerResult.AsJsonString().c_str());