diff --git a/Lib/idlelib/UndoDelegator.py b/Lib/idlelib/UndoDelegator.py index 04c1cf5a273..1c2502d8182 100644 --- a/Lib/idlelib/UndoDelegator.py +++ b/Lib/idlelib/UndoDelegator.py @@ -336,30 +336,33 @@ class CommandSequence(Command): self.depth = self.depth + incr return self.depth -def _undo_delegator(parent): - from idlelib.Percolator import Percolator - root = Tk() - root.title("Test UndoDelegator") - width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - root.geometry("+%d+%d"%(x, y + 150)) - text = Text(root) - text.config(height=10) +def _undo_delegator(parent): # htest # + import re + import tkinter as tk + from idlelib.Percolator import Percolator + undowin = tk.Toplevel() + undowin.title("Test UndoDelegator") + width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) + undowin.geometry("+%d+%d"%(x, y + 150)) + + text = Text(undowin, height=10) text.pack() text.focus_set() p = Percolator(text) d = UndoDelegator() p.insertfilter(d) - undo = Button(root, text="Undo", command=lambda:d.undo_event(None)) + undo = Button(undowin, text="Undo", command=lambda:d.undo_event(None)) undo.pack(side='left') - redo = Button(root, text="Redo", command=lambda:d.redo_event(None)) + redo = Button(undowin, text="Redo", command=lambda:d.redo_event(None)) redo.pack(side='left') - dump = Button(root, text="Dump", command=lambda:d.dump_event(None)) + dump = Button(undowin, text="Dump", command=lambda:d.dump_event(None)) dump.pack(side='left') - root.mainloop() - if __name__ == "__main__": + import unittest + unittest.main('idlelib.idle_test.test_undodelegator', verbosity=2, + exit=False) from idlelib.idle_test.htest import run run(_undo_delegator) diff --git a/Lib/idlelib/idle_test/test_undodelegator.py b/Lib/idlelib/idle_test/test_undodelegator.py new file mode 100644 index 00000000000..26579844712 --- /dev/null +++ b/Lib/idlelib/idle_test/test_undodelegator.py @@ -0,0 +1,134 @@ +"""Unittest for UndoDelegator in idlelib.UndoDelegator. + +Coverage about 80% (retest). +""" +from test.support import requires +requires('gui') + +import unittest +from unittest.mock import Mock +from tkinter import Text, Tk +from idlelib.UndoDelegator import UndoDelegator +from idlelib.Percolator import Percolator + + +class UndoDelegatorTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.root = Tk() + cls.text = Text(cls.root) + cls.percolator = Percolator(cls.text) + + @classmethod + def tearDownClass(cls): + cls.percolator.redir.close() + cls.root.destroy() + del cls.percolator, cls.text, cls.root + + def setUp(self): + self.delegator = UndoDelegator() + self.percolator.insertfilter(self.delegator) + self.delegator.bell = Mock(wraps=self.delegator.bell) + + def tearDown(self): + self.percolator.removefilter(self.delegator) + self.text.delete('1.0', 'end') + self.delegator.resetcache() + + def test_undo_event(self): + text = self.text + + text.insert('insert', 'foobar') + text.insert('insert', 'h') + text.event_generate('<>') + self.assertEqual(text.get('1.0', 'end'), '\n') + + text.insert('insert', 'foo') + text.insert('insert', 'bar') + text.delete('1.2', '1.4') + text.insert('insert', 'hello') + text.event_generate('<>') + self.assertEqual(text.get('1.0', '1.4'), 'foar') + text.event_generate('<>') + self.assertEqual(text.get('1.0', '1.6'), 'foobar') + text.event_generate('<>') + self.assertEqual(text.get('1.0', '1.3'), 'foo') + text.event_generate('<>') + self.delegator.undo_event('event') + self.assertTrue(self.delegator.bell.called) + + def test_redo_event(self): + text = self.text + + text.insert('insert', 'foo') + text.insert('insert', 'bar') + text.delete('1.0', '1.3') + text.event_generate('<>') + text.event_generate('<>') + self.assertEqual(text.get('1.0', '1.3'), 'bar') + text.event_generate('<>') + self.assertTrue(self.delegator.bell.called) + + def test_dump_event(self): + """ + Dump_event cannot be tested directly without changing + environment variables. So, test statements in dump_event + indirectly + """ + text = self.text + d = self.delegator + + text.insert('insert', 'foo') + text.insert('insert', 'bar') + text.delete('1.2', '1.4') + self.assertTupleEqual((d.pointer, d.can_merge), (3, True)) + text.event_generate('<>') + self.assertTupleEqual((d.pointer, d.can_merge), (2, False)) + + def test_get_set_saved(self): + # test the getter method get_saved + # test the setter method set_saved + # indirectly test check_saved + d = self.delegator + + self.assertTrue(d.get_saved()) + self.text.insert('insert', 'a') + self.assertFalse(d.get_saved()) + d.saved_change_hook = Mock() + + d.set_saved(True) + self.assertEqual(d.pointer, d.saved) + self.assertTrue(d.saved_change_hook.called) + + d.set_saved(False) + self.assertEqual(d.saved, -1) + self.assertTrue(d.saved_change_hook.called) + + def test_undo_start_stop(self): + # test the undo_block_start and undo_block_stop methods + text = self.text + + text.insert('insert', 'foo') + self.delegator.undo_block_start() + text.insert('insert', 'bar') + text.insert('insert', 'bar') + self.delegator.undo_block_stop() + self.assertEqual(text.get('1.0', '1.3'), 'foo') + + # test another code path + self.delegator.undo_block_start() + text.insert('insert', 'bar') + self.delegator.undo_block_stop() + self.assertEqual(text.get('1.0', '1.3'), 'foo') + + def test_addcmd(self): + text = self.text + # when number of undo operations exceeds max_undo + self.delegator.max_undo = max_undo = 10 + for i in range(max_undo + 10): + text.insert('insert', 'foo') + self.assertLessEqual(len(self.delegator.undolist), max_undo) + +if __name__ == '__main__': + unittest.main(verbosity=2, exit=False)