157 lines
5.4 KiB
Python
157 lines
5.4 KiB
Python
#!/usr/bin/env python3
|
|
import os
|
|
import sys
|
|
import io
|
|
import subprocess
|
|
import time
|
|
import threading
|
|
import queue
|
|
import argparse
|
|
import _thread
|
|
from pathlib import Path
|
|
from typing import List, Tuple, Any, Optional
|
|
from ci.running_process import RunningProcess
|
|
|
|
_PIO_CHECK_ENABLED = False
|
|
|
|
_IS_GITHUB = os.environ.get('GITHUB_ACTIONS') == 'true'
|
|
|
|
def run_command(cmd: List[str], **kwargs: Any) -> None:
|
|
"""Run a command and handle errors"""
|
|
try:
|
|
subprocess.run(cmd, check=True, **kwargs)
|
|
except subprocess.CalledProcessError as e:
|
|
sys.exit(e.returncode)
|
|
|
|
def output_reader(process: subprocess.Popen[str],
|
|
output_queue: queue.Queue[Tuple[str, str]],
|
|
stop_event: threading.Event) -> None:
|
|
"""Read output from process and put it in the queue"""
|
|
try:
|
|
assert process.stdout is not None # for mypy
|
|
assert process.stderr is not None # for mypy
|
|
|
|
while not stop_event.is_set():
|
|
# Use a small timeout so we can check the stop_event regularly
|
|
if process.stdout.readable():
|
|
stdout_line = process.stdout.readline()
|
|
if stdout_line:
|
|
output_queue.put(('stdout', stdout_line))
|
|
if process.stderr.readable():
|
|
stderr_line = process.stderr.readline()
|
|
if stderr_line:
|
|
output_queue.put(('stderr', stderr_line))
|
|
|
|
# Check if process has ended and all output has been read
|
|
if process.poll() is not None:
|
|
# Get any remaining output
|
|
remaining_out, remaining_err = process.communicate()
|
|
if remaining_out:
|
|
output_queue.put(('stdout', remaining_out))
|
|
if remaining_err:
|
|
output_queue.put(('stderr', remaining_err))
|
|
break
|
|
except KeyboardInterrupt:
|
|
# Interrupt main thread and exit
|
|
_thread.interrupt_main()
|
|
return
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
"""Parse command line arguments"""
|
|
parser = argparse.ArgumentParser(description='Run FastLED tests')
|
|
parser.add_argument('--cpp', action='store_true',
|
|
help='Run C++ tests only')
|
|
parser.add_argument('test', type=str, nargs='?', default=None,
|
|
help='Specific C++ test to run')
|
|
parser.add_argument("--clang", action="store_true", help="Use Clang compiler")
|
|
parser.add_argument("--clean", action="store_true", help="Clean build before compiling")
|
|
return parser.parse_args()
|
|
|
|
|
|
def _make_pio_check_cmd() -> List[str]:
|
|
return ['pio', 'check', '--skip-packages',
|
|
'--src-filters=+<src/>', '--severity=medium',
|
|
'--fail-on-defect=high', '--flags',
|
|
'--inline-suppr --enable=all --std=c++17']
|
|
|
|
|
|
def main() -> None:
|
|
try:
|
|
args = parse_args()
|
|
|
|
# Change to script directory
|
|
os.chdir(Path(__file__).parent)
|
|
|
|
cmd_list = [
|
|
"uv",
|
|
"run",
|
|
"ci/cpp_test_run.py"
|
|
]
|
|
|
|
if args.clang:
|
|
cmd_list.append("--clang")
|
|
|
|
if args.test:
|
|
cmd_list.append("--test")
|
|
cmd_list.append(args.test)
|
|
if args.clean:
|
|
cmd_list.append("--clean")
|
|
|
|
cmd_str_cpp = subprocess.list2cmdline(cmd_list)
|
|
|
|
if args.cpp:
|
|
# Compile and run C++ tests
|
|
start_time = time.time()
|
|
if args.test:
|
|
# Run specific C++ test
|
|
proc = RunningProcess(cmd_str_cpp)
|
|
proc.wait()
|
|
if proc.returncode != 0:
|
|
print(f"Command failed: {proc.command}")
|
|
sys.exit(proc.returncode)
|
|
else:
|
|
# Run all C++ tests
|
|
proc = RunningProcess(cmd_str_cpp)
|
|
proc.wait()
|
|
if proc.returncode != 0:
|
|
print(f"Command failed: {proc.command}")
|
|
sys.exit(proc.returncode)
|
|
print(f"Time elapsed: {time.time() - start_time:.2f}s")
|
|
return
|
|
|
|
|
|
|
|
cmd_list = _make_pio_check_cmd()
|
|
if not _PIO_CHECK_ENABLED:
|
|
cmd_list = ['echo', 'pio check is disabled']
|
|
|
|
cmd_str = subprocess.list2cmdline(cmd_list)
|
|
|
|
print(f"Running command (in the background): {cmd_str}")
|
|
pio_process = RunningProcess(cmd_str, echo=False, auto_run=not _IS_GITHUB)
|
|
cpp_test_proc = RunningProcess(cmd_str_cpp)
|
|
compile_native_proc = RunningProcess('uv run ci/ci-compile-native.py', echo=False)
|
|
pytest_proc = RunningProcess('uv run pytest ci/tests', echo=False)
|
|
tests = [cpp_test_proc, compile_native_proc, pytest_proc, pio_process]
|
|
|
|
for test in tests:
|
|
sys.stdout.flush()
|
|
if not test.auto_run:
|
|
test.run()
|
|
test.wait()
|
|
if not test.echo:
|
|
for line in test.stdout.splitlines():
|
|
print(line)
|
|
if test.returncode != 0:
|
|
[t.kill() for t in tests]
|
|
print(f"\nCommand failed: {test.command} with return code {test.returncode}")
|
|
sys.exit(test.returncode)
|
|
|
|
print("All tests passed")
|
|
sys.exit(0)
|
|
except KeyboardInterrupt:
|
|
sys.exit(130) # Standard Unix practice: 128 + SIGINT's signal number (2)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|