@@ -1070,38 +1070,6 @@ def test_cmdloop_without_rawinput() -> None:
1070
1070
1071
1071
def test_cmdfinalizations_runs (base_app , monkeypatch ) -> None :
1072
1072
"""Make sure _run_cmdfinalization_hooks is run after each command."""
1073
- termios_settings = [
1074
- 27394 ,
1075
- 3 ,
1076
- 19200 ,
1077
- 536872399 ,
1078
- 38400 ,
1079
- 38400 ,
1080
- [
1081
- b'\x04 ' ,
1082
- b'\xff ' ,
1083
- b'\xff ' ,
1084
- b'\x7f ' ,
1085
- b'\x17 ' ,
1086
- b'\x15 ' ,
1087
- b'\x12 ' ,
1088
- b'\x00 ' ,
1089
- b'\x03 ' ,
1090
- b'\x1c ' ,
1091
- b'\x1a ' ,
1092
- b'\x19 ' ,
1093
- b'\x11 ' ,
1094
- b'\x13 ' ,
1095
- b'\x16 ' ,
1096
- b'\x0f ' ,
1097
- b'\x01 ' ,
1098
- b'\x00 ' ,
1099
- b'\x14 ' ,
1100
- b'\x00 ' ,
1101
- ],
1102
- ]
1103
-
1104
- base_app ._initial_termios_settings = termios_settings
1105
1073
with (
1106
1074
mock .patch ('sys.stdin.isatty' , mock .MagicMock (name = 'isatty' , return_value = True )),
1107
1075
mock .patch ('sys.stdin.fileno' , mock .MagicMock (name = 'fileno' , return_value = 0 )),
@@ -1116,6 +1084,56 @@ def test_cmdfinalizations_runs(base_app, monkeypatch) -> None:
1116
1084
cmd_fin .assert_called_once ()
1117
1085
1118
1086
1087
+ @pytest .mark .skipif (sys .platform .startswith ('win' ), reason = "termios is not available on Windows" )
1088
+ @pytest .mark .parametrize (
1089
+ ('is_tty' , 'settings_set' , 'raised_exception' , 'should_call' ),
1090
+ [
1091
+ (True , True , None , True ),
1092
+ (True , True , 'termios_error' , True ),
1093
+ (True , True , 'unsupported_operation' , True ),
1094
+ (False , True , None , False ),
1095
+ (True , False , None , False ),
1096
+ ],
1097
+ )
1098
+ def test_restore_termios_settings (base_app , monkeypatch , is_tty , settings_set , raised_exception , should_call ):
1099
+ """Test that terminal settings are restored after a command and that errors are suppressed."""
1100
+ import io
1101
+ import termios # Mock termios since it's imported within the method
1102
+
1103
+ termios_mock = mock .MagicMock ()
1104
+ # The error attribute needs to be the actual exception for isinstance checks
1105
+ termios_mock .error = termios .error
1106
+ monkeypatch .setitem (sys .modules , 'termios' , termios_mock )
1107
+
1108
+ # Set the exception to be raised by tcsetattr
1109
+ if raised_exception == 'termios_error' :
1110
+ termios_mock .tcsetattr .side_effect = termios .error ("test termios error" )
1111
+ elif raised_exception == 'unsupported_operation' :
1112
+ termios_mock .tcsetattr .side_effect = io .UnsupportedOperation ("test io error" )
1113
+
1114
+ # Set initial termios settings so the logic will run
1115
+ if settings_set :
1116
+ termios_settings = ["dummy settings" ]
1117
+ base_app ._initial_termios_settings = termios_settings
1118
+ else :
1119
+ base_app ._initial_termios_settings = None
1120
+ termios_settings = None # for the assert
1121
+
1122
+ # Mock stdin to make it look like a TTY
1123
+ monkeypatch .setattr (base_app .stdin , "isatty" , lambda : is_tty )
1124
+ monkeypatch .setattr (base_app .stdin , "fileno" , lambda : 0 )
1125
+
1126
+ # Run a command to trigger _run_cmdfinalization_hooks
1127
+ # This should not raise an exception
1128
+ base_app .onecmd_plus_hooks ('help' )
1129
+
1130
+ # Verify that tcsetattr was called with the correct arguments
1131
+ if should_call :
1132
+ termios_mock .tcsetattr .assert_called_once_with (0 , termios_mock .TCSANOW , termios_settings )
1133
+ else :
1134
+ termios_mock .tcsetattr .assert_not_called ()
1135
+
1136
+
1119
1137
def test_sigint_handler (base_app ) -> None :
1120
1138
# No KeyboardInterrupt should be raised when using sigint_protection
1121
1139
with base_app .sigint_protection :
0 commit comments