Coverage for core\test_leoserver.py: 100%
91 statements
« prev ^ index » next coverage.py v6.4, created at 2022-05-24 10:21 -0500
« prev ^ index » next coverage.py v6.4, created at 2022-05-24 10:21 -0500
1#@+leo-ver=5-thin
2#@+node:ekr.20210820203000.1: * @file ../unittests/core/test_leoserver.py
3"""Unit tests for leo/core/leoserver.py"""
5import json
6import os
7import leo.core.leoserver as leoserver
8from leo.core.leoTest2 import LeoUnitTest
10# Globals.
11g = None
12g_leoserver = None
13g_server = None
15#@+others
16#@+node:ekr.20210901070918.1: ** class TestLeoServer(LeoUnitTest)
17class TestLeoServer(LeoUnitTest):
18 """Tests of LeoServer class."""
19 request_number = 0
20 #@+others
21 #@+node:felix.20210621233316.99: *3* TestLeoServer: Setup and TearDown
22 @classmethod
23 def setUpClass(cls):
24 # Assume we are running in the leo-editor directory.
25 # pylint: disable=import-self
26 global g, g_leoserver, g_server
27 g_leoserver = leoserver
28 g_server = leoserver.LeoServer(testing=True)
29 g = g_server.g
30 assert g
32 @classmethod
33 def tearDownClass(cls):
34 global g_leoserver, g_server
35 try:
36 g_server.shut_down({})
37 print('===== server did not terminate properly ====') # pragma:no cover
38 except g_leoserver.TerminateServer:
39 pass
40 except leoserver.ServerError: # pragma:no cover
41 pass
43 def setUp(self):
44 global g_server
45 self.server = g_server
46 g.unitTesting = True
48 def tearDown(self):
49 g.unitTesting = False
51 #@+node:felix.20210621233316.100: *3* TestLeoServer._request
52 def _request(self, action, param=None):
53 server = self.server
54 self.request_number += 1
55 log_flag = param.get("log")
56 # Direct server commands require an exclamation mark '!' prefix
57 # to distinguish them from Leo's commander's own methods.
58 d = {
59 "action": action,
60 "id": self.request_number
61 }
62 if param:
63 d["param"] = param
64 response = server._do_message(d)
65 # _make_response calls json_dumps. Undo it with json.loads.
66 answer = json.loads(response)
67 if log_flag:
68 g.printObj(answer, tag=f"response to {action!r}") # pragma: no cover
69 return answer
70 #@+node:felix.20210621233316.102: *3* TestLeoServer.test_most_public_server_methods
71 def test_most_public_server_methods(self):
72 server = self.server
73 tag = 'test_most_public_server_methods'
74 assert isinstance(server, g_leoserver.LeoServer), self.server
75 test_dot_leo = g.os_path_finalize_join(g.app.loadDir, '..', 'test', 'test.leo')
76 assert os.path.exists(test_dot_leo), repr(test_dot_leo)
77 methods = server._get_all_server_commands()
78 # Ensure that some methods happen at the end.
79 for z in ('toggle_mark', 'undo', 'redo'):
80 methods.remove(z)
81 for z in ('toggle_mark', 'toggle_mark', 'undo', 'redo'):
82 methods.append(z)
83 # g.printObj(methods, tag=methods)
84 exclude = [
85 # Find methods...
86 'change_all', 'change_then_find',
87 'clone_find_all', 'clone_find_all_flattened', 'clone_find_tag',
88 'find_all', 'find_def', 'find_next', 'find_previous', 'find_var',
89 'goto_script',
90 'tag_children',
91 # Other methods
92 'delete_node', 'cut_node', # dangerous.
93 'click_button', 'get_buttons', 'remove_button', # Require plugins.
94 'paste_node', 'paste_as_clone_node', # New exclusion.
95 'save_file', # way too dangerous!
96 # 'set_selection', # Not ready yet.
97 'open_file', 'close_file', # Done by hand.
98 'import_any_file',
99 'insert_child_named_node',
100 'insert_named_node',
101 'set_ask_result',
102 'set_opened_file',
103 'set_search_settings',
104 'shut_down', # Don't shut down the server.
105 ]
106 expected = ['error']
107 param_d = {
108 "remove_tag": {"tag": "testTag"},
109 "tag_node": {"tag": "testTag"},
110 # "apply_config": {"config": {"whatever": True}},
111 "get_focus": {"log": False},
112 "set_body": {"body": "new body\n", 'gnx': "ekr.20061008140603"},
113 "set_headline": {"name": "new headline"},
114 "get_all_server_commands": {"log": False},
115 "get_all_leo_commands": {"log": False},
116 # "paste_node": {"name", "paste-node-name"},
117 # "paste_as_clone_node": {"name", "paste-node-name"},
118 }
119 # First open a test file & performa all tests.
120 server.open_file({"filename": test_dot_leo}) # A real file.
121 try:
122 id_ = 0
123 for method_name in methods:
124 id_ += 1
125 if method_name not in exclude:
126 assert getattr(server, method_name), method_name
127 param = param_d.get(method_name, {})
128 message = {
129 "id": id_,
130 "action": "!" + method_name,
131 "param": param,
132 }
133 try:
134 # Don't call the method directly.
135 # That would disable trace/verbose logic, checking, etc.
136 server._do_message(message)
137 except Exception as e:
138 if method_name not in expected:
139 print(f"Exception in {tag}: {method_name!r} {e}") # pragma:no cover
140 finally:
141 server.close_file({"forced": True})
142 #@+node:felix.20210621233316.103: *3* TestLeoServer.test_open_and_close
143 def test_open_and_close(self):
144 # server = self.server
145 test_dot_leo = g.os_path_finalize_join(g.app.loadDir, '..', 'test', 'test.leo')
146 assert os.path.exists(test_dot_leo), repr(test_dot_leo)
147 log = False
148 table = [
149 # Open file.
150 ("!open_file", {"log": log, "filename": "xyzzy.leo"}), # Does not exist.
151 # Switch to the second file.
152 ("!open_file", {"log": log, "filename": test_dot_leo}), # Does exist.
153 # Open again. This should be valid.
154 ("!open_file", {"log": False, "filename": test_dot_leo}),
155 # Better test of _ap_to_p.
156 ("!set_current_position", {
157 "ap": {
158 "gnx": "ekr.20180311131424.1", # Recent
159 "childIndex": 1,
160 "stack": [],
161 }
162 }),
163 ("!get_ua", {"log": log}),
164 # Close the second file.
165 ("!close_file", {"log": log, "forced": True}),
166 # Close the first file.
167 ("!close_file", {"log": log, "forced": True}),
168 ]
169 for action, package in table:
170 self._request(action, package)
171 #@+node:felix.20210621233316.104: *3* TestLeoServer.test_find_commands
172 def test_find_commands(self):
174 tag = 'test_find_commands'
175 test_dot_leo = g.os_path_finalize_join(g.app.loadDir, '..', 'test', 'test.leo')
176 assert os.path.exists(test_dot_leo), repr(test_dot_leo)
177 log = False
178 # Open the file & create the StringFindTabManager.
179 self._request("!open_file", {"log": False, "filename": test_dot_leo})
180 #
181 # Batch find commands: The answer is a count of found nodes.
182 for method in ('!find_all', '!clone_find_all', '!clone_find_all_flattened'):
183 answer = self._request(method, {"log": log, "find_text": "def"})
184 if log:
185 g.printObj(answer, tag=f"{tag}:{method}: answer") # pragma: no cover
186 #
187 # Find commands that may select text: The answer is (p, pos, newpos).
188 for method in ('!find_next', '!find_previous', '!find_def', '!find_var'):
189 answer = self._request(method, {"log": log, "find_text": "def"})
190 if log:
191 g.printObj(answer, tag=f"{tag}:{method}: answer") # pragma: no cover
192 #
193 # Change commands: The answer is a count of changed nodes.
194 for method in ('!replace_all', '!replace_then_find'):
195 answer = self._request(method, {"log": log, "find_text": "def", "change_text": "DEF"})
196 if log:
197 g.printObj(answer, tag=f"{tag}:{method}: answer") # pragma: no cover
198 #
199 # Tag commands. Why they are in leoFind.py??
200 for method in ('!clone_find_tag', '!tag_children'):
201 answer = self._request(method, {"log": log, "tag": "my-tag"})
202 if log:
203 g.printObj(answer, tag=f"{tag}:{method}: answer") # pragma: no cover
205 #@-others
206#@-others
208#@-leo