Skip to content

Commit ab5b48b

Browse files
gchwiernashif
authored andcommitted
twister: pytest: Wrap iter_stdout
Added waiting for prompt instead of calling time.sleep, to fix issue with longer setting up platforms with TF-M enabled. Wrapped iter_stdout generator, to avoid issues when generator is created and called multiple times in test scenarios. Fixes: #58747 Signed-off-by: Grzegorz Chwierut <[email protected]>
1 parent 86dd23e commit ab5b48b

File tree

5 files changed

+46
-43
lines changed

5 files changed

+46
-43
lines changed

samples/subsys/testsuite/pytest/shell/pytest/test_shell.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,15 @@
44

55
import time
66
import logging
7-
import pytest # noqa # pylint: disable=unused-import
87

98
from twister_harness.device.device_abstract import DeviceAbstract
109

1110
logger = logging.getLogger(__name__)
1211

1312

14-
def wait_for_message(iter_stdout, message, timeout=60):
13+
def wait_for_message(dut: DeviceAbstract, message, timeout=20):
1514
time_started = time.time()
16-
for line in iter_stdout:
15+
for line in dut.iter_stdout:
1716
if line:
1817
logger.debug("#: " + line)
1918
if message in line:
@@ -22,15 +21,25 @@ def wait_for_message(iter_stdout, message, timeout=60):
2221
return False
2322

2423

25-
def test_shell_print_help(dut: DeviceAbstract):
26-
time.sleep(1) # wait for application initialization on DUT
24+
def wait_for_prompt(dut: DeviceAbstract, prompt='uart:~$', timeout=20):
25+
time_started = time.time()
26+
while True:
27+
dut.write(b'\n')
28+
for line in dut.iter_stdout:
29+
if prompt in line:
30+
logger.debug('Got prompt')
31+
return True
32+
if time.time() > time_started + timeout:
33+
return False
2734

35+
36+
def test_shell_print_help(dut: DeviceAbstract):
37+
wait_for_prompt(dut)
2838
dut.write(b'help\n')
29-
assert wait_for_message(dut.iter_stdout, "Available commands")
39+
assert wait_for_message(dut, "Available commands")
3040

3141

3242
def test_shell_print_version(dut: DeviceAbstract):
33-
time.sleep(1) # wait for application initialization on DUT
34-
43+
wait_for_prompt(dut)
3544
dut.write(b'kernel version\n')
36-
assert wait_for_message(dut.iter_stdout, "Zephyr version")
45+
assert wait_for_message(dut, "Zephyr version")

scripts/pylib/pytest-twister-harness/src/twister_harness/device/device_abstract.py

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ def __init__(self, device_config: DeviceConfig, **kwargs) -> None:
2525
self.device_config: DeviceConfig = device_config
2626
self.handler_log_file: LogFile = NullLogFile.create()
2727
self.device_log_file: LogFile = NullLogFile.create()
28+
self.iter_object: Generator[str, None, None] | None = None
2829

2930
def __repr__(self) -> str:
3031
return f'{self.__class__.__name__}()'
@@ -55,11 +56,6 @@ def flash_and_run(self, timeout: float = 60.0) -> None:
5556
:param timeout: time out in seconds
5657
"""
5758

58-
@property
59-
@abc.abstractmethod
60-
def iter_stdout(self) -> Generator[str, None, None]:
61-
"""Iterate stdout from a device."""
62-
6359
@abc.abstractmethod
6460
def write(self, data: bytes) -> None:
6561
"""Write data bytes to device"""
@@ -73,22 +69,17 @@ def initialize_log_files(self):
7369
def stop(self) -> None:
7470
"""Stop device."""
7571

76-
# @abc.abstractmethod
77-
# def read(self, size=1) -> None:
78-
# """Read size bytes from device"""
79-
80-
# def read_until(self, expected, size=None):
81-
# """Read until an expected bytes sequence is found"""
82-
# lenterm = len(expected)
83-
# line = bytearray()
84-
# while True:
85-
# c = self.read(1)
86-
# if c:
87-
# line += c
88-
# if line[-lenterm:] == expected:
89-
# break
90-
# if size is not None and len(line) >= size:
91-
# break
92-
# else:
93-
# break
94-
# return bytes(line)
72+
@abc.abstractmethod
73+
def iter_stdout_lines(self) -> Generator[str, None, None]:
74+
"""A generator that yields lines read from device"""
75+
76+
@property
77+
def iter_stdout(self) -> Generator[str, None, None]:
78+
"""
79+
Get generator object to iterate stdout from a device.
80+
This wrapper method is added to avoid problems, when
81+
user creates an instance of generator multiple times.
82+
"""
83+
if not self.iter_object:
84+
self.iter_object = self.iter_stdout_lines()
85+
return self.iter_object

scripts/pylib/pytest-twister-harness/src/twister_harness/device/hardware_adapter.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,7 @@ def flash_and_run(self, timeout: float = 60.0) -> None:
201201
if self.device_config.post_flash_script:
202202
self.run_custom_script(self.device_config.post_flash_script, 30)
203203

204-
@property
205-
def iter_stdout(self) -> Generator[str, None, None]:
204+
def iter_stdout_lines(self) -> Generator[str, None, None]:
206205
"""Return output from serial."""
207206
if not self.connection:
208207
return
@@ -212,6 +211,7 @@ def iter_stdout(self) -> Generator[str, None, None]:
212211
stream = self.connection.readline()
213212
self.handler_log_file.handle(data=stream)
214213
yield stream.decode(errors='ignore').strip()
214+
self.iter_object = None
215215

216216
def write(self, data: bytes) -> None:
217217
"""Write data to serial"""

scripts/pylib/pytest-twister-harness/src/twister_harness/device/qemu_adapter.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,11 @@ def _wait_for_fifo(self):
145145
logger.error(msg)
146146
raise TwisterHarnessException(msg)
147147

148-
@property
149-
def iter_stdout(self) -> Generator[str, None, None]:
148+
def iter_stdout_lines(self) -> Generator[str, None, None]:
150149
if not self.connection:
151150
return
152-
# fifo file can be not create yet, so we need to wait for a while
153-
self._wait_for_fifo()
151+
if not self.connection.is_open:
152+
self._wait_for_fifo()
154153

155154
# create unblocking reading from fifo file
156155
q: Queue = Queue()
@@ -184,11 +183,15 @@ def read_lines():
184183
pass
185184
finally:
186185
t.join(1)
186+
self.iter_object = None
187187

188188
def write(self, data: bytes) -> None:
189189
"""Write data to serial"""
190-
if self.connection:
191-
self.connection.write(data)
190+
if not self.connection:
191+
return
192+
if not self.connection.is_open:
193+
self._wait_for_fifo()
194+
self.connection.write(data)
192195

193196
def initialize_log_files(self):
194197
self.handler_log_file = HandlerLogFile.create(build_dir=self.device_config.build_dir)

scripts/pylib/pytest-twister-harness/src/twister_harness/device/simulator_adapter.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,7 @@ def stop(self) -> None:
166166
if self._exc:
167167
raise self._exc
168168

169-
@property
170-
def iter_stdout(self) -> Generator[str, None, None]:
169+
def iter_stdout_lines(self) -> Generator[str, None, None]:
171170
"""Return output from serial."""
172171
while True:
173172
stream = self.queue.get()
@@ -177,6 +176,7 @@ def iter_stdout(self) -> Generator[str, None, None]:
177176
self.handler_log_file.handle(data=stream + '\n')
178177
yield stream
179178
self.queue.task_done()
179+
self.iter_object = None
180180

181181
def write(self, data: bytes) -> None:
182182
"""Write data to serial"""

0 commit comments

Comments
 (0)