77import re
88import pytest
99import types
10- from utils .flash_tools import recover_device
10+ from utils .flash_tools import recover_device , flash_device , reset_device
1111from utils .uart import Uart , UartBinary
1212import sys
1313sys .path .append (os .getcwd ())
1414from utils .logger import get_logger
1515from utils .nrfcloud import NRFCloud , NRFCloudFOTA
1616
1717logger = get_logger ()
18+ TEST_OUTCOMES = {}
1819
1920UART_TIMEOUT = 60 * 30
2021
@@ -30,10 +31,8 @@ def get_uarts():
3031
3132 if platform .system () == "Darwin" : # macOS
3233 base_path = "/dev"
33- pattern = "tty.*"
3434 else : # Linux
3535 base_path = "/dev/serial/by-id"
36- pattern = None
3736
3837 try :
3938 if platform .system () == "Darwin" :
@@ -57,12 +56,87 @@ def scan_log_for_assertions(log):
5756
5857@pytest .hookimpl (tryfirst = True )
5958def pytest_runtest_logstart (nodeid , location ):
59+ print (f"\n ===== TEST_ITEM_START { nodeid } =====" , flush = True )
6060 logger .info (f"Starting test: { nodeid } " )
6161
6262@pytest .hookimpl (trylast = True )
6363def pytest_runtest_logfinish (nodeid , location ):
64+ print (f"===== TEST_ITEM_FINISH { nodeid } =====\n " , flush = True )
6465 logger .info (f"Finished test: { nodeid } " )
6566
67+ @pytest .hookimpl (tryfirst = True )
68+ def pytest_runtest_setup (item ):
69+ print (f"----- TEST_SETUP_START { item .nodeid } -----" , flush = True )
70+
71+ @pytest .hookimpl (tryfirst = True )
72+ def pytest_runtest_call (item ):
73+ print (f"----- TEST_CALL_START { item .nodeid } -----" , flush = True )
74+ # Tests using prepare_dut enable UART output after setup completes.
75+ if "prepare_dut" not in item .fixturenames :
76+ uart = _get_uart_for_item (item )
77+ if uart :
78+ uart .begin_test_run (item .nodeid )
79+
80+ @pytest .hookimpl (tryfirst = True )
81+ def pytest_runtest_teardown (item , nextitem ):
82+ status = TEST_OUTCOMES .get (item .nodeid , "unknown" )
83+ uart = _get_uart_for_item (item )
84+ if uart :
85+ if uart .is_pretest_phase_active ():
86+ uart .end_pretest_phase (reason = "teardown_fallback" )
87+ uart .mark (f"UART_TEST_END { item .nodeid } status={ status } " )
88+ uart .set_live_output (False )
89+ print (f"----- TEST_TEARDOWN_END { item .nodeid } status={ status } -----" , flush = True )
90+
91+ @pytest .hookimpl (hookwrapper = True )
92+ def pytest_runtest_makereport (item , call ):
93+ outcome = yield
94+ report = outcome .get_result ()
95+ if report .when == "setup" and report .failed :
96+ TEST_OUTCOMES [item .nodeid ] = "failed_setup"
97+ elif report .when == "call" :
98+ TEST_OUTCOMES [item .nodeid ] = report .outcome
99+ print (f"----- TEST_CALL_END { item .nodeid } outcome={ report .outcome } -----" , flush = True )
100+ elif report .when == "teardown" and item .nodeid not in TEST_OUTCOMES :
101+ TEST_OUTCOMES [item .nodeid ] = report .outcome
102+
103+ def _get_uart_for_item (item ):
104+ funcargs = getattr (item , "funcargs" , {})
105+ for fixture_name in ("dut_cloud" , "dut_fota" , "dut_board" , "dut_traces" ):
106+ fixture = funcargs .get (fixture_name )
107+ if fixture and hasattr (fixture , "uart" ):
108+ return fixture .uart
109+ return None
110+
111+ @pytest .fixture (scope = "function" )
112+ def prepare_dut (request ):
113+ nodeid = request .node .nodeid
114+
115+ def _prepare_dut (dut , hex_file_path , boot_timeout = 240 , max_boot_retries = 3 ):
116+ uart = dut .uart
117+ uart .begin_pretest_phase (nodeid )
118+ try :
119+ flash_device (os .path .abspath (hex_file_path ))
120+ uart .xfactoryreset ()
121+ uart .flush ()
122+ reset_device ()
123+ for attempt in range (max_boot_retries ):
124+ try :
125+ uart .wait_for_app_boot (timeout = boot_timeout )
126+ break
127+ except AssertionError :
128+ if attempt == max_boot_retries - 1 :
129+ raise
130+ reset_device ()
131+ uart .end_pretest_phase (reason = "setup_complete" )
132+ uart .begin_test_run (nodeid )
133+ except Exception :
134+ if uart .is_pretest_phase_active ():
135+ uart .end_pretest_phase (reason = "setup_failed" )
136+ raise
137+
138+ return _prepare_dut
139+
66140@pytest .fixture (scope = "session" , autouse = True )
67141def _purge_pending_fota_jobs ():
68142 """Cancel leftover FOTA jobs queued for the test device before any test runs. """
@@ -80,6 +154,7 @@ def dut_board():
80154 pytest .fail ("No UARTs found" )
81155 log_uart_string = all_uarts [0 ]
82156 uart = Uart (log_uart_string , timeout = UART_TIMEOUT )
157+ uart .set_live_output (False )
83158
84159 yield types .SimpleNamespace (
85160 uart = uart ,
0 commit comments