| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  | """Test sidebar, coverage 85%""" | 
					
						
							|  |  |  | from textwrap import dedent | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  | from itertools import chain | 
					
						
							|  |  |  | import unittest | 
					
						
							|  |  |  | import unittest.mock | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  | from test.support import requires, swap_attr | 
					
						
							| 
									
										
										
										
											2022-09-02 09:35:08 -07:00
										 |  |  | from test import support | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  | import tkinter as tk | 
					
						
							| 
									
										
										
										
											2021-04-29 23:52:47 -04:00
										 |  |  | from idlelib.idle_test.tkinter_testing_utils import run_in_tk_mainloop | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | from idlelib.delegator import Delegator | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  | from idlelib.editor import fixwordbreaks | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  | from idlelib.percolator import Percolator | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  | import idlelib.pyshell | 
					
						
							|  |  |  | from idlelib.pyshell import fix_x11_paste, PyShell, PyShellFileList | 
					
						
							|  |  |  | from idlelib.run import fix_scaling | 
					
						
							|  |  |  | import idlelib.sidebar | 
					
						
							|  |  |  | from idlelib.sidebar import get_end_linenumber, get_lineno | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Dummy_editwin: | 
					
						
							|  |  |  |     def __init__(self, text): | 
					
						
							|  |  |  |         self.text = text | 
					
						
							|  |  |  |         self.text_frame = self.text.master | 
					
						
							|  |  |  |         self.per = Percolator(text) | 
					
						
							|  |  |  |         self.undo = Delegator() | 
					
						
							|  |  |  |         self.per.insertfilter(self.undo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setvar(self, name, value): | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getlineno(self, index): | 
					
						
							|  |  |  |         return int(float(self.text.index(index))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class LineNumbersTest(unittest.TestCase): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def setUpClass(cls): | 
					
						
							|  |  |  |         requires('gui') | 
					
						
							|  |  |  |         cls.root = tk.Tk() | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |         cls.root.withdraw() | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         cls.text_frame = tk.Frame(cls.root) | 
					
						
							|  |  |  |         cls.text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) | 
					
						
							|  |  |  |         cls.text_frame.rowconfigure(1, weight=1) | 
					
						
							|  |  |  |         cls.text_frame.columnconfigure(1, weight=1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         cls.text = tk.Text(cls.text_frame, width=80, height=24, wrap=tk.NONE) | 
					
						
							|  |  |  |         cls.text.grid(row=1, column=1, sticky=tk.NSEW) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         cls.editwin = Dummy_editwin(cls.text) | 
					
						
							|  |  |  |         cls.editwin.vbar = tk.Scrollbar(cls.text_frame) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def tearDownClass(cls): | 
					
						
							|  |  |  |         cls.editwin.per.close() | 
					
						
							| 
									
										
										
										
											2023-05-01 22:53:16 -04:00
										 |  |  |         cls.root.update_idletasks() | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         cls.root.destroy() | 
					
						
							|  |  |  |         del cls.text, cls.text_frame, cls.editwin, cls.root | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							|  |  |  |         self.linenumber = idlelib.sidebar.LineNumbers(self.editwin) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.highlight_cfg = {"background": '#abcdef', | 
					
						
							|  |  |  |                               "foreground": '#123456'} | 
					
						
							|  |  |  |         orig_idleConf_GetHighlight = idlelib.sidebar.idleConf.GetHighlight | 
					
						
							|  |  |  |         def mock_idleconf_GetHighlight(theme, element): | 
					
						
							|  |  |  |             if element == 'linenumber': | 
					
						
							|  |  |  |                 return self.highlight_cfg | 
					
						
							|  |  |  |             return orig_idleConf_GetHighlight(theme, element) | 
					
						
							|  |  |  |         GetHighlight_patcher = unittest.mock.patch.object( | 
					
						
							|  |  |  |             idlelib.sidebar.idleConf, 'GetHighlight', mock_idleconf_GetHighlight) | 
					
						
							|  |  |  |         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( | 
					
						
							|  |  |  |             idlelib.sidebar.idleConf, 'GetFont', mock_idleconf_GetFont) | 
					
						
							|  |  |  |         GetFont_patcher.start() | 
					
						
							|  |  |  |         self.addCleanup(GetFont_patcher.stop) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def tearDown(self): | 
					
						
							|  |  |  |         self.text.delete('1.0', 'end') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_selection(self): | 
					
						
							|  |  |  |         return tuple(map(str, self.text.tag_ranges('sel'))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_line_screen_position(self, line): | 
					
						
							|  |  |  |         bbox = self.linenumber.sidebar_text.bbox(f'{line}.end -1c') | 
					
						
							|  |  |  |         x = bbox[0] + 2 | 
					
						
							|  |  |  |         y = bbox[1] + 2 | 
					
						
							|  |  |  |         return x, y | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def assert_state_disabled(self): | 
					
						
							|  |  |  |         state = self.linenumber.sidebar_text.config()['state'] | 
					
						
							|  |  |  |         self.assertEqual(state[-1], tk.DISABLED) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_sidebar_text_contents(self): | 
					
						
							|  |  |  |         return self.linenumber.sidebar_text.get('1.0', tk.END) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def assert_sidebar_n_lines(self, n_lines): | 
					
						
							|  |  |  |         expected = '\n'.join(chain(map(str, range(1, n_lines + 1)), [''])) | 
					
						
							|  |  |  |         self.assertEqual(self.get_sidebar_text_contents(), expected) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def assert_text_equals(self, expected): | 
					
						
							|  |  |  |         return self.assertEqual(self.text.get('1.0', 'end'), expected) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_init_empty(self): | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_init_not_empty(self): | 
					
						
							|  |  |  |         self.text.insert('insert', 'foo bar\n'*3) | 
					
						
							|  |  |  |         self.assert_text_equals('foo bar\n'*3 + '\n') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(4) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_toggle_linenumbering(self): | 
					
						
							|  |  |  |         self.assertEqual(self.linenumber.is_shown, False) | 
					
						
							|  |  |  |         self.linenumber.show_sidebar() | 
					
						
							|  |  |  |         self.assertEqual(self.linenumber.is_shown, True) | 
					
						
							|  |  |  |         self.linenumber.hide_sidebar() | 
					
						
							|  |  |  |         self.assertEqual(self.linenumber.is_shown, False) | 
					
						
							|  |  |  |         self.linenumber.hide_sidebar() | 
					
						
							|  |  |  |         self.assertEqual(self.linenumber.is_shown, False) | 
					
						
							|  |  |  |         self.linenumber.show_sidebar() | 
					
						
							|  |  |  |         self.assertEqual(self.linenumber.is_shown, True) | 
					
						
							|  |  |  |         self.linenumber.show_sidebar() | 
					
						
							|  |  |  |         self.assertEqual(self.linenumber.is_shown, True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_insert(self): | 
					
						
							|  |  |  |         self.text.insert('insert', 'foobar') | 
					
						
							|  |  |  |         self.assert_text_equals('foobar\n') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(1) | 
					
						
							|  |  |  |         self.assert_state_disabled() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.insert('insert', '\nfoo') | 
					
						
							|  |  |  |         self.assert_text_equals('foobar\nfoo\n') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(2) | 
					
						
							|  |  |  |         self.assert_state_disabled() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.insert('insert', 'hello\n'*2) | 
					
						
							|  |  |  |         self.assert_text_equals('foobar\nfoohello\nhello\n\n') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(4) | 
					
						
							|  |  |  |         self.assert_state_disabled() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.insert('insert', '\nworld') | 
					
						
							|  |  |  |         self.assert_text_equals('foobar\nfoohello\nhello\n\nworld\n') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(5) | 
					
						
							|  |  |  |         self.assert_state_disabled() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_delete(self): | 
					
						
							|  |  |  |         self.text.insert('insert', 'foobar') | 
					
						
							|  |  |  |         self.assert_text_equals('foobar\n') | 
					
						
							|  |  |  |         self.text.delete('1.1', '1.3') | 
					
						
							|  |  |  |         self.assert_text_equals('fbar\n') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(1) | 
					
						
							|  |  |  |         self.assert_state_disabled() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.insert('insert', 'foo\n'*2) | 
					
						
							|  |  |  |         self.assert_text_equals('fbarfoo\nfoo\n\n') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(3) | 
					
						
							|  |  |  |         self.assert_state_disabled() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |         # Deleting up to "2.end" doesn't delete the final newline. | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         self.text.delete('2.0', '2.end') | 
					
						
							|  |  |  |         self.assert_text_equals('fbarfoo\n\n\n') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(3) | 
					
						
							|  |  |  |         self.assert_state_disabled() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.delete('1.3', 'end') | 
					
						
							|  |  |  |         self.assert_text_equals('fba\n') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(1) | 
					
						
							|  |  |  |         self.assert_state_disabled() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |         # Text widgets always keep a single '\n' character at the end. | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         self.text.delete('1.0', 'end') | 
					
						
							|  |  |  |         self.assert_text_equals('\n') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(1) | 
					
						
							|  |  |  |         self.assert_state_disabled() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_sidebar_text_width(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Test that linenumber text widget is always at the minimum | 
					
						
							|  |  |  |         width | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         def get_width(): | 
					
						
							|  |  |  |             return self.linenumber.sidebar_text.config()['width'][-1] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(1) | 
					
						
							|  |  |  |         self.assertEqual(get_width(), 1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.insert('insert', 'foo') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(1) | 
					
						
							|  |  |  |         self.assertEqual(get_width(), 1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.insert('insert', 'foo\n'*8) | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(9) | 
					
						
							|  |  |  |         self.assertEqual(get_width(), 1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.insert('insert', 'foo\n') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(10) | 
					
						
							|  |  |  |         self.assertEqual(get_width(), 2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.insert('insert', 'foo\n') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(11) | 
					
						
							|  |  |  |         self.assertEqual(get_width(), 2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.delete('insert -1l linestart', 'insert linestart') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(10) | 
					
						
							|  |  |  |         self.assertEqual(get_width(), 2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.delete('insert -1l linestart', 'insert linestart') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(9) | 
					
						
							|  |  |  |         self.assertEqual(get_width(), 1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.insert('insert', 'foo\n'*90) | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(99) | 
					
						
							|  |  |  |         self.assertEqual(get_width(), 2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.insert('insert', 'foo\n') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(100) | 
					
						
							|  |  |  |         self.assertEqual(get_width(), 3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.insert('insert', 'foo\n') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(101) | 
					
						
							|  |  |  |         self.assertEqual(get_width(), 3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.delete('insert -1l linestart', 'insert linestart') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(100) | 
					
						
							|  |  |  |         self.assertEqual(get_width(), 3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.delete('insert -1l linestart', 'insert linestart') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(99) | 
					
						
							|  |  |  |         self.assertEqual(get_width(), 2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.delete('50.0 -1c', 'end -1c') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(49) | 
					
						
							|  |  |  |         self.assertEqual(get_width(), 2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.text.delete('5.0 -1c', 'end -1c') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(4) | 
					
						
							|  |  |  |         self.assertEqual(get_width(), 1) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |         # Text widgets always keep a single '\n' character at the end. | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         self.text.delete('1.0', 'end -1c') | 
					
						
							|  |  |  |         self.assert_sidebar_n_lines(1) | 
					
						
							|  |  |  |         self.assertEqual(get_width(), 1) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |     # The following tests are temporarily disabled due to relying on | 
					
						
							|  |  |  |     # simulated user input and inspecting which text is selected, which | 
					
						
							|  |  |  |     # are fragile and can fail when several GUI tests are run in parallel | 
					
						
							|  |  |  |     # or when the windows created by the test lose focus. | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     # TODO: Re-work these tests or remove them from the test suite. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @unittest.skip('test disabled') | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |     def test_click_selection(self): | 
					
						
							|  |  |  |         self.linenumber.show_sidebar() | 
					
						
							|  |  |  |         self.text.insert('1.0', 'one\ntwo\nthree\nfour\n') | 
					
						
							|  |  |  |         self.root.update() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Click on the second line. | 
					
						
							|  |  |  |         x, y = self.get_line_screen_position(2) | 
					
						
							|  |  |  |         self.linenumber.sidebar_text.event_generate('<Button-1>', x=x, y=y) | 
					
						
							|  |  |  |         self.linenumber.sidebar_text.update() | 
					
						
							|  |  |  |         self.root.update() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(self.get_selection(), ('2.0', '3.0')) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-04 19:25:27 +03:00
										 |  |  |     def simulate_drag(self, start_line, end_line): | 
					
						
							|  |  |  |         start_x, start_y = self.get_line_screen_position(start_line) | 
					
						
							|  |  |  |         end_x, end_y = self.get_line_screen_position(end_line) | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         self.linenumber.sidebar_text.event_generate('<Button-1>', | 
					
						
							|  |  |  |                                                     x=start_x, y=start_y) | 
					
						
							| 
									
										
										
										
											2019-08-04 19:25:27 +03:00
										 |  |  |         self.root.update() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def lerp(a, b, steps): | 
					
						
							|  |  |  |             """linearly interpolate from a to b (inclusive) in equal steps""" | 
					
						
							|  |  |  |             last_step = steps - 1 | 
					
						
							|  |  |  |             for i in range(steps): | 
					
						
							|  |  |  |                 yield ((last_step - i) / last_step) * a + (i / last_step) * b | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for x, y in zip( | 
					
						
							|  |  |  |                 map(int, lerp(start_x, end_x, steps=11)), | 
					
						
							|  |  |  |                 map(int, lerp(start_y, end_y, steps=11)), | 
					
						
							|  |  |  |         ): | 
					
						
							|  |  |  |             self.linenumber.sidebar_text.event_generate('<B1-Motion>', x=x, y=y) | 
					
						
							|  |  |  |             self.root.update() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         self.linenumber.sidebar_text.event_generate('<ButtonRelease-1>', | 
					
						
							|  |  |  |                                                     x=end_x, y=end_y) | 
					
						
							|  |  |  |         self.root.update() | 
					
						
							| 
									
										
										
										
											2019-08-04 19:25:27 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |     @unittest.skip('test disabled') | 
					
						
							| 
									
										
										
										
											2019-08-04 19:25:27 +03:00
										 |  |  |     def test_drag_selection_down(self): | 
					
						
							|  |  |  |         self.linenumber.show_sidebar() | 
					
						
							|  |  |  |         self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n') | 
					
						
							|  |  |  |         self.root.update() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Drag from the second line to the fourth line. | 
					
						
							|  |  |  |         self.simulate_drag(2, 4) | 
					
						
							| 
									
										
										
										
											2019-07-29 17:57:36 -04:00
										 |  |  |         self.assertEqual(self.get_selection(), ('2.0', '5.0')) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |     @unittest.skip('test disabled') | 
					
						
							| 
									
										
										
										
											2019-07-29 17:57:36 -04:00
										 |  |  |     def test_drag_selection_up(self): | 
					
						
							|  |  |  |         self.linenumber.show_sidebar() | 
					
						
							|  |  |  |         self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n') | 
					
						
							|  |  |  |         self.root.update() | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-29 17:57:36 -04:00
										 |  |  |         # Drag from the fourth line to the second line. | 
					
						
							| 
									
										
										
										
											2019-08-04 19:25:27 +03:00
										 |  |  |         self.simulate_drag(4, 2) | 
					
						
							| 
									
										
										
										
											2019-07-29 17:57:36 -04:00
										 |  |  |         self.assertEqual(self.get_selection(), ('2.0', '5.0')) | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_scroll(self): | 
					
						
							|  |  |  |         self.linenumber.show_sidebar() | 
					
						
							|  |  |  |         self.text.insert('1.0', 'line\n' * 100) | 
					
						
							|  |  |  |         self.root.update() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Scroll down 10 lines. | 
					
						
							|  |  |  |         self.text.yview_scroll(10, 'unit') | 
					
						
							|  |  |  |         self.root.update() | 
					
						
							|  |  |  |         self.assertEqual(self.text.index('@0,0'), '11.0') | 
					
						
							|  |  |  |         self.assertEqual(self.linenumber.sidebar_text.index('@0,0'), '11.0') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Generate a mouse-wheel event and make sure it scrolled up or down. | 
					
						
							| 
									
										
										
										
											2023-04-24 08:40:30 -05:00
										 |  |  |         # The meaning of the "delta" is OS-dependent, so this just checks for | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         # any change. | 
					
						
							|  |  |  |         self.linenumber.sidebar_text.event_generate('<MouseWheel>', | 
					
						
							|  |  |  |                                                     x=0, y=0, | 
					
						
							|  |  |  |                                                     delta=10) | 
					
						
							|  |  |  |         self.root.update() | 
					
						
							|  |  |  |         self.assertNotEqual(self.text.index('@0,0'), '11.0') | 
					
						
							|  |  |  |         self.assertNotEqual(self.linenumber.sidebar_text.index('@0,0'), '11.0') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_font(self): | 
					
						
							|  |  |  |         ln = self.linenumber | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         orig_font = ln.sidebar_text['font'] | 
					
						
							|  |  |  |         test_font = 'TkTextFont' | 
					
						
							|  |  |  |         self.assertNotEqual(orig_font, test_font) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Ensure line numbers aren't shown. | 
					
						
							|  |  |  |         ln.hide_sidebar() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.font_override = test_font | 
					
						
							|  |  |  |         # Nothing breaks when line numbers aren't shown. | 
					
						
							|  |  |  |         ln.update_font() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Activate line numbers, previous font change is immediately effective. | 
					
						
							|  |  |  |         ln.show_sidebar() | 
					
						
							|  |  |  |         self.assertEqual(ln.sidebar_text['font'], test_font) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Call the font update with line numbers shown, change is picked up. | 
					
						
							|  |  |  |         self.font_override = orig_font | 
					
						
							|  |  |  |         ln.update_font() | 
					
						
							|  |  |  |         self.assertEqual(ln.sidebar_text['font'], orig_font) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_highlight_colors(self): | 
					
						
							|  |  |  |         ln = self.linenumber | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         orig_colors = dict(self.highlight_cfg) | 
					
						
							|  |  |  |         test_colors = {'background': '#222222', 'foreground': '#ffff00'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def assert_colors_are_equal(colors): | 
					
						
							|  |  |  |             self.assertEqual(ln.sidebar_text['background'], colors['background']) | 
					
						
							|  |  |  |             self.assertEqual(ln.sidebar_text['foreground'], colors['foreground']) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Ensure line numbers aren't shown. | 
					
						
							|  |  |  |         ln.hide_sidebar() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.highlight_cfg = test_colors | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |         # Nothing breaks with inactive line numbers. | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  |         ln.update_colors() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Show line numbers, previous colors change is immediately effective. | 
					
						
							|  |  |  |         ln.show_sidebar() | 
					
						
							|  |  |  |         assert_colors_are_equal(test_colors) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Call colors update with no change to the configured colors. | 
					
						
							|  |  |  |         ln.update_colors() | 
					
						
							|  |  |  |         assert_colors_are_equal(test_colors) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Call the colors update with line numbers shown, change is picked up. | 
					
						
							|  |  |  |         self.highlight_cfg = orig_colors | 
					
						
							|  |  |  |         ln.update_colors() | 
					
						
							|  |  |  |         assert_colors_are_equal(orig_colors) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  | class ShellSidebarTest(unittest.TestCase): | 
					
						
							|  |  |  |     root: tk.Tk = None | 
					
						
							|  |  |  |     shell: PyShell = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def setUpClass(cls): | 
					
						
							|  |  |  |         requires('gui') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         cls.root = root = tk.Tk() | 
					
						
							|  |  |  |         root.withdraw() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         fix_scaling(root) | 
					
						
							|  |  |  |         fixwordbreaks(root) | 
					
						
							|  |  |  |         fix_x11_paste(root) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         cls.flist = flist = PyShellFileList(root) | 
					
						
							| 
									
										
										
										
											2021-04-29 23:52:47 -04:00
										 |  |  |         # See #43981 about macosx.setupApp(root, flist) causing failure. | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |         root.update_idletasks() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         cls.init_shell() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def tearDownClass(cls): | 
					
						
							|  |  |  |         if cls.shell is not None: | 
					
						
							|  |  |  |             cls.shell.executing = False | 
					
						
							|  |  |  |             cls.shell.close() | 
					
						
							|  |  |  |             cls.shell = None | 
					
						
							|  |  |  |         cls.flist = None | 
					
						
							|  |  |  |         cls.root.update_idletasks() | 
					
						
							|  |  |  |         cls.root.destroy() | 
					
						
							|  |  |  |         cls.root = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def init_shell(cls): | 
					
						
							|  |  |  |         cls.shell = cls.flist.open_shell() | 
					
						
							|  |  |  |         cls.shell.pollinterval = 10 | 
					
						
							|  |  |  |         cls.root.update() | 
					
						
							|  |  |  |         cls.n_preface_lines = get_lineno(cls.shell.text, 'end-1c') - 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def reset_shell(cls): | 
					
						
							|  |  |  |         cls.shell.per.bottom.delete(f'{cls.n_preface_lines+1}.0', 'end-1c') | 
					
						
							|  |  |  |         cls.shell.shell_sidebar.update_sidebar() | 
					
						
							|  |  |  |         cls.root.update() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							|  |  |  |         # In some test environments, e.g. Azure Pipelines (as of | 
					
						
							|  |  |  |         # Apr. 2021), sys.stdout is changed between tests. However, | 
					
						
							|  |  |  |         # PyShell relies on overriding sys.stdout when run without a | 
					
						
							|  |  |  |         # sub-process (as done here; see setUpClass). | 
					
						
							|  |  |  |         self._saved_stdout = None | 
					
						
							|  |  |  |         if sys.stdout != self.shell.stdout: | 
					
						
							|  |  |  |             self._saved_stdout = sys.stdout | 
					
						
							|  |  |  |             sys.stdout = self.shell.stdout | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.reset_shell() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def tearDown(self): | 
					
						
							|  |  |  |         if self._saved_stdout is not None: | 
					
						
							|  |  |  |             sys.stdout = self._saved_stdout | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_sidebar_lines(self): | 
					
						
							|  |  |  |         canvas = self.shell.shell_sidebar.canvas | 
					
						
							|  |  |  |         texts = list(canvas.find(tk.ALL)) | 
					
						
							|  |  |  |         texts_by_y_coords = { | 
					
						
							|  |  |  |             canvas.bbox(text)[1]: canvas.itemcget(text, 'text') | 
					
						
							|  |  |  |             for text in texts | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         line_y_coords = self.get_shell_line_y_coords() | 
					
						
							|  |  |  |         return [texts_by_y_coords.get(y, None) for y in line_y_coords] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def assert_sidebar_lines_end_with(self, expected_lines): | 
					
						
							|  |  |  |         self.shell.shell_sidebar.update_sidebar() | 
					
						
							|  |  |  |         self.assertEqual( | 
					
						
							|  |  |  |             self.get_sidebar_lines()[-len(expected_lines):], | 
					
						
							|  |  |  |             expected_lines, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_shell_line_y_coords(self): | 
					
						
							|  |  |  |         text = self.shell.text | 
					
						
							|  |  |  |         y_coords = [] | 
					
						
							|  |  |  |         index = text.index("@0,0") | 
					
						
							|  |  |  |         if index.split('.', 1)[1] != '0': | 
					
						
							|  |  |  |             index = text.index(f"{index} +1line linestart") | 
					
						
							| 
									
										
										
										
											2022-02-02 19:59:24 -06:00
										 |  |  |         while (lineinfo := text.dlineinfo(index)) is not None: | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |             y_coords.append(lineinfo[1]) | 
					
						
							|  |  |  |             index = text.index(f"{index} +1line") | 
					
						
							|  |  |  |         return y_coords | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_sidebar_line_y_coords(self): | 
					
						
							|  |  |  |         canvas = self.shell.shell_sidebar.canvas | 
					
						
							|  |  |  |         texts = list(canvas.find(tk.ALL)) | 
					
						
							|  |  |  |         texts.sort(key=lambda text: canvas.bbox(text)[1]) | 
					
						
							|  |  |  |         return [canvas.bbox(text)[1] for text in texts] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def assert_sidebar_lines_synced(self): | 
					
						
							|  |  |  |         self.assertLessEqual( | 
					
						
							|  |  |  |             set(self.get_sidebar_line_y_coords()), | 
					
						
							|  |  |  |             set(self.get_shell_line_y_coords()), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def do_input(self, input): | 
					
						
							|  |  |  |         shell = self.shell | 
					
						
							|  |  |  |         text = shell.text | 
					
						
							|  |  |  |         for line_index, line in enumerate(input.split('\n')): | 
					
						
							|  |  |  |             if line_index > 0: | 
					
						
							|  |  |  |                 text.event_generate('<<newline-and-indent>>') | 
					
						
							|  |  |  |             text.insert('insert', line, 'stdin') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_initial_state(self): | 
					
						
							|  |  |  |         sidebar_lines = self.get_sidebar_lines() | 
					
						
							|  |  |  |         self.assertEqual( | 
					
						
							|  |  |  |             sidebar_lines, | 
					
						
							|  |  |  |             [None] * (len(sidebar_lines) - 1) + ['>>>'], | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         self.assert_sidebar_lines_synced() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-03 02:53:41 +03:00
										 |  |  |     @run_in_tk_mainloop() | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |     def test_single_empty_input(self): | 
					
						
							|  |  |  |         self.do_input('\n') | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |         self.assert_sidebar_lines_end_with(['>>>', '>>>']) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-03 02:53:41 +03:00
										 |  |  |     @run_in_tk_mainloop() | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |     def test_single_line_statement(self): | 
					
						
							|  |  |  |         self.do_input('1\n') | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |         self.assert_sidebar_lines_end_with(['>>>', None, '>>>']) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-03 02:53:41 +03:00
										 |  |  |     @run_in_tk_mainloop() | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |     def test_multi_line_statement(self): | 
					
						
							|  |  |  |         # Block statements are not indented because IDLE auto-indents. | 
					
						
							|  |  |  |         self.do_input(dedent('''\
 | 
					
						
							|  |  |  |             if True: | 
					
						
							|  |  |  |             print(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             '''))
 | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |         self.assert_sidebar_lines_end_with([ | 
					
						
							|  |  |  |             '>>>', | 
					
						
							|  |  |  |             '...', | 
					
						
							|  |  |  |             '...', | 
					
						
							|  |  |  |             '...', | 
					
						
							|  |  |  |             None, | 
					
						
							|  |  |  |             '>>>', | 
					
						
							|  |  |  |         ]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-03 02:53:41 +03:00
										 |  |  |     @run_in_tk_mainloop() | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |     def test_single_long_line_wraps(self): | 
					
						
							|  |  |  |         self.do_input('1' * 200 + '\n') | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |         self.assert_sidebar_lines_end_with(['>>>', None, '>>>']) | 
					
						
							|  |  |  |         self.assert_sidebar_lines_synced() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-03 02:53:41 +03:00
										 |  |  |     @run_in_tk_mainloop() | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |     def test_squeeze_multi_line_output(self): | 
					
						
							|  |  |  |         shell = self.shell | 
					
						
							|  |  |  |         text = shell.text | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.do_input('print("a\\nb\\nc")\n') | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |         self.assert_sidebar_lines_end_with(['>>>', None, None, None, '>>>']) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         text.mark_set('insert', f'insert -1line linestart') | 
					
						
							|  |  |  |         text.event_generate('<<squeeze-current-text>>') | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |         self.assert_sidebar_lines_end_with(['>>>', None, '>>>']) | 
					
						
							|  |  |  |         self.assert_sidebar_lines_synced() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         shell.squeezer.expandingbuttons[0].expand() | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |         self.assert_sidebar_lines_end_with(['>>>', None, None, None, '>>>']) | 
					
						
							|  |  |  |         self.assert_sidebar_lines_synced() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-03 02:53:41 +03:00
										 |  |  |     @run_in_tk_mainloop() | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |     def test_interrupt_recall_undo_redo(self): | 
					
						
							|  |  |  |         text = self.shell.text | 
					
						
							|  |  |  |         # Block statements are not indented because IDLE auto-indents. | 
					
						
							|  |  |  |         initial_sidebar_lines = self.get_sidebar_lines() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.do_input(dedent('''\
 | 
					
						
							|  |  |  |             if True: | 
					
						
							|  |  |  |             print(1) | 
					
						
							|  |  |  |             '''))
 | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |         self.assert_sidebar_lines_end_with(['>>>', '...', '...']) | 
					
						
							|  |  |  |         with_block_sidebar_lines = self.get_sidebar_lines() | 
					
						
							|  |  |  |         self.assertNotEqual(with_block_sidebar_lines, initial_sidebar_lines) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Control-C | 
					
						
							|  |  |  |         text.event_generate('<<interrupt-execution>>') | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |         self.assert_sidebar_lines_end_with(['>>>', '...', '...', None, '>>>']) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Recall previous via history | 
					
						
							|  |  |  |         text.event_generate('<<history-previous>>') | 
					
						
							|  |  |  |         text.event_generate('<<interrupt-execution>>') | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |         self.assert_sidebar_lines_end_with(['>>>', '...', None, '>>>']) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Recall previous via recall | 
					
						
							|  |  |  |         text.mark_set('insert', text.index('insert -2l')) | 
					
						
							|  |  |  |         text.event_generate('<<newline-and-indent>>') | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         text.event_generate('<<undo>>') | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |         self.assert_sidebar_lines_end_with(['>>>']) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         text.event_generate('<<redo>>') | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |         self.assert_sidebar_lines_end_with(['>>>', '...']) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         text.event_generate('<<newline-and-indent>>') | 
					
						
							|  |  |  |         text.event_generate('<<newline-and-indent>>') | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |         self.assert_sidebar_lines_end_with( | 
					
						
							|  |  |  |             ['>>>', '...', '...', '...', None, '>>>'] | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-03 02:53:41 +03:00
										 |  |  |     @run_in_tk_mainloop() | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |     def test_very_long_wrapped_line(self): | 
					
						
							| 
									
										
										
										
											2022-09-02 09:35:08 -07:00
										 |  |  |         with support.adjust_int_max_str_digits(11_111), \ | 
					
						
							|  |  |  |                 swap_attr(self.shell, 'squeezer', None): | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |             self.do_input('x = ' + '1'*10_000 + '\n') | 
					
						
							|  |  |  |             yield | 
					
						
							|  |  |  |             self.assertEqual(self.get_sidebar_lines(), ['>>>']) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_font(self): | 
					
						
							|  |  |  |         sidebar = self.shell.shell_sidebar | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         test_font = 'TkTextFont' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def mock_idleconf_GetFont(root, configType, section): | 
					
						
							|  |  |  |             return test_font | 
					
						
							|  |  |  |         GetFont_patcher = unittest.mock.patch.object( | 
					
						
							|  |  |  |             idlelib.sidebar.idleConf, 'GetFont', mock_idleconf_GetFont) | 
					
						
							|  |  |  |         GetFont_patcher.start() | 
					
						
							|  |  |  |         def cleanup(): | 
					
						
							|  |  |  |             GetFont_patcher.stop() | 
					
						
							|  |  |  |             sidebar.update_font() | 
					
						
							|  |  |  |         self.addCleanup(cleanup) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def get_sidebar_font(): | 
					
						
							|  |  |  |             canvas = sidebar.canvas | 
					
						
							|  |  |  |             texts = list(canvas.find(tk.ALL)) | 
					
						
							|  |  |  |             fonts = {canvas.itemcget(text, 'font') for text in texts} | 
					
						
							|  |  |  |             self.assertEqual(len(fonts), 1) | 
					
						
							|  |  |  |             return next(iter(fonts)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertNotEqual(get_sidebar_font(), test_font) | 
					
						
							|  |  |  |         sidebar.update_font() | 
					
						
							|  |  |  |         self.assertEqual(get_sidebar_font(), test_font) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_highlight_colors(self): | 
					
						
							|  |  |  |         sidebar = self.shell.shell_sidebar | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         test_colors = {"background": '#abcdef', "foreground": '#123456'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         orig_idleConf_GetHighlight = idlelib.sidebar.idleConf.GetHighlight | 
					
						
							|  |  |  |         def mock_idleconf_GetHighlight(theme, element): | 
					
						
							|  |  |  |             if element in ['linenumber', 'console']: | 
					
						
							|  |  |  |                 return test_colors | 
					
						
							|  |  |  |             return orig_idleConf_GetHighlight(theme, element) | 
					
						
							|  |  |  |         GetHighlight_patcher = unittest.mock.patch.object( | 
					
						
							|  |  |  |             idlelib.sidebar.idleConf, 'GetHighlight', | 
					
						
							|  |  |  |             mock_idleconf_GetHighlight) | 
					
						
							|  |  |  |         GetHighlight_patcher.start() | 
					
						
							|  |  |  |         def cleanup(): | 
					
						
							|  |  |  |             GetHighlight_patcher.stop() | 
					
						
							|  |  |  |             sidebar.update_colors() | 
					
						
							|  |  |  |         self.addCleanup(cleanup) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def get_sidebar_colors(): | 
					
						
							|  |  |  |             canvas = sidebar.canvas | 
					
						
							|  |  |  |             texts = list(canvas.find(tk.ALL)) | 
					
						
							|  |  |  |             fgs = {canvas.itemcget(text, 'fill') for text in texts} | 
					
						
							|  |  |  |             self.assertEqual(len(fgs), 1) | 
					
						
							|  |  |  |             fg = next(iter(fgs)) | 
					
						
							|  |  |  |             bg = canvas.cget('background') | 
					
						
							|  |  |  |             return {"background": bg, "foreground": fg} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertNotEqual(get_sidebar_colors(), test_colors) | 
					
						
							|  |  |  |         sidebar.update_colors() | 
					
						
							|  |  |  |         self.assertEqual(get_sidebar_colors(), test_colors) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-03 02:53:41 +03:00
										 |  |  |     @run_in_tk_mainloop() | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |     def test_mousewheel(self): | 
					
						
							|  |  |  |         sidebar = self.shell.shell_sidebar | 
					
						
							|  |  |  |         text = self.shell.text | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Enter a 100-line string to scroll the shell screen down. | 
					
						
							|  |  |  |         self.do_input('x = """' + '\n'*100 + '"""\n') | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |         self.assertGreater(get_lineno(text, '@0,0'), 1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         last_lineno = get_end_linenumber(text) | 
					
						
							|  |  |  |         self.assertIsNotNone(text.dlineinfo(text.index(f'{last_lineno}.0'))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Scroll up using the <MouseWheel> event. | 
					
						
							| 
									
										
										
										
											2023-04-24 08:40:30 -05:00
										 |  |  |         # The meaning of delta is platform-dependent. | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  |         delta = -1 if sys.platform == 'darwin' else 120 | 
					
						
							|  |  |  |         sidebar.canvas.event_generate('<MouseWheel>', x=0, y=0, delta=delta) | 
					
						
							|  |  |  |         yield | 
					
						
							| 
									
										
										
										
											2023-05-01 22:53:16 -04:00
										 |  |  |         if sys.platform != 'darwin':  # .update_idletasks() does not work. | 
					
						
							|  |  |  |             self.assertIsNone(text.dlineinfo(text.index(f'{last_lineno}.0'))) | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Scroll back down using the <Button-5> event. | 
					
						
							|  |  |  |         sidebar.canvas.event_generate('<Button-5>', x=0, y=0) | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  |         self.assertIsNotNone(text.dlineinfo(text.index(f'{last_lineno}.0'))) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-03 02:53:41 +03:00
										 |  |  |     @run_in_tk_mainloop() | 
					
						
							| 
									
										
										
										
											2021-05-03 05:27:38 +03:00
										 |  |  |     def test_copy(self): | 
					
						
							|  |  |  |         sidebar = self.shell.shell_sidebar | 
					
						
							|  |  |  |         text = self.shell.text | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         first_line = get_end_linenumber(text) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.do_input(dedent('''\
 | 
					
						
							|  |  |  |             if True: | 
					
						
							|  |  |  |             print(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             '''))
 | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         text.tag_add('sel', f'{first_line}.0', 'end-1c') | 
					
						
							|  |  |  |         selected_text = text.get('sel.first', 'sel.last') | 
					
						
							|  |  |  |         self.assertTrue(selected_text.startswith('if True:\n')) | 
					
						
							|  |  |  |         self.assertIn('\n1\n', selected_text) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         text.event_generate('<<copy>>') | 
					
						
							|  |  |  |         self.addCleanup(text.clipboard_clear) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         copied_text = text.clipboard_get() | 
					
						
							|  |  |  |         self.assertEqual(copied_text, selected_text) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-03 02:53:41 +03:00
										 |  |  |     @run_in_tk_mainloop() | 
					
						
							| 
									
										
										
										
											2021-05-03 05:27:38 +03:00
										 |  |  |     def test_copy_with_prompts(self): | 
					
						
							|  |  |  |         sidebar = self.shell.shell_sidebar | 
					
						
							|  |  |  |         text = self.shell.text | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         first_line = get_end_linenumber(text) | 
					
						
							|  |  |  |         self.do_input(dedent('''\
 | 
					
						
							|  |  |  |             if True: | 
					
						
							| 
									
										
										
										
											2022-08-01 01:06:13 -04:00
										 |  |  |                 print(1) | 
					
						
							| 
									
										
										
										
											2021-05-03 05:27:38 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |             '''))
 | 
					
						
							|  |  |  |         yield | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         text.tag_add('sel', f'{first_line}.3', 'end-1c') | 
					
						
							|  |  |  |         selected_text = text.get('sel.first', 'sel.last') | 
					
						
							|  |  |  |         self.assertTrue(selected_text.startswith('True:\n')) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         selected_lines_text = text.get('sel.first linestart', 'sel.last') | 
					
						
							|  |  |  |         selected_lines = selected_lines_text.split('\n') | 
					
						
							| 
									
										
										
										
											2022-08-01 01:06:13 -04:00
										 |  |  |         selected_lines.pop()  # Final '' is a split artifact, not a line. | 
					
						
							|  |  |  |         # Expect a block of input and a single output line. | 
					
						
							| 
									
										
										
										
											2021-05-03 05:27:38 +03:00
										 |  |  |         expected_prompts = \ | 
					
						
							| 
									
										
										
										
											2022-08-01 01:06:13 -04:00
										 |  |  |             ['>>>'] + ['...'] * (len(selected_lines) - 2) + [None] | 
					
						
							| 
									
										
										
										
											2021-05-03 05:27:38 +03:00
										 |  |  |         selected_text_with_prompts = '\n'.join( | 
					
						
							|  |  |  |             line if prompt is None else prompt + ' ' + line | 
					
						
							|  |  |  |             for prompt, line in zip(expected_prompts, | 
					
						
							|  |  |  |                                     selected_lines, | 
					
						
							|  |  |  |                                     strict=True) | 
					
						
							|  |  |  |         ) + '\n' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         text.event_generate('<<copy-with-prompts>>') | 
					
						
							|  |  |  |         self.addCleanup(text.clipboard_clear) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         copied_text = text.clipboard_get() | 
					
						
							|  |  |  |         self.assertEqual(copied_text, selected_text_with_prompts) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-29 01:27:55 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-23 15:22:11 +03:00
										 |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     unittest.main(verbosity=2) |