Skip to content

Commit 356317a

Browse files
authored
[CI] When the CI is starting the server (chip-tool or darwin-framework-tool) wait to see for the websocket message ready before trying to connect (#32006)
* [darwin-framework-tool] Do not use platform::LogV since this is a no-op now * [CI] When the CI is starting the server (chip-tool or darwin-framework-tool) wait to see for the websocket message ready before trying to connect
1 parent a422b7a commit 356317a

File tree

4 files changed

+55
-8
lines changed

4 files changed

+55
-8
lines changed

examples/common/websocket-server/WebSocketServer.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
constexpr uint16_t kDefaultWebSocketServerPort = 9002;
2828
constexpr uint16_t kMaxMessageBufferLen = 8192;
29+
constexpr char kWebSocketServerReadyMessage[] = "== WebSocket Server Ready";
2930

3031
namespace {
3132
lws * gWebSocketInstance = nullptr;
@@ -153,6 +154,10 @@ static int OnWebSocketCallback(lws * wsi, lws_callback_reasons reason, void * us
153154
{
154155
gWebSocketInstance = nullptr;
155156
}
157+
else if (LWS_CALLBACK_PROTOCOL_INIT == reason)
158+
{
159+
ChipLogProgress(chipTool, "%s", kWebSocketServerReadyMessage);
160+
}
156161

157162
return 0;
158163
}

examples/darwin-framework-tool/commands/interactive/InteractiveCommands.mm

+3-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "InteractiveCommands.h"
2020

2121
#include <lib/support/Base64.h>
22+
#include <logging/logging.h>
2223
#include <platform/logging/LogV.h>
2324

2425
#include <editline.h>
@@ -72,7 +73,7 @@ void ClearLine()
7273
void ENFORCE_FORMAT(3, 0) LoggingCallback(const char * module, uint8_t category, const char * msg, va_list args)
7374
{
7475
ClearLine();
75-
chip::Logging::Platform::LogV(module, category, msg, args);
76+
dft::logging::LogRedirectCallback(module, category, msg, args);
7677
ClearLine();
7778
}
7879

@@ -244,7 +245,7 @@ void ENFORCE_FORMAT(3, 0) InteractiveServerLoggingCallback(const char * module,
244245
va_list args_copy;
245246
va_copy(args_copy, args);
246247

247-
chip::Logging::Platform::LogV(module, category, msg, args);
248+
dft::logging::LogRedirectCallback(module, category, msg, args);
248249

249250
char message[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE];
250251
vsnprintf(message, sizeof(message), msg, args_copy);

examples/darwin-framework-tool/logging/logging.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ namespace dft {
2020
namespace logging {
2121

2222
void Setup();
23+
void LogRedirectCallback(const char * moduleName, uint8_t category, const char * format, va_list args);
2324

24-
}
25+
} // namespace logging
2526
} // namespace dft

scripts/py_matter_yamltests/matter_yamltests/websocket_runner.py

+45-5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16+
import logging
17+
import re
18+
import select
1619
import subprocess
1720
import time
1821
from dataclasses import dataclass
@@ -24,6 +27,10 @@
2427

2528
_KEEP_ALIVE_TIMEOUT_IN_SECONDS = 120
2629
_MAX_MESSAGE_SIZE_IN_BYTES = 10485760 # 10 MB
30+
_CONNECT_MAX_RETRIES_DEFAULT = 4
31+
_WEBSOCKET_SERVER_MESSAGE = '== WebSocket Server Ready'
32+
_WEBSOCKET_SERVER_MESSAGE_TIMEOUT = 60 # seconds
33+
_WEBSOCKET_SERVER_TERMINATE_TIMEOUT = 10 # seconds
2734

2835

2936
@dataclass
@@ -54,7 +61,7 @@ def is_connected(self) -> bool:
5461
return self._client.state == websockets.protocol.State.OPEN
5562

5663
async def start(self):
57-
self._server = await self._start_server(self._server_startup_command)
64+
self._server = await self._start_server(self._server_startup_command, self._server_connection_url)
5865
self._client = await self._start_client(self._server_connection_url)
5966

6067
async def stop(self):
@@ -70,7 +77,7 @@ async def execute(self, request):
7077
return await instance.recv()
7178
return None
7279

73-
async def _start_client(self, url, max_retries=5, interval_between_retries=1):
80+
async def _start_client(self, url, max_retries=_CONNECT_MAX_RETRIES_DEFAULT, interval_between_retries=1):
7481
if max_retries:
7582
start = time.time()
7683
try:
@@ -93,15 +100,48 @@ async def _stop_client(self, instance):
93100
if instance:
94101
await instance.close()
95102

96-
async def _start_server(self, command):
103+
async def _start_server(self, command, url):
97104
instance = None
98105
if command:
99-
instance = subprocess.Popen(command, stdout=subprocess.DEVNULL)
106+
start_time = time.time()
107+
108+
command = ['stdbuf', '-o0', '-e0'] + command # disable buffering
109+
instance = subprocess.Popen(
110+
command, text=False, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
111+
112+
# Loop to read the subprocess output with a timeout
113+
lines = []
114+
while True:
115+
if time.time() - start_time > _WEBSOCKET_SERVER_MESSAGE_TIMEOUT:
116+
for line in lines:
117+
print(line.decode('utf-8'), end='')
118+
self._hooks.abort(url)
119+
await self._stop_server(instance)
120+
raise Exception(
121+
f'Connecting to {url} failed. WebSocket startup has not been detected.')
122+
123+
ready, _, _ = select.select([instance.stdout], [], [], 1)
124+
if ready:
125+
line = instance.stdout.readline()
126+
if len(line):
127+
lines.append(line)
128+
if re.search(_WEBSOCKET_SERVER_MESSAGE, line.decode('utf-8')):
129+
break # Exit the loop if the pattern is found
130+
else:
131+
continue
132+
instance.stdout.close()
133+
100134
return instance
101135

102136
async def _stop_server(self, instance):
103137
if instance:
104-
instance.kill()
138+
instance.terminate() # sends SIGTERM
139+
try:
140+
instance.wait(_WEBSOCKET_SERVER_TERMINATE_TIMEOUT)
141+
except subprocess.TimeoutExpired:
142+
logging.debug(
143+
'Subprocess did not terminate on SIGTERM, killing it now')
144+
instance.kill()
105145

106146
def _make_server_connection_url(self, address: str, port: int):
107147
return 'ws://' + address + ':' + str(port)

0 commit comments

Comments
 (0)