Skip to content

Commit

Permalink
Make tv casting tests use multithreading. (#34457)
Browse files Browse the repository at this point in the history
* Make tv casting tests use multithreading.

* Fix linter complaints

* Use the send to program method instead of manual flush

* return none on empty line, to not raise an empty exception. This will cause a timeout to be reported by the app

* Fix import for linter

* Actually allow json log decoding in our apps

* Update scripts/tests/run_tv_casting_test.py

Co-authored-by: Shao Ling Tan <[email protected]>

* Update scripts/tests/linux/log_line_processing.py

Co-authored-by: Shao Ling Tan <[email protected]>

* Update scripts/tests/linux/log_line_processing.py

Co-authored-by: Shao Ling Tan <[email protected]>

* Update scripts/tests/linux/log_line_processing.py

Co-authored-by: Shao Ling Tan <[email protected]>

* Fix param

* Also write empty stderr if they occur

---------

Co-authored-by: Andrei Litvin <[email protected]>
Co-authored-by: Shao Ling Tan <[email protected]>
  • Loading branch information
3 people authored and pull[bot] committed Dec 6, 2024
1 parent 252476b commit 1633868
Show file tree
Hide file tree
Showing 4 changed files with 377 additions and 208 deletions.
2 changes: 2 additions & 0 deletions examples/tv-app/linux/args.gni
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ chip_enable_additional_data_advertising = true
chip_enable_rotating_device_id = true

matter_enable_tracing_support = true
matter_log_json_payload_hex = true
matter_log_json_payload_decode_full = true

# Thread devices do not support WakeOnLan because their mac address is >48bit
chip_enable_openthread = false
2 changes: 2 additions & 0 deletions examples/tv-casting-app/linux/args.gni
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ chip_max_discovered_ip_addresses = 20
enable_rtti = true

matter_enable_tracing_support = true
matter_log_json_payload_hex = true
matter_log_json_payload_decode_full = true
121 changes: 121 additions & 0 deletions scripts/tests/linux/log_line_processing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Copyright (c) 2024 Project CHIP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
import queue
import select
import subprocess
import threading
from typing import List


class ProcessOutputCapture:
"""
Captures stdout from a process and redirects such stdout to a given file.
The capture serves several purposes as opposed to just reading stdout:
- as data is received, it will get written to a separate file
- data is accumulated in memory for later processing (does not starve/block
the process stdout)
- provides read timeouts for incoming data
Use as part of a resource management block like:
with ProcessOutputCapture("test.sh", "logs.txt") as p:
p.send_to_program("input\n")
while True:
l = p.next_output_line(timeout_sec = 1)
if not l:
break
"""

def __init__(self, command: List[str], output_path: str):
# in/out/err are pipes
self.command = command
self.output_path = output_path
self.output_lines = queue.Queue()
self.process = None
self.io_thread = None
self.done = False

def _io_thread(self):
"""Reads process lines and writes them to an output file.
It also sends the output lines to `self.output_lines` for later
reading
"""
out_wait = select.poll()
out_wait.register(self.process.stdout, select.POLLIN)

err_wait = select.poll()
err_wait.register(self.process.stderr, select.POLLIN)

with open(self.output_path, "wt") as f:
while not self.done:
changes = out_wait.poll(0.1)
if changes:
out_line = self.process.stdout.readline()
f.write(out_line)
self.output_lines.put(out_line)

changes = err_wait.poll(0)
if changes:
err_line = self.process.stderr.readline()
f.write(f"!!STDERR!! : {err_line}")

def __enter__(self):
self.done = False
self.process = subprocess.Popen(
self.command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
self.io_thread = threading.Thread(target=self._io_thread)
self.io_thread.start()
return self

def __exit__(self, exception_type, exception_value, traceback):
self.done = True
if self.process:
self.process.terminate()
self.process.wait()

if self.io_thread:
self.io_thread.join()

if exception_value:
# When we fail because of an exception, report the entire log content
logging.error(f"-------- START: LOG DUMP FOR {self.command!r} -----")
with open(self.output_path, "rt") as f:
for output_line in f.readlines():
logging.error(output_line.strip())
logging.error(f"-------- END: LOG DUMP FOR {self.command!r} -----")

def next_output_line(self, timeout_sec=None):
"""Fetch an item from the output queue, potentially with a timeout."""
try:
return self.output_lines.get(timeout=timeout_sec)
except queue.Empty:
return None

def send_to_program(self, input_cmd):
"""Sends the given input command string to the program.
NOTE: remember to append a `\n` for terminal applications
"""
self.process.stdin.write(input_cmd)
self.process.stdin.flush()
Loading

0 comments on commit 1633868

Please sign in to comment.