cpython/Lib/idlelib/idle_test/test_zzdummy.py
CoolCat467 208195dff4
gh-89520: Load extension settings and keybindings from user config (GH-28713)
Extension keybindings defined in ~/.idlerc/config-extensions.cfg were silently ignored because GetExtensionKeys, __GetRawExtensionKeys, and GetExtensionBindings only checked default config. Fix these to check user config as well, and update the extensions config dialog to handle user-only extensions correctly.

---------

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
2026-04-12 04:44:33 +00:00

193 lines
5.9 KiB
Python

"Test zzdummy, coverage 100%."
from idlelib import zzdummy
import unittest
from test.support import requires
from tkinter import Tk, Text
from unittest import mock
from idlelib import config
from idlelib import editor
from idlelib import format
usercfg = zzdummy.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
"""
class DummyEditwin:
get_selection_indices = editor.EditorWindow.get_selection_indices
def __init__(self, root, text):
self.root = root
self.top = root
self.text = text
self.fregion = format.FormatRegion(self)
self.text.undo_block_start = mock.Mock()
self.text.undo_block_stop = mock.Mock()
class ZZDummyMixin:
"""Shared tests for ZzDummy with default and user configs."""
def checklines(self, text, value):
# Verify that there are lines being checked.
end_line = int(float(text.index('end')))
# Check each line for the starting text.
actual = []
for line in range(1, end_line):
txt = text.get(f'{line}.0', f'{line}.end')
actual.append(txt.startswith(value))
return actual
def test_init(self):
zz = self.zz
self.assertEqual(zz.editwin, self.editor)
self.assertEqual(zz.text, self.editor.text)
def test_reload(self):
self.assertEqual(self.zz.ztext, '# ignore #')
zzdummy.idleConf.userCfg['extensions'].SetOption(
'ZzDummy', 'z-text', 'spam')
zzdummy.ZzDummy.reload()
self.assertEqual(self.zz.ztext, 'spam')
def test_z_in_event(self):
eq = self.assertEqual
zz = self.zz
text = zz.text
eq(self.zz.ztext, '# ignore #')
# No lines have the leading text.
expected = [False, False, False, False, False, False, False]
actual = self.checklines(text, zz.ztext)
eq(expected, actual)
text.tag_add('sel', '2.0', '4.end')
eq(zz.z_in_event(), 'break')
expected = [False, True, True, True, False, False, False]
actual = self.checklines(text, zz.ztext)
eq(expected, actual)
text.undo_block_start.assert_called_once()
text.undo_block_stop.assert_called_once()
def test_z_out_event(self):
eq = self.assertEqual
zz = self.zz
text = zz.text
eq(self.zz.ztext, '# ignore #')
# Prepend text.
text.tag_add('sel', '2.0', '5.end')
zz.z_in_event()
text.undo_block_start.reset_mock()
text.undo_block_stop.reset_mock()
# Select a few lines to remove text.
text.tag_remove('sel', '1.0', 'end')
text.tag_add('sel', '3.0', '4.end')
eq(zz.z_out_event(), 'break')
expected = [False, True, False, False, True, False, False]
actual = self.checklines(text, zz.ztext)
eq(expected, actual)
text.undo_block_start.assert_called_once()
text.undo_block_stop.assert_called_once()
def test_roundtrip(self):
# Insert and remove to all code should give back original text.
zz = self.zz
text = zz.text
text.tag_add('sel', '1.0', 'end-1c')
zz.z_in_event()
zz.z_out_event()
self.assertEqual(text.get('1.0', 'end-1c'), code_sample)
class ZZDummyTest(ZZDummyMixin, unittest.TestCase):
@classmethod
def setUpClass(cls):
requires('gui')
root = cls.root = Tk()
root.withdraw()
text = cls.text = Text(cls.root)
cls.editor = DummyEditwin(root, text)
zzdummy.idleConf.userCfg = testcfg
@classmethod
def tearDownClass(cls):
zzdummy.idleConf.userCfg = usercfg
del cls.editor, cls.text
cls.root.update_idletasks()
for id in cls.root.tk.call('after', 'info'):
cls.root.after_cancel(id) # Need for EditorWindow.
cls.root.destroy()
del cls.root
def setUp(self):
text = self.text
text.insert('1.0', code_sample)
text.undo_block_start.reset_mock()
text.undo_block_stop.reset_mock()
zz = self.zz = zzdummy.ZzDummy(self.editor)
zzdummy.ZzDummy.ztext = '# ignore #'
def tearDown(self):
self.text.delete('1.0', 'end')
del self.zz
def test_exists(self):
conf = zzdummy.idleConf
self.assertEqual(
conf.GetSectionList('user', 'extensions'), [])
self.assertEqual(
conf.GetSectionList('default', 'extensions'),
['AutoComplete', 'CodeContext', 'FormatParagraph',
'ParenMatch', 'ZzDummy', 'ZzDummy_cfgBindings',
'ZzDummy_bindings'])
self.assertIn("ZzDummy", conf.GetExtensions(False))
self.assertNotIn("ZzDummy", conf.GetExtensions())
self.assertEqual(
conf.GetExtensionKeys("ZzDummy"), {})
self.assertEqual(
conf.GetExtensionBindings("ZzDummy"),
{'<<z-out>>': ['<Control-Shift-KeyRelease-Delete>']})
def test_exists_user(self):
conf = zzdummy.idleConf
conf.userCfg["extensions"].read_dict({
"ZzDummy": {'enable': 'True'}
})
self.assertEqual(
conf.GetSectionList('user', 'extensions'),
["ZzDummy"])
self.assertIn("ZzDummy", conf.GetExtensions())
self.assertEqual(
conf.GetExtensionKeys("ZzDummy"),
{'<<z-in>>': ['<Control-Shift-KeyRelease-Insert>']})
self.assertEqual(
conf.GetExtensionBindings("ZzDummy"),
{'<<z-in>>': ['<Control-Shift-KeyRelease-Insert>'],
'<<z-out>>': ['<Control-Shift-KeyRelease-Delete>']})
# Restore
conf.userCfg["extensions"].remove_section("ZzDummy")
if __name__ == '__main__':
unittest.main(verbosity=2)