|
| 1 | +# |
| 2 | +# Copyright (C) 2020 Arm Mbed. All rights reserved. |
| 3 | +# SPDX-License-Identifier: Apache-2.0 |
| 4 | +# |
| 5 | +from unittest import mock |
| 6 | + |
| 7 | +import sys |
| 8 | + |
| 9 | +import pytest |
| 10 | + |
| 11 | +from mbed_tools.sterm import terminal |
| 12 | + |
| 13 | + |
| 14 | +@pytest.fixture |
| 15 | +def mock_serial(): |
| 16 | + with mock.patch("mbed_tools.sterm.terminal.Serial") as serial: |
| 17 | + yield serial |
| 18 | + |
| 19 | + |
| 20 | +@pytest.fixture |
| 21 | +def mock_console(): |
| 22 | + with mock.patch("serial.tools.miniterm.Console") as console: |
| 23 | + yield console |
| 24 | + |
| 25 | + |
| 26 | +@pytest.fixture |
| 27 | +def mock_sterm(): |
| 28 | + with mock.patch("mbed_tools.sterm.terminal.SerialTerminal") as sterm: |
| 29 | + yield sterm |
| 30 | + |
| 31 | + |
| 32 | +def test_initialises_serial_port(mock_sterm, mock_serial): |
| 33 | + port = "tty.1111" |
| 34 | + baud = 9600 |
| 35 | + |
| 36 | + terminal.run(port, baud) |
| 37 | + |
| 38 | + mock_serial.assert_called_once_with(port=port, baudrate=str(baud)) |
| 39 | + |
| 40 | + |
| 41 | +def test_initialises_sterm(mock_sterm, mock_serial): |
| 42 | + port = "tty.1111" |
| 43 | + baud = "9600" |
| 44 | + |
| 45 | + terminal.run(port, baud) |
| 46 | + |
| 47 | + mock_sterm.assert_called_once_with(mock_serial(), echo=True) |
| 48 | + |
| 49 | + |
| 50 | +def test_starts_sterm_thread(mock_sterm, mock_serial): |
| 51 | + terminal.run("tty.122", 9600) |
| 52 | + |
| 53 | + mock_sterm().start.assert_called_once() |
| 54 | + |
| 55 | + |
| 56 | +def test_joins_tx_and_rx_threads(mock_sterm, mock_serial): |
| 57 | + terminal.run("tty.122", 9600) |
| 58 | + |
| 59 | + mock_sterm().join.assert_any_call(True) |
| 60 | + |
| 61 | + |
| 62 | +def test_joins_tx_thread_after_keyboard_interrupt(mock_sterm, mock_serial): |
| 63 | + mock_sterm().join.side_effect = (KeyboardInterrupt(), None) |
| 64 | + |
| 65 | + terminal.run("tty.122", 9600) |
| 66 | + |
| 67 | + mock_sterm().join.assert_called_with() |
| 68 | + |
| 69 | + |
| 70 | +def test_closes_sterm(mock_sterm, mock_serial): |
| 71 | + terminal.run("tty.122", 9600) |
| 72 | + |
| 73 | + mock_sterm().close.assert_called_once() |
| 74 | + |
| 75 | + |
| 76 | +def test_closes_sterm_after_exception(mock_sterm, mock_serial): |
| 77 | + mock_sterm().join.side_effect = (Exception(), None) |
| 78 | + with pytest.raises(Exception): |
| 79 | + terminal.run("tty.122", 9600) |
| 80 | + |
| 81 | + mock_sterm().close.assert_called_once() |
| 82 | + |
| 83 | + |
| 84 | +def test_closes_sterm_after_keyboard_interrupt(mock_sterm, mock_serial): |
| 85 | + mock_sterm().join.side_effect = (KeyboardInterrupt(), None) |
| 86 | + terminal.run("tty.122", 9600) |
| 87 | + |
| 88 | + mock_sterm().close.assert_called_once() |
| 89 | + |
| 90 | + |
| 91 | +def test_sets_terminal_special_chars(mock_serial, mock_console): |
| 92 | + term = terminal.SerialTerminal(mock_serial()) |
| 93 | + |
| 94 | + assert term.exit_character == terminal.CTRL_C |
| 95 | + assert term.menu_character == terminal.CTRL_T |
| 96 | + assert term.reset_character == terminal.CTRL_B |
| 97 | + assert term.help_character == terminal.CTRL_H |
| 98 | + |
| 99 | + |
| 100 | +def test_sets_terminal_rx_and_tx_encoding_to_utf8(mock_serial, mock_console): |
| 101 | + term = terminal.SerialTerminal(mock_serial()) |
| 102 | + |
| 103 | + assert term.input_encoding == "UTF-8" |
| 104 | + assert term.output_encoding == "UTF-8" |
| 105 | + |
| 106 | + |
| 107 | +def test_stops_terminal_when_ctrl_c_received(mock_serial, mock_console): |
| 108 | + term = terminal.SerialTerminal(mock_serial()) |
| 109 | + term.alive = True |
| 110 | + mock_console().getkey.return_value = terminal.CTRL_C |
| 111 | + |
| 112 | + term.writer() |
| 113 | + |
| 114 | + assert term.alive is False |
| 115 | + |
| 116 | + |
| 117 | +def test_stops_terminal_on_keyboard_interrupt(mock_serial, mock_console): |
| 118 | + term = terminal.SerialTerminal(mock_serial()) |
| 119 | + term.alive = True |
| 120 | + mock_console().getkey.side_effect = KeyboardInterrupt() |
| 121 | + |
| 122 | + term.writer() |
| 123 | + |
| 124 | + assert term.alive is False |
| 125 | + |
| 126 | + |
| 127 | +@pytest.mark.parametrize("menu_key", terminal.VALID_MENU_KEYS) |
| 128 | +def test_handles_valid_menu_key(menu_key, mock_serial, mock_console): |
| 129 | + term = terminal.SerialTerminal(mock_serial()) |
| 130 | + term.handle_menu_key = mock.Mock() |
| 131 | + term.alive = True |
| 132 | + mock_console().getkey.side_effect = (terminal.CTRL_T, menu_key, terminal.CTRL_C) |
| 133 | + |
| 134 | + term.writer() |
| 135 | + |
| 136 | + term.handle_menu_key.assert_called_once_with(menu_key) |
| 137 | + |
| 138 | + |
| 139 | +INVALID_MENU_KEYS = tuple(set(chr(i) for i in range(0, 127)) - set(terminal.VALID_MENU_KEYS) - set([terminal.CTRL_H])) |
| 140 | + |
| 141 | + |
| 142 | +@pytest.mark.parametrize("menu_key", INVALID_MENU_KEYS) |
| 143 | +def test_ignores_invalid_menu_key(menu_key, mock_serial, mock_console): |
| 144 | + term = terminal.SerialTerminal(mock_serial()) |
| 145 | + term.handle_menu_key = mock.Mock() |
| 146 | + term.alive = True |
| 147 | + mock_console().getkey.side_effect = (terminal.CTRL_T, menu_key) |
| 148 | + |
| 149 | + with pytest.raises(StopIteration): |
| 150 | + term.writer() |
| 151 | + |
| 152 | + term.handle_menu_key.assert_not_called() |
| 153 | + |
| 154 | + |
| 155 | +def test_reset_sends_serial_break(mock_serial, mock_console): |
| 156 | + term = terminal.SerialTerminal(mock_serial()) |
| 157 | + |
| 158 | + term.reset() |
| 159 | + |
| 160 | + mock_serial().sendBreak.assert_called_once() |
| 161 | + |
| 162 | + |
| 163 | +def test_ctrl_b_sends_reset_to_serial_port(mock_serial, mock_console): |
| 164 | + term = terminal.SerialTerminal(mock_serial()) |
| 165 | + term.alive = True |
| 166 | + mock_console().getkey.side_effect = (terminal.CTRL_B,) |
| 167 | + |
| 168 | + with pytest.raises(StopIteration): |
| 169 | + term.writer() |
| 170 | + |
| 171 | + mock_serial().sendBreak.assert_called_once() |
| 172 | + |
| 173 | + |
| 174 | +def test_ctrl_h_prints_help_text(mock_serial, mock_console): |
| 175 | + sys.stderr.write = mock.Mock() |
| 176 | + term = terminal.SerialTerminal(mock_serial()) |
| 177 | + term.alive = True |
| 178 | + mock_console().getkey.side_effect = (terminal.CTRL_H,) |
| 179 | + |
| 180 | + with pytest.raises(StopIteration): |
| 181 | + term.writer() |
| 182 | + |
| 183 | + sys.stderr.write.assert_called_once_with(term.get_help_text()) |
| 184 | + |
| 185 | + |
| 186 | +def test_help_text_is_correct(mock_serial, mock_console): |
| 187 | + term = terminal.SerialTerminal(mock_serial()) |
| 188 | + |
| 189 | + assert term.get_help_text() == terminal.HELP_TEXT |
| 190 | + |
| 191 | + |
| 192 | +def test_writes_normal_char_to_serial_output(mock_serial, mock_console): |
| 193 | + term = terminal.SerialTerminal(mock_serial()) |
| 194 | + term.alive = True |
| 195 | + normal_char = "h" |
| 196 | + mock_console().getkey.side_effect = (normal_char,) |
| 197 | + |
| 198 | + with pytest.raises(StopIteration): |
| 199 | + term.writer() |
| 200 | + |
| 201 | + mock_serial().write.assert_called_once_with(term.tx_encoder.encode(normal_char)) |
| 202 | + |
| 203 | + |
| 204 | +def test_echo_to_console_is_default_disabled(mock_serial, mock_console): |
| 205 | + term = terminal.SerialTerminal(mock_serial()) |
| 206 | + term.alive = True |
| 207 | + normal_char = "h" |
| 208 | + mock_console().getkey.side_effect = (normal_char,) |
| 209 | + |
| 210 | + with pytest.raises(StopIteration): |
| 211 | + term.writer() |
| 212 | + |
| 213 | + mock_console().write.assert_not_called() |
| 214 | + |
| 215 | + |
| 216 | +def test_echo_to_console_can_be_enabled(mock_serial, mock_console): |
| 217 | + term = terminal.SerialTerminal(mock_serial(), echo=True) |
| 218 | + term.alive = True |
| 219 | + normal_char = "h" |
| 220 | + mock_console().getkey.side_effect = (normal_char,) |
| 221 | + |
| 222 | + with pytest.raises(StopIteration): |
| 223 | + term.writer() |
| 224 | + |
| 225 | + mock_console().write.assert_called_once_with(normal_char) |
0 commit comments