esp-test-utils 0.2.2__tar.gz → 0.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/CHANGELOG.md +7 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/PKG-INFO +1 -1
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esp_test_utils.egg-info/PKG-INFO +1 -1
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esp_test_utils.egg-info/SOURCES.txt +3 -1
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/adapter/dut/esp_mixin.py +5 -3
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/adapter/port/base_port.py +22 -7
- esp_test_utils-0.2.3/esptest/adapter/port/shell_port.py +191 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/config/env_config.py +1 -4
- esp_test_utils-0.2.3/tests/adapter/test_shell_port.py +94 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/test_EnvConfig.py +24 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/.github/.gitkeep +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/.github/workflows/pypi-publish.yml +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/.gitignore +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/.gitlab-ci.yml +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/.pre-commit-config.yaml +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/CONTRIBUTING.md +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/LICENSE +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/README.md +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/docs/Makefile +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/docs/conf.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/docs/index.rst +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/docs/make.bat +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esp_test_utils.egg-info/dependency_links.txt +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esp_test_utils.egg-info/entry_points.txt +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esp_test_utils.egg-info/requires.txt +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esp_test_utils.egg-info/top_level.txt +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/__init__.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/__main__.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/adapter/__init__.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/adapter/dut/__init__.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/adapter/dut/create_dut.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/adapter/dut/dut_base.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/adapter/dut/esp_dut.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/adapter/dut/esp_port.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/adapter/dut/mac_mixin.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/adapter/dut/wrapper.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/adapter/port/__init__.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/adapter/port/serial_port.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/all.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/common/__init__.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/common/compat_typing.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/common/data_monitor.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/common/decorators.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/common/encoding.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/common/generator.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/common/shell.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/common/timestamp.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/config/__init__.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/config/default_config.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/db/__init__.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/db/runners.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/devices/__init__.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/devices/attenuator.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/devices/esp_serial.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/devices/serial_dut.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/devices/serial_tools.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/env/__init__.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/env/base_env.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/env/wifi_env.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/esp_console/__init__.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/esp_console/wifi_cmd.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/interface/__init__.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/interface/dut.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/interface/port.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/iperf_utility/__init__.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/iperf_utility/iperf_results.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/iperf_utility/iperf_test.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/iperf_utility/iperf_test.test.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/iperf_utility/line_chart.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/logger/__init__.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/logger/logger.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/network/__init__.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/network/mac.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/network/netif.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/network/nic.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/scripts/downbin.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/scripts/list_ports.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/scripts/monitor.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/scripts/set_att.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/tools/copy_bin.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/tools/download_bin.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/tools/http_download.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/tools/pip_check.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/utility/gen_esp32part.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/esptest/utility/parse_bin_path.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/example/jap_test.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/example/restart_test.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/pyproject.toml +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/setup.cfg +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/__init__.py +0 -0
- {esp_test_utils-0.2.2/tests → esp_test_utils-0.2.3/tests/adapter}/test_Dut.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/basic/test_decorators.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/basic/test_network.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/conftest.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/db/test_db_runners.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/esp_console/_files/wifi_cmd_connected_1.log +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/esp_console/_files/wifi_cmd_connected_2.log +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/esp_console/conftest.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/esp_console/test_WifiCmd.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/iperf_utility/_files/dut_iperf_rx1.log +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/iperf_utility/_files/dut_iperf_rx2.log +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/iperf_utility/_files/pc_iperf_rx.log +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/iperf_utility/_files/pc_iperf_rx2.log +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/iperf_utility/test_chart.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/iperf_utility/test_iperf_results.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/iperf_utility/test_iperf_util.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/test_common.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/test_import.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/tools/test_download_file.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/tools/test_pip_check.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/utility/_files/test-bin.zip +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/utility/_files/test-get-baud/ESP32AT-V4.1.1.0/sdkconfig +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/utility/test_parse_bin_path.py +0 -0
- {esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tools/ci/check_dev_version.py +0 -0
|
@@ -33,6 +33,7 @@ esptest/adapter/dut/wrapper.py
|
|
|
33
33
|
esptest/adapter/port/__init__.py
|
|
34
34
|
esptest/adapter/port/base_port.py
|
|
35
35
|
esptest/adapter/port/serial_port.py
|
|
36
|
+
esptest/adapter/port/shell_port.py
|
|
36
37
|
esptest/common/__init__.py
|
|
37
38
|
esptest/common/compat_typing.py
|
|
38
39
|
esptest/common/data_monitor.py
|
|
@@ -84,10 +85,11 @@ example/jap_test.py
|
|
|
84
85
|
example/restart_test.py
|
|
85
86
|
tests/__init__.py
|
|
86
87
|
tests/conftest.py
|
|
87
|
-
tests/test_Dut.py
|
|
88
88
|
tests/test_EnvConfig.py
|
|
89
89
|
tests/test_common.py
|
|
90
90
|
tests/test_import.py
|
|
91
|
+
tests/adapter/test_Dut.py
|
|
92
|
+
tests/adapter/test_shell_port.py
|
|
91
93
|
tests/basic/test_decorators.py
|
|
92
94
|
tests/basic/test_network.py
|
|
93
95
|
tests/db/test_db_runners.py
|
|
@@ -51,15 +51,17 @@ class EspSerial:
|
|
|
51
51
|
|
|
52
52
|
class EspMixin(BaseProtocol):
|
|
53
53
|
def _esptool_open_port(self, port: str, initial_baud: int, **kwargs: t.Any) -> esptool.ESPLoader:
|
|
54
|
-
ports = [p.device for p in get_all_serial_ports()]
|
|
55
54
|
port = compute_serial_port(port) if port else ''
|
|
55
|
+
serial_list = [port] if port else [p.device for p in get_all_serial_ports()]
|
|
56
|
+
# esptool.get_default_connected_device always detect_chip from serial_list
|
|
56
57
|
esp = esptool.get_default_connected_device(
|
|
57
|
-
|
|
58
|
-
port,
|
|
58
|
+
serial_list,
|
|
59
|
+
port=port or None, # type: ignore
|
|
59
60
|
connect_attempts=3,
|
|
60
61
|
initial_baud=initial_baud,
|
|
61
62
|
chip=kwargs.get('chip', 'auto'),
|
|
62
63
|
)
|
|
64
|
+
assert esp, f'Failed to connect to {port}'
|
|
63
65
|
return esp
|
|
64
66
|
|
|
65
67
|
def _esptool_path(self, use_esptool: str = '') -> str:
|
|
@@ -31,9 +31,12 @@ class RawPort(metaclass=abc.ABCMeta):
|
|
|
31
31
|
"""Define a minimum Dut class, the dut objects should at least support these methods
|
|
32
32
|
|
|
33
33
|
the dut should at least support these attributes:
|
|
34
|
-
- attribute name with type str
|
|
35
34
|
- method: write_bytes() with parameters: data[bytes]
|
|
36
35
|
- method: read_bytes() with parameters: timeout[float]
|
|
36
|
+
|
|
37
|
+
optional attribute & method:
|
|
38
|
+
- attribute: name with type str
|
|
39
|
+
- attribute: read_timeout with type float
|
|
37
40
|
"""
|
|
38
41
|
|
|
39
42
|
@classmethod
|
|
@@ -243,7 +246,11 @@ class PortSpawn(pexpect.spawnbase.SpawnBase, t.Generic[T]):
|
|
|
243
246
|
self._log(ret_data, 'read') # type: ignore
|
|
244
247
|
return ret_data
|
|
245
248
|
|
|
249
|
+
@deprecated('Should use close() for Spawn')
|
|
246
250
|
def stop(self) -> None:
|
|
251
|
+
self.close()
|
|
252
|
+
|
|
253
|
+
def close(self) -> None:
|
|
247
254
|
"""Stop and clean up"""
|
|
248
255
|
self.logger.debug(f'Stopping SerialSpawn {self.name}')
|
|
249
256
|
self._read_thread_stop_event.set()
|
|
@@ -359,7 +366,7 @@ class BasePort(PortInterface, t.Generic[T]):
|
|
|
359
366
|
if new_log_file == self._log_file:
|
|
360
367
|
return
|
|
361
368
|
if self._pexpect_spawn:
|
|
362
|
-
self._pexpect_spawn.
|
|
369
|
+
self._pexpect_spawn.log_file = new_log_file
|
|
363
370
|
self._log_file = new_log_file
|
|
364
371
|
|
|
365
372
|
@property
|
|
@@ -381,7 +388,7 @@ class BasePort(PortInterface, t.Generic[T]):
|
|
|
381
388
|
if not self._pexpect_spawn:
|
|
382
389
|
return False
|
|
383
390
|
self._init_log_file()
|
|
384
|
-
self._pexpect_spawn.
|
|
391
|
+
self._pexpect_spawn.close()
|
|
385
392
|
self._pexpect_spawn = None
|
|
386
393
|
return True
|
|
387
394
|
|
|
@@ -486,9 +493,13 @@ class BasePort(PortInterface, t.Generic[T]):
|
|
|
486
493
|
"""
|
|
487
494
|
buffer = b''
|
|
488
495
|
if flush:
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
496
|
+
# pexpect may return empty bytes if b'(.*)' is used
|
|
497
|
+
try:
|
|
498
|
+
match = self.expect(re.compile(b'(.+)', re.DOTALL), timeout=0)
|
|
499
|
+
assert match
|
|
500
|
+
buffer = match.group(0)
|
|
501
|
+
except TimeoutError:
|
|
502
|
+
pass
|
|
492
503
|
else:
|
|
493
504
|
# flush spawn buffer
|
|
494
505
|
assert self._pexpect_spawn
|
|
@@ -499,7 +510,11 @@ class BasePort(PortInterface, t.Generic[T]):
|
|
|
499
510
|
|
|
500
511
|
def close(self) -> None:
|
|
501
512
|
if self._close_redirect_thread_when_exit and self._pexpect_spawn:
|
|
502
|
-
self._pexpect_spawn.
|
|
513
|
+
self._pexpect_spawn.close()
|
|
514
|
+
if self.raw_port:
|
|
515
|
+
if hasattr(self.raw_port, 'close'):
|
|
516
|
+
assert callable(self.raw_port.close) # type: ignore
|
|
517
|
+
self.raw_port.close() # type: ignore
|
|
503
518
|
|
|
504
519
|
def __enter__(self) -> 't.Self':
|
|
505
520
|
return self
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
import pexpect
|
|
6
|
+
import psutil
|
|
7
|
+
|
|
8
|
+
import esptest.common.compat_typing as t
|
|
9
|
+
|
|
10
|
+
from ...logger import get_logger
|
|
11
|
+
from .base_port import BasePort, RawPort
|
|
12
|
+
|
|
13
|
+
logger = get_logger('shell_port')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ShellRaw(RawPort):
|
|
17
|
+
"""A subprocess Raw Port class that supports shell read, write
|
|
18
|
+
|
|
19
|
+
is a subclass of RawPort
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, cmd: str = '/bin/bash', env: t.Optional[t.Dict[str, str]] = None) -> None:
|
|
23
|
+
self.env = env or os.environ.copy()
|
|
24
|
+
self.env['PYTHONUNBUFFERED'] = 'true' # for python scripts, disable output buffering
|
|
25
|
+
self.cmd = cmd
|
|
26
|
+
self.proc: t.Optional[subprocess.Popen] = None
|
|
27
|
+
self.read_timeout = 0.002 # default read_timeout
|
|
28
|
+
self.open()
|
|
29
|
+
|
|
30
|
+
def open(self) -> None:
|
|
31
|
+
if not self.proc:
|
|
32
|
+
self.proc = subprocess.Popen( # pylint: disable=consider-using-with
|
|
33
|
+
self.cmd,
|
|
34
|
+
shell=True,
|
|
35
|
+
env=self.env,
|
|
36
|
+
stdin=subprocess.PIPE,
|
|
37
|
+
stdout=subprocess.PIPE,
|
|
38
|
+
stderr=subprocess.STDOUT,
|
|
39
|
+
)
|
|
40
|
+
# Set stdout to non-blocking
|
|
41
|
+
os.set_blocking(self.proc.stdout.fileno(), False) # type: ignore
|
|
42
|
+
|
|
43
|
+
def close(self) -> None:
|
|
44
|
+
"""Close subprocess."""
|
|
45
|
+
if self.proc:
|
|
46
|
+
if self.proc.pid:
|
|
47
|
+
try:
|
|
48
|
+
proc = psutil.Process(self.proc.pid)
|
|
49
|
+
for child in proc.children(recursive=True):
|
|
50
|
+
child.kill()
|
|
51
|
+
proc.kill()
|
|
52
|
+
time.sleep(0.01)
|
|
53
|
+
except psutil.Error:
|
|
54
|
+
pass
|
|
55
|
+
# # Unix / Linux - does not work
|
|
56
|
+
# try:
|
|
57
|
+
# os.killpg(self.proc.pid, signal.SIGTERM) # send SIGTERM to all in the group
|
|
58
|
+
# os.killpg(self.proc.pid, signal.SIGKILL) # send SIGTERM to all in the group
|
|
59
|
+
# except ProcessLookupError:
|
|
60
|
+
# pass
|
|
61
|
+
self.proc.terminate()
|
|
62
|
+
self.proc.kill()
|
|
63
|
+
self.proc.wait()
|
|
64
|
+
logger.info(f'shell command [{self.cmd}] was killed')
|
|
65
|
+
self.proc = None
|
|
66
|
+
|
|
67
|
+
def write_bytes(self, data: bytes) -> None:
|
|
68
|
+
"""Write bytes to subprocess stdin."""
|
|
69
|
+
if self.proc:
|
|
70
|
+
self.proc.stdin.write(data) # type: ignore
|
|
71
|
+
self.proc.stdin.flush() # type: ignore
|
|
72
|
+
return
|
|
73
|
+
raise ValueError('Subprocess not initialized.')
|
|
74
|
+
|
|
75
|
+
def read_bytes(self, timeout: float = 0) -> bytes:
|
|
76
|
+
"""blocking read bytes"""
|
|
77
|
+
data = self.read_bytes_nonblocking()
|
|
78
|
+
if not data and timeout > 0:
|
|
79
|
+
time.sleep(timeout) # blocking read
|
|
80
|
+
data = self.read_bytes_nonblocking()
|
|
81
|
+
if data:
|
|
82
|
+
logger.debug(f'[{self.cmd}] read_bytes timeout={timeout}, data={str(data)}')
|
|
83
|
+
return data
|
|
84
|
+
|
|
85
|
+
def read_bytes_nonblocking(self, size: int = -1) -> bytes:
|
|
86
|
+
"""non-blocking read bytes"""
|
|
87
|
+
if self.proc:
|
|
88
|
+
self.proc.stdout.flush() # type: ignore
|
|
89
|
+
return self.proc.stdout.read(size) # type: ignore
|
|
90
|
+
return b''
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class ShellPort(BasePort[ShellRaw]):
|
|
94
|
+
"""A combined port class that supports shell read, write, expect"""
|
|
95
|
+
|
|
96
|
+
def __init__(
|
|
97
|
+
self,
|
|
98
|
+
cmd: str = '/bin/bash',
|
|
99
|
+
env: t.Optional[dict[str, str]] = None,
|
|
100
|
+
name: str = '',
|
|
101
|
+
log_file: str = '',
|
|
102
|
+
**kwargs: t.Any,
|
|
103
|
+
) -> None:
|
|
104
|
+
raw_port = ShellRaw(cmd=cmd, env=env)
|
|
105
|
+
super().__init__(raw_port, name, log_file, **kwargs)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class InvalidRaw(RawPort):
|
|
109
|
+
"""A invalid Raw Port class that always raise NotImplementedError to pass type check"""
|
|
110
|
+
|
|
111
|
+
def write_bytes(self, data: bytes) -> None:
|
|
112
|
+
"""Write bytes to subprocess stdin."""
|
|
113
|
+
raise NotImplementedError('Invalid Raw Port.')
|
|
114
|
+
|
|
115
|
+
def read_bytes(self, timeout: float = 0) -> bytes:
|
|
116
|
+
"""blocking read bytes"""
|
|
117
|
+
raise NotImplementedError('Invalid Raw Port.')
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class PexpectPort(BasePort[InvalidRaw]):
|
|
121
|
+
"""A pexpect Port class that supports shell read, write, expect
|
|
122
|
+
|
|
123
|
+
based on pexpect.spawn but use different expect method
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
def __init__(
|
|
127
|
+
self,
|
|
128
|
+
cmd: str = '/bin/bash',
|
|
129
|
+
name: str = '',
|
|
130
|
+
log_file: str = '',
|
|
131
|
+
**kwargs: t.Any,
|
|
132
|
+
) -> None:
|
|
133
|
+
self._cmd = cmd
|
|
134
|
+
raw_port = InvalidRaw()
|
|
135
|
+
self._pexpect_spawn: t.Optional[pexpect.spawn] = None # change type
|
|
136
|
+
self.log_file_f = open(log_file, 'wb') if log_file else None # pylint: disable=consider-using-with
|
|
137
|
+
super().__init__(raw_port, name, log_file, **kwargs)
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def log_file(self) -> str:
|
|
141
|
+
"""Get Current dut log file."""
|
|
142
|
+
if not self._log_file:
|
|
143
|
+
return ''
|
|
144
|
+
return os.path.abspath(self._log_file)
|
|
145
|
+
|
|
146
|
+
@log_file.setter
|
|
147
|
+
def log_file(self, new_log_file: str) -> None:
|
|
148
|
+
"""Set Current dut log file."""
|
|
149
|
+
if new_log_file == self._log_file:
|
|
150
|
+
return
|
|
151
|
+
if self.log_file_f:
|
|
152
|
+
if self._pexpect_spawn:
|
|
153
|
+
self._pexpect_spawn.logfile = None # type: ignore
|
|
154
|
+
self.log_file_f.close()
|
|
155
|
+
if self._pexpect_spawn:
|
|
156
|
+
self.log_file_f = open(new_log_file, 'wb') if new_log_file else None # pylint: disable=consider-using-with
|
|
157
|
+
self._pexpect_spawn.logfile = self.log_file_f # type: ignore
|
|
158
|
+
self._log_file = new_log_file
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def spawn(self) -> t.Optional[pexpect.spawn]: # type: ignore
|
|
162
|
+
"""Allow the use of pexpect spawn enhancements, if pexpect process is available"""
|
|
163
|
+
return self._pexpect_spawn
|
|
164
|
+
|
|
165
|
+
def start_redirect_thread(self) -> None:
|
|
166
|
+
"""Start a new thread to read data from port and save to data cache."""
|
|
167
|
+
if self._pexpect_spawn:
|
|
168
|
+
return
|
|
169
|
+
self._init_log_file()
|
|
170
|
+
env = os.environ.copy()
|
|
171
|
+
env['PYTHONUNBUFFERED'] = 'true' # for python scripts, disable output buffering
|
|
172
|
+
self._pexpect_spawn = pexpect.spawn(self._cmd, maxread=8192, echo=False, env=env) # type: ignore
|
|
173
|
+
self._pexpect_spawn.logfile = self.log_file_f # type: ignore
|
|
174
|
+
# self._pexpect_spawn.delaybeforesend = 0.001
|
|
175
|
+
|
|
176
|
+
def stop_redirect_thread(self) -> bool:
|
|
177
|
+
"""Stop the redirect thread and pexpect process."""
|
|
178
|
+
if not self._pexpect_spawn:
|
|
179
|
+
return False
|
|
180
|
+
self._init_log_file()
|
|
181
|
+
self._pexpect_spawn.close()
|
|
182
|
+
self._pexpect_spawn = None # type: ignore
|
|
183
|
+
return True
|
|
184
|
+
|
|
185
|
+
def close(self) -> None:
|
|
186
|
+
"""Close pexpect process."""
|
|
187
|
+
super().close()
|
|
188
|
+
self.stop_redirect_thread()
|
|
189
|
+
if self.log_file_f:
|
|
190
|
+
self.log_file_f.close()
|
|
191
|
+
self.log_file_f = None
|
|
@@ -113,10 +113,7 @@ class EnvConfig:
|
|
|
113
113
|
continue
|
|
114
114
|
config_file = os.path.join(_dir, cls.ENV_CONFIG_FILE_BASE_NAME)
|
|
115
115
|
if not config_file:
|
|
116
|
-
_msg = (
|
|
117
|
-
'Can not find env config file from:\n ',
|
|
118
|
-
' \n'.join(cls._search_dirs()),
|
|
119
|
-
)
|
|
116
|
+
_msg = 'Can not find env config file from:\n ' + ' \n'.join(cls._search_dirs())
|
|
120
117
|
logging.warning(_msg)
|
|
121
118
|
if not cls.ALLOW_INPUT:
|
|
122
119
|
raise FileNotFoundError(f'Could not find config file: {cls.ENV_CONFIG_FILE_BASE_NAME}')
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import random
|
|
2
|
+
import re
|
|
3
|
+
import subprocess
|
|
4
|
+
import time
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from esptest.adapter.port.shell_port import PexpectPort, ShellPort, ShellRaw
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_shell_raw_open_close() -> None:
|
|
13
|
+
ran_int = random.randint(12345678, 87654321)
|
|
14
|
+
raw_port = ShellRaw(cmd=f'sleep {ran_int}')
|
|
15
|
+
assert raw_port.proc is not None
|
|
16
|
+
output = subprocess.check_output(f'ps -ef | grep {ran_int}', shell=True).decode('utf-8')
|
|
17
|
+
assert f'sleep {ran_int}' in output
|
|
18
|
+
raw_port.close()
|
|
19
|
+
output = subprocess.check_output(f'ps -ef | grep {ran_int}', shell=True).decode('utf-8')
|
|
20
|
+
assert f'sleep {ran_int}' not in output
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_shell_port_open_close() -> None:
|
|
24
|
+
ran_int = random.randint(12345678, 87654321)
|
|
25
|
+
# close by close method
|
|
26
|
+
port = ShellPort(cmd=f'sleep {ran_int}')
|
|
27
|
+
assert isinstance(port.raw_port, ShellRaw)
|
|
28
|
+
output = subprocess.check_output(f'ps -ef | grep {ran_int}', shell=True).decode('utf-8')
|
|
29
|
+
assert f'sleep {ran_int}' in output
|
|
30
|
+
port.close()
|
|
31
|
+
output = subprocess.check_output(f'ps -ef | grep {ran_int}', shell=True).decode('utf-8')
|
|
32
|
+
assert f'sleep {ran_int}' not in output
|
|
33
|
+
# close by with statement
|
|
34
|
+
with ShellPort(cmd=f'sleep {ran_int}') as port:
|
|
35
|
+
assert isinstance(port.raw_port, ShellRaw)
|
|
36
|
+
output = subprocess.check_output(f'ps -ef | grep {ran_int}', shell=True).decode('utf-8')
|
|
37
|
+
assert f'sleep {ran_int}' in output
|
|
38
|
+
output = subprocess.check_output(f'ps -ef | grep {ran_int}', shell=True).decode('utf-8')
|
|
39
|
+
assert f'sleep {ran_int}' not in output
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_shell_port_read_write() -> None:
|
|
43
|
+
with ShellPort(cmd='/bin/bash') as port:
|
|
44
|
+
port.write_line('echo hello')
|
|
45
|
+
time.sleep(0.1) # wait for the receive thread
|
|
46
|
+
assert 'hello' in port.read_all_data()
|
|
47
|
+
port.write_line('sleep 0.1 && echo world')
|
|
48
|
+
assert 'world' not in port.read_all_data()
|
|
49
|
+
match = port.expect(re.compile('world'))
|
|
50
|
+
assert match.group(0) == 'world'
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_shell_port_logfile(tmp_path: Path) -> None:
|
|
54
|
+
log_file = tmp_path / 'shell_port1.log'
|
|
55
|
+
with ShellPort(cmd='/bin/bash', log_file=str(log_file)) as port:
|
|
56
|
+
port.write_line('echo hello')
|
|
57
|
+
time.sleep(0.1) # wait for the receive thread
|
|
58
|
+
with open(str(log_file), 'r') as f:
|
|
59
|
+
assert 'hello' in f.read()
|
|
60
|
+
port.log_file = str(tmp_path / 'shell_port2.log')
|
|
61
|
+
port.write_line('echo world')
|
|
62
|
+
time.sleep(0.1) # wait for the receive thread
|
|
63
|
+
with open(str(tmp_path / 'shell_port2.log'), 'r') as f:
|
|
64
|
+
assert 'world' in f.read()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_pexpect_spawn_port_read_write() -> None:
|
|
68
|
+
with PexpectPort(cmd='/bin/bash') as port:
|
|
69
|
+
port.write_line('echo hello')
|
|
70
|
+
time.sleep(0.5) # wait for the receive thread
|
|
71
|
+
assert 'hello' in port.read_all_data()
|
|
72
|
+
port.write_line('sleep 0.1 && echo world')
|
|
73
|
+
assert 'world' not in port.read_all_data()
|
|
74
|
+
match = port.expect(re.compile('world'))
|
|
75
|
+
assert match.group(0) == 'world'
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_pexpect_spawn_port_logfile(tmp_path: Path) -> None:
|
|
79
|
+
log_file = tmp_path / 'shell_port1.log'
|
|
80
|
+
with PexpectPort(cmd='/bin/bash', log_file=str(log_file)) as port:
|
|
81
|
+
port.write_line('echo hello')
|
|
82
|
+
time.sleep(0.5) # wait for the receive thread
|
|
83
|
+
with open(str(log_file), 'r') as f:
|
|
84
|
+
assert 'hello' in f.read()
|
|
85
|
+
port.log_file = str(tmp_path / 'shell_port2.log')
|
|
86
|
+
port.write_line('echo world')
|
|
87
|
+
time.sleep(0.1) # wait for the receive thread
|
|
88
|
+
with open(str(tmp_path / 'shell_port2.log'), 'r') as f:
|
|
89
|
+
assert 'world' in f.read()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
if __name__ == '__main__':
|
|
93
|
+
# Breakpoints do not work with coverage, disable coverage for debugging
|
|
94
|
+
pytest.main([__file__, '--no-cov', '--log-cli-level=DEBUG'])
|
|
@@ -65,6 +65,30 @@ def test_env_config_get_var(tmp_path: Path) -> None:
|
|
|
65
65
|
env_config.get_variable('dut_port')
|
|
66
66
|
|
|
67
67
|
|
|
68
|
+
def test_env_config_from_shell_env(tmp_path: Path) -> None:
|
|
69
|
+
# Test Get variable from console
|
|
70
|
+
config_file = tmp_path / 'not_exist_config.yml'
|
|
71
|
+
env = {
|
|
72
|
+
'TEST_ENV_CONFIG_FILE': str(config_file),
|
|
73
|
+
}
|
|
74
|
+
with reload_envconfig(env):
|
|
75
|
+
try:
|
|
76
|
+
os.environ.pop('RUNNER_WIFI_SSID')
|
|
77
|
+
except KeyError:
|
|
78
|
+
pass
|
|
79
|
+
try:
|
|
80
|
+
os.environ.pop('RUNNER_AP_SSID')
|
|
81
|
+
except KeyError:
|
|
82
|
+
pass
|
|
83
|
+
env_config = EnvConfig()
|
|
84
|
+
env_config.ALLOW_INPUT = False
|
|
85
|
+
with pytest.raises(ValueError):
|
|
86
|
+
var = env_config.get_variable('ap_ssid')
|
|
87
|
+
os.environ['RUNNER_AP_SSID'] = 'ssid_from_env'
|
|
88
|
+
var = env_config.get_variable('ap_ssid')
|
|
89
|
+
assert var == 'ssid_from_env'
|
|
90
|
+
|
|
91
|
+
|
|
68
92
|
def test_env_config_from_console(tmp_path, monkeypatch): # type: ignore
|
|
69
93
|
# Test Get variable from console
|
|
70
94
|
config_file = tmp_path / 'not_exist_config.yml'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/esp_console/_files/wifi_cmd_connected_1.log
RENAMED
|
File without changes
|
{esp_test_utils-0.2.2 → esp_test_utils-0.2.3}/tests/esp_console/_files/wifi_cmd_connected_2.log
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|