1"""Unittest for UndoDelegator in idlelib.undo.py.
2
3Coverage about 80% (retest).
4"""
5from test.support import requires
6requires('gui')
7
8import unittest
9from unittest.mock import Mock
10from tkinter import Text, Tk
11from idlelib.undo import UndoDelegator
12from idlelib.percolator import Percolator
13
14
15class UndoDelegatorTest(unittest.TestCase):
16
17    @classmethod
18    def setUpClass(cls):
19        cls.root = Tk()
20        cls.text = Text(cls.root)
21        cls.percolator = Percolator(cls.text)
22
23    @classmethod
24    def tearDownClass(cls):
25        cls.percolator.redir.close()
26        del cls.percolator, cls.text
27        cls.root.destroy()
28        del cls.root
29
30    def setUp(self):
31        self.delegator = UndoDelegator()
32        self.delegator.bell = Mock()
33        self.percolator.insertfilter(self.delegator)
34
35    def tearDown(self):
36        self.percolator.removefilter(self.delegator)
37        self.text.delete('1.0', 'end')
38        self.delegator.resetcache()
39
40    def test_undo_event(self):
41        text = self.text
42
43        text.insert('insert', 'foobar')
44        text.insert('insert', 'h')
45        text.event_generate('<<undo>>')
46        self.assertEqual(text.get('1.0', 'end'), '\n')
47
48        text.insert('insert', 'foo')
49        text.insert('insert', 'bar')
50        text.delete('1.2', '1.4')
51        text.insert('insert', 'hello')
52        text.event_generate('<<undo>>')
53        self.assertEqual(text.get('1.0', '1.4'), 'foar')
54        text.event_generate('<<undo>>')
55        self.assertEqual(text.get('1.0', '1.6'), 'foobar')
56        text.event_generate('<<undo>>')
57        self.assertEqual(text.get('1.0', '1.3'), 'foo')
58        text.event_generate('<<undo>>')
59        self.delegator.undo_event('event')
60        self.assertTrue(self.delegator.bell.called)
61
62    def test_redo_event(self):
63        text = self.text
64
65        text.insert('insert', 'foo')
66        text.insert('insert', 'bar')
67        text.delete('1.0', '1.3')
68        text.event_generate('<<undo>>')
69        text.event_generate('<<redo>>')
70        self.assertEqual(text.get('1.0', '1.3'), 'bar')
71        text.event_generate('<<redo>>')
72        self.assertTrue(self.delegator.bell.called)
73
74    def test_dump_event(self):
75        """
76        Dump_event cannot be tested directly without changing
77        environment variables. So, test statements in dump_event
78        indirectly
79        """
80        text = self.text
81        d = self.delegator
82
83        text.insert('insert', 'foo')
84        text.insert('insert', 'bar')
85        text.delete('1.2', '1.4')
86        self.assertTupleEqual((d.pointer, d.can_merge), (3, True))
87        text.event_generate('<<undo>>')
88        self.assertTupleEqual((d.pointer, d.can_merge), (2, False))
89
90    def test_get_set_saved(self):
91        # test the getter method get_saved
92        # test the setter method set_saved
93        # indirectly test check_saved
94        d = self.delegator
95
96        self.assertTrue(d.get_saved())
97        self.text.insert('insert', 'a')
98        self.assertFalse(d.get_saved())
99        d.saved_change_hook = Mock()
100
101        d.set_saved(True)
102        self.assertEqual(d.pointer, d.saved)
103        self.assertTrue(d.saved_change_hook.called)
104
105        d.set_saved(False)
106        self.assertEqual(d.saved, -1)
107        self.assertTrue(d.saved_change_hook.called)
108
109    def test_undo_start_stop(self):
110        # test the undo_block_start and undo_block_stop methods
111        text = self.text
112
113        text.insert('insert', 'foo')
114        self.delegator.undo_block_start()
115        text.insert('insert', 'bar')
116        text.insert('insert', 'bar')
117        self.delegator.undo_block_stop()
118        self.assertEqual(text.get('1.0', '1.3'), 'foo')
119
120        # test another code path
121        self.delegator.undo_block_start()
122        text.insert('insert', 'bar')
123        self.delegator.undo_block_stop()
124        self.assertEqual(text.get('1.0', '1.3'), 'foo')
125
126    def test_addcmd(self):
127        text = self.text
128        # when number of undo operations exceeds max_undo
129        self.delegator.max_undo = max_undo = 10
130        for i in range(max_undo + 10):
131            text.insert('insert', 'foo')
132            self.assertLessEqual(len(self.delegator.undolist), max_undo)
133
134if __name__ == '__main__':
135    unittest.main(verbosity=2, exit=False)
136