| 
									
										
										
										
											2018-06-15 18:20:55 -04:00
										 |  |  | "Test codecontext, coverage 100%" | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-15 18:20:55 -04:00
										 |  |  | from idlelib import codecontext | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | import unittest | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  | import unittest.mock | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | from test.support import requires | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  | from tkinter import NSEW, Tk, Frame, Text, TclError | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-19 19:12:52 -04:00
										 |  |  | from unittest import mock | 
					
						
							| 
									
										
										
										
											2018-06-15 18:20:55 -04:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | from idlelib import config | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | usercfg = codecontext.idleConf.userCfg | 
					
						
							|  |  |  | testcfg = { | 
					
						
							|  |  |  |     'main': config.IdleUserConfParser(''), | 
					
						
							|  |  |  |     'highlight': config.IdleUserConfParser(''), | 
					
						
							|  |  |  |     'keys': config.IdleUserConfParser(''), | 
					
						
							|  |  |  |     'extensions': config.IdleUserConfParser(''), | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | code_sample = """\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class C1(): | 
					
						
							|  |  |  |     # Class comment. | 
					
						
							|  |  |  |     def __init__(self, a, b): | 
					
						
							|  |  |  |         self.a = a | 
					
						
							|  |  |  |         self.b = b | 
					
						
							|  |  |  |     def compare(self): | 
					
						
							|  |  |  |         if a > b: | 
					
						
							|  |  |  |             return a | 
					
						
							|  |  |  |         elif a < b: | 
					
						
							|  |  |  |             return b | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DummyEditwin: | 
					
						
							|  |  |  |     def __init__(self, root, frame, text): | 
					
						
							|  |  |  |         self.root = root | 
					
						
							|  |  |  |         self.top = root | 
					
						
							|  |  |  |         self.text_frame = frame | 
					
						
							|  |  |  |         self.text = text | 
					
						
							| 
									
										
										
										
											2018-12-22 01:25:45 -05:00
										 |  |  |         self.label = '' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |     def getlineno(self, index): | 
					
						
							|  |  |  |         return int(float(self.text.index(index))) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-22 01:25:45 -05:00
										 |  |  |     def update_menu_label(self, **kwargs): | 
					
						
							|  |  |  |         self.label = kwargs['label'] | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class CodeContextTest(unittest.TestCase): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def setUpClass(cls): | 
					
						
							|  |  |  |         requires('gui') | 
					
						
							|  |  |  |         root = cls.root = Tk() | 
					
						
							|  |  |  |         root.withdraw() | 
					
						
							|  |  |  |         frame = cls.frame = Frame(root) | 
					
						
							|  |  |  |         text = cls.text = Text(frame) | 
					
						
							|  |  |  |         text.insert('1.0', code_sample) | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         # Need to pack for creation of code context text widget. | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |         frame.pack(side='left', fill='both', expand=1) | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         text.grid(row=1, column=1, sticky=NSEW) | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |         cls.editor = DummyEditwin(root, frame, text) | 
					
						
							|  |  |  |         codecontext.idleConf.userCfg = testcfg | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def tearDownClass(cls): | 
					
						
							|  |  |  |         codecontext.idleConf.userCfg = usercfg | 
					
						
							|  |  |  |         cls.editor.text.delete('1.0', 'end') | 
					
						
							|  |  |  |         del cls.editor, cls.frame, cls.text | 
					
						
							|  |  |  |         cls.root.update_idletasks() | 
					
						
							|  |  |  |         cls.root.destroy() | 
					
						
							|  |  |  |         del cls.root | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							| 
									
										
										
										
											2018-06-08 01:21:15 -04:00
										 |  |  |         self.text.yview(0) | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         self.text['font'] = 'TkFixedFont' | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |         self.cc = codecontext.CodeContext(self.editor) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |         self.highlight_cfg = {"background": '#abcdef', | 
					
						
							|  |  |  |                               "foreground": '#123456'} | 
					
						
							|  |  |  |         orig_idleConf_GetHighlight = codecontext.idleConf.GetHighlight | 
					
						
							|  |  |  |         def mock_idleconf_GetHighlight(theme, element): | 
					
						
							|  |  |  |             if element == 'context': | 
					
						
							|  |  |  |                 return self.highlight_cfg | 
					
						
							|  |  |  |             return orig_idleConf_GetHighlight(theme, element) | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         GetHighlight_patcher = unittest.mock.patch.object( | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |             codecontext.idleConf, 'GetHighlight', mock_idleconf_GetHighlight) | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         GetHighlight_patcher.start() | 
					
						
							|  |  |  |         self.addCleanup(GetHighlight_patcher.stop) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.font_override = 'TkFixedFont' | 
					
						
							|  |  |  |         def mock_idleconf_GetFont(root, configType, section): | 
					
						
							|  |  |  |             return self.font_override | 
					
						
							|  |  |  |         GetFont_patcher = unittest.mock.patch.object( | 
					
						
							|  |  |  |             codecontext.idleConf, 'GetFont', mock_idleconf_GetFont) | 
					
						
							|  |  |  |         GetFont_patcher.start() | 
					
						
							|  |  |  |         self.addCleanup(GetFont_patcher.stop) | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |     def tearDown(self): | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         if self.cc.context: | 
					
						
							|  |  |  |             self.cc.context.destroy() | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |         # Explicitly call __del__ to remove scheduled scripts. | 
					
						
							|  |  |  |         self.cc.__del__() | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         del self.cc.context, self.cc | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_init(self): | 
					
						
							|  |  |  |         eq = self.assertEqual | 
					
						
							|  |  |  |         ed = self.editor | 
					
						
							|  |  |  |         cc = self.cc | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         eq(cc.editwin, ed) | 
					
						
							|  |  |  |         eq(cc.text, ed.text) | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |         eq(cc.text['font'], ed.text['font']) | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         self.assertIsNone(cc.context) | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |         eq(cc.info, [(0, -1, '', False)]) | 
					
						
							|  |  |  |         eq(cc.topvisible, 1) | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |         self.assertIsNone(self.cc.t1) | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_del(self): | 
					
						
							|  |  |  |         self.cc.__del__() | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_del_with_timer(self): | 
					
						
							|  |  |  |         timer = self.cc.t1 = self.text.after(10000, lambda: None) | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |         self.cc.__del__() | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |         with self.assertRaises(TclError) as cm: | 
					
						
							|  |  |  |             self.root.tk.call('after', 'info', timer) | 
					
						
							|  |  |  |         self.assertIn("doesn't exist", str(cm.exception)) | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_reload(self): | 
					
						
							|  |  |  |         codecontext.CodeContext.reload() | 
					
						
							| 
									
										
										
										
											2018-06-01 19:23:00 -04:00
										 |  |  |         self.assertEqual(self.cc.context_depth, 15) | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_toggle_code_context_event(self): | 
					
						
							|  |  |  |         eq = self.assertEqual | 
					
						
							|  |  |  |         cc = self.cc | 
					
						
							|  |  |  |         toggle = cc.toggle_code_context_event | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Make sure code context is off. | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         if cc.context: | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |             toggle() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Toggle on. | 
					
						
							| 
									
										
										
										
											2019-07-18 23:03:18 +03:00
										 |  |  |         toggle() | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         self.assertIsNotNone(cc.context) | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |         eq(cc.context['font'], self.text['font']) | 
					
						
							|  |  |  |         eq(cc.context['fg'], self.highlight_cfg['foreground']) | 
					
						
							|  |  |  |         eq(cc.context['bg'], self.highlight_cfg['background']) | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         eq(cc.context.get('1.0', 'end-1c'), '') | 
					
						
							| 
									
										
										
										
											2018-12-22 01:25:45 -05:00
										 |  |  |         eq(cc.editwin.label, 'Hide Code Context') | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |         eq(self.root.tk.call('after', 'info', self.cc.t1)[1], 'timer') | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Toggle off. | 
					
						
							| 
									
										
										
										
											2019-07-18 23:03:18 +03:00
										 |  |  |         toggle() | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         self.assertIsNone(cc.context) | 
					
						
							| 
									
										
										
										
											2018-12-22 01:25:45 -05:00
										 |  |  |         eq(cc.editwin.label, 'Show Code Context') | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |         self.assertIsNone(self.cc.t1) | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-18 23:03:18 +03:00
										 |  |  |         # Scroll down and toggle back on. | 
					
						
							|  |  |  |         line11_context = '\n'.join(x[2] for x in cc.get_context(11)[0]) | 
					
						
							|  |  |  |         cc.text.yview(11) | 
					
						
							|  |  |  |         toggle() | 
					
						
							|  |  |  |         eq(cc.context.get('1.0', 'end-1c'), line11_context) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Toggle off and on again. | 
					
						
							|  |  |  |         toggle() | 
					
						
							|  |  |  |         toggle() | 
					
						
							|  |  |  |         eq(cc.context.get('1.0', 'end-1c'), line11_context) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |     def test_get_context(self): | 
					
						
							|  |  |  |         eq = self.assertEqual | 
					
						
							|  |  |  |         gc = self.cc.get_context | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # stopline must be greater than 0. | 
					
						
							|  |  |  |         with self.assertRaises(AssertionError): | 
					
						
							|  |  |  |             gc(1, stopline=0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         eq(gc(3), ([(2, 0, 'class C1():', 'class')], 0)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Don't return comment. | 
					
						
							|  |  |  |         eq(gc(4), ([(2, 0, 'class C1():', 'class')], 0)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Two indentation levels and no comment. | 
					
						
							|  |  |  |         eq(gc(5), ([(2, 0, 'class C1():', 'class'), | 
					
						
							|  |  |  |                     (4, 4, '    def __init__(self, a, b):', 'def')], 0)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Only one 'def' is returned, not both at the same indent level. | 
					
						
							|  |  |  |         eq(gc(10), ([(2, 0, 'class C1():', 'class'), | 
					
						
							|  |  |  |                      (7, 4, '    def compare(self):', 'def'), | 
					
						
							|  |  |  |                      (8, 8, '        if a > b:', 'if')], 0)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # With 'elif', also show the 'if' even though it's at the same level. | 
					
						
							|  |  |  |         eq(gc(11), ([(2, 0, 'class C1():', 'class'), | 
					
						
							|  |  |  |                      (7, 4, '    def compare(self):', 'def'), | 
					
						
							|  |  |  |                      (8, 8, '        if a > b:', 'if'), | 
					
						
							|  |  |  |                      (10, 8, '        elif a < b:', 'elif')], 0)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Set stop_line to not go back to first line in source code. | 
					
						
							|  |  |  |         # Return includes stop_line. | 
					
						
							|  |  |  |         eq(gc(11, stopline=2), ([(2, 0, 'class C1():', 'class'), | 
					
						
							|  |  |  |                                  (7, 4, '    def compare(self):', 'def'), | 
					
						
							|  |  |  |                                  (8, 8, '        if a > b:', 'if'), | 
					
						
							|  |  |  |                                  (10, 8, '        elif a < b:', 'elif')], 0)) | 
					
						
							|  |  |  |         eq(gc(11, stopline=3), ([(7, 4, '    def compare(self):', 'def'), | 
					
						
							|  |  |  |                                  (8, 8, '        if a > b:', 'if'), | 
					
						
							|  |  |  |                                  (10, 8, '        elif a < b:', 'elif')], 4)) | 
					
						
							|  |  |  |         eq(gc(11, stopline=8), ([(8, 8, '        if a > b:', 'if'), | 
					
						
							|  |  |  |                                  (10, 8, '        elif a < b:', 'elif')], 8)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Set stop_indent to test indent level to stop at. | 
					
						
							|  |  |  |         eq(gc(11, stopindent=4), ([(7, 4, '    def compare(self):', 'def'), | 
					
						
							|  |  |  |                                    (8, 8, '        if a > b:', 'if'), | 
					
						
							|  |  |  |                                    (10, 8, '        elif a < b:', 'elif')], 4)) | 
					
						
							|  |  |  |         # Check that the 'if' is included. | 
					
						
							|  |  |  |         eq(gc(11, stopindent=8), ([(8, 8, '        if a > b:', 'if'), | 
					
						
							|  |  |  |                                    (10, 8, '        elif a < b:', 'elif')], 8)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_update_code_context(self): | 
					
						
							|  |  |  |         eq = self.assertEqual | 
					
						
							|  |  |  |         cc = self.cc | 
					
						
							|  |  |  |         # Ensure code context is active. | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         if not cc.context: | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |             cc.toggle_code_context_event() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Invoke update_code_context without scrolling - nothing happens. | 
					
						
							|  |  |  |         self.assertIsNone(cc.update_code_context()) | 
					
						
							|  |  |  |         eq(cc.info, [(0, -1, '', False)]) | 
					
						
							|  |  |  |         eq(cc.topvisible, 1) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-01 19:23:00 -04:00
										 |  |  |         # Scroll down to line 1. | 
					
						
							|  |  |  |         cc.text.yview(1) | 
					
						
							|  |  |  |         cc.update_code_context() | 
					
						
							|  |  |  |         eq(cc.info, [(0, -1, '', False)]) | 
					
						
							|  |  |  |         eq(cc.topvisible, 2) | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         eq(cc.context.get('1.0', 'end-1c'), '') | 
					
						
							| 
									
										
										
										
											2018-06-01 19:23:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |         # Scroll down to line 2. | 
					
						
							|  |  |  |         cc.text.yview(2) | 
					
						
							|  |  |  |         cc.update_code_context() | 
					
						
							|  |  |  |         eq(cc.info, [(0, -1, '', False), (2, 0, 'class C1():', 'class')]) | 
					
						
							|  |  |  |         eq(cc.topvisible, 3) | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         eq(cc.context.get('1.0', 'end-1c'), 'class C1():') | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Scroll down to line 3.  Since it's a comment, nothing changes. | 
					
						
							|  |  |  |         cc.text.yview(3) | 
					
						
							|  |  |  |         cc.update_code_context() | 
					
						
							|  |  |  |         eq(cc.info, [(0, -1, '', False), (2, 0, 'class C1():', 'class')]) | 
					
						
							|  |  |  |         eq(cc.topvisible, 4) | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         eq(cc.context.get('1.0', 'end-1c'), 'class C1():') | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Scroll down to line 4. | 
					
						
							|  |  |  |         cc.text.yview(4) | 
					
						
							|  |  |  |         cc.update_code_context() | 
					
						
							|  |  |  |         eq(cc.info, [(0, -1, '', False), | 
					
						
							|  |  |  |                      (2, 0, 'class C1():', 'class'), | 
					
						
							|  |  |  |                      (4, 4, '    def __init__(self, a, b):', 'def')]) | 
					
						
							|  |  |  |         eq(cc.topvisible, 5) | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n' | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |                                             '    def __init__(self, a, b):') | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Scroll down to line 11.  Last 'def' is removed. | 
					
						
							|  |  |  |         cc.text.yview(11) | 
					
						
							|  |  |  |         cc.update_code_context() | 
					
						
							|  |  |  |         eq(cc.info, [(0, -1, '', False), | 
					
						
							|  |  |  |                      (2, 0, 'class C1():', 'class'), | 
					
						
							|  |  |  |                      (7, 4, '    def compare(self):', 'def'), | 
					
						
							|  |  |  |                      (8, 8, '        if a > b:', 'if'), | 
					
						
							|  |  |  |                      (10, 8, '        elif a < b:', 'elif')]) | 
					
						
							|  |  |  |         eq(cc.topvisible, 12) | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n' | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |                                             '    def compare(self):\n' | 
					
						
							|  |  |  |                                             '        if a > b:\n' | 
					
						
							|  |  |  |                                             '        elif a < b:') | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # No scroll.  No update, even though context_depth changed. | 
					
						
							|  |  |  |         cc.update_code_context() | 
					
						
							|  |  |  |         cc.context_depth = 1 | 
					
						
							|  |  |  |         eq(cc.info, [(0, -1, '', False), | 
					
						
							|  |  |  |                      (2, 0, 'class C1():', 'class'), | 
					
						
							|  |  |  |                      (7, 4, '    def compare(self):', 'def'), | 
					
						
							|  |  |  |                      (8, 8, '        if a > b:', 'if'), | 
					
						
							|  |  |  |                      (10, 8, '        elif a < b:', 'elif')]) | 
					
						
							|  |  |  |         eq(cc.topvisible, 12) | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         eq(cc.context.get('1.0', 'end-1c'), 'class C1():\n' | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |                                             '    def compare(self):\n' | 
					
						
							|  |  |  |                                             '        if a > b:\n' | 
					
						
							|  |  |  |                                             '        elif a < b:') | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Scroll up. | 
					
						
							|  |  |  |         cc.text.yview(5) | 
					
						
							|  |  |  |         cc.update_code_context() | 
					
						
							|  |  |  |         eq(cc.info, [(0, -1, '', False), | 
					
						
							|  |  |  |                      (2, 0, 'class C1():', 'class'), | 
					
						
							|  |  |  |                      (4, 4, '    def __init__(self, a, b):', 'def')]) | 
					
						
							|  |  |  |         eq(cc.topvisible, 6) | 
					
						
							|  |  |  |         # context_depth is 1. | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         eq(cc.context.get('1.0', 'end-1c'), '    def __init__(self, a, b):') | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-08 01:21:15 -04:00
										 |  |  |     def test_jumptoline(self): | 
					
						
							|  |  |  |         eq = self.assertEqual | 
					
						
							|  |  |  |         cc = self.cc | 
					
						
							|  |  |  |         jump = cc.jumptoline | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not cc.context: | 
					
						
							|  |  |  |             cc.toggle_code_context_event() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Empty context. | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |         cc.text.yview('2.0') | 
					
						
							| 
									
										
										
										
											2018-06-08 01:21:15 -04:00
										 |  |  |         cc.update_code_context() | 
					
						
							|  |  |  |         eq(cc.topvisible, 2) | 
					
						
							|  |  |  |         cc.context.mark_set('insert', '1.5') | 
					
						
							|  |  |  |         jump() | 
					
						
							|  |  |  |         eq(cc.topvisible, 1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # 4 lines of context showing. | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |         cc.text.yview('12.0') | 
					
						
							| 
									
										
										
										
											2018-06-08 01:21:15 -04:00
										 |  |  |         cc.update_code_context() | 
					
						
							|  |  |  |         eq(cc.topvisible, 12) | 
					
						
							|  |  |  |         cc.context.mark_set('insert', '3.0') | 
					
						
							|  |  |  |         jump() | 
					
						
							|  |  |  |         eq(cc.topvisible, 8) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # More context lines than limit. | 
					
						
							|  |  |  |         cc.context_depth = 2 | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |         cc.text.yview('12.0') | 
					
						
							| 
									
										
										
										
											2018-06-08 01:21:15 -04:00
										 |  |  |         cc.update_code_context() | 
					
						
							|  |  |  |         eq(cc.topvisible, 12) | 
					
						
							|  |  |  |         cc.context.mark_set('insert', '1.0') | 
					
						
							|  |  |  |         jump() | 
					
						
							|  |  |  |         eq(cc.topvisible, 8) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-28 13:22:55 -05:00
										 |  |  |         # Context selection stops jump. | 
					
						
							|  |  |  |         cc.text.yview('5.0') | 
					
						
							|  |  |  |         cc.update_code_context() | 
					
						
							|  |  |  |         cc.context.tag_add('sel', '1.0', '2.0') | 
					
						
							|  |  |  |         cc.context.mark_set('insert', '1.0') | 
					
						
							|  |  |  |         jump()  # Without selection, to line 2. | 
					
						
							|  |  |  |         eq(cc.topvisible, 5) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |     @mock.patch.object(codecontext.CodeContext, 'update_code_context') | 
					
						
							|  |  |  |     def test_timer_event(self, mock_update): | 
					
						
							|  |  |  |         # Ensure code context is not active. | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         if self.cc.context: | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |             self.cc.toggle_code_context_event() | 
					
						
							|  |  |  |         self.cc.timer_event() | 
					
						
							|  |  |  |         mock_update.assert_not_called() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Activate code context. | 
					
						
							|  |  |  |         self.cc.toggle_code_context_event() | 
					
						
							|  |  |  |         self.cc.timer_event() | 
					
						
							|  |  |  |         mock_update.assert_called() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |     def test_font(self): | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |         eq = self.assertEqual | 
					
						
							|  |  |  |         cc = self.cc | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         orig_font = cc.text['font'] | 
					
						
							| 
									
										
										
										
											2019-07-18 23:03:18 +03:00
										 |  |  |         test_font = 'TkTextFont' | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         self.assertNotEqual(orig_font, test_font) | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Ensure code context is not active. | 
					
						
							|  |  |  |         if cc.context is not None: | 
					
						
							|  |  |  |             cc.toggle_code_context_event() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         self.font_override = test_font | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |         # Nothing breaks or changes with inactive code context. | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         cc.update_font() | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         # Activate code context, previous font change is immediately effective. | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |         cc.toggle_code_context_event() | 
					
						
							|  |  |  |         eq(cc.context['font'], test_font) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         # Call the font update, change is picked up. | 
					
						
							|  |  |  |         self.font_override = orig_font | 
					
						
							|  |  |  |         cc.update_font() | 
					
						
							|  |  |  |         eq(cc.context['font'], orig_font) | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_highlight_colors(self): | 
					
						
							|  |  |  |         eq = self.assertEqual | 
					
						
							|  |  |  |         cc = self.cc | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         orig_colors = dict(self.highlight_cfg) | 
					
						
							| 
									
										
										
										
											2018-06-01 21:45:54 -04:00
										 |  |  |         test_colors = {'background': '#222222', 'foreground': '#ffff00'} | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         def assert_colors_are_equal(colors): | 
					
						
							|  |  |  |             eq(cc.context['background'], colors['background']) | 
					
						
							|  |  |  |             eq(cc.context['foreground'], colors['foreground']) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |         # Ensure code context is not active. | 
					
						
							| 
									
										
										
										
											2018-06-04 11:58:44 -04:00
										 |  |  |         if cc.context: | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |             cc.toggle_code_context_event() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         self.highlight_cfg = test_colors | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |         # Nothing breaks with inactive code context. | 
					
						
							|  |  |  |         cc.update_highlight_colors() | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         # Activate code context, previous colors change is immediately effective. | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |         cc.toggle_code_context_event() | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         assert_colors_are_equal(test_colors) | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         # Call colors update with no change to the configured colors. | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |         cc.update_highlight_colors() | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         assert_colors_are_equal(test_colors) | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         # Call the colors update with code context active, change is picked up. | 
					
						
							|  |  |  |         self.highlight_cfg = orig_colors | 
					
						
							| 
									
										
										
										
											2019-07-17 11:15:53 +03:00
										 |  |  |         cc.update_highlight_colors() | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         assert_colors_are_equal(orig_colors) | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class HelperFunctionText(unittest.TestCase): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-23 22:18:15 -04:00
										 |  |  |     def test_get_spaces_firstword(self): | 
					
						
							|  |  |  |         get = codecontext.get_spaces_firstword | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  |         test_lines = ( | 
					
						
							|  |  |  |             ('    first word', ('    ', 'first')), | 
					
						
							|  |  |  |             ('\tfirst word', ('\t', 'first')), | 
					
						
							|  |  |  |             ('  \u19D4\u19D2: ', ('  ', '\u19D4\u19D2')), | 
					
						
							|  |  |  |             ('no spaces', ('', 'no')), | 
					
						
							|  |  |  |             ('', ('', '')), | 
					
						
							|  |  |  |             ('# TEST COMMENT', ('', '')), | 
					
						
							|  |  |  |             ('    (continuation)', ('    ', '')) | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         for line, expected_output in test_lines: | 
					
						
							|  |  |  |             self.assertEqual(get(line), expected_output) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Send the pattern in the call. | 
					
						
							|  |  |  |         self.assertEqual(get('    (continuation)', | 
					
						
							|  |  |  |                              c=re.compile(r'^(\s*)([^\s]*)')), | 
					
						
							|  |  |  |                          ('    ', '(continuation)')) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-23 22:18:15 -04:00
										 |  |  |     def test_get_line_info(self): | 
					
						
							|  |  |  |         eq = self.assertEqual | 
					
						
							|  |  |  |         gli = codecontext.get_line_info | 
					
						
							|  |  |  |         lines = code_sample.splitlines() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Line 1 is not a BLOCKOPENER. | 
					
						
							|  |  |  |         eq(gli(lines[0]), (codecontext.INFINITY, '', False)) | 
					
						
							|  |  |  |         # Line 2 is a BLOCKOPENER without an indent. | 
					
						
							|  |  |  |         eq(gli(lines[1]), (0, 'class C1():', 'class')) | 
					
						
							|  |  |  |         # Line 3 is not a BLOCKOPENER and does not return the indent level. | 
					
						
							|  |  |  |         eq(gli(lines[2]), (codecontext.INFINITY, '    # Class comment.', False)) | 
					
						
							|  |  |  |         # Line 4 is a BLOCKOPENER and is indented. | 
					
						
							|  |  |  |         eq(gli(lines[3]), (4, '    def __init__(self, a, b):', 'def')) | 
					
						
							|  |  |  |         # Line 8 is a different BLOCKOPENER and is indented. | 
					
						
							|  |  |  |         eq(gli(lines[7]), (8, '        if a > b:', 'if')) | 
					
						
							|  |  |  |         # Test tab. | 
					
						
							|  |  |  |         eq(gli('\tif a == b:'), (1, '\tif a == b:', 'if')) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-19 15:34:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     unittest.main(verbosity=2) |