import unittest import tkinter from tkinter import TclError from test.support import requires from test.test_tkinter.support import setUpModule # noqa: F401 from test.test_tkinter.support import AbstractTkTest, requires_tk requires('gui') class TextTest(AbstractTkTest, unittest.TestCase): def setUp(self): super().setUp() self.text = tkinter.Text(self.root) self.text.pack() def test_debug(self): text = self.text olddebug = text.debug() try: text.debug(0) self.assertEqual(text.debug(), 0) text.debug(1) self.assertEqual(text.debug(), 1) finally: text.debug(olddebug) self.assertEqual(text.debug(), olddebug) def test_index(self): text = self.text text.insert('1.0', 'Lorem ipsum\ndolor sit amet') self.assertEqual(text.index('1.0'), '1.0') self.assertEqual(text.index('1.end'), '1.11') self.assertEqual(text.index('end'), '3.0') self.assertEqual(text.index('2.5'), '2.5') # Index past the end of a line is clamped to its end. self.assertEqual(text.index('1.100'), '1.11') self.assertRaises(TclError, text.index, 'invalid') self.assertRaises(TypeError, text.index) self.assertRaises(TypeError, text.index, '1.0', '2.5') def test_compare(self): text = self.text text.insert('1.0', 'Lorem ipsum\ndolor sit amet') self.assertIs(text.compare('1.0', '<', '2.0'), True) self.assertIs(text.compare('2.0', '<', '1.0'), False) self.assertIs(text.compare('1.5', '==', '1.5'), True) self.assertIs(text.compare('1.5', '!=', '1.5'), False) self.assertIs(text.compare('2.0', '>=', '2.0'), True) self.assertIs(text.compare('1.0', '<=', 'end'), True) self.assertRaises(TclError, text.compare, '1.0', 'invalid', '2.0') self.assertRaises(TypeError, text.compare, '1.0', '<') self.assertRaises(TypeError, text.compare, '1.0', '<', '2.0', '3.0') def test_insert_get_delete(self): text = self.text text.insert('1.0', 'Lorem ipsum') self.assertEqual(text.get('1.0', 'end'), 'Lorem ipsum\n') self.assertEqual(text.get('1.0', '1.5'), 'Lorem') self.assertEqual(text.get('1.6'), 'i') # single character # insert before an existing index text.insert('1.0', '*** ') self.assertEqual(text.get('1.0', 'end'), '*** Lorem ipsum\n') text.delete('1.0', '1.4') self.assertEqual(text.get('1.0', 'end'), 'Lorem ipsum\n') text.delete('1.5') # delete a single character self.assertEqual(text.get('1.0', 'end'), 'Loremipsum\n') self.assertRaises(TypeError, text.get) self.assertRaises(TypeError, text.get, '1.0', '1.5', 'end') self.assertRaises(TypeError, text.delete, '1.0', '1.5', 'end') def test_insert_with_tags(self): text = self.text text.insert('1.0', 'hello', 'a', ' ', ('a', 'b'), 'world', 'b') self.assertEqual(text.get('1.0', 'end'), 'hello world\n') self.assertEqual([str(i) for i in text.tag_ranges('a')], ['1.0', '1.6']) self.assertEqual([str(i) for i in text.tag_ranges('b')], ['1.5', '1.11']) def test_replace(self): text = self.text text.insert('1.0', 'Lorem ipsum') text.replace('1.0', '1.5', 'Hello') self.assertEqual(text.get('1.0', 'end'), 'Hello ipsum\n') text.replace('1.6', 'end - 1 char', 'world') self.assertEqual(text.get('1.0', 'end'), 'Hello world\n') self.assertRaises(TclError, text.replace, 'invalid', '1.5', 'x') # The first index must not be after the second. self.assertRaises(TclError, text.replace, '1.5', '1.0', 'x') self.assertRaises(TypeError, text.replace, '1.0', '1.5') def test_mark(self): text = self.text text.insert('1.0', 'Lorem ipsum\ndolor sit amet') # 'insert' and 'current' marks always exist. self.assertIn('insert', text.mark_names()) self.assertIn('current', text.mark_names()) text.mark_set('here', '1.5') self.assertIn('here', text.mark_names()) self.assertEqual(text.index('here'), '1.5') text.mark_set('here', '2.3') self.assertEqual(text.index('here'), '2.3') text.mark_unset('here') self.assertNotIn('here', text.mark_names()) # Unsetting a non-existent mark is not an error. text.mark_unset('nonexistent') self.assertRaises(TclError, text.mark_set, 'm', 'invalid') self.assertRaises(TypeError, text.mark_set, 'm') self.assertRaises(TypeError, text.mark_set, 'm', '1.0', '2.0') def test_mark_gravity(self): text = self.text text.insert('1.0', 'Lorem ipsum') text.mark_set('here', '1.5') # The default gravity is right. self.assertEqual(text.mark_gravity('here'), 'right') text.mark_gravity('here', 'left') self.assertEqual(text.mark_gravity('here'), 'left') # With left gravity the mark stays before inserted text. text.insert('here', 'XXX') self.assertEqual(text.index('here'), '1.5') # With right gravity the mark moves after inserted text. text.mark_gravity('here', 'right') text.insert('here', 'YYY') self.assertEqual(text.index('here'), '1.8') self.assertRaises(TclError, text.mark_gravity, 'nonexistent') self.assertRaises(TclError, text.mark_gravity, 'here', 'invalid') self.assertRaises(TypeError, text.mark_gravity) self.assertRaises(TypeError, text.mark_gravity, 'here', 'left', 'extra') def test_mark_next_previous(self): text = self.text text.insert('1.0', 'Lorem ipsum') # Keep the builtin 'insert' and 'current' marks at 1.0 so they do # not interfere with the queries below. text.mark_set('insert', '1.0') text.mark_set('current', '1.0') text.mark_set('m1', '1.3') text.mark_set('m2', '1.7') # mark_next finds a mark at a position at or after the index, # mark_previous finds one at a position strictly before it. self.assertEqual(text.mark_next('1.1'), 'm1') self.assertEqual(text.mark_next('1.3'), 'm1') self.assertEqual(text.mark_next('1.4'), 'm2') self.assertIsNone(text.mark_next('1.8')) self.assertEqual(text.mark_previous('1.4'), 'm1') self.assertEqual(text.mark_previous('1.7'), 'm1') self.assertEqual(text.mark_previous('end'), 'm2') self.assertIsNone(text.mark_previous('1.0')) self.assertRaises(TclError, text.mark_next, 'invalid') self.assertRaises(TclError, text.mark_previous, 'invalid') self.assertRaises(TypeError, text.mark_next) self.assertRaises(TypeError, text.mark_previous) self.assertRaises(TypeError, text.mark_next, '1.0', '2.0') self.assertRaises(TypeError, text.mark_previous, '1.0', '2.0') def test_tag_add_remove_ranges(self): text = self.text text.insert('1.0', 'Lorem ipsum\ndolor sit amet') self.assertEqual(text.tag_ranges('sel'), ()) text.tag_add('big', '1.0', '1.5') self.assertEqual([str(i) for i in text.tag_ranges('big')], ['1.0', '1.5']) # Add a second, disjoint range. text.tag_add('big', '2.0', '2.5') self.assertEqual([str(i) for i in text.tag_ranges('big')], ['1.0', '1.5', '2.0', '2.5']) text.tag_remove('big', '1.0', '1.3') self.assertEqual([str(i) for i in text.tag_ranges('big')], ['1.3', '1.5', '2.0', '2.5']) # tag_ranges of an undefined tag is empty, not an error. self.assertEqual(text.tag_ranges('nonexistent'), ()) self.assertRaises(TclError, text.tag_add, 'big', 'invalid') self.assertRaises(TypeError, text.tag_add, 'big') self.assertRaises(TclError, text.tag_remove, 'big', 'invalid') self.assertRaises(TypeError, text.tag_remove, 'big', '1.0', '2.0', '3.0') self.assertRaises(TypeError, text.tag_ranges, 'big', 'extra') def test_tag_names(self): text = self.text text.insert('1.0', 'Lorem ipsum') text.tag_add('a', '1.0', '1.5') text.tag_add('b', '1.3', '1.8') # 'sel' always exists; order reflects priority (creation order). self.assertEqual(set(text.tag_names()), {'sel', 'a', 'b'}) # Tags applied to the character at the given index. self.assertEqual(set(text.tag_names('1.4')), {'a', 'b'}) self.assertEqual(set(text.tag_names('1.0')), {'a'}) self.assertEqual(set(text.tag_names('1.10')), set()) self.assertRaises(TclError, text.tag_names, 'invalid') self.assertRaises(TypeError, text.tag_names, '1.0', '2.0') def test_tag_nextrange_prevrange(self): text = self.text text.insert('1.0', 'Lorem ipsum dolor') text.tag_add('a', '1.0', '1.5') text.tag_add('a', '1.12', '1.17') self.assertEqual([str(i) for i in text.tag_nextrange('a', '1.0')], ['1.0', '1.5']) self.assertEqual([str(i) for i in text.tag_nextrange('a', '1.5')], ['1.12', '1.17']) self.assertEqual(text.tag_nextrange('a', '1.17'), ()) self.assertEqual([str(i) for i in text.tag_prevrange('a', 'end')], ['1.12', '1.17']) self.assertEqual([str(i) for i in text.tag_prevrange('a', '1.12')], ['1.0', '1.5']) self.assertEqual(text.tag_prevrange('a', '1.0'), ()) # An undefined tag has no ranges, but the index must be valid. self.assertEqual(text.tag_nextrange('nonexistent', '1.0'), ()) self.assertRaises(TclError, text.tag_nextrange, 'a', 'invalid') self.assertRaises(TclError, text.tag_prevrange, 'a', 'invalid') self.assertRaises(TypeError, text.tag_nextrange, 'a') self.assertRaises(TypeError, text.tag_prevrange, 'a') self.assertRaises(TypeError, text.tag_nextrange, 'a', '1.0', '2.0', '3.0') self.assertRaises(TypeError, text.tag_prevrange, 'a', '1.0', '2.0', '3.0') def test_tag_delete(self): text = self.text text.insert('1.0', 'Lorem ipsum') text.tag_add('a', '1.0', '1.5') text.tag_add('b', '1.6', '1.11') self.assertEqual(set(text.tag_names()), {'sel', 'a', 'b'}) text.tag_delete('a', 'b') self.assertEqual(set(text.tag_names()), {'sel'}) self.assertEqual(text.tag_ranges('a'), ()) def test_tag_raise_lower(self): text = self.text text.tag_configure('a', foreground='red') text.tag_configure('b', foreground='green') text.tag_configure('c', foreground='blue') # Creation order is lowest-to-highest priority; tag_names lists # them in increasing priority order. self.assertEqual(text.tag_names(), ('sel', 'a', 'b', 'c')) text.tag_raise('a') self.assertEqual(text.tag_names(), ('sel', 'b', 'c', 'a')) text.tag_lower('a') self.assertEqual(text.tag_names(), ('a', 'sel', 'b', 'c')) text.tag_raise('a', 'b') self.assertEqual(text.tag_names(), ('sel', 'b', 'a', 'c')) text.tag_lower('c', 'b') self.assertEqual(text.tag_names(), ('sel', 'c', 'b', 'a')) self.assertRaises(TclError, text.tag_raise, 'nonexistent') self.assertRaises(TclError, text.tag_lower, 'nonexistent') self.assertRaises(TclError, text.tag_raise, 'a', 'nonexistent') self.assertRaises(TypeError, text.tag_raise) self.assertRaises(TypeError, text.tag_raise, 'a', 'b', 'c') self.assertRaises(TypeError, text.tag_lower, 'a', 'b', 'c') def test_tag_cget_configure(self): text = self.text text.tag_configure('a', foreground='red', underline=True) self.assertEqual(str(text.tag_cget('a', 'foreground')), 'red') self.assertIn(text.tag_cget('a', 'underline'), (1, '1', True)) # tag_cget normalizes the option name (no leading hyphen needed). self.assertEqual(str(text.tag_cget('a', '-foreground')), 'red') # configure() query returns the full option spec. self.assertEqual(text.tag_configure('a', 'foreground')[-1], 'red') text.tag_configure('a', foreground='blue') self.assertEqual(str(text.tag_cget('a', 'foreground')), 'blue') self.assertRaises(TclError, text.tag_cget, 'nonexistent', 'foreground') self.assertRaises(TypeError, text.tag_cget, 'a') self.assertRaises(TypeError, text.tag_cget, 'a', 'foreground', 'extra') def test_edit_modified(self): text = self.text self.assertEqual(text.edit_modified(), 0) text.insert('1.0', 'spam') self.assertEqual(text.edit_modified(), 1) text.edit_modified(False) self.assertEqual(text.edit_modified(), 0) text.edit_modified(True) self.assertEqual(text.edit_modified(), 1) def test_edit_undo_redo(self): text = self.text text.configure(undo=True) text.insert('1.0', 'spam') text.edit_separator() text.insert('end', ' eggs') self.assertEqual(text.get('1.0', 'end'), 'spam eggs\n') text.edit_undo() self.assertEqual(text.get('1.0', 'end'), 'spam\n') text.edit_redo() self.assertEqual(text.get('1.0', 'end'), 'spam eggs\n') # An empty undo stack raises. text.edit_reset() self.assertRaises(TclError, text.edit_undo) def test_dump(self): text = self.text text.insert('1.0', 'hello') text.tag_add('a', '1.0', '1.3') text.mark_set('here', '1.2') dump = text.dump('1.0', 'end') self.assertIsInstance(dump, list) for item in dump: self.assertIsInstance(item, tuple) self.assertEqual(len(item), 3) kinds = {item[0] for item in dump} self.assertIn('text', kinds) self.assertIn('tagon', kinds) self.assertIn('mark', kinds) # Filtering by kind. text_only = text.dump('1.0', 'end', text=True) self.assertEqual({item[0] for item in text_only}, {'text'}) self.assertEqual(''.join(item[1] for item in text_only), 'hello\n') # The command callback receives each triple and suppresses the result. collected = [] result = text.dump('1.0', 'end', text=True, command=lambda *a: collected.append(a)) self.assertIsNone(result) self.assertEqual(''.join(key_value_index[1] for key_value_index in collected), 'hello\n') self.assertRaises(TclError, text.dump, 'invalid') self.assertRaises(TypeError, text.dump) self.assertRaises(TypeError, text.dump, '1.0', 'end', None, 'extra') def test_image(self): text = self.text image = tkinter.PhotoImage(master=self.root, width=10, height=10) text.insert('1.0', 'AB') name = text.image_create('1.1', image=image) self.assertIsInstance(name, str) self.assertIn(name, self.root.splitlist(text.image_names())) self.assertEqual(str(text.image_cget(name, 'image')), str(image)) text.image_configure(name, align='top') self.assertEqual(str(text.image_cget(name, 'align')), 'top') # An embedded image occupies a single index position. self.assertEqual(text.index('end - 1 char'), '1.3') # Either a name or an image is required, and the index must be valid. self.assertRaises(TclError, text.image_create, '1.0') self.assertRaises(TclError, text.image_create, 'invalid', image=image) self.assertRaises(TypeError, text.image_cget, name) self.assertRaises(TypeError, text.image_cget, name, 'image', 'extra') def test_window(self): text = self.text button = tkinter.Button(text, text='ok') text.insert('1.0', 'AB') text.window_create('1.1', window=button) self.assertEqual(self.root.splitlist(text.window_names()), (str(button),)) self.assertEqual(text.window_cget('1.1', 'window'), str(button)) text.window_configure('1.1', padx=5) self.assertEqual(text.window_cget('1.1', 'padx'), 5) # No embedded window at this index, and the index must be valid. self.assertRaises(TclError, text.window_cget, '1.0', 'window') self.assertRaises(TclError, text.window_cget, 'invalid', 'window') self.assertRaises(TypeError, text.window_cget, '1.1') self.assertRaises(TypeError, text.window_cget, '1.1', 'window', 'extra') button.destroy() def test_tag_configure(self): text = self.text tag = 'a' getint = text.tk.getint getboolean = text.tk.getboolean # Color options. for opt in ('foreground', 'background'): text.tag_configure(tag, **{opt: '#ff0000'}) self.assertEqual(str(text.tag_cget(tag, opt)), '#ff0000') # Stipple (bitmap) options. for opt in ('bgstipple', 'fgstipple'): text.tag_configure(tag, **{opt: 'gray50'}) self.assertEqual(str(text.tag_cget(tag, opt)), 'gray50') # Enumerated options. for opt, value in (('justify', 'center'), ('wrap', 'word'), ('relief', 'raised'), ('tabstyle', 'wordprocessor'), ('offset', '5')): text.tag_configure(tag, **{opt: value}) self.assertEqual(str(text.tag_cget(tag, 'justify')), 'center') self.assertEqual(str(text.tag_cget(tag, 'wrap')), 'word') self.assertEqual(str(text.tag_cget(tag, 'relief')), 'raised') self.assertEqual(str(text.tag_cget(tag, 'tabstyle')), 'wordprocessor') # Boolean options. for opt in ('underline', 'overstrike', 'elide'): text.tag_configure(tag, **{opt: True}) self.assertIs(getboolean(text.tag_cget(tag, opt)), True) text.tag_configure(tag, **{opt: False}) self.assertIs(getboolean(text.tag_cget(tag, opt)), False) # Screen-distance (pixel) options. for opt in ('borderwidth', 'lmargin1', 'lmargin2', 'rmargin', 'spacing1', 'spacing2', 'spacing3'): text.tag_configure(tag, **{opt: 7}) self.assertEqual(getint(text.tag_cget(tag, opt)), 7) # Other options. text.tag_configure(tag, font='Helvetica 12') self.assertEqual(str(text.tag_cget(tag, 'font')), 'Helvetica 12') text.tag_configure(tag, tabs=(10.2, '1i')) self.assertEqual([str(x) for x in text.tag_ranges('sel')], []) self.assertRaises(TclError, text.tag_cget, tag, 'spam') @requires_tk(8, 6, 6) def test_tag_configure_colors(self): # Tag color options added in Tk 8.6.6. text = self.text tag = 'a' for opt in ('selectforeground', 'selectbackground', 'lmargincolor', 'rmargincolor', 'underlinefg', 'overstrikefg'): text.tag_configure(tag, **{opt: '#00ff00'}) self.assertEqual(str(text.tag_cget(tag, opt)), '#00ff00') def test_tag_configure_query(self): text = self.text tag = 'a' # Querying all options returns a dict keyed by option name. cnf = text.tag_configure(tag) self.assertIsInstance(cnf, dict) self.assertIn('foreground', cnf) # The value is the full 5-tuple option specification. self.assertEqual(len(cnf['foreground']), 5) # Querying a single option returns its specification. spec = text.tag_configure(tag, 'foreground') self.assertEqual(spec[0], 'foreground') self.assertEqual(spec[-1], '') # unset by default # Setting via keyword arguments and via a dict are equivalent. text.tag_configure(tag, foreground='red') self.assertEqual(str(text.tag_configure(tag, 'foreground')[-1]), 'red') text.tag_configure(tag, {'foreground': 'blue'}) self.assertEqual(str(text.tag_cget(tag, 'foreground')), 'blue') # tag_config is an alias of tag_configure. self.assertEqual(text.tag_config, text.tag_configure) def test_image_configure(self): text = self.text image = tkinter.PhotoImage(master=self.root, width=10, height=10) text.insert('1.0', 'AB') name = text.image_create('1.1', image=image, name='img') self.assertEqual(name, 'img') self.assertEqual(str(text.image_cget(name, 'name')), 'img') self.assertEqual(str(text.image_cget(name, 'image')), str(image)) for value in ('top', 'center', 'bottom', 'baseline'): text.image_configure(name, align=value) self.assertEqual(str(text.image_cget(name, 'align')), value) text.image_configure(name, padx=3, pady=4) self.assertEqual(text.tk.getint(text.image_cget(name, 'padx')), 3) self.assertEqual(text.tk.getint(text.image_cget(name, 'pady')), 4) # Querying returns the full option set. cnf = text.image_configure(name) self.assertIsInstance(cnf, dict) self.assertIn('align', cnf) self.assertRaises(TclError, text.image_cget, name, 'spam') def test_window_configure(self): text = self.text button = tkinter.Button(text, text='ok') text.insert('1.0', 'AB') text.window_create('1.1', window=button) self.assertEqual(text.window_cget('1.1', 'window'), str(button)) for value in ('top', 'center', 'bottom', 'baseline'): text.window_configure('1.1', align=value) self.assertEqual(str(text.window_cget('1.1', 'align')), value) text.window_configure('1.1', padx=3, pady=4) self.assertEqual(text.tk.getint(text.window_cget('1.1', 'padx')), 3) self.assertEqual(text.tk.getint(text.window_cget('1.1', 'pady')), 4) text.window_configure('1.1', stretch=True) self.assertIs(text.tk.getboolean(text.window_cget('1.1', 'stretch')), True) cnf = text.window_configure('1.1') self.assertIsInstance(cnf, dict) self.assertIn('stretch', cnf) self.assertRaises(TclError, text.window_cget, '1.1', 'spam') self.assertEqual(text.window_config, text.window_configure) button.destroy() def test_peer(self): text = self.text text.insert('1.0', 'Lorem ipsum') self.assertEqual(text.peer_names(), ()) text.peer_create('.peer1') names = self.root.splitlist(text.tk.call('winfo', 'children', '.')) self.assertIn('.peer1', [str(n) for n in names]) self.assertEqual([str(p) for p in text.peer_names()], ['.peer1']) # Peers share content. self.assertEqual(text.tk.call('.peer1', 'get', '1.0', 'end'), 'Lorem ipsum\n') text.tk.call('destroy', '.peer1') def test_bbox(self): text = self.text text.insert('1.0', 'hello') text.update() bbox = text.bbox('1.0') self.assertIsInstance(bbox, tuple) self.assertEqual(len(bbox), 4) for v in bbox: self.assertIsInstance(v, int) # A character that is not displayed has no bounding box. self.assertIsNone(text.bbox('end')) self.assertRaises(TclError, text.bbox, 'invalid') self.assertRaises(TypeError, text.bbox) self.assertRaises(TypeError, text.bbox, '1.0', '2.0') def test_dlineinfo(self): text = self.text text.insert('1.0', 'hello\nworld') text.update() info = text.dlineinfo('1.0') self.assertIsInstance(info, tuple) self.assertEqual(len(info), 5) for v in info: self.assertIsInstance(v, int) self.assertRaises(TclError, text.dlineinfo, 'invalid') self.assertRaises(TypeError, text.dlineinfo) self.assertRaises(TypeError, text.dlineinfo, '1.0', '2.0') def test_see(self): text = self.text text.insert('1.0', '\n'.join('line %d' % i for i in range(200))) text.update() # Initially the last line is not visible. self.assertIsNone(text.bbox('200.0')) text.see('200.0') text.update() self.assertIsNotNone(text.bbox('200.0')) self.assertRaises(TclError, text.see, 'invalid') self.assertRaises(TypeError, text.see) self.assertRaises(TypeError, text.see, '1.0', '2.0') # yview_pickplace is a deprecated way to make an index visible. text.yview_pickplace('1.0') text.update() self.assertIsNotNone(text.bbox('1.0')) def test_search(self): text = self.text # pattern and index are obligatory arguments. self.assertRaises(TclError, text.search, None, '1.0') self.assertRaises(TclError, text.search, 'a', None) self.assertRaises(TclError, text.search, None, None) # Invalid text index. self.assertRaises(TclError, text.search, '', 0) self.assertRaises(TclError, text.search, '', '') self.assertRaises(TclError, text.search, '', 'invalid') self.assertRaises(TclError, text.search, '', '1.0', 0) self.assertRaises(TclError, text.search, '', '1.0', '') self.assertRaises(TclError, text.search, '', '1.0', 'invalid') text.insert('1.0', 'This is a test. This is only a test.\n' 'Another line.\n' 'Yet another line.\n' '64-bit') self.assertEqual(text.search('test', '1.0'), '1.10') self.assertEqual(text.search('test', '1.0', 'end'), '1.10') self.assertEqual(text.search('test', '1.0', '1.10'), '') self.assertEqual(text.search('test', '1.11'), '1.31') self.assertEqual(text.search('test', '1.32', 'end'), '') self.assertEqual(text.search('test', '1.32'), '1.10') self.assertEqual(text.search('', '1.0'), '1.0') # empty pattern self.assertEqual(text.search('nonexistent', '1.0'), '') self.assertEqual(text.search('-bit', '1.0'), '4.2') # starts with a hyphen self.assertEqual(text.search('line', '3.0'), '3.12') self.assertEqual(text.search('line', '3.0', forwards=True), '3.12') self.assertEqual(text.search('line', '3.0', backwards=True), '2.8') self.assertEqual(text.search('line', '3.0', forwards=True, backwards=True), '2.8') self.assertEqual(text.search('t.', '1.0'), '1.13') self.assertEqual(text.search('t.', '1.0', exact=True), '1.13') self.assertEqual(text.search('t.', '1.0', regexp=True), '1.10') self.assertEqual(text.search('t.', '1.0', exact=True, regexp=True), '1.10') self.assertEqual(text.search('TEST', '1.0'), '') self.assertEqual(text.search('TEST', '1.0', nocase=True), '1.10') self.assertEqual(text.search('.*line', '1.0', regexp=True), '2.0') self.assertEqual(text.search('.*line', '1.0', regexp=True, nolinestop=True), '1.0') self.assertEqual(text.search('test', '1.0', '1.13'), '1.10') self.assertEqual(text.search('test', '1.0', '1.13', strictlimits=True), '') self.assertEqual(text.search('test', '1.0', '1.14', strictlimits=True), '1.10') var = tkinter.Variable(self.root) self.assertEqual(text.search('test', '1.0', count=var), '1.10') self.assertEqual(var.get(), 4 if self.wantobjects else '4') # TODO: Add test for elide=True def test_search_all(self): text = self.text # pattern and index are obligatory arguments. self.assertRaises(TclError, text.search_all, None, '1.0') self.assertRaises(TclError, text.search_all, 'a', None) self.assertRaises(TclError, text.search_all, None, None) # Keyword-only arguments self.assertRaises(TypeError, text.search_all, 'a', '1.0', 'end', None) # Invalid text index. self.assertRaises(TclError, text.search_all, '', 0) self.assertRaises(TclError, text.search_all, '', '') self.assertRaises(TclError, text.search_all, '', 'invalid') self.assertRaises(TclError, text.search_all, '', '1.0', 0) self.assertRaises(TclError, text.search_all, '', '1.0', '') self.assertRaises(TclError, text.search_all, '', '1.0', 'invalid') def eq(res, expected): self.assertIsInstance(res, tuple) self.assertEqual([str(i) for i in res], expected) text.insert('1.0', 'ababa\naba\n64-bit') eq(text.search_all('aba', '1.0'), ['1.0', '2.0']) eq(text.search_all('aba', '1.0', 'end'), ['1.0', '2.0']) eq(text.search_all('aba', '1.1', 'end'), ['1.2', '2.0']) eq(text.search_all('aba', '1.1'), ['1.2', '2.0', '1.0']) res = text.search_all('', '1.0') # empty pattern eq(res[:5], ['1.0', '1.1', '1.2', '1.3', '1.4']) eq(res[-5:], ['3.2', '3.3', '3.4', '3.5', '3.6']) eq(text.search_all('nonexistent', '1.0'), []) eq(text.search_all('-bit', '1.0'), ['3.2']) # starts with a hyphen eq(text.search_all('aba', '1.0', 'end', forwards=True), ['1.0', '2.0']) eq(text.search_all('aba', 'end', '1.0', backwards=True), ['2.0', '1.2']) eq(text.search_all('aba', '1.0', overlap=True), ['1.0', '1.2', '2.0']) eq(text.search_all('aba', 'end', '1.0', overlap=True, backwards=True), ['2.0', '1.2', '1.0']) eq(text.search_all('aba', '1.0', exact=True), ['1.0', '2.0']) eq(text.search_all('a.a', '1.0', exact=True), []) eq(text.search_all('a.a', '1.0', regexp=True), ['1.0', '2.0']) eq(text.search_all('ABA', '1.0'), []) eq(text.search_all('ABA', '1.0', nocase=True), ['1.0', '2.0']) eq(text.search_all('a.a', '1.0', regexp=True), ['1.0', '2.0']) eq(text.search_all('a.a', '1.0', regexp=True, nolinestop=True), ['1.0', '1.4']) eq(text.search_all('aba', '1.0', '2.2'), ['1.0', '2.0']) eq(text.search_all('aba', '1.0', '2.2', strictlimits=True), ['1.0']) eq(text.search_all('aba', '1.0', '2.3', strictlimits=True), ['1.0', '2.0']) var = tkinter.Variable(self.root) eq(text.search_all('aba', '1.0', count=var), ['1.0', '2.0']) self.assertEqual(var.get(), (3, 3) if self.wantobjects else '3 3') # TODO: Add test for elide=True def test_count(self): text = self.text text.insert('1.0', 'Lorem ipsum dolor sit amet,\n' 'consectetur adipiscing elit,\n' 'sed do eiusmod tempor incididunt\n' 'ut labore et dolore magna aliqua.') options = ('chars', 'indices', 'lines', 'displaychars', 'displayindices', 'displaylines', 'xpixels', 'ypixels') self.assertEqual(len(text.count('1.0', 'end', *options, return_ints=True)), 8) self.assertEqual(len(text.count('1.0', 'end', *options)), 8) self.assertEqual(text.count('1.0', 'end', 'chars', 'lines', return_ints=True), (124, 4)) self.assertEqual(text.count('1.3', '4.5', 'chars', 'lines'), (92, 3)) self.assertEqual(text.count('4.5', '1.3', 'chars', 'lines', return_ints=True), (-92, -3)) self.assertEqual(text.count('4.5', '1.3', 'chars', 'lines'), (-92, -3)) self.assertEqual(text.count('1.3', '1.3', 'chars', 'lines', return_ints=True), (0, 0)) self.assertEqual(text.count('1.3', '1.3', 'chars', 'lines'), (0, 0)) self.assertEqual(text.count('1.0', 'end', 'lines', return_ints=True), 4) self.assertEqual(text.count('1.0', 'end', 'lines'), (4,)) self.assertEqual(text.count('end', '1.0', 'lines', return_ints=True), -4) self.assertEqual(text.count('end', '1.0', 'lines'), (-4,)) self.assertEqual(text.count('1.3', '1.5', 'lines', return_ints=True), 0) self.assertEqual(text.count('1.3', '1.5', 'lines'), None) self.assertEqual(text.count('1.3', '1.3', 'lines', return_ints=True), 0) self.assertEqual(text.count('1.3', '1.3', 'lines'), None) # Count 'indices' by default. self.assertEqual(text.count('1.0', 'end', return_ints=True), 124) self.assertEqual(text.count('1.0', 'end'), (124,)) self.assertEqual(text.count('1.0', 'end', 'indices', return_ints=True), 124) self.assertEqual(text.count('1.0', 'end', 'indices'), (124,)) self.assertRaises(TclError, text.count, '1.0', 'end', 'spam') self.assertRaises(TclError, text.count, '1.0', 'end', '-lines') self.assertIsInstance(text.count('1.3', '1.5', 'ypixels', return_ints=True), int) self.assertIsInstance(text.count('1.3', '1.5', 'ypixels'), tuple) self.assertIsInstance(text.count('1.3', '1.5', 'update', 'ypixels', return_ints=True), int) self.assertIsInstance(text.count('1.3', '1.5', 'update', 'ypixels'), int) self.assertEqual(text.count('1.3', '1.3', 'update', 'ypixels', return_ints=True), 0) self.assertEqual(text.count('1.3', '1.3', 'update', 'ypixels'), None) self.assertEqual(text.count('1.3', '1.5', 'update', 'indices', return_ints=True), 2) self.assertEqual(text.count('1.3', '1.5', 'update', 'indices'), 2) self.assertEqual(text.count('1.3', '1.3', 'update', 'indices', return_ints=True), 0) self.assertEqual(text.count('1.3', '1.3', 'update', 'indices'), None) self.assertEqual(text.count('1.3', '1.5', 'update', return_ints=True), 2) self.assertEqual(text.count('1.3', '1.5', 'update'), (2,)) self.assertEqual(text.count('1.3', '1.3', 'update', return_ints=True), 0) self.assertEqual(text.count('1.3', '1.3', 'update'), None) if __name__ == "__main__": unittest.main()