[Python-checkins] cpython (3.5): Issue #21676: test IDLE replace dialog. Original patch by Saimadhav Heblikar.

terry.reedy python-checkins at python.org
Tue May 17 19:58:42 EDT 2016


https://hg.python.org/cpython/rev/d8ed90a35505
changeset:   101416:d8ed90a35505
branch:      3.5
parent:      101413:a2cb99027646
user:        Terry Jan Reedy <tjreedy at udel.edu>
date:        Tue May 17 19:58:02 2016 -0400
summary:
  Issue #21676: test IDLE replace dialog.  Original patch by Saimadhav Heblikar.

files:
  Lib/idlelib/ReplaceDialog.py                |   38 +-
  Lib/idlelib/idle_test/test_replacedialog.py |  292 ++++++++++
  2 files changed, 321 insertions(+), 9 deletions(-)


diff --git a/Lib/idlelib/ReplaceDialog.py b/Lib/idlelib/ReplaceDialog.py
--- a/Lib/idlelib/ReplaceDialog.py
+++ b/Lib/idlelib/ReplaceDialog.py
@@ -1,3 +1,8 @@
+"""Replace dialog for IDLE. Inherits SearchDialogBase for GUI.
+Uses idlelib.SearchEngine for search capability.
+Defines various replace related functions like replace, replace all,
+replace+find.
+"""
 from tkinter import *
 
 from idlelib import SearchEngine
@@ -6,6 +11,8 @@
 
 
 def replace(text):
+    """Returns a singleton ReplaceDialog instance.The single dialog
+     saves user entries and preferences across instances."""
     root = text._root()
     engine = SearchEngine.get(root)
     if not hasattr(engine, "_replacedialog"):
@@ -24,6 +31,7 @@
         self.replvar = StringVar(root)
 
     def open(self, text):
+        """Display the replace dialog"""
         SearchDialogBase.open(self, text)
         try:
             first = text.index("sel.first")
@@ -39,6 +47,7 @@
         self.ok = 1
 
     def create_entries(self):
+        """Create label and text entry widgets"""
         SearchDialogBase.create_entries(self)
         self.replent = self.make_entry("Replace with:", self.replvar)[0]
 
@@ -57,9 +66,10 @@
             self.do_replace()
 
     def default_command(self, event=None):
+        "Replace and find next."
         if self.do_find(self.ok):
-            if self.do_replace():   # Only find next match if replace succeeded.
-                                    # A bad re can cause it to fail.
+            if self.do_replace():  # Only find next match if replace succeeded.
+                                   # A bad re can cause it to fail.
                 self.do_find(0)
 
     def _replace_expand(self, m, repl):
@@ -77,6 +87,7 @@
         return new
 
     def replace_all(self, event=None):
+        """Replace all instances of patvar with replvar in text"""
         prog = self.engine.getprog()
         if not prog:
             return
@@ -173,6 +184,8 @@
         return True
 
     def show_hit(self, first, last):
+        """Highlight text from 'first' to 'last'.
+        'first', 'last' - Text indices"""
         text = self.text
         text.mark_set("insert", first)
         text.tag_remove("sel", "1.0", "end")
@@ -189,11 +202,13 @@
         SearchDialogBase.close(self, event)
         self.text.tag_remove("hit", "1.0", "end")
 
-def _replace_dialog(parent):
-    root = Tk()
-    root.title("Test ReplaceDialog")
+
+def _replace_dialog(parent):  # htest #
+    """htest wrapper function"""
+    box = Toplevel(parent)
+    box.title("Test ReplaceDialog")
     width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
-    root.geometry("+%d+%d"%(x, y + 150))
+    box.geometry("+%d+%d"%(x, y + 150))
 
     # mock undo delegator methods
     def undo_block_start():
@@ -202,20 +217,25 @@
     def undo_block_stop():
         pass
 
-    text = Text(root)
+    text = Text(box, inactiveselectbackground='gray')
     text.undo_block_start = undo_block_start
     text.undo_block_stop = undo_block_stop
     text.pack()
-    text.insert("insert","This is a sample string.\n"*10)
+    text.insert("insert","This is a sample sTring\nPlus MORE.")
+    text.focus_set()
 
     def show_replace():
         text.tag_add(SEL, "1.0", END)
         replace(text)
         text.tag_remove(SEL, "1.0", END)
 
-    button = Button(root, text="Replace", command=show_replace)
+    button = Button(box, text="Replace", command=show_replace)
     button.pack()
 
 if __name__ == '__main__':
+    import unittest
+    unittest.main('idlelib.idle_test.test_replacedialog',
+                verbosity=2, exit=False)
+
     from idlelib.idle_test.htest import run
     run(_replace_dialog)
diff --git a/Lib/idlelib/idle_test/test_replacedialog.py b/Lib/idlelib/idle_test/test_replacedialog.py
new file mode 100644
--- /dev/null
+++ b/Lib/idlelib/idle_test/test_replacedialog.py
@@ -0,0 +1,292 @@
+"""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
+        cls.root.destroy()
+        del cls.text, cls.dialog, cls.engine, 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)

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list