blob: 9abd10885d56ea096333aa12e8c111dc858b9b36 [file]
import sys
import pytest
import serial.tools.list_ports as list_ports
from serial.tools.list_ports_common import ListPortInfo
class ComportsMock:
"""Mock `comports()`, but allow tests to customize the ports that were "found"."""
def __init__(self):
self.call_count = 0
self.include_links_args = []
self.ports = []
def __call__(self, *args, **kwargs):
self.call_count += 1
if args:
self.include_links_args.append(args[0])
else:
assert kwargs
self.include_links_args.append(kwargs["include_links"])
return self.ports
def set_ports(self, ports):
# Tests call this function to customize the ports that were "found".
self.ports = ports
@pytest.fixture(autouse=True)
def comports(monkeypatch):
"""Monkeypatch serial.tools.list_ports.comports."""
comports_mock = ComportsMock()
monkeypatch.setattr(list_ports, "comports", comports_mock)
yield comports_mock
# `comports()` must be called a maximum of one time.
assert comports_mock.call_count in (0, 1)
@pytest.fixture(autouse=True)
def set_cli_args(monkeypatch):
"""Monkeypatch the CLI arguments. By default, there are no CLI args."""
def setter(*args):
sys_argv = [""]
sys_argv.extend([arg for arg in args if arg is not None])
monkeypatch.setattr("sys.argv", sys_argv)
monkeypatch.setattr(sys, "argv", [""])
yield setter
@pytest.mark.parametrize("quiet_arg", ("-q", "--quiet", None))
def test_quiet_arg(set_cli_args, capsys, quiet_arg):
"""Test behavior of the `--quiet` option."""
set_cli_args(quiet_arg)
list_ports.main()
stdout, stderr = capsys.readouterr()
assert stdout == ""
if quiet_arg:
assert stderr == ""
else:
assert "no ports found" in stderr.strip().lower()
@pytest.mark.parametrize("include_links_arg", ("-s", "--include-links", None))
@pytest.mark.parametrize("regex_pattern", (None, "."))
def test_include_links_arg(set_cli_args, comports, include_links_arg, regex_pattern):
"""Test behavior of the `--include-links` option."""
set_cli_args(regex_pattern, include_links_arg)
list_ports.main()
assert comports.include_links_args == [bool(include_links_arg)]
def get_some_ports():
"""Generate some unique `ListPortInfo` instances."""
ports = []
for i in range(1, 4):
port = ListPortInfo(f"port{i}{i}{i}", skip_link_detection=True)
port.description = f"desc{i}{i}{i}"
port.hwid = f"hwid{i}{i}{i}"
ports.append(port)
return ports
@pytest.mark.parametrize("verbose_arg", ("-v", "--verbose", None))
def test_verbose_arg(set_cli_args, capsys, comports, verbose_arg):
"""Test behavior of the `--verbose` option."""
# Setup
set_cli_args(verbose_arg)
ports = get_some_ports()
comports.set_ports(ports)
# Act
list_ports.main()
# Verify
stdout, stderr = capsys.readouterr()
lines = [line.strip() for line in stdout.strip().splitlines()]
for port in ports:
assert port.device in lines
if verbose_arg is not None:
assert f"desc: {port.description}" in lines
assert f"hwid: {port.hwid}" in lines
assert stderr.lower().strip() == f"{len(ports)} ports found"
@pytest.mark.parametrize("only_one_arg", ("-1", "--only-one"))
@pytest.mark.parametrize("port_count", (0, 1, 2))
def test_only_one_arg(set_cli_args, capsys, comports, only_one_arg, port_count):
"""Test behavior of the `--only-one` option."""
# Setup
set_cli_args(only_one_arg)
ports = get_some_ports()[:port_count]
comports.set_ports(ports)
# Act and verify
if len(ports) == 1:
list_ports.main()
else:
with pytest.raises(SystemExit) as error:
list_ports.main()
assert error.value.code == 1
stdout, stderr = capsys.readouterr()
assert stdout == ""
expected_message = f"error: {len(ports) or 'no'} serial ports"
assert stderr.lower().strip().startswith(expected_message)
@pytest.mark.parametrize("pattern", ("port.2.", "desc.2.", "hwid.2."))
@pytest.mark.parametrize("quiet_arg", ("--quiet", None))
def test_regex_filtering(set_cli_args, capsys, comports, pattern, quiet_arg):
"""Test behavior of the regex argument.
*pattern* confirms that the device, description, and hwid values are each searched.
"""
# Setup
set_cli_args(pattern, quiet_arg)
ports = get_some_ports()
comports.set_ports(ports)
# Act
list_ports.main()
# Verify
stdout, stderr = capsys.readouterr()
assert "2" in stdout
if not quiet_arg:
assert f"filtered list with regexp: {pattern!r}" in stderr.lower()
assert "1 ports found" in stderr.lower()
def test_n_arg(set_cli_args, capsys, comports):
"""Test behavior of the `-n` option."""
set_cli_args("-n", "1")
ports = get_some_ports()
comports.set_ports(ports)
list_ports.main()
stdout, _ = capsys.readouterr()
assert "port111" in stdout
@pytest.mark.parametrize("n_arg", (0, sys.maxsize))
def test_n_arg_out_of_range(set_cli_args, capsys, comports, n_arg):
"""Test that an out-of-range `-n` value eliminates all results."""
set_cli_args("-n", str(n_arg))
ports = get_some_ports()
comports.set_ports(ports)
list_ports.main()
stdout, stderr = capsys.readouterr()
assert stdout == ""
assert stderr.strip().lower() == "no ports found"