summaryrefslogtreecommitdiffstats
path: root/Lib/idlelib/idle_test
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/idlelib/idle_test')
-rw-r--r--Lib/idlelib/idle_test/README.txt138
-rw-r--r--Lib/idlelib/idle_test/__init__.py6
-rw-r--r--Lib/idlelib/idle_test/htest.py3
-rw-r--r--Lib/idlelib/idle_test/mock_tk.py5
-rw-r--r--Lib/idlelib/idle_test/test_autocomplete.py3
-rw-r--r--Lib/idlelib/idle_test/test_autoexpand.py2
-rw-r--r--Lib/idlelib/idle_test/test_config_help.py106
-rw-r--r--Lib/idlelib/idle_test/test_configdialog.py22
-rw-r--r--Lib/idlelib/idle_test/test_delegator.py23
-rw-r--r--Lib/idlelib/idle_test/test_editmenu.py71
-rw-r--r--Lib/idlelib/idle_test/test_formatparagraph.py3
-rw-r--r--Lib/idlelib/idle_test/test_help_about.py52
-rw-r--r--Lib/idlelib/idle_test/test_parenmatch.py13
-rw-r--r--Lib/idlelib/idle_test/test_percolator.py118
-rw-r--r--Lib/idlelib/idle_test/test_replacedialog.py293
-rw-r--r--Lib/idlelib/idle_test/test_searchdialog.py80
-rw-r--r--Lib/idlelib/idle_test/test_searchengine.py2
-rw-r--r--Lib/idlelib/idle_test/test_textview.py6
-rw-r--r--Lib/idlelib/idle_test/test_undodelegator.py135
-rw-r--r--Lib/idlelib/idle_test/test_warning.py9
-rw-r--r--Lib/idlelib/idle_test/test_widgetredir.py29
21 files changed, 1002 insertions, 117 deletions
diff --git a/Lib/idlelib/idle_test/README.txt b/Lib/idlelib/idle_test/README.txt
index 2339926..dc7a286 100644
--- a/Lib/idlelib/idle_test/README.txt
+++ b/Lib/idlelib/idle_test/README.txt
@@ -2,12 +2,12 @@ README FOR IDLE TESTS IN IDLELIB.IDLE_TEST
0. Quick Start
-Automated unit tests were added in 2.7 for Python 2.x and 3.3 for Python 3.x.
+Automated unit tests were added in 3.3 for Python 3.x.
To run the tests from a command line:
python -m test.test_idle
-Human-mediated tests were added later in 2.7 and in 3.4.
+Human-mediated tests were added later in 3.4.
python -m idlelib.idle_test.htest
@@ -15,9 +15,9 @@ python -m idlelib.idle_test.htest
1. Test Files
The idle directory, idlelib, has over 60 xyz.py files. The idle_test
-subdirectory should contain a test_xyz.py for each, where 'xyz' is lowercased
-even if xyz.py is not. Here is a possible template, with the blanks after after
-'.' and 'as', and before and after '_' to be filled in.
+subdirectory should contain a test_xyz.py for each, where 'xyz' is
+lowercased even if xyz.py is not. Here is a possible template, with the
+blanks after '.' and 'as', and before and after '_' to be filled in.
import unittest
from test.support import requires
@@ -30,9 +30,9 @@ class _Test(unittest.TestCase):
if __name__ == '__main__':
unittest.main(verbosity=2)
-Add the following at the end of xyy.py, with the appropriate name added after
-'test_'. Some files already have something like this for htest. If so, insert
-the import and unittest.main lines before the htest lines.
+Add the following at the end of xyy.py, with the appropriate name added
+after 'test_'. Some files already have something like this for htest.
+If so, insert the import and unittest.main lines before the htest lines.
if __name__ == "__main__":
import unittest
@@ -42,64 +42,82 @@ if __name__ == "__main__":
2. GUI Tests
-When run as part of the Python test suite, Idle gui tests need to run
-test.support.requires('gui') (test.test_support in 2.7). A test is a gui test
-if it creates a Tk root or master object either directly or indirectly by
-instantiating a tkinter or idle class. For the benefit of test processes that
-either have no graphical environment available or are not allowed to use it, gui
-tests must be 'guarded' by "requires('gui')" in a setUp function or method.
-This will typically be setUpClass.
+When run as part of the Python test suite, Idle GUI tests need to run
+test.support.requires('gui'). A test is a GUI test if it creates a
+tkinter.Tk root or master object either directly or indirectly by
+instantiating a tkinter or idle class. GUI tests cannot run in test
+processes that either have no graphical environment available or are not
+allowed to use it.
-To avoid interfering with other gui tests, all gui objects must be destroyed and
-deleted by the end of the test. Widgets, such as a Tk root, created in a setUpX
-function, should be destroyed in the corresponding tearDownX. Module and class
-widget attributes should also be deleted..
+To guard a module consisting entirely of GUI tests, start with
+
+from test.support import requires
+requires('gui')
+
+To guard a test class, put "requires('gui')" in its setUpClass function.
+
+To avoid interfering with other GUI tests, all GUI objects must be destroyed and
+deleted by the end of the test. The Tk root created in a setUpX function should
+be destroyed in the corresponding tearDownX and the module or class attribute
+deleted. Others widgets should descend from the single root and the attributes
+deleted BEFORE root is destroyed. See https://bugs.python.org/issue20567.
@classmethod
def setUpClass(cls):
requires('gui')
cls.root = tk.Tk()
+ cls.text = tk.Text(root)
@classmethod
def tearDownClass(cls):
+ del cls.text
+ cls.root.update_idletasks()
cls.root.destroy()
del cls.root
+The update_idletasks call is sometimes needed to prevent the following warning
+either when running a test alone or as part of the test suite (#27196).
+ can't invoke "event" command: application has been destroyed
+ ...
+ "ttk::ThemeChanged"
Requires('gui') causes the test(s) it guards to be skipped if any of
-a few conditions are met:
-
- - The tests are being run by regrtest.py, and it was started without enabling
- the "gui" resource with the "-u" command line option.
-
- - The tests are being run on Windows by a service that is not allowed to
- interact with the graphical environment.
-
- - The tests are being run on Mac OSX in a process that cannot make a window
- manager connection.
-
+these conditions are met:
+
+ - The tests are being run by regrtest.py, and it was started without
+ enabling the "gui" resource with the "-u" command line option.
+
+ - The tests are being run on Windows by a service that is not allowed
+ to interact with the graphical environment.
+
+ - The tests are being run on Linux and X Windows is not available.
+
+ - The tests are being run on Mac OSX in a process that cannot make a
+ window manager connection.
+
- tkinter.Tk cannot be successfully instantiated for some reason.
-
+
- test.support.use_resources has been set by something other than
regrtest.py and does not contain "gui".
-
-Tests of non-gui operations should avoid creating tk widgets. Incidental uses of
-tk variables and messageboxes can be replaced by the mock classes in
-idle_test/mock_tk.py. The mock text handles some uses of the tk Text widget.
+
+Tests of non-GUI operations should avoid creating tk widgets. Incidental
+uses of tk variables and messageboxes can be replaced by the mock
+classes in idle_test/mock_tk.py. The mock text handles some uses of the
+tk Text widget.
3. Running Unit Tests
Assume that xyz.py and test_xyz.py both end with a unittest.main() call.
-Running either from an Idle editor runs all tests in the test_xyz file with the
-version of Python running Idle. Test output appears in the Shell window. The
-'verbosity=2' option lists all test methods in the file, which is appropriate
-when developing tests. The 'exit=False' option is needed in xyx.py files when an
-htest follows.
+Running either from an Idle editor runs all tests in the test_xyz file
+with the version of Python running Idle. Test output appears in the
+Shell window. The 'verbosity=2' option lists all test methods in the
+file, which is appropriate when developing tests. The 'exit=False'
+option is needed in xyx.py files when an htest follows.
The following command lines also run all test methods, including
-gui tests, in test_xyz.py. (Both '-m idlelib' and '-m idlelib.idle' start
-Idle and so cannot run tests.)
+GUI tests, in test_xyz.py. (Both '-m idlelib' and '-m idlelib.idle'
+start Idle and so cannot run tests.)
python -m idlelib.xyz
python -m idlelib.idle_test.test_xyz
@@ -109,35 +127,35 @@ The following runs all idle_test/test_*.py tests interactively.
>>> import unittest
>>> unittest.main('idlelib.idle_test', verbosity=2)
-The following run all Idle tests at a command line. Option '-v' is the same as
-'verbosity=2'. (For 2.7, replace 'test' in the second line with
-'test.regrtest'.)
+The following run all Idle tests at a command line. Option '-v' is the
+same as 'verbosity=2'.
python -m unittest -v idlelib.idle_test
python -m test -v -ugui test_idle
python -m test.test_idle
-The idle tests are 'discovered' by idlelib.idle_test.__init__.load_tests,
-which is also imported into test.test_idle. Normally, neither file should be
-changed when working on individual test modules. The third command runs
-unittest indirectly through regrtest. The same happens when the entire test
-suite is run with 'python -m test'. So that command must work for buildbots
-to stay green. Idle tests must not disturb the environment in a way that
-makes other tests fail (issue 18081).
+The idle tests are 'discovered' by
+idlelib.idle_test.__init__.load_tests, which is also imported into
+test.test_idle. Normally, neither file should be changed when working on
+individual test modules. The third command runs unittest indirectly
+through regrtest. The same happens when the entire test suite is run
+with 'python -m test'. So that command must work for buildbots to stay
+green. Idle tests must not disturb the environment in a way that makes
+other tests fail (issue 18081).
-To run an individual Testcase or test method, extend the dotted name given to
-unittest on the command line.
+To run an individual Testcase or test method, extend the dotted name
+given to unittest on the command line.
python -m unittest -v idlelib.idle_test.test_xyz.Test_case.test_meth
4. Human-mediated Tests
-Human-mediated tests are widget tests that cannot be automated but need human
-verification. They are contained in idlelib/idle_test/htest.py, which has
-instructions. (Some modules need an auxiliary function, identified with # htest
-# on the header line.) The set is about complete, though some tests need
-improvement. To run all htests, run the htest file from an editor or from the
-command line with:
+Human-mediated tests are widget tests that cannot be automated but need
+human verification. They are contained in idlelib/idle_test/htest.py,
+which has instructions. (Some modules need an auxiliary function,
+identified with "# htest # on the header line.) The set is about
+complete, though some tests need improvement. To run all htests, run the
+htest file from an editor or from the command line with:
python -m idlelib.idle_test.htest
diff --git a/Lib/idlelib/idle_test/__init__.py b/Lib/idlelib/idle_test/__init__.py
index 1bc9536..845c92d 100644
--- a/Lib/idlelib/idle_test/__init__.py
+++ b/Lib/idlelib/idle_test/__init__.py
@@ -1,3 +1,9 @@
+'''idlelib.idle_test is a private implementation of test.test_idle,
+which tests the IDLE application as part of the stdlib test suite.
+Run IDLE tests alone with "python -m test.test_idle".
+This package and its contained modules are subject to change and
+any direct use is at your own risk.
+'''
from os.path import dirname
def load_tests(loader, standard_tests, pattern):
diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py
index 3e24518..58e62cb 100644
--- a/Lib/idlelib/idle_test/htest.py
+++ b/Lib/idlelib/idle_test/htest.py
@@ -192,7 +192,10 @@ _io_binding_spec = {
'msg': "Test the following bindings.\n"
"<Control-o> to open file from dialog.\n"
"Edit the file.\n"
+ "<Control-p> to print the file.\n"
"<Control-s> to save the file.\n"
+ "<Alt-s> to save-as another file.\n"
+ "<Control-c> to save-copy-as another file.\n"
"Check that changes were saved by opening the file elsewhere."
}
diff --git a/Lib/idlelib/idle_test/mock_tk.py b/Lib/idlelib/idle_test/mock_tk.py
index 86fe848..6e35129 100644
--- a/Lib/idlelib/idle_test/mock_tk.py
+++ b/Lib/idlelib/idle_test/mock_tk.py
@@ -296,3 +296,8 @@ class Text:
def bind(sequence=None, func=None, add=None):
"Bind to this widget at event sequence a call to function func."
pass
+
+class Entry:
+ "Mock for tkinter.Entry."
+ def focus_set(self):
+ pass
diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py
index 3a2192e..e4493d1 100644
--- a/Lib/idlelib/idle_test/test_autocomplete.py
+++ b/Lib/idlelib/idle_test/test_autocomplete.py
@@ -33,9 +33,8 @@ class AutoCompleteTest(unittest.TestCase):
@classmethod
def tearDownClass(cls):
+ del cls.editor, cls.text
cls.root.destroy()
- del cls.text
- del cls.editor
del cls.root
def setUp(self):
diff --git a/Lib/idlelib/idle_test/test_autoexpand.py b/Lib/idlelib/idle_test/test_autoexpand.py
index 7ca941e..d2a3156 100644
--- a/Lib/idlelib/idle_test/test_autoexpand.py
+++ b/Lib/idlelib/idle_test/test_autoexpand.py
@@ -25,10 +25,10 @@ class AutoExpandTest(unittest.TestCase):
@classmethod
def tearDownClass(cls):
+ del cls.text, cls.auto_expand
if hasattr(cls, 'tk'):
cls.tk.destroy()
del cls.tk
- del cls.text, cls.auto_expand
def tearDown(self):
self.text.delete('1.0', 'end')
diff --git a/Lib/idlelib/idle_test/test_config_help.py b/Lib/idlelib/idle_test/test_config_help.py
new file mode 100644
index 0000000..664f8ed
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_config_help.py
@@ -0,0 +1,106 @@
+"""Unittests for idlelib.configHelpSourceEdit"""
+import unittest
+from idlelib.idle_test.mock_tk import Var, Mbox, Entry
+from idlelib import configHelpSourceEdit as help_dialog_module
+
+help_dialog = help_dialog_module.GetHelpSourceDialog
+
+
+class Dummy_help_dialog:
+ # Mock for testing the following methods of help_dialog
+ menu_ok = help_dialog.menu_ok
+ path_ok = help_dialog.path_ok
+ ok = help_dialog.ok
+ cancel = help_dialog.cancel
+ # Attributes, constant or variable, needed for tests
+ menu = Var()
+ entryMenu = Entry()
+ path = Var()
+ entryPath = Entry()
+ result = None
+ destroyed = False
+
+ def destroy(self):
+ self.destroyed = True
+
+
+# menu_ok and path_ok call Mbox.showerror if menu and path are not ok.
+orig_mbox = help_dialog_module.tkMessageBox
+showerror = Mbox.showerror
+
+
+class ConfigHelpTest(unittest.TestCase):
+ dialog = Dummy_help_dialog()
+
+ @classmethod
+ def setUpClass(cls):
+ help_dialog_module.tkMessageBox = Mbox
+
+ @classmethod
+ def tearDownClass(cls):
+ help_dialog_module.tkMessageBox = orig_mbox
+
+ def test_blank_menu(self):
+ self.dialog.menu.set('')
+ self.assertFalse(self.dialog.menu_ok())
+ self.assertEqual(showerror.title, 'Menu Item Error')
+ self.assertIn('No', showerror.message)
+
+ def test_long_menu(self):
+ self.dialog.menu.set('hello' * 10)
+ self.assertFalse(self.dialog.menu_ok())
+ self.assertEqual(showerror.title, 'Menu Item Error')
+ self.assertIn('long', showerror.message)
+
+ def test_good_menu(self):
+ self.dialog.menu.set('help')
+ showerror.title = 'No Error' # should not be called
+ self.assertTrue(self.dialog.menu_ok())
+ self.assertEqual(showerror.title, 'No Error')
+
+ def test_blank_path(self):
+ self.dialog.path.set('')
+ self.assertFalse(self.dialog.path_ok())
+ self.assertEqual(showerror.title, 'File Path Error')
+ self.assertIn('No', showerror.message)
+
+ def test_invalid_file_path(self):
+ self.dialog.path.set('foobar' * 100)
+ self.assertFalse(self.dialog.path_ok())
+ self.assertEqual(showerror.title, 'File Path Error')
+ self.assertIn('not exist', showerror.message)
+
+ def test_invalid_url_path(self):
+ self.dialog.path.set('ww.foobar.com')
+ self.assertFalse(self.dialog.path_ok())
+ self.assertEqual(showerror.title, 'File Path Error')
+ self.assertIn('not exist', showerror.message)
+
+ self.dialog.path.set('htt.foobar.com')
+ self.assertFalse(self.dialog.path_ok())
+ self.assertEqual(showerror.title, 'File Path Error')
+ self.assertIn('not exist', showerror.message)
+
+ def test_good_path(self):
+ self.dialog.path.set('https://docs.python.org')
+ showerror.title = 'No Error' # should not be called
+ self.assertTrue(self.dialog.path_ok())
+ self.assertEqual(showerror.title, 'No Error')
+
+ def test_ok(self):
+ self.dialog.destroyed = False
+ self.dialog.menu.set('help')
+ self.dialog.path.set('https://docs.python.org')
+ self.dialog.ok()
+ self.assertEqual(self.dialog.result, ('help',
+ 'https://docs.python.org'))
+ self.assertTrue(self.dialog.destroyed)
+
+ def test_cancel(self):
+ self.dialog.destroyed = False
+ self.dialog.cancel()
+ self.assertEqual(self.dialog.result, None)
+ self.assertTrue(self.dialog.destroyed)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, exit=False)
diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py
index 6883123..b063601 100644
--- a/Lib/idlelib/idle_test/test_configdialog.py
+++ b/Lib/idlelib/idle_test/test_configdialog.py
@@ -1,31 +1,31 @@
-'''Unittests for idlelib/configHandler.py
-
-Coverage: 46% just by creating dialog. The other half is change code.
+'''Test idlelib.configDialog.
+Coverage: 46% just by creating dialog.
+The other half is code for working with user customizations.
'''
-import unittest
+from idlelib.configDialog import ConfigDialog # always test import
from test.support import requires
+requires('gui')
from tkinter import Tk
-from idlelib.configDialog import ConfigDialog
-from idlelib.macosxSupport import _initializeTkVariantTests
-
+import unittest
+from idlelib import macosxSupport as macosx
class ConfigDialogTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
- requires('gui')
cls.root = Tk()
- _initializeTkVariantTests(cls.root)
+ macosx._initializeTkVariantTests(cls.root)
@classmethod
def tearDownClass(cls):
+ cls.root.update_idletasks()
cls.root.destroy()
del cls.root
def test_dialog(self):
- d=ConfigDialog(self.root, 'Test', _utest=True)
- d.destroy()
+ d = ConfigDialog(self.root, 'Test', _utest=True)
+ d.remove_var_callbacks()
if __name__ == '__main__':
diff --git a/Lib/idlelib/idle_test/test_delegator.py b/Lib/idlelib/idle_test/test_delegator.py
index b8ae5ee..1f0baa9 100644
--- a/Lib/idlelib/idle_test/test_delegator.py
+++ b/Lib/idlelib/idle_test/test_delegator.py
@@ -4,34 +4,37 @@ from idlelib.Delegator import Delegator
class DelegatorTest(unittest.TestCase):
def test_mydel(self):
- # test a simple use scenario
+ # Test a simple use scenario.
- # initialize
+ # Initialize an int delegator.
mydel = Delegator(int)
self.assertIs(mydel.delegate, int)
self.assertEqual(mydel._Delegator__cache, set())
-
- # add an attribute:
+ # Trying to access a non-attribute of int fails.
self.assertRaises(AttributeError, mydel.__getattr__, 'xyz')
+
+ # Add real int attribute 'bit_length' by accessing it.
bl = mydel.bit_length
self.assertIs(bl, int.bit_length)
self.assertIs(mydel.__dict__['bit_length'], int.bit_length)
self.assertEqual(mydel._Delegator__cache, {'bit_length'})
- # add a second attribute
+ # Add attribute 'numerator'.
mydel.numerator
self.assertEqual(mydel._Delegator__cache, {'bit_length', 'numerator'})
- # delete the second (which, however, leaves it in the name cache)
+ # Delete 'numerator'.
del mydel.numerator
self.assertNotIn('numerator', mydel.__dict__)
- self.assertIn('numerator', mydel._Delegator__cache)
+ # The current implementation leaves it in the name cache.
+ # self.assertIn('numerator', mydel._Delegator__cache)
+ # However, this is not required and not part of the specification
- # reset by calling .setdelegate, which calls .resetcache
- mydel.setdelegate(float)
- self.assertIs(mydel.delegate, float)
+ # Change delegate to float, first resetting the attributes.
+ mydel.setdelegate(float) # calls resetcache
self.assertNotIn('bit_length', mydel.__dict__)
self.assertEqual(mydel._Delegator__cache, set())
+ self.assertIs(mydel.delegate, float)
if __name__ == '__main__':
unittest.main(verbosity=2, exit=2)
diff --git a/Lib/idlelib/idle_test/test_editmenu.py b/Lib/idlelib/idle_test/test_editmenu.py
new file mode 100644
index 0000000..50317a9
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_editmenu.py
@@ -0,0 +1,71 @@
+'''Test (selected) IDLE Edit menu items.
+
+Edit modules have their own test files files
+'''
+from test.support import requires
+requires('gui')
+import tkinter as tk
+import unittest
+from idlelib import PyShell
+
+class PasteTest(unittest.TestCase):
+ '''Test pasting into widgets that allow pasting.
+
+ On X11, replacing selections requires tk fix.
+ '''
+ @classmethod
+ def setUpClass(cls):
+ cls.root = root = tk.Tk()
+ PyShell.fix_x11_paste(root)
+ cls.text = tk.Text(root)
+ cls.entry = tk.Entry(root)
+ cls.spin = tk.Spinbox(root)
+ root.clipboard_clear()
+ root.clipboard_append('two')
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.text, cls.entry, cls.spin
+ cls.root.clipboard_clear()
+ cls.root.update_idletasks()
+ cls.root.destroy()
+ del cls.root
+
+ def test_paste_text(self):
+ "Test pasting into text with and without a selection."
+ text = self.text
+ for tag, ans in ('', 'onetwo\n'), ('sel', 'two\n'):
+ with self.subTest(tag=tag, ans=ans):
+ text.delete('1.0', 'end')
+ text.insert('1.0', 'one', tag)
+ text.event_generate('<<Paste>>')
+ self.assertEqual(text.get('1.0', 'end'), ans)
+
+ def test_paste_entry(self):
+ "Test pasting into an entry with and without a selection."
+ # On 3.6, generated <<Paste>> fails without empty select range
+ # for 'no selection'. Live widget works fine.
+ entry = self.entry
+ for end, ans in (0, 'onetwo'), ('end', 'two'):
+ with self.subTest(entry=entry, end=end, ans=ans):
+ entry.delete(0, 'end')
+ entry.insert(0, 'one')
+ entry.select_range(0, end) # see note
+ entry.event_generate('<<Paste>>')
+ self.assertEqual(entry.get(), ans)
+
+ def test_paste_spin(self):
+ "Test pasting into a spinbox with and without a selection."
+ # See note above for entry.
+ spin = self.spin
+ for end, ans in (0, 'onetwo'), ('end', 'two'):
+ with self.subTest(end=end, ans=ans):
+ spin.delete(0, 'end')
+ spin.insert(0, 'one')
+ spin.selection('range', 0, end) # see note
+ spin.event_generate('<<Paste>>')
+ self.assertEqual(spin.get(), ans)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_formatparagraph.py b/Lib/idlelib/idle_test/test_formatparagraph.py
index f6039e6..e5561d8 100644
--- a/Lib/idlelib/idle_test/test_formatparagraph.py
+++ b/Lib/idlelib/idle_test/test_formatparagraph.py
@@ -276,10 +276,9 @@ class FormatEventTest(unittest.TestCase):
@classmethod
def tearDownClass(cls):
+ del cls.text, cls.formatter
cls.root.destroy()
del cls.root
- del cls.text
- del cls.formatter
def test_short_line(self):
self.text.insert('1.0', "Short line\n")
diff --git a/Lib/idlelib/idle_test/test_help_about.py b/Lib/idlelib/idle_test/test_help_about.py
new file mode 100644
index 0000000..d0a0127
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_help_about.py
@@ -0,0 +1,52 @@
+'''Test idlelib.help_about.
+
+Coverage:
+'''
+from idlelib import aboutDialog as help_about
+from idlelib import textView as textview
+from idlelib.idle_test.mock_idle import Func
+from idlelib.idle_test.mock_tk import Mbox
+import unittest
+
+About = help_about.AboutDialog
+class Dummy_about_dialog():
+ # Dummy class for testing file display functions.
+ idle_credits = About.ShowIDLECredits
+ idle_readme = About.ShowIDLEAbout
+ idle_news = About.ShowIDLENEWS
+ # Called by the above
+ display_file_text = About.display_file_text
+
+
+class DisplayFileTest(unittest.TestCase):
+ "Test that .txt files are found and properly decoded."
+ dialog = Dummy_about_dialog()
+
+ @classmethod
+ def setUpClass(cls):
+ cls.orig_mbox = textview.tkMessageBox
+ cls.orig_view = textview.view_text
+ cls.mbox = Mbox()
+ cls.view = Func()
+ textview.tkMessageBox = cls.mbox
+ textview.view_text = cls.view
+ cls.About = Dummy_about_dialog()
+
+ @classmethod
+ def tearDownClass(cls):
+ textview.tkMessageBox = cls.orig_mbox
+ textview.view_text = cls.orig_view
+
+ def test_file_isplay(self):
+ for handler in (self.dialog.idle_credits,
+ self.dialog.idle_readme,
+ self.dialog.idle_news):
+ self.mbox.showerror.message = ''
+ self.view.called = False
+ handler()
+ self.assertEqual(self.mbox.showerror.message, '')
+ self.assertEqual(self.view.called, True)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_parenmatch.py b/Lib/idlelib/idle_test/test_parenmatch.py
index 9aba4be..95cc22c 100644
--- a/Lib/idlelib/idle_test/test_parenmatch.py
+++ b/Lib/idlelib/idle_test/test_parenmatch.py
@@ -1,10 +1,13 @@
-"""Test idlelib.ParenMatch."""
-# This must currently be a gui test because ParenMatch methods use
-# several text methods not defined on idlelib.idle_test.mock_tk.Text.
+'''Test idlelib.ParenMatch.
+
+This must currently be a gui test because ParenMatch methods use
+several text methods not defined on idlelib.idle_test.mock_tk.Text.
+'''
+from test.support import requires
+requires('gui')
import unittest
from unittest.mock import Mock
-from test.support import requires
from tkinter import Tk, Text
from idlelib.ParenMatch import ParenMatch
@@ -20,7 +23,6 @@ class ParenMatchTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
- requires('gui')
cls.root = Tk()
cls.text = Text(cls.root)
cls.editwin = DummyEditwin(cls.text)
@@ -29,6 +31,7 @@ class ParenMatchTest(unittest.TestCase):
@classmethod
def tearDownClass(cls):
del cls.text, cls.editwin
+ cls.root.update_idletasks()
cls.root.destroy()
del cls.root
diff --git a/Lib/idlelib/idle_test/test_percolator.py b/Lib/idlelib/idle_test/test_percolator.py
new file mode 100644
index 0000000..4c0a7ad
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_percolator.py
@@ -0,0 +1,118 @@
+'''Test Percolator'''
+from test.support import requires
+requires('gui')
+
+import unittest
+from tkinter import Text, Tk, END
+from idlelib.Percolator import Percolator, Delegator
+
+
+class MyFilter(Delegator):
+ def __init__(self):
+ Delegator.__init__(self, None)
+
+ def insert(self, *args):
+ self.insert_called_with = args
+ self.delegate.insert(*args)
+
+ def delete(self, *args):
+ self.delete_called_with = args
+ self.delegate.delete(*args)
+
+ def uppercase_insert(self, index, chars, tags=None):
+ chars = chars.upper()
+ self.delegate.insert(index, chars)
+
+ def lowercase_insert(self, index, chars, tags=None):
+ chars = chars.lower()
+ self.delegate.insert(index, chars)
+
+ def dont_insert(self, index, chars, tags=None):
+ pass
+
+
+class PercolatorTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.root = Tk()
+ cls.text = Text(cls.root)
+
+ @classmethod
+ def tearDownClass(cls):
+ del cls.text
+ cls.root.destroy()
+ del cls.root
+
+ def setUp(self):
+ self.percolator = Percolator(self.text)
+ self.filter_one = MyFilter()
+ self.filter_two = MyFilter()
+ self.percolator.insertfilter(self.filter_one)
+ self.percolator.insertfilter(self.filter_two)
+
+ def tearDown(self):
+ self.percolator.close()
+ self.text.delete('1.0', END)
+
+ def test_insertfilter(self):
+ self.assertIsNotNone(self.filter_one.delegate)
+ self.assertEqual(self.percolator.top, self.filter_two)
+ self.assertEqual(self.filter_two.delegate, self.filter_one)
+ self.assertEqual(self.filter_one.delegate, self.percolator.bottom)
+
+ def test_removefilter(self):
+ filter_three = MyFilter()
+ self.percolator.removefilter(self.filter_two)
+ self.assertEqual(self.percolator.top, self.filter_one)
+ self.assertIsNone(self.filter_two.delegate)
+
+ filter_three = MyFilter()
+ self.percolator.insertfilter(self.filter_two)
+ self.percolator.insertfilter(filter_three)
+ self.percolator.removefilter(self.filter_one)
+ self.assertEqual(self.percolator.top, filter_three)
+ self.assertEqual(filter_three.delegate, self.filter_two)
+ self.assertEqual(self.filter_two.delegate, self.percolator.bottom)
+ self.assertIsNone(self.filter_one.delegate)
+
+ def test_insert(self):
+ self.text.insert('insert', 'foo')
+ self.assertEqual(self.text.get('1.0', END), 'foo\n')
+ self.assertTupleEqual(self.filter_one.insert_called_with,
+ ('insert', 'foo', None))
+
+ def test_modify_insert(self):
+ self.filter_one.insert = self.filter_one.uppercase_insert
+ self.text.insert('insert', 'bAr')
+ self.assertEqual(self.text.get('1.0', END), 'BAR\n')
+
+ def test_modify_chain_insert(self):
+ filter_three = MyFilter()
+ self.percolator.insertfilter(filter_three)
+ self.filter_two.insert = self.filter_two.uppercase_insert
+ self.filter_one.insert = self.filter_one.lowercase_insert
+ self.text.insert('insert', 'BaR')
+ self.assertEqual(self.text.get('1.0', END), 'bar\n')
+
+ def test_dont_insert(self):
+ self.filter_one.insert = self.filter_one.dont_insert
+ self.text.insert('insert', 'foo bar')
+ self.assertEqual(self.text.get('1.0', END), '\n')
+ self.filter_one.insert = self.filter_one.dont_insert
+ self.text.insert('insert', 'foo bar')
+ self.assertEqual(self.text.get('1.0', END), '\n')
+
+ def test_without_filter(self):
+ self.text.insert('insert', 'hello')
+ self.assertEqual(self.text.get('1.0', 'end'), 'hello\n')
+
+ def test_delete(self):
+ self.text.insert('insert', 'foo')
+ self.text.delete('1.0', '1.2')
+ self.assertEqual(self.text.get('1.0', END), 'o\n')
+ self.assertTupleEqual(self.filter_one.delete_called_with,
+ ('1.0', '1.2'))
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_replacedialog.py b/Lib/idlelib/idle_test/test_replacedialog.py
new file mode 100644
index 0000000..ff44820
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_replacedialog.py
@@ -0,0 +1,293 @@
+"""Unittest for idlelib.ReplaceDialog"""
+from test.support import requires
+requires('gui')
+
+import unittest
+from unittest.mock import Mock
+from tkinter import Tk, Text
+from idlelib.idle_test.mock_tk import Mbox
+import idlelib.SearchEngine as se
+import idlelib.ReplaceDialog as rd
+
+orig_mbox = se.tkMessageBox
+showerror = Mbox.showerror
+
+
+class ReplaceDialogTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.root = Tk()
+ cls.root.withdraw()
+ se.tkMessageBox = Mbox
+ cls.engine = se.SearchEngine(cls.root)
+ cls.dialog = rd.ReplaceDialog(cls.root, cls.engine)
+ cls.dialog.ok = Mock()
+ cls.text = Text(cls.root)
+ cls.text.undo_block_start = Mock()
+ cls.text.undo_block_stop = Mock()
+ cls.dialog.text = cls.text
+
+ @classmethod
+ def tearDownClass(cls):
+ se.tkMessageBox = orig_mbox
+ del cls.text, cls.dialog, cls.engine
+ cls.root.destroy()
+ del cls.root
+
+ def setUp(self):
+ self.text.insert('insert', 'This is a sample sTring')
+
+ def tearDown(self):
+ self.engine.patvar.set('')
+ self.dialog.replvar.set('')
+ self.engine.wordvar.set(False)
+ self.engine.casevar.set(False)
+ self.engine.revar.set(False)
+ self.engine.wrapvar.set(True)
+ self.engine.backvar.set(False)
+ showerror.title = ''
+ showerror.message = ''
+ self.text.delete('1.0', 'end')
+
+ def test_replace_simple(self):
+ # Test replace function with all options at default setting.
+ # Wrap around - True
+ # Regular Expression - False
+ # Match case - False
+ # Match word - False
+ # Direction - Forwards
+ text = self.text
+ equal = self.assertEqual
+ pv = self.engine.patvar
+ rv = self.dialog.replvar
+ replace = self.dialog.replace_it
+
+ # test accessor method
+ self.engine.setpat('asdf')
+ equal(self.engine.getpat(), pv.get())
+
+ # text found and replaced
+ pv.set('a')
+ rv.set('asdf')
+ self.dialog.open(self.text)
+ replace()
+ equal(text.get('1.8', '1.12'), 'asdf')
+
+ # dont "match word" case
+ text.mark_set('insert', '1.0')
+ pv.set('is')
+ rv.set('hello')
+ replace()
+ equal(text.get('1.2', '1.7'), 'hello')
+
+ # dont "match case" case
+ pv.set('string')
+ rv.set('world')
+ replace()
+ equal(text.get('1.23', '1.28'), 'world')
+
+ # without "regular expression" case
+ text.mark_set('insert', 'end')
+ text.insert('insert', '\nline42:')
+ before_text = text.get('1.0', 'end')
+ pv.set('[a-z][\d]+')
+ replace()
+ after_text = text.get('1.0', 'end')
+ equal(before_text, after_text)
+
+ # test with wrap around selected and complete a cycle
+ text.mark_set('insert', '1.9')
+ pv.set('i')
+ rv.set('j')
+ replace()
+ equal(text.get('1.8'), 'i')
+ equal(text.get('2.1'), 'j')
+ replace()
+ equal(text.get('2.1'), 'j')
+ equal(text.get('1.8'), 'j')
+ before_text = text.get('1.0', 'end')
+ replace()
+ after_text = text.get('1.0', 'end')
+ equal(before_text, after_text)
+
+ # text not found
+ before_text = text.get('1.0', 'end')
+ pv.set('foobar')
+ replace()
+ after_text = text.get('1.0', 'end')
+ equal(before_text, after_text)
+
+ # test access method
+ self.dialog.find_it(0)
+
+ def test_replace_wrap_around(self):
+ text = self.text
+ equal = self.assertEqual
+ pv = self.engine.patvar
+ rv = self.dialog.replvar
+ replace = self.dialog.replace_it
+ self.engine.wrapvar.set(False)
+
+ # replace candidate found both after and before 'insert'
+ text.mark_set('insert', '1.4')
+ pv.set('i')
+ rv.set('j')
+ replace()
+ equal(text.get('1.2'), 'i')
+ equal(text.get('1.5'), 'j')
+ replace()
+ equal(text.get('1.2'), 'i')
+ equal(text.get('1.20'), 'j')
+ replace()
+ equal(text.get('1.2'), 'i')
+
+ # replace candidate found only before 'insert'
+ text.mark_set('insert', '1.8')
+ pv.set('is')
+ before_text = text.get('1.0', 'end')
+ replace()
+ after_text = text.get('1.0', 'end')
+ equal(before_text, after_text)
+
+ def test_replace_whole_word(self):
+ text = self.text
+ equal = self.assertEqual
+ pv = self.engine.patvar
+ rv = self.dialog.replvar
+ replace = self.dialog.replace_it
+ self.engine.wordvar.set(True)
+
+ pv.set('is')
+ rv.set('hello')
+ replace()
+ equal(text.get('1.0', '1.4'), 'This')
+ equal(text.get('1.5', '1.10'), 'hello')
+
+ def test_replace_match_case(self):
+ equal = self.assertEqual
+ text = self.text
+ pv = self.engine.patvar
+ rv = self.dialog.replvar
+ replace = self.dialog.replace_it
+ self.engine.casevar.set(True)
+
+ before_text = self.text.get('1.0', 'end')
+ pv.set('this')
+ rv.set('that')
+ replace()
+ after_text = self.text.get('1.0', 'end')
+ equal(before_text, after_text)
+
+ pv.set('This')
+ replace()
+ equal(text.get('1.0', '1.4'), 'that')
+
+ def test_replace_regex(self):
+ equal = self.assertEqual
+ text = self.text
+ pv = self.engine.patvar
+ rv = self.dialog.replvar
+ replace = self.dialog.replace_it
+ self.engine.revar.set(True)
+
+ before_text = text.get('1.0', 'end')
+ pv.set('[a-z][\d]+')
+ rv.set('hello')
+ replace()
+ after_text = text.get('1.0', 'end')
+ equal(before_text, after_text)
+
+ text.insert('insert', '\nline42')
+ replace()
+ equal(text.get('2.0', '2.8'), 'linhello')
+
+ pv.set('')
+ replace()
+ self.assertIn('error', showerror.title)
+ self.assertIn('Empty', showerror.message)
+
+ pv.set('[\d')
+ replace()
+ self.assertIn('error', showerror.title)
+ self.assertIn('Pattern', showerror.message)
+
+ showerror.title = ''
+ showerror.message = ''
+ pv.set('[a]')
+ rv.set('test\\')
+ replace()
+ self.assertIn('error', showerror.title)
+ self.assertIn('Invalid Replace Expression', showerror.message)
+
+ # test access method
+ self.engine.setcookedpat("\'")
+ equal(pv.get(), "\\'")
+
+ def test_replace_backwards(self):
+ equal = self.assertEqual
+ text = self.text
+ pv = self.engine.patvar
+ rv = self.dialog.replvar
+ replace = self.dialog.replace_it
+ self.engine.backvar.set(True)
+
+ text.insert('insert', '\nis as ')
+
+ pv.set('is')
+ rv.set('was')
+ replace()
+ equal(text.get('1.2', '1.4'), 'is')
+ equal(text.get('2.0', '2.3'), 'was')
+ replace()
+ equal(text.get('1.5', '1.8'), 'was')
+ replace()
+ equal(text.get('1.2', '1.5'), 'was')
+
+ def test_replace_all(self):
+ text = self.text
+ pv = self.engine.patvar
+ rv = self.dialog.replvar
+ replace_all = self.dialog.replace_all
+
+ text.insert('insert', '\n')
+ text.insert('insert', text.get('1.0', 'end')*100)
+ pv.set('is')
+ rv.set('was')
+ replace_all()
+ self.assertNotIn('is', text.get('1.0', 'end'))
+
+ self.engine.revar.set(True)
+ pv.set('')
+ replace_all()
+ self.assertIn('error', showerror.title)
+ self.assertIn('Empty', showerror.message)
+
+ pv.set('[s][T]')
+ rv.set('\\')
+ replace_all()
+
+ self.engine.revar.set(False)
+ pv.set('text which is not present')
+ rv.set('foobar')
+ replace_all()
+
+ def test_default_command(self):
+ text = self.text
+ pv = self.engine.patvar
+ rv = self.dialog.replvar
+ replace_find = self.dialog.default_command
+ equal = self.assertEqual
+
+ pv.set('This')
+ rv.set('was')
+ replace_find()
+ equal(text.get('sel.first', 'sel.last'), 'was')
+
+ self.engine.revar.set(True)
+ pv.set('')
+ replace_find()
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_searchdialog.py b/Lib/idlelib/idle_test/test_searchdialog.py
new file mode 100644
index 0000000..190c866
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_searchdialog.py
@@ -0,0 +1,80 @@
+"""Test SearchDialog class in SearchDialogue.py"""
+
+# Does not currently test the event handler wrappers.
+# A usage test should simulate clicks and check hilighting.
+# Tests need to be coordinated with SearchDialogBase tests
+# to avoid duplication.
+
+from test.support import requires
+requires('gui')
+
+import unittest
+import tkinter as tk
+from tkinter import BooleanVar
+import idlelib.SearchEngine as se
+import idlelib.SearchDialog as sd
+
+
+class SearchDialogTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.root = tk.Tk()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.root.destroy()
+ del cls.root
+
+ def setUp(self):
+ self.engine = se.SearchEngine(self.root)
+ self.dialog = sd.SearchDialog(self.root, self.engine)
+ self.text = tk.Text(self.root)
+ self.text.insert('1.0', 'Hello World!')
+
+ def test_find_again(self):
+ # Search for various expressions
+ text = self.text
+
+ self.engine.setpat('')
+ self.assertFalse(self.dialog.find_again(text))
+
+ self.engine.setpat('Hello')
+ self.assertTrue(self.dialog.find_again(text))
+
+ self.engine.setpat('Goodbye')
+ self.assertFalse(self.dialog.find_again(text))
+
+ self.engine.setpat('World!')
+ self.assertTrue(self.dialog.find_again(text))
+
+ self.engine.setpat('Hello World!')
+ self.assertTrue(self.dialog.find_again(text))
+
+ # Regular expression
+ self.engine.revar = BooleanVar(self.root, True)
+ self.engine.setpat('W[aeiouy]r')
+ self.assertTrue(self.dialog.find_again(text))
+
+ def test_find_selection(self):
+ # Select some text and make sure it's found
+ text = self.text
+ # Add additional line to find
+ self.text.insert('2.0', 'Hello World!')
+
+ text.tag_add('sel', '1.0', '1.4') # Select 'Hello'
+ self.assertTrue(self.dialog.find_selection(text))
+
+ text.tag_remove('sel', '1.0', 'end')
+ text.tag_add('sel', '1.6', '1.11') # Select 'World!'
+ self.assertTrue(self.dialog.find_selection(text))
+
+ text.tag_remove('sel', '1.0', 'end')
+ text.tag_add('sel', '1.0', '1.11') # Select 'Hello World!'
+ self.assertTrue(self.dialog.find_selection(text))
+
+ # Remove additional line
+ text.delete('2.0', 'end')
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, exit=2)
diff --git a/Lib/idlelib/idle_test/test_searchengine.py b/Lib/idlelib/idle_test/test_searchengine.py
index c7792fb..edbd558 100644
--- a/Lib/idlelib/idle_test/test_searchengine.py
+++ b/Lib/idlelib/idle_test/test_searchengine.py
@@ -178,7 +178,7 @@ class SearchEngineTest(unittest.TestCase):
engine.revar.set(1)
Equal(engine.getprog(), None)
self.assertEqual(Mbox.showerror.message,
- 'Error: nothing to repeat\nPattern: +')
+ 'Error: nothing to repeat at position 0\nPattern: +')
def test_report_error(self):
showerror = Mbox.showerror
diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py
index 68e5b82..02d1472 100644
--- a/Lib/idlelib/idle_test/test_textview.py
+++ b/Lib/idlelib/idle_test/test_textview.py
@@ -1,4 +1,4 @@
-'''Test the functions and main class method of textView.py.
+'''Test idlelib.textView.
Since all methods and functions create (or destroy) a TextViewer, which
is a widget containing multiple widgets, all tests must be gui tests.
@@ -22,7 +22,9 @@ def setUpModule():
root = Tk()
def tearDownModule():
- global root
+ global root, TV
+ del TV
+ root.update_idletasks()
root.destroy() # pyflakes falsely sees root as undefined
del root
diff --git a/Lib/idlelib/idle_test/test_undodelegator.py b/Lib/idlelib/idle_test/test_undodelegator.py
new file mode 100644
index 0000000..2b83c99
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_undodelegator.py
@@ -0,0 +1,135 @@
+"""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()
+ del cls.percolator, cls.text
+ cls.root.destroy()
+ del 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('<<undo>>')
+ 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('<<undo>>')
+ self.assertEqual(text.get('1.0', '1.4'), 'foar')
+ text.event_generate('<<undo>>')
+ self.assertEqual(text.get('1.0', '1.6'), 'foobar')
+ text.event_generate('<<undo>>')
+ self.assertEqual(text.get('1.0', '1.3'), 'foo')
+ text.event_generate('<<undo>>')
+ 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('<<undo>>')
+ text.event_generate('<<redo>>')
+ self.assertEqual(text.get('1.0', '1.3'), 'bar')
+ text.event_generate('<<redo>>')
+ 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('<<undo>>')
+ 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)
diff --git a/Lib/idlelib/idle_test/test_warning.py b/Lib/idlelib/idle_test/test_warning.py
index 54ac993..18627dd 100644
--- a/Lib/idlelib/idle_test/test_warning.py
+++ b/Lib/idlelib/idle_test/test_warning.py
@@ -68,15 +68,6 @@ class ShellWarnTest(unittest.TestCase):
'Test', UserWarning, 'test_warning.py', 99, f, 'Line of code')
self.assertEqual(shellmsg.splitlines(), f.getvalue().splitlines())
-class ImportWarnTest(unittest.TestCase):
- def test_idlever(self):
- with warnings.catch_warnings(record=True) as w:
- warnings.simplefilter("always")
- import idlelib.idlever
- self.assertEqual(len(w), 1)
- self.assertTrue(issubclass(w[-1].category, DeprecationWarning))
- self.assertIn("version", str(w[-1].message))
-
if __name__ == '__main__':
unittest.main(verbosity=2, exit=False)
diff --git a/Lib/idlelib/idle_test/test_widgetredir.py b/Lib/idlelib/idle_test/test_widgetredir.py
index 6440561..c68dfcc 100644
--- a/Lib/idlelib/idle_test/test_widgetredir.py
+++ b/Lib/idlelib/idle_test/test_widgetredir.py
@@ -1,7 +1,7 @@
-"""Unittest for idlelib.WidgetRedirector
+'''Test idlelib.WidgetRedirector.
100% coverage
-"""
+'''
from test.support import requires
import unittest
from idlelib.idle_test.mock_idle import Func
@@ -14,14 +14,14 @@ class InitCloseTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
requires('gui')
- cls.tk = Tk()
- cls.text = Text(cls.tk)
+ cls.root = Tk()
+ cls.text = Text(cls.root)
@classmethod
def tearDownClass(cls):
- cls.text.destroy()
- cls.tk.destroy()
- del cls.text, cls.tk
+ del cls.text
+ cls.root.destroy()
+ del cls.root
def test_init(self):
redir = WidgetRedirector(self.text)
@@ -43,14 +43,15 @@ class WidgetRedirectorTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
requires('gui')
- cls.tk = Tk()
- cls.text = Text(cls.tk)
+ cls.root = Tk()
+ cls.text = Text(cls.root)
@classmethod
def tearDownClass(cls):
- cls.text.destroy()
- cls.tk.destroy()
- del cls.text, cls.tk
+ del cls.text
+ cls.root.update_idletasks()
+ cls.root.destroy()
+ del cls.root
def setUp(self):
self.redir = WidgetRedirector(self.text)
@@ -108,13 +109,13 @@ class WidgetRedirectorTest(unittest.TestCase):
def test_command_dispatch(self):
# Test that .__init__ causes redirection of tk calls
# through redir.dispatch
- self.tk.call(self.text._w, 'insert', 'hello')
+ self.root.call(self.text._w, 'insert', 'hello')
self.assertEqual(self.func.args, ('hello',))
self.assertEqual(self.text.get('1.0', 'end'), '\n')
# Ensure that called through redir .dispatch and not through
# self.text.insert by having mock raise TclError.
self.func.__init__(TclError())
- self.assertEqual(self.tk.call(self.text._w, 'insert', 'boo'), '')
+ self.assertEqual(self.root.call(self.text._w, 'insert', 'boo'), '')