File: test_debug_chat.py

package info (click to toggle)
freeorion 0.5.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 194,940 kB
  • sloc: cpp: 186,508; python: 40,969; ansic: 1,164; xml: 719; makefile: 32; sh: 7
file content (279 lines) | stat: -rw-r--r-- 9,431 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
from io import StringIO
from typing import Any

# Tests classes
import pytest

from freeorion_tools.chat_handler.chat_formatter import ChatFormatter
from freeorion_tools.chat_handler.debug_chat_handler import DebugChatHandler
from freeorion_tools.chat_handler.shell_variable import ShellVariable


class Printer:
    def __init__(self):
        self.input = StringIO()

    def __call__(self, message):
        self.input.write(str(message))
        self.input.write("\n")


class DummyProvider:
    def __init__(self, player_id):
        self._player_id = player_id

    def get_player_ids(self):
        return [1, 2, 3]

    def is_host(self, player_id):
        return player_id == 1

    def player_id(self):
        return self._player_id

    def player_name(self, player_id):
        return "Player_%s" % player_id

    def get_empire_id(self, player_id):
        return player_id

    def get_empire_name(self, player_id):
        return "Empire_%s" % player_id

    def get_empire_color(self, player_id):
        return 0, 0, 0, 0


class DummyFormatter(ChatFormatter):
    def _get_tag_rgb(self, color: (int, int, int, int)) -> (str, str):
        return "", ""

    def _get_underline_tag(self) -> (str, str):
        return "", ""

    def _wrap_to_tag(self, tag: str, message: Any) -> str:
        return str(message)


@pytest.fixture
def debug_handler():
    _shell_locals = [
        ShellVariable(
            variable="variable_a",
            expression="'value_a'",
            description="description for a",
        ),
        ShellVariable(
            variable="variable_b",
            expression="'value_b'",
            description="description for b",
        ),
        ShellVariable(
            variable="variable_c",
            expression="'value_c'",
            description="variable with import",
            imports=("from glob import has_magic",),
        ),
        ShellVariable(
            variable="variable_a_plus_b",
            expression="variable_a + variable_b",
            description="description for a+b",
        ),
    ]

    provider = DummyProvider(2)
    return DebugChatHandler(
        2,
        provider=provider,
        shell_locals=_shell_locals,
        formatter=DummyFormatter(),
        chat_printer=Printer(),
        log_printer=Printer(),
    )


@pytest.fixture
def debug_handler_for_muted_ai():
    provider = DummyProvider(3)
    chat_printer = Printer()
    return chat_printer.input, DebugChatHandler(
        3,
        provider=provider,
        shell_locals=[],
        formatter=DummyFormatter(),
        chat_printer=chat_printer,
        log_printer=Printer(),
    )


def get_output_from_handler(handler):
    chat = handler._chat_printer.input.getvalue()
    log = handler._log_printer.input.getvalue()
    return chat, log


@pytest.fixture
def test_caller(debug_handler: DebugChatHandler):
    def function(message):
        # clear previous logs
        debug_handler._chat_printer.input.truncate(0)
        debug_handler._log_printer.input.truncate(0)
        assert debug_handler.process_message(1, message)
        chat, log = get_output_from_handler(debug_handler)
        assert "SyntaxError" not in chat
        assert "SyntaxError" not in log
        return chat, log

    return function


class TestNonDebugModeForAiWhoResponds:
    def test_send_help_prints_user_names(self, test_caller):
        chat, log = test_caller("help")
        assert "player_1" not in chat
        assert "Empire_2 by Player_2" in chat
        assert "Empire_3 by Player_3" in chat

    def test_sending_unknown_message_is_ignored_by_handler(self, debug_handler: DebugChatHandler):
        handled = debug_handler.process_message(1, "hello world")
        assert handled is False
        chat, log = get_output_from_handler(debug_handler)
        assert chat == ""
        assert log == ""

    def test_sending_stop_before_start_is_swallowed_by_handler(self, debug_handler: DebugChatHandler):
        result = debug_handler.process_message(1, "stop")
        assert result is True
        chat, log = get_output_from_handler(debug_handler)
        assert chat == ""
        assert log == ""


class TestNonDebugModeForAiWhoMuted:
    def test_send_help_is_ignored_by_second_ai(self, debug_handler_for_muted_ai: tuple[StringIO, DebugChatHandler]):
        chat_io, handler = debug_handler_for_muted_ai
        assert handler.process_message(1, "help")
        assert chat_io.getvalue() == ""


class TestDebugModStart:
    def test_sending_message_from_ai_is_propagated_by_handler(self, debug_handler: DebugChatHandler):
        # ignore messages not from human
        result = debug_handler.process_message(3, "start 2")
        assert result is False
        chat, log = get_output_from_handler(debug_handler)
        assert chat == ""
        assert log == ""

    def test_starting_with_not_existing_ai_is_swallowed_by_handler(self, debug_handler: DebugChatHandler):
        result = debug_handler.process_message(1, "start 10")
        assert result is True
        chat, log = get_output_from_handler(debug_handler)
        assert chat == ""
        assert log == ""

    def test_starting_with_nonnumerical_ai_is_swallowed_by_handler(self, debug_handler: DebugChatHandler):
        assert debug_handler.process_message(1, "start xxx")
        chat, log = get_output_from_handler(debug_handler)
        assert "Invalid empire id, please input valid number" in chat

    def test_start_start_debug_mode_for_respond_ai(self, test_caller):
        chat, log = test_caller("start 2")
        assert "Entering debug mode" in chat

    def test_start_start_debug_mode_for_muted_ai(self, debug_handler_for_muted_ai: tuple[StringIO, DebugChatHandler]):
        chat_io, handler = debug_handler_for_muted_ai
        handler.process_message(1, "start 3")
        assert "Entering debug mode" in chat_io.getvalue()

    def test_sending_start_prints_intro_message(self, test_caller):
        chat, log = test_caller("start 2")
        assert "variable_a" in chat
        assert "description for a" in chat
        assert "variable_b" in chat
        assert "description for b" in chat
        assert "variable_a_plus_b" in chat
        assert "scription for a+b" in chat


@pytest.fixture
def start_debug(test_caller):
    return test_caller("start 2")


@pytest.mark.usefixtures("start_debug")
class TestInDebugMode:
    def test_sending_stop_pauses_debug(self, test_caller):
        chat, log = test_caller("stop")
        assert "Exiting debug mode" in chat

    def test_evaluate_initial_variables_prints_them_to_chat(self, test_caller):
        chat, log = test_caller("variable_a")
        assert "value_a" in chat
        chat, log = test_caller("variable_b")
        assert "value_b" in chat
        chat, log = test_caller("variable_a_plus_b")
        assert "value_avalue_b" in chat

    def test_evaluating_command_prints_command_to_log(self, test_caller):
        chat, log = test_caller("'free' + 'orion'")
        assert "'free' + 'orion'" in log

    def test_evaluating_command_prints_result(self, test_caller):
        chat, log = test_caller("'free' + 'orion'")
        assert "freeorion" in chat

    def test_evaluating_assignment_assigns_variable(self, test_caller):
        test_caller("variable_c = 'value_c'")
        chat, log = test_caller("variable_c")
        assert "value_c" in chat

    def test_evaluating_erroneous_statement_prints_exception_to_console(self, debug_handler: DebugChatHandler):
        assert debug_handler.process_message(1, "1/0")
        chat, log = get_output_from_handler(debug_handler)
        assert "ZeroDivisionError: division by zero" in chat
        assert "ZeroDivisionError: division by zero" in log
        assert debug_handler.process_message(1, "variable_a")
        chat, log = get_output_from_handler(debug_handler)
        assert "value_a" in chat

    def test_start_with_initial_imports_evaluates_imports(self, test_caller):
        chat, log = test_caller("has_magic")
        assert "function has_magic at" in chat

    def test_help_call_is_ignored(self, test_caller):
        chat, log = test_caller("help")
        assert "Type help() for interactive help" in chat

    def test_start_call_is_ignored(self, test_caller):
        chat, log = test_caller("start")
        assert " name 'start' is not defined" in chat

    def test_start_with_number_call_is_ignored(self, debug_handler: DebugChatHandler):
        assert debug_handler.process_message(1, "start 2")
        chat, log = get_output_from_handler(debug_handler)
        assert "SyntaxError" in log
        assert "SyntaxError" in chat


class TestRestart:
    def test_modified_initial_variables_are_restored(self, test_caller):
        test_caller("start 2")
        test_caller("variable_b = 'value_d'")
        chat, log = test_caller("variable_b")
        assert "value_d" in chat
        test_caller("stop")
        test_caller("start 2")
        chat, log = test_caller("variable_b")
        assert "value_b" in chat
        assert "value_d" not in chat

    def test_not_initial_variables_are_preserved(self, test_caller):
        test_caller("start 2")
        test_caller("variable_d = 'value_d'")
        chat, log = test_caller("variable_d")
        assert "value_d" in chat
        test_caller("stop")
        test_caller("start 2")
        chat, log = test_caller("variable_d")
        assert "value_d" in chat