diff --git a/docs/options.md b/docs/options.md index 93bfa1e5c..e72063dd8 100644 --- a/docs/options.md +++ b/docs/options.md @@ -400,7 +400,7 @@ Controls whether all 256 ADIv5 AP addresses will be probed. bool True -When this option is True, the GDB server, probe server, semihosting telnet, and raw SWV server are only served +When this option is True, the GDB server, probe server, stdio server, rtt server, and raw SWV server are only served on localhost, making them inaccessible across the network. Set to False to enable connecting to these ports from any machine on the network. @@ -472,7 +472,7 @@ Examples: -telnet_mode +stdio_mode str, list of str 'server' Controls connection to standard I/O. Must be one of 'off', 'console', 'server', or 'file'. For multicore devices @@ -487,10 +487,10 @@ console and the standard I/O for core1 is routed to the Telnet Server.< -telnet_port +stdio_port int, list of int 4444 -The option applies only when telnet_mode is set to 'server'. Base TCP port for the Telnet Server. +The option applies only when stdio_mode is set to 'server'. Base TCP port for the Telnet Server. For multicore targets the zero-based core number will be added to the value unless a list is specified. Examples: @@ -502,10 +502,10 @@ Examples: -telnet_file_in +stdio_file_in str, list of str <target>.in -The option applies only when telnet_mode is set to 'file'. Input filename to use as stdin. +The option applies only when stdio_mode is set to 'file'. Input filename to use as stdin. When a single string is used, the core number (_<core>) will be appended to the filename in case of multicore targets. @@ -519,10 +519,10 @@ assigned to core0 and mytarget_cm0.in is assigned to core1 -telnet_file_out +stdio_file_out str, list of str <target>.out -The option applies only when telnet_mode is set to 'file'. Output filename to use as stdout. +The option applies only when stdio_mode is set to 'file'. Output filename to use as stdout. When a single string is used, the core number (_<core>) will be appended to the filename in case of multicore targets. diff --git a/pyocd/core/options.py b/pyocd/core/options.py index e55d42046..cff12df16 100644 --- a/pyocd/core/options.py +++ b/pyocd/core/options.py @@ -139,7 +139,7 @@ class OptionInfo(NamedTuple): OptionInfo('scan_all_aps', bool, False, "Controls whether all 256 ADIv5 AP addresses will be probed. Default is False."), OptionInfo('serve_local_only', bool, True, - "When this option is True, the GDB server, probe server, and semihosting telnet, and raw SWV " + "When this option is True, the GDB server, probe server, stdio server, rtt server, and raw SWV " "server are only served on localhost. Set to False to enable remote connections."), OptionInfo('smart_flash', bool, True, "If set to True, the flash loader will attempt to not program pages whose contents are not " @@ -196,8 +196,6 @@ class OptionInfo(NamedTuple): "TCP port number for the raw SWV stream server."), OptionInfo('swv_raw_file', str, None, "File path for the raw SWV stream output."), - OptionInfo('telnet_port', (int, tuple), 4444, - "Base TCP port number for the semihosting telnet server."), OptionInfo('vector_catch', str, 'h', "Enable vector catch sources."), OptionInfo('register_fields', bool, True, @@ -206,13 +204,15 @@ class OptionInfo(NamedTuple): OptionInfo('soft_bkpt_as_hard', bool, False, "Replace software breakpoints with hardware breakpoints."), - # Internal cbuild-run session options - OptionInfo('telnet_mode', (str, tuple), None, - "List of telnet modes for each core."), - OptionInfo('telnet_file_in', (str, tuple), None, - "List of telnet input file paths for each core."), - OptionInfo('telnet_file_out', (str, tuple), None, - "List of telnet output file paths for each core."), + # Extended options with multicore support + OptionInfo('stdio_mode', (str, tuple), None, + "List of STDIO modes for each core."), + OptionInfo('stdio_port', (int, tuple), 4444, + "Base TCP port number for the STDIO server."), + OptionInfo('stdio_file_in', (str, tuple), None, + "List of STDIO input file paths for each core."), + OptionInfo('stdio_file_out', (str, tuple), None, + "List of STDIO output file paths for each core."), OptionInfo('rtt', tuple, None, "List of RTT configurations for each core."), OptionInfo('systemview_file', str, None, @@ -223,6 +223,14 @@ class OptionInfo(NamedTuple): "Enable automatic stop of SystemView."), ] +## @brief Aliases for backwards-compatible option names. +OPTIONS_ALIASES: Dict[str, str] = { + 'telnet_port': 'stdio_port', + 'telnet_mode': 'stdio_mode', + 'telnet_file_in': 'stdio_file_in', + 'telnet_file_out': 'stdio_file_out', +} + ## @brief The runtime dictionary of options. OPTIONS_INFO: Dict[str, OptionInfo] = {} diff --git a/pyocd/core/options_manager.py b/pyocd/core/options_manager.py index 862a44595..412df94af 100644 --- a/pyocd/core/options_manager.py +++ b/pyocd/core/options_manager.py @@ -1,5 +1,5 @@ # pyOCD debugger -# Copyright (c) 2019-2020 Arm Limited +# Copyright (c) 2019-2020,2026 Arm Limited # Copyright (c) 2021 Chris Reed # SPDX-License-Identifier: Apache-2.0 # @@ -19,7 +19,7 @@ from functools import partial from typing import (Any, Callable, Dict, List, Mapping, NamedTuple, Optional) -from .options import OPTIONS_INFO +from .options import (OPTIONS_ALIASES, OPTIONS_INFO) from ..utility.notification import Notifier LOG = logging.getLogger(__name__) @@ -101,14 +101,15 @@ def _convert_options(self, new_options: LayerType) -> LayerType: 1. Strip dictionary entries with a value of None. 2. Replace double-underscores ("__") with a dot ("."). 3. Convert option names to all-lowercase. + 4. Resolve option aliases. """ output = {} for name, value in new_options.items(): if value is None: continue - else: - name = name.replace("__", ".").lower() - output[name] = value + name = name.replace("__", ".").lower() + name = OPTIONS_ALIASES.get(name, name) + output[name] = value return output def is_set(self, key: str) -> bool: diff --git a/pyocd/core/session.py b/pyocd/core/session.py index 197c38342..c0bf0cd5e 100644 --- a/pyocd/core/session.py +++ b/pyocd/core/session.py @@ -295,11 +295,11 @@ def _get_cbuild_run_config(self, command: Optional[str]) -> Dict[str, Any]: debugger_options['connect_mode'] = connect_mode debugger_options['gdbserver_port'] = self.cbuild_run.gdbserver_port - debugger_options['telnet_port'] = self.cbuild_run.telnet_port - debugger_options['telnet_mode'] = self.cbuild_run.telnet_mode - telnet_file = self.cbuild_run.telnet_file - debugger_options['telnet_file_in'] = telnet_file.get('in') - debugger_options['telnet_file_out'] = telnet_file.get('out') + debugger_options['stdio_port'] = self.cbuild_run.stdio_port + debugger_options['stdio_mode'] = self.cbuild_run.stdio_mode + stdio_file = self.cbuild_run.stdio_file + debugger_options['stdio_file_in'] = stdio_file.get('in') + debugger_options['stdio_file_out'] = stdio_file.get('out') debugger_options['rtt'] = self.cbuild_run.rtt debugger_options['systemview_file'] = self.cbuild_run.systemview_file diff --git a/pyocd/target/pack/cbuild_run.py b/pyocd/target/pack/cbuild_run.py index d90a08e86..3ec731845 100644 --- a/pyocd/target/pack/cbuild_run.py +++ b/pyocd/target/pack/cbuild_run.py @@ -518,8 +518,12 @@ def flashinfo(self) -> List[dict]: def debugger(self) -> Dict[str, Any]: """@brief Debugger section of cbuild-run.""" if self._debugger is None: - self._debugger = self._data.get('debugger', {}) - LOG.debug("Read debugger configuration: %s", self._debugger) + _debugger = self._data.get('debugger') or {} + LOG.debug("Read debugger configuration: %s", _debugger) + if 'stdio' in _debugger and 'telnet' in _debugger: + LOG.warning("Both 'stdio' and 'telnet' sections found in debugger configuration. " + "Using 'stdio' section and ignoring 'telnet'.") + self._debugger = _debugger return self._debugger @property @@ -627,52 +631,53 @@ def gdbserver_port(self) -> Optional[Tuple]: return self._get_server_port('gdbserver') @property - def telnet_port(self) -> Optional[Tuple]: - """@brief Telnet server port assignments from debugger section. + def stdio_port(self) -> Optional[Tuple]: + """@brief STDIO server port assignments from debugger section. The method will not be called frequently, so performance is not critical. """ - return self._get_server_port('telnet') + server_type = 'stdio' if 'stdio' in self.debugger else 'telnet' + return self._get_server_port(server_type) @property - def telnet_mode(self) -> Tuple: - """@brief Telnet server mode assignments from debugger section. + def stdio_mode(self) -> Tuple: + """@brief STDIO mode assignments from debugger section. The method will not be called frequently, so performance is not critical. """ SUPPORTED_MODES = { 'off', 'server', 'file', 'console' } MODE_ALIASES = { False: 'off', 'monitor': 'server' } - # Get telnet configuration from debugger section - telnet_config = self.debugger.get('telnet') or [] - valid_config = any('mode' in t for t in telnet_config) + # Get STDIO configuration from debugger section + stdio_config = self._get_stdio_config() + valid_config = any('mode' in s for s in stdio_config) # Determine global mode if specified, default to 'off' otherwise - global_mode = next((t.get('mode') for t in telnet_config if 'pname' not in t), 'off') + global_mode = next((s.get('mode') for s in stdio_config if 'pname' not in s), 'off') global_mode = MODE_ALIASES.get(global_mode, global_mode) - # Build list of telnet modes for each core - telnet_mode = [] + # Build list of STDIO modes for each core + stdio_mode = [] for core in self.sorted_processors: - mode = next((t.get('mode') for t in telnet_config if t.get('pname') == core.name), global_mode) + mode = next((s.get('mode') for s in stdio_config if s.get('pname') == core.name), global_mode) mode = MODE_ALIASES.get(mode, mode) if mode not in SUPPORTED_MODES: if valid_config: - LOG.warning("Invalid telnet mode '%s' for core '%s' in cbuild-run, defaulting to '%s'", + LOG.warning("Invalid STDIO mode '%s' for core '%s' in cbuild-run, defaulting to '%s'", mode, core.name, global_mode) mode = global_mode - telnet_mode.append(mode) + stdio_mode.append(mode) - return tuple(telnet_mode) + return tuple(stdio_mode) @property - def telnet_file(self) -> Dict[str, Optional[Tuple]]: - """@brief Telnet file path assignments from debugger section. + def stdio_file(self) -> Dict[str, Optional[Tuple]]: + """@brief STDIO file path assignments from debugger section. The method will not be called frequently, so performance is not critical. """ - # Get telnet configuration from debugger section - telnet_config = self.debugger.get('telnet') or [] - telnet_mode = self.telnet_mode + # Get STDIO configuration from debugger section + stdio_config = self._get_stdio_config() + stdio_mode = self.stdio_mode - if not any(mode == 'file' for mode in telnet_mode): - # No telnet file paths needed + if not any(mode == 'file' for mode in stdio_mode): + # No STDIO file paths needed return {'in': None, 'out': None} def _resolve_path(file_path: Optional[str], strict: bool = False) -> Optional[str]: @@ -685,7 +690,7 @@ def _resolve_path(file_path: Optional[str], strict: bool = False) -> Optional[st resolved_path = file_path_obj.resolve() # In strict mode check if the file exists if strict and not resolved_path.is_file(): - LOG.warning("Telnet file '%s' not found", resolved_path) + LOG.warning("STDIO file '%s' not found", resolved_path) return str(resolved_path) @@ -693,11 +698,11 @@ def _resolve_path(file_path: Optional[str], strict: bool = False) -> Optional[st out_files = [] # Per pname configuration - config_by_pname = {t['pname']: t for t in telnet_config if 'pname' in t} + config_by_pname = {s['pname']: s for s in stdio_config if 'pname' in s} if config_by_pname: # Build config per pname - for proc_info, mode in zip(self.sorted_processors, telnet_mode): + for proc_info, mode in zip(self.sorted_processors, stdio_mode): if mode != 'file': in_files.append(None) out_files.append(None) @@ -714,11 +719,11 @@ def _resolve_path(file_path: Optional[str], strict: bool = False) -> Optional[st out_file = str((self._base_path / f"{self._cbuild_name}.{proc_info.name}.out").resolve()) out_files.append(out_file) else: - config = next((t for t in telnet_config if t.get('mode') == 'file'), None) + config = next((s for s in stdio_config if s.get('mode') == 'file'), None) if config is not None: if len(self.sorted_processors) > 1: - LOG.warning("Ignoring invalid telnet file configuration for multicore target in cbuild-run") - for proc_info, mode in zip(self.sorted_processors, telnet_mode): + LOG.warning("Ignoring invalid STDIO file configuration for multicore target in cbuild-run") + for proc_info, mode in zip(self.sorted_processors, stdio_mode): if mode != 'file': in_files.append(None) out_files.append(None) @@ -795,6 +800,11 @@ def populate_target(self, target: Optional[str] = None) -> None: }) TARGET[target] = tgt + def _get_stdio_config(self) -> List[dict]: + """@brief Returns STDIO configuration from debugger section, with telnet as an alias.""" + server_type = 'stdio' if 'stdio' in self.debugger else 'telnet' + return self.debugger.get(server_type) or [] + def _get_server_port(self, server_type: str) -> Optional[Tuple]: """@brief Generic method to get server port assignments from debugger section.""" server_config = self.debugger.get(server_type, []) diff --git a/pyocd/utility/cmdline.py b/pyocd/utility/cmdline.py index 7b785179e..54fc77d62 100644 --- a/pyocd/utility/cmdline.py +++ b/pyocd/utility/cmdline.py @@ -21,7 +21,7 @@ import yaml from ..core.target import Target -from ..core.options import OPTIONS_INFO +from ..core.options import (OPTIONS_ALIASES, OPTIONS_INFO) from ..utility.compatibility import to_str_safe LOG = logging.getLogger(__name__) @@ -165,6 +165,7 @@ def convert_one_session_option(name: str, value: Optional[str]) -> Tuple[str, An had_no_prefix = True else: had_no_prefix = False + name = OPTIONS_ALIASES.get(name, name) # Look up this option. try: diff --git a/pyocd/utility/stdio.py b/pyocd/utility/stdio.py index 02ce515e9..d5eeaacd6 100644 --- a/pyocd/utility/stdio.py +++ b/pyocd/utility/stdio.py @@ -54,33 +54,33 @@ class StdioOff(StdioBase): def info(self) -> str: return "off" -class StdioTelnet(StdioBase): +class StdioServer(StdioBase): """STDIO backend that uses a telnet server for reading from and writing to stdin/stdout.""" def __init__(self, session: Session, core: int = 0) -> None: - _telnet_ports = session.options.get('telnet_port') - if isinstance(_telnet_ports, (list, tuple)): - if len(_telnet_ports) <= core or _telnet_ports[core] is None: - raise ValueError(f"STDIO: telnet for core {core} requires a port number in the 'telnet_port' list") - telnet_port = _telnet_ports[core] + _stdio_ports = session.options.get('stdio_port') + if isinstance(_stdio_ports, (list, tuple)): + if len(_stdio_ports) <= core or _stdio_ports[core] is None: + raise ValueError(f"STDIO: server for core {core} requires a port number in the 'stdio_port' list") + stdio_port = _stdio_ports[core] else: - telnet_port = _telnet_ports - if telnet_port != 0: - telnet_port += core + stdio_port = _stdio_ports + if stdio_port != 0: + stdio_port += core serve_local_only = session.options.get('serve_local_only') self._server = StreamServer( - port=telnet_port, + port=stdio_port, serve_local_only=serve_local_only, name="STDIO", is_read_only=False, extra_info=f"core {core}" ) - if telnet_port == 0: - telnet_port = self._server.port + if stdio_port == 0: + stdio_port = self._server.port - self._telnet_port = telnet_port + self._stdio_port = stdio_port # ToDo: consider waiting for client to connect # while self._server._connected_socket is None: @@ -90,7 +90,7 @@ def write(self, data: bytes) -> int: try: return self._server.write(data) except Exception as e: - LOG.debug("Error writing to STDIO telnet server (port %d): %s", self._telnet_port, e) + LOG.debug("Error writing to STDIO telnet server (port %d): %s", self._stdio_port, e) return 0 def read(self, max_bytes: int) -> bytes: @@ -98,7 +98,7 @@ def read(self, max_bytes: int) -> bytes: try: data = self._server.read(max_bytes) except Exception as e: - LOG.debug("Error reading from STDIO telnet server (port %d): %s", self._telnet_port, e) + LOG.debug("Error reading from STDIO telnet server (port %d): %s", self._stdio_port, e) if data is None: return b"" return bytes(data) @@ -107,7 +107,7 @@ def shutdown(self) -> None: try: self._server.stop() except Exception as e: - LOG.debug("Error stopping STDIO telnet server (port %d): %s", self._telnet_port, e) + LOG.debug("Error stopping STDIO telnet server (port %d): %s", self._stdio_port, e) @property def info(self) -> str: @@ -120,60 +120,60 @@ def __init__(self, session: Session, core: int = 0) -> None: is_multi_core = len(session.board.target.cores) > 1 # Get file paths from session options - if session.options.is_set('telnet_file_out'): - _telnet_file_out = session.options.get('telnet_file_out') - if isinstance(_telnet_file_out, (list, tuple)): - if len(_telnet_file_out) <= core or _telnet_file_out[core] is None: + if session.options.is_set('stdio_file_out'): + _stdio_file_out = session.options.get('stdio_file_out') + if isinstance(_stdio_file_out, (list, tuple)): + if len(_stdio_file_out) <= core or _stdio_file_out[core] is None: raise ValueError(f"STDIO file for core {core} requires a valid output file path") - telnet_file_out = _telnet_file_out[core] + stdio_file_out = _stdio_file_out[core] else: if is_multi_core: - root, ext = os.path.splitext(_telnet_file_out) - _telnet_file_out = root + f"_{core}" + ext - telnet_file_out = _telnet_file_out + root, ext = os.path.splitext(_stdio_file_out) + _stdio_file_out = root + f"_{core}" + ext + stdio_file_out = _stdio_file_out else: # Default target_type = session.board.target_type - telnet_file_out = f"{target_type}_{core}.out" if is_multi_core else f"{target_type}.out" + stdio_file_out = f"{target_type}_{core}.out" if is_multi_core else f"{target_type}.out" - telnet_file_in = None - if session.options.is_set('telnet_file_in'): - _telnet_file_in = session.options.get('telnet_file_in') - if isinstance(_telnet_file_in, (list, tuple)): - if len(_telnet_file_in) <= core or _telnet_file_in[core] is None: + stdio_file_in = None + if session.options.is_set('stdio_file_in'): + _stdio_file_in = session.options.get('stdio_file_in') + if isinstance(_stdio_file_in, (list, tuple)): + if len(_stdio_file_in) <= core or _stdio_file_in[core] is None: LOG.debug("No input file configured for core %d", core) - telnet_file_in = _telnet_file_in[core] + stdio_file_in = _stdio_file_in[core] else: if is_multi_core: - root, ext = os.path.splitext(_telnet_file_in) - _telnet_file_in = root + f"_{core}" + ext - telnet_file_in = _telnet_file_in + root, ext = os.path.splitext(_stdio_file_in) + _stdio_file_in = root + f"_{core}" + ext + stdio_file_in = _stdio_file_in else: # Default target_type = session.board.target_type - telnet_file_in = f"{target_type}_{core}.in" if is_multi_core else f"{target_type}.in" + stdio_file_in = f"{target_type}_{core}.in" if is_multi_core else f"{target_type}.in" # Check if the folder exists for input/output files - dir_out = os.path.dirname(telnet_file_out) - self._fname_out = os.path.basename(telnet_file_out) + dir_out = os.path.dirname(stdio_file_out) + self._fname_out = os.path.basename(stdio_file_out) if dir_out and not os.path.exists(dir_out): raise FileNotFoundError(f"Directory {dir_out} for STDIO file {self._fname_out} does not exist") # Open files - if telnet_file_in is not None and os.path.exists(telnet_file_in): - self._input_file = open(telnet_file_in, 'rb') - self._fname_in = os.path.basename(telnet_file_in) + if stdio_file_in is not None and os.path.exists(stdio_file_in): + self._input_file = open(stdio_file_in, 'rb') + self._fname_in = os.path.basename(stdio_file_in) else: - LOG.debug("Input file '%s' does not exist; STDIN will be disabled", telnet_file_in) + LOG.debug("Input file '%s' does not exist; STDIN will be disabled", stdio_file_in) self._input_file = None self._fname_in = None try: - self._output_file = open(telnet_file_out, 'wb') + self._output_file = open(stdio_file_out, 'wb') except OSError as e: if self._input_file: self._input_file.close() - raise IOError(f"Failed to open STDIO file {telnet_file_out}: {e}") + raise IOError(f"Failed to open STDIO file {stdio_file_out}: {e}") def write(self, data: bytes) -> int: # Output file is valid - else exception raised in constructor @@ -296,8 +296,8 @@ def info(self) -> str: _BACKEND_CLASSES: Dict[str or bool, Type[StdioBase]] = { False: StdioOff, "off": StdioOff, - "server": StdioTelnet, - "telnet": StdioTelnet, + "server": StdioServer, + "telnet": StdioServer, "file": StdioFile, "console": StdioConsole } @@ -308,14 +308,14 @@ class StdioHandler(StdioBase): """ def __init__(self, session: Session, core: int = 0, eot_enabled: bool = False) -> None: - if session.options.is_set('telnet_mode'): - _telnet_mode = session.options.get('telnet_mode') - if isinstance(_telnet_mode, (list, tuple)): - if len(_telnet_mode) <= core or _telnet_mode[core] is None: - raise ValueError(f"STDIO mode for core {core} requires a 'telnet_mode'") - stdio_mode = _telnet_mode[core] + if session.options.is_set('stdio_mode'): + _stdio_mode = session.options.get('stdio_mode') + if isinstance(_stdio_mode, (list, tuple)): + if len(_stdio_mode) <= core or _stdio_mode[core] is None: + raise ValueError(f"STDIO mode for core {core} requires a 'stdio_mode'") + stdio_mode = _stdio_mode[core] else: - stdio_mode = _telnet_mode + stdio_mode = _stdio_mode else: stdio_mode = session.options.get('semihost_console_type') diff --git a/test/unit/test_cmdline.py b/test/unit/test_cmdline.py index 680af4740..15d16a7d0 100644 --- a/test/unit/test_cmdline.py +++ b/test/unit/test_cmdline.py @@ -1,5 +1,5 @@ # pyOCD debugger -# Copyright (c) 2015,2018-2019 Arm Limited +# Copyright (c) 2015,2018-2019,2026 Arm Limited # Copyright (c) 2022 Chris Reed # SPDX-License-Identifier: Apache-2.0 # @@ -143,6 +143,10 @@ def test_str(self): # Valid assert convert_session_options(['test_binary=abc']) == {'test_binary': 'abc'} + def test_option_alias(self): + assert convert_session_options(['telnet_mode=file']) == {'stdio_mode': 'file'} + assert convert_session_options(['telnet_port=4445']) == {'stdio_port': 4445} + class TestTargetTypeNormalisation: def test_passthrough(self): assert normalise_target_type_name("foobar") == "foobar" diff --git a/test/unit/test_options_manager.py b/test/unit/test_options_manager.py index d8659ffaf..0130ce29c 100644 --- a/test/unit/test_options_manager.py +++ b/test/unit/test_options_manager.py @@ -1,5 +1,5 @@ # pyOCD debugger -# Copyright (c) 2019 Arm Limited +# Copyright (c) 2019,2026 Arm Limited # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,7 +17,7 @@ import pytest from pyocd.core.options_manager import OptionsManager -from pyocd.core.options import OPTIONS_INFO +from pyocd.core.options import (OPTIONS_ALIASES, OPTIONS_INFO) @pytest.fixture(scope='function') def mgr(): @@ -73,6 +73,30 @@ def test_convert_double_underscore(self, mgr): assert 'debug.traceback' in mgr assert mgr.get('debug.traceback') == False + def test_option_alias(self, mgr): + mgr.add_back({'telnet_mode': 'file'}) + assert 'stdio_mode' in mgr + assert 'telnet_mode' not in mgr + assert mgr.get('stdio_mode') == 'file' + assert mgr.get('telnet_mode') is None + assert mgr.get_default('telnet_mode') is None + + def test_option_alias_layer_priority(self, mgr): + mgr.add_back({'stdio_mode': 'server'}) + mgr.add_front({'telnet_mode': 'file'}) + assert mgr.get('stdio_mode') == 'file' + + def test_option_alias_same_layer_order(self, mgr): + mgr.add_back({'telnet_mode': 'file', 'stdio_mode': 'console'}) + assert mgr.get('stdio_mode') == 'console' + + mgr = OptionsManager() + mgr.add_back({'stdio_mode': 'console', 'telnet_mode': 'file'}) + assert mgr.get('stdio_mode') == 'file' + + def test_legacy_aliases_not_registered_as_options(self): + assert not any(name in OPTIONS_INFO for name in OPTIONS_ALIASES) + def test_set(self, mgr, layer1): mgr.add_front(layer1) mgr.set('buzz', 1234)