1"""Test query, coverage 93%).
2
3Non-gui tests for Query, SectionName, ModuleName, and HelpSource use
4dummy versions that extract the non-gui methods and add other needed
5attributes.  GUI tests create an instance of each class and simulate
6entries and button clicks.  Subclass tests only target the new code in
7the subclass definition.
8
9The appearance of the widgets is checked by the Query and
10HelpSource htests.  These are run by running query.py.
11"""
12from idlelib import query
13import unittest
14from test.support import requires
15from tkinter import Tk, END
16
17import sys
18from unittest import mock
19from idlelib.idle_test.mock_tk import Var
20
21
22# NON-GUI TESTS
23
24class QueryTest(unittest.TestCase):
25    "Test Query base class."
26
27    class Dummy_Query:
28        # Test the following Query methods.
29        entry_ok = query.Query.entry_ok
30        ok = query.Query.ok
31        cancel = query.Query.cancel
32        # Add attributes and initialization needed for tests.
33        def __init__(self, dummy_entry):
34            self.entry = Var(value=dummy_entry)
35            self.entry_error = {'text': ''}
36            self.result = None
37            self.destroyed = False
38        def showerror(self, message):
39            self.entry_error['text'] = message
40        def destroy(self):
41            self.destroyed = True
42
43    def test_entry_ok_blank(self):
44        dialog = self.Dummy_Query(' ')
45        self.assertEqual(dialog.entry_ok(), None)
46        self.assertEqual((dialog.result, dialog.destroyed), (None, False))
47        self.assertIn('blank line', dialog.entry_error['text'])
48
49    def test_entry_ok_good(self):
50        dialog = self.Dummy_Query('  good ')
51        Equal = self.assertEqual
52        Equal(dialog.entry_ok(), 'good')
53        Equal((dialog.result, dialog.destroyed), (None, False))
54        Equal(dialog.entry_error['text'], '')
55
56    def test_ok_blank(self):
57        dialog = self.Dummy_Query('')
58        dialog.entry.focus_set = mock.Mock()
59        self.assertEqual(dialog.ok(), None)
60        self.assertTrue(dialog.entry.focus_set.called)
61        del dialog.entry.focus_set
62        self.assertEqual((dialog.result, dialog.destroyed), (None, False))
63
64    def test_ok_good(self):
65        dialog = self.Dummy_Query('good')
66        self.assertEqual(dialog.ok(), None)
67        self.assertEqual((dialog.result, dialog.destroyed), ('good', True))
68
69    def test_cancel(self):
70        dialog = self.Dummy_Query('does not matter')
71        self.assertEqual(dialog.cancel(), None)
72        self.assertEqual((dialog.result, dialog.destroyed), (None, True))
73
74
75class SectionNameTest(unittest.TestCase):
76    "Test SectionName subclass of Query."
77
78    class Dummy_SectionName:
79        entry_ok = query.SectionName.entry_ok  # Function being tested.
80        used_names = ['used']
81        def __init__(self, dummy_entry):
82            self.entry = Var(value=dummy_entry)
83            self.entry_error = {'text': ''}
84        def showerror(self, message):
85            self.entry_error['text'] = message
86
87    def test_blank_section_name(self):
88        dialog = self.Dummy_SectionName(' ')
89        self.assertEqual(dialog.entry_ok(), None)
90        self.assertIn('no name', dialog.entry_error['text'])
91
92    def test_used_section_name(self):
93        dialog = self.Dummy_SectionName('used')
94        self.assertEqual(dialog.entry_ok(), None)
95        self.assertIn('use', dialog.entry_error['text'])
96
97    def test_long_section_name(self):
98        dialog = self.Dummy_SectionName('good'*8)
99        self.assertEqual(dialog.entry_ok(), None)
100        self.assertIn('longer than 30', dialog.entry_error['text'])
101
102    def test_good_section_name(self):
103        dialog = self.Dummy_SectionName('  good ')
104        self.assertEqual(dialog.entry_ok(), 'good')
105        self.assertEqual(dialog.entry_error['text'], '')
106
107
108class ModuleNameTest(unittest.TestCase):
109    "Test ModuleName subclass of Query."
110
111    class Dummy_ModuleName:
112        entry_ok = query.ModuleName.entry_ok  # Function being tested.
113        text0 = ''
114        def __init__(self, dummy_entry):
115            self.entry = Var(value=dummy_entry)
116            self.entry_error = {'text': ''}
117        def showerror(self, message):
118            self.entry_error['text'] = message
119
120    def test_blank_module_name(self):
121        dialog = self.Dummy_ModuleName(' ')
122        self.assertEqual(dialog.entry_ok(), None)
123        self.assertIn('no name', dialog.entry_error['text'])
124
125    def test_bogus_module_name(self):
126        dialog = self.Dummy_ModuleName('__name_xyz123_should_not_exist__')
127        self.assertEqual(dialog.entry_ok(), None)
128        self.assertIn('not found', dialog.entry_error['text'])
129
130    def test_c_source_name(self):
131        dialog = self.Dummy_ModuleName('itertools')
132        self.assertEqual(dialog.entry_ok(), None)
133        self.assertIn('source-based', dialog.entry_error['text'])
134
135    def test_good_module_name(self):
136        dialog = self.Dummy_ModuleName('idlelib')
137        self.assertTrue(dialog.entry_ok().endswith('__init__.py'))
138        self.assertEqual(dialog.entry_error['text'], '')
139        dialog = self.Dummy_ModuleName('os.path')
140        self.assertTrue(dialog.entry_ok().endswith('path.py'))
141        self.assertEqual(dialog.entry_error['text'], '')
142
143
144class GotoTest(unittest.TestCase):
145    "Test Goto subclass of Query."
146
147    class Dummy_ModuleName:
148        entry_ok = query.Goto.entry_ok  # Function being tested.
149        def __init__(self, dummy_entry):
150            self.entry = Var(value=dummy_entry)
151            self.entry_error = {'text': ''}
152        def showerror(self, message):
153            self.entry_error['text'] = message
154
155    def test_bogus_goto(self):
156        dialog = self.Dummy_ModuleName('a')
157        self.assertEqual(dialog.entry_ok(), None)
158        self.assertIn('not a base 10 integer', dialog.entry_error['text'])
159
160    def test_bad_goto(self):
161        dialog = self.Dummy_ModuleName('0')
162        self.assertEqual(dialog.entry_ok(), None)
163        self.assertIn('not a positive integer', dialog.entry_error['text'])
164
165    def test_good_goto(self):
166        dialog = self.Dummy_ModuleName('1')
167        self.assertEqual(dialog.entry_ok(), 1)
168        self.assertEqual(dialog.entry_error['text'], '')
169
170
171# 3 HelpSource test classes each test one method.
172
173class HelpsourceBrowsefileTest(unittest.TestCase):
174    "Test browse_file method of ModuleName subclass of Query."
175
176    class Dummy_HelpSource:
177        browse_file = query.HelpSource.browse_file
178        pathvar = Var()
179
180    def test_file_replaces_path(self):
181        dialog = self.Dummy_HelpSource()
182        # Path is widget entry, either '' or something.
183        # Func return is file dialog return, either '' or something.
184        # Func return should override widget entry.
185        # We need all 4 combinations to test all (most) code paths.
186        for path, func, result in (
187                ('', lambda a,b,c:'', ''),
188                ('', lambda a,b,c: __file__, __file__),
189                ('htest', lambda a,b,c:'', 'htest'),
190                ('htest', lambda a,b,c: __file__, __file__)):
191            with self.subTest():
192                dialog.pathvar.set(path)
193                dialog.askfilename = func
194                dialog.browse_file()
195                self.assertEqual(dialog.pathvar.get(), result)
196
197
198class HelpsourcePathokTest(unittest.TestCase):
199    "Test path_ok method of HelpSource subclass of Query."
200
201    class Dummy_HelpSource:
202        path_ok = query.HelpSource.path_ok
203        def __init__(self, dummy_path):
204            self.path = Var(value=dummy_path)
205            self.path_error = {'text': ''}
206        def showerror(self, message, widget=None):
207            self.path_error['text'] = message
208
209    orig_platform = query.platform  # Set in test_path_ok_file.
210    @classmethod
211    def tearDownClass(cls):
212        query.platform = cls.orig_platform
213
214    def test_path_ok_blank(self):
215        dialog = self.Dummy_HelpSource(' ')
216        self.assertEqual(dialog.path_ok(), None)
217        self.assertIn('no help file', dialog.path_error['text'])
218
219    def test_path_ok_bad(self):
220        dialog = self.Dummy_HelpSource(__file__ + 'bad-bad-bad')
221        self.assertEqual(dialog.path_ok(), None)
222        self.assertIn('not exist', dialog.path_error['text'])
223
224    def test_path_ok_web(self):
225        dialog = self.Dummy_HelpSource('')
226        Equal = self.assertEqual
227        for url in 'www.py.org', 'http://py.org':
228            with self.subTest():
229                dialog.path.set(url)
230                self.assertEqual(dialog.path_ok(), url)
231                self.assertEqual(dialog.path_error['text'], '')
232
233    def test_path_ok_file(self):
234        dialog = self.Dummy_HelpSource('')
235        for platform, prefix in ('darwin', 'file://'), ('other', ''):
236            with self.subTest():
237                query.platform = platform
238                dialog.path.set(__file__)
239                self.assertEqual(dialog.path_ok(), prefix + __file__)
240                self.assertEqual(dialog.path_error['text'], '')
241
242
243class HelpsourceEntryokTest(unittest.TestCase):
244    "Test entry_ok method of HelpSource subclass of Query."
245
246    class Dummy_HelpSource:
247        entry_ok = query.HelpSource.entry_ok
248        entry_error = {}
249        path_error = {}
250        def item_ok(self):
251            return self.name
252        def path_ok(self):
253            return self.path
254
255    def test_entry_ok_helpsource(self):
256        dialog = self.Dummy_HelpSource()
257        for name, path, result in ((None, None, None),
258                                   (None, 'doc.txt', None),
259                                   ('doc', None, None),
260                                   ('doc', 'doc.txt', ('doc', 'doc.txt'))):
261            with self.subTest():
262                dialog.name, dialog.path = name, path
263                self.assertEqual(dialog.entry_ok(), result)
264
265
266# 2 CustomRun test classes each test one method.
267
268class CustomRunCLIargsokTest(unittest.TestCase):
269    "Test cli_ok method of the CustomRun subclass of Query."
270
271    class Dummy_CustomRun:
272        cli_args_ok = query.CustomRun.cli_args_ok
273        def __init__(self, dummy_entry):
274            self.entry = Var(value=dummy_entry)
275            self.entry_error = {'text': ''}
276        def showerror(self, message):
277            self.entry_error['text'] = message
278
279    def test_blank_args(self):
280        dialog = self.Dummy_CustomRun(' ')
281        self.assertEqual(dialog.cli_args_ok(), [])
282
283    def test_invalid_args(self):
284        dialog = self.Dummy_CustomRun("'no-closing-quote")
285        self.assertEqual(dialog.cli_args_ok(), None)
286        self.assertIn('No closing', dialog.entry_error['text'])
287
288    def test_good_args(self):
289        args = ['-n', '10', '--verbose', '-p', '/path', '--name']
290        dialog = self.Dummy_CustomRun(' '.join(args) + ' "my name"')
291        self.assertEqual(dialog.cli_args_ok(), args + ["my name"])
292        self.assertEqual(dialog.entry_error['text'], '')
293
294
295class CustomRunEntryokTest(unittest.TestCase):
296    "Test entry_ok method of the CustomRun subclass of Query."
297
298    class Dummy_CustomRun:
299        entry_ok = query.CustomRun.entry_ok
300        entry_error = {}
301        restartvar = Var()
302        def cli_args_ok(self):
303            return self.cli_args
304
305    def test_entry_ok_customrun(self):
306        dialog = self.Dummy_CustomRun()
307        for restart in {True, False}:
308            dialog.restartvar.set(restart)
309            for cli_args, result in ((None, None),
310                                     (['my arg'], (['my arg'], restart))):
311                with self.subTest(restart=restart, cli_args=cli_args):
312                    dialog.cli_args = cli_args
313                    self.assertEqual(dialog.entry_ok(), result)
314
315
316# GUI TESTS
317
318class QueryGuiTest(unittest.TestCase):
319
320    @classmethod
321    def setUpClass(cls):
322        requires('gui')
323        cls.root = root = Tk()
324        cls.root.withdraw()
325        cls.dialog = query.Query(root, 'TEST', 'test', _utest=True)
326        cls.dialog.destroy = mock.Mock()
327
328    @classmethod
329    def tearDownClass(cls):
330        del cls.dialog.destroy
331        del cls.dialog
332        cls.root.destroy()
333        del cls.root
334
335    def setUp(self):
336        self.dialog.entry.delete(0, 'end')
337        self.dialog.result = None
338        self.dialog.destroy.reset_mock()
339
340    def test_click_ok(self):
341        dialog = self.dialog
342        dialog.entry.insert(0, 'abc')
343        dialog.button_ok.invoke()
344        self.assertEqual(dialog.result, 'abc')
345        self.assertTrue(dialog.destroy.called)
346
347    def test_click_blank(self):
348        dialog = self.dialog
349        dialog.button_ok.invoke()
350        self.assertEqual(dialog.result, None)
351        self.assertFalse(dialog.destroy.called)
352
353    def test_click_cancel(self):
354        dialog = self.dialog
355        dialog.entry.insert(0, 'abc')
356        dialog.button_cancel.invoke()
357        self.assertEqual(dialog.result, None)
358        self.assertTrue(dialog.destroy.called)
359
360
361class SectionnameGuiTest(unittest.TestCase):
362
363    @classmethod
364    def setUpClass(cls):
365        requires('gui')
366
367    def test_click_section_name(self):
368        root = Tk()
369        root.withdraw()
370        dialog =  query.SectionName(root, 'T', 't', {'abc'}, _utest=True)
371        Equal = self.assertEqual
372        self.assertEqual(dialog.used_names, {'abc'})
373        dialog.entry.insert(0, 'okay')
374        dialog.button_ok.invoke()
375        self.assertEqual(dialog.result, 'okay')
376        root.destroy()
377
378
379class ModulenameGuiTest(unittest.TestCase):
380
381    @classmethod
382    def setUpClass(cls):
383        requires('gui')
384
385    def test_click_module_name(self):
386        root = Tk()
387        root.withdraw()
388        dialog =  query.ModuleName(root, 'T', 't', 'idlelib', _utest=True)
389        self.assertEqual(dialog.text0, 'idlelib')
390        self.assertEqual(dialog.entry.get(), 'idlelib')
391        dialog.button_ok.invoke()
392        self.assertTrue(dialog.result.endswith('__init__.py'))
393        root.destroy()
394
395
396class GotoGuiTest(unittest.TestCase):
397
398    @classmethod
399    def setUpClass(cls):
400        requires('gui')
401
402    def test_click_module_name(self):
403        root = Tk()
404        root.withdraw()
405        dialog =  query.Goto(root, 'T', 't', _utest=True)
406        dialog.entry.insert(0, '22')
407        dialog.button_ok.invoke()
408        self.assertEqual(dialog.result, 22)
409        root.destroy()
410
411
412class HelpsourceGuiTest(unittest.TestCase):
413
414    @classmethod
415    def setUpClass(cls):
416        requires('gui')
417
418    def test_click_help_source(self):
419        root = Tk()
420        root.withdraw()
421        dialog =  query.HelpSource(root, 'T', menuitem='__test__',
422                                   filepath=__file__, _utest=True)
423        Equal = self.assertEqual
424        Equal(dialog.entry.get(), '__test__')
425        Equal(dialog.path.get(), __file__)
426        dialog.button_ok.invoke()
427        prefix = "file://" if sys.platform == 'darwin' else ''
428        Equal(dialog.result, ('__test__', prefix + __file__))
429        root.destroy()
430
431
432class CustomRunGuiTest(unittest.TestCase):
433
434    @classmethod
435    def setUpClass(cls):
436        requires('gui')
437
438    def test_click_args(self):
439        root = Tk()
440        root.withdraw()
441        dialog =  query.CustomRun(root, 'Title',
442                                  cli_args=['a', 'b=1'], _utest=True)
443        self.assertEqual(dialog.entry.get(), 'a b=1')
444        dialog.entry.insert(END, ' c')
445        dialog.button_ok.invoke()
446        self.assertEqual(dialog.result, (['a', 'b=1', 'c'], True))
447        root.destroy()
448
449
450if __name__ == '__main__':
451    unittest.main(verbosity=2, exit=False)
452