diff -r 3d5f6198c945 Lib/idlelib/ReplaceDialog.py --- a/Lib/idlelib/ReplaceDialog.py Wed Jun 04 21:01:08 2014 -0400 +++ b/Lib/idlelib/ReplaceDialog.py Fri Jun 06 11:20:28 2014 +0530 @@ -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) @@ -57,9 +66,10 @@ self.do_replace() def default_command(self, event=None): + """ Function for 'replace+find' action""" if self.do_find(self.ok): - if self.do_replace(): # Only find next match if replace succeeded. - # A bad re can cause a it to fail. + if self.do_replace(): # Only find next match if replace succeeded + # A bad re can cause a 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") @@ -190,6 +203,7 @@ self.text.tag_remove("hit", "1.0", "end") def _replace_dialog(parent): + """htest wrapper function""" root = Tk() root.title("Test ReplaceDialog") width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) @@ -206,7 +220,8 @@ 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") + text.focus_set() def show_replace(): text.tag_add(SEL, "1.0", END) @@ -217,5 +232,9 @@ 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 -r 3d5f6198c945 Lib/idlelib/idle_test/test_replacedialog.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/idlelib/idle_test/test_replacedialog.py Fri Jun 06 11:20:28 2014 +0530 @@ -0,0 +1,294 @@ +"""Unittest for idlelib.ReplaceDialog""" +import unittest +from unittest.mock import Mock +from test.support import requires +from tkinter import Tk, Text +#from idlelib.idle_test.mock_tk import 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): + requires('gui') + cls.tk = Tk() + cls.tk.withdraw() + se.tkMessageBox = Mbox + cls.engine = se.SearchEngine(cls.tk) + cls.dialog = rd.ReplaceDialog(cls.tk, cls.engine) + cls.dialog.ok = Mock() + cls.text = Text() + 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.text.destroy() + del cls.text + cls.tk.destroy() + del cls.tk + + 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): + """ + A simple sanity check for replace function. + when all options are at default setting i.e. + 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') + 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('unexpected', 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)