Coverage for idle_test/test_colorizer.py: 27%

370 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-11 13:22 -0700

1"Test colorizer, coverage 99%." 

2from idlelib import colorizer 

3from test.support import requires 

4import unittest 

5from unittest import mock 

6from idlelib.idle_test.tkinter_testing_utils import run_in_tk_mainloop 

7 

8from functools import partial 

9import textwrap 

10from tkinter import Tk, Text 

11from idlelib import config 

12from idlelib.percolator import Percolator 

13 

14 

15usercfg = colorizer.idleConf.userCfg 

16testcfg = { 

17 'main': config.IdleUserConfParser(''), 

18 'highlight': config.IdleUserConfParser(''), 

19 'keys': config.IdleUserConfParser(''), 

20 'extensions': config.IdleUserConfParser(''), 

21} 

22 

23source = textwrap.dedent("""\ 

24 if True: int ('1') # keyword, builtin, string, comment 

25 elif False: print(0) # 'string' in comment 

26 else: float(None) # if in comment 

27 if iF + If + IF: 'keyword matching must respect case' 

28 if'': x or'' # valid keyword-string no-space combinations 

29 async def f(): await g() 

30 # Strings should be entirely colored, including quotes. 

31 'x', '''x''', "x", \"""x\""" 

32 'abc\\ 

33 def' 

34 '''abc\\ 

35 def''' 

36 # All valid prefixes for unicode and byte strings should be colored. 

37 r'x', u'x', R'x', U'x', f'x', F'x' 

38 fr'x', Fr'x', fR'x', FR'x', rf'x', rF'x', Rf'x', RF'x' 

39 b'x',B'x', br'x',Br'x',bR'x',BR'x', rb'x', rB'x',Rb'x',RB'x' 

40 # Invalid combinations of legal characters should be half colored. 

41 ur'x', ru'x', uf'x', fu'x', UR'x', ufr'x', rfu'x', xf'x', fx'x' 

42 match point: 

43 case (x, 0) as _: 

44 print(f"X={x}") 

45 case [_, [_], "_", 

46 _]: 

47 pass 

48 case _ if ("a" if _ else set()): pass 

49 case _: 

50 raise ValueError("Not a point _") 

51 ''' 

52 case _:''' 

53 "match x:" 

54 """) 

55 

56 

57def setUpModule(): 

58 colorizer.idleConf.userCfg = testcfg 

59 

60 

61def tearDownModule(): 

62 colorizer.idleConf.userCfg = usercfg 

63 

64 

65class FunctionTest(unittest.TestCase): 

66 

67 def test_any(self): 

68 self.assertEqual(colorizer.any('test', ('a', 'b', 'cd')), 1d

69 '(?P<test>a|b|cd)') 

70 

71 def test_make_pat(self): 

72 # Tested in more detail by testing prog. 

73 self.assertTrue(colorizer.make_pat()) 1e

74 

75 def test_prog(self): 

76 prog = colorizer.prog 1b

77 eq = self.assertEqual 1b

78 line = 'def f():\n print("hello")\n' 1b

79 m = prog.search(line) 1b

80 eq(m.groupdict()['KEYWORD'], 'def') 1b

81 m = prog.search(line, m.end()) 1b

82 eq(m.groupdict()['SYNC'], '\n') 1b

83 m = prog.search(line, m.end()) 1b

84 eq(m.groupdict()['BUILTIN'], 'print') 1b

85 m = prog.search(line, m.end()) 1b

86 eq(m.groupdict()['STRING'], '"hello"') 1b

87 m = prog.search(line, m.end()) 1b

88 eq(m.groupdict()['SYNC'], '\n') 1b

89 

90 def test_idprog(self): 

91 idprog = colorizer.idprog 1c

92 m = idprog.match('nospace') 1c

93 self.assertIsNone(m) 1c

94 m = idprog.match(' space') 1c

95 self.assertEqual(m.group(0), ' space') 1c

96 

97 

98class ColorConfigTest(unittest.TestCase): 

99 

100 @classmethod 

101 def setUpClass(cls): 

102 requires('gui') 

103 root = cls.root = Tk() 

104 root.withdraw() 

105 cls.text = Text(root) 

106 

107 @classmethod 

108 def tearDownClass(cls): 

109 del cls.text 

110 cls.root.update_idletasks() 

111 cls.root.destroy() 

112 del cls.root 

113 

114 def test_color_config(self): 

115 text = self.text 

116 eq = self.assertEqual 

117 colorizer.color_config(text) 

118 # Uses IDLE Classic theme as default. 

119 eq(text['background'], '#ffffff') 

120 eq(text['foreground'], '#000000') 

121 eq(text['selectbackground'], 'gray') 

122 eq(text['selectforeground'], '#000000') 

123 eq(text['insertbackground'], 'black') 

124 eq(text['inactiveselectbackground'], 'gray') 

125 

126 

127class ColorDelegatorInstantiationTest(unittest.TestCase): 

128 

129 @classmethod 

130 def setUpClass(cls): 

131 requires('gui') 

132 root = cls.root = Tk() 

133 root.withdraw() 

134 cls.text = Text(root) 

135 

136 @classmethod 

137 def tearDownClass(cls): 

138 del cls.text 

139 cls.root.update_idletasks() 

140 cls.root.destroy() 

141 del cls.root 

142 

143 def setUp(self): 

144 self.color = colorizer.ColorDelegator() 

145 

146 def tearDown(self): 

147 self.color.close() 

148 self.text.delete('1.0', 'end') 

149 self.color.resetcache() 

150 del self.color 

151 

152 def test_init(self): 

153 color = self.color 

154 self.assertIsInstance(color, colorizer.ColorDelegator) 

155 

156 def test_init_state(self): 

157 # init_state() is called during the instantiation of 

158 # ColorDelegator in setUp(). 

159 color = self.color 

160 self.assertIsNone(color.after_id) 

161 self.assertTrue(color.allow_colorizing) 

162 self.assertFalse(color.colorizing) 

163 self.assertFalse(color.stop_colorizing) 

164 

165 

166class ColorDelegatorTest(unittest.TestCase): 

167 

168 @classmethod 

169 def setUpClass(cls): 

170 requires('gui') 

171 root = cls.root = Tk() 

172 root.withdraw() 

173 text = cls.text = Text(root) 

174 cls.percolator = Percolator(text) 

175 # Delegator stack = [Delegator(text)] 

176 

177 @classmethod 

178 def tearDownClass(cls): 

179 cls.percolator.close() 

180 del cls.percolator, cls.text 

181 cls.root.update_idletasks() 

182 cls.root.destroy() 

183 del cls.root 

184 

185 def setUp(self): 

186 self.color = colorizer.ColorDelegator() 

187 self.percolator.insertfilter(self.color) 

188 # Calls color.setdelegate(Delegator(text)). 

189 

190 def tearDown(self): 

191 self.color.close() 

192 self.percolator.removefilter(self.color) 

193 self.text.delete('1.0', 'end') 

194 self.color.resetcache() 

195 del self.color 

196 

197 def test_setdelegate(self): 

198 # Called in setUp when filter is attached to percolator. 

199 color = self.color 

200 self.assertIsInstance(color.delegate, colorizer.Delegator) 

201 # It is too late to mock notify_range, so test side effect. 

202 self.assertEqual(self.root.tk.call( 

203 'after', 'info', color.after_id)[1], 'timer') 

204 

205 def test_LoadTagDefs(self): 

206 highlight = partial(config.idleConf.GetHighlight, theme='IDLE Classic') 

207 for tag, colors in self.color.tagdefs.items(): 

208 with self.subTest(tag=tag): 

209 self.assertIn('background', colors) 

210 self.assertIn('foreground', colors) 

211 if tag not in ('SYNC', 'TODO'): 

212 self.assertEqual(colors, highlight(element=tag.lower())) 

213 

214 def test_config_colors(self): 

215 text = self.text 

216 highlight = partial(config.idleConf.GetHighlight, theme='IDLE Classic') 

217 for tag in self.color.tagdefs: 

218 for plane in ('background', 'foreground'): 

219 with self.subTest(tag=tag, plane=plane): 

220 if tag in ('SYNC', 'TODO'): 

221 self.assertEqual(text.tag_cget(tag, plane), '') 

222 else: 

223 self.assertEqual(text.tag_cget(tag, plane), 

224 highlight(element=tag.lower())[plane]) 

225 # 'sel' is marked as the highest priority. 

226 self.assertEqual(text.tag_names()[-1], 'sel') 

227 

228 @mock.patch.object(colorizer.ColorDelegator, 'notify_range') 

229 def test_insert(self, mock_notify): 

230 text = self.text 

231 # Initial text. 

232 text.insert('insert', 'foo') 

233 self.assertEqual(text.get('1.0', 'end'), 'foo\n') 

234 mock_notify.assert_called_with('1.0', '1.0+3c') 

235 # Additional text. 

236 text.insert('insert', 'barbaz') 

237 self.assertEqual(text.get('1.0', 'end'), 'foobarbaz\n') 

238 mock_notify.assert_called_with('1.3', '1.3+6c') 

239 

240 @mock.patch.object(colorizer.ColorDelegator, 'notify_range') 

241 def test_delete(self, mock_notify): 

242 text = self.text 

243 # Initialize text. 

244 text.insert('insert', 'abcdefghi') 

245 self.assertEqual(text.get('1.0', 'end'), 'abcdefghi\n') 

246 # Delete single character. 

247 text.delete('1.7') 

248 self.assertEqual(text.get('1.0', 'end'), 'abcdefgi\n') 

249 mock_notify.assert_called_with('1.7') 

250 # Delete multiple characters. 

251 text.delete('1.3', '1.6') 

252 self.assertEqual(text.get('1.0', 'end'), 'abcgi\n') 

253 mock_notify.assert_called_with('1.3') 

254 

255 def test_notify_range(self): 

256 text = self.text 

257 color = self.color 

258 eq = self.assertEqual 

259 

260 # Colorizing already scheduled. 

261 save_id = color.after_id 

262 eq(self.root.tk.call('after', 'info', save_id)[1], 'timer') 

263 self.assertFalse(color.colorizing) 

264 self.assertFalse(color.stop_colorizing) 

265 self.assertTrue(color.allow_colorizing) 

266 

267 # Coloring scheduled and colorizing in progress. 

268 color.colorizing = True 

269 color.notify_range('1.0', 'end') 

270 self.assertFalse(color.stop_colorizing) 

271 eq(color.after_id, save_id) 

272 

273 # No colorizing scheduled and colorizing in progress. 

274 text.after_cancel(save_id) 

275 color.after_id = None 

276 color.notify_range('1.0', '1.0+3c') 

277 self.assertTrue(color.stop_colorizing) 

278 self.assertIsNotNone(color.after_id) 

279 eq(self.root.tk.call('after', 'info', color.after_id)[1], 'timer') 

280 # New event scheduled. 

281 self.assertNotEqual(color.after_id, save_id) 

282 

283 # No colorizing scheduled and colorizing off. 

284 text.after_cancel(color.after_id) 

285 color.after_id = None 

286 color.allow_colorizing = False 

287 color.notify_range('1.4', '1.4+10c') 

288 # Nothing scheduled when colorizing is off. 

289 self.assertIsNone(color.after_id) 

290 

291 def test_toggle_colorize_event(self): 

292 color = self.color 

293 eq = self.assertEqual 

294 

295 # Starts with colorizing allowed and scheduled. 

296 self.assertFalse(color.colorizing) 

297 self.assertFalse(color.stop_colorizing) 

298 self.assertTrue(color.allow_colorizing) 

299 eq(self.root.tk.call('after', 'info', color.after_id)[1], 'timer') 

300 

301 # Toggle colorizing off. 

302 color.toggle_colorize_event() 

303 self.assertIsNone(color.after_id) 

304 self.assertFalse(color.colorizing) 

305 self.assertFalse(color.stop_colorizing) 

306 self.assertFalse(color.allow_colorizing) 

307 

308 # Toggle on while colorizing in progress (doesn't add timer). 

309 color.colorizing = True 

310 color.toggle_colorize_event() 

311 self.assertIsNone(color.after_id) 

312 self.assertTrue(color.colorizing) 

313 self.assertFalse(color.stop_colorizing) 

314 self.assertTrue(color.allow_colorizing) 

315 

316 # Toggle off while colorizing in progress. 

317 color.toggle_colorize_event() 

318 self.assertIsNone(color.after_id) 

319 self.assertTrue(color.colorizing) 

320 self.assertTrue(color.stop_colorizing) 

321 self.assertFalse(color.allow_colorizing) 

322 

323 # Toggle on while colorizing not in progress. 

324 color.colorizing = False 

325 color.toggle_colorize_event() 

326 eq(self.root.tk.call('after', 'info', color.after_id)[1], 'timer') 

327 self.assertFalse(color.colorizing) 

328 self.assertTrue(color.stop_colorizing) 

329 self.assertTrue(color.allow_colorizing) 

330 

331 @mock.patch.object(colorizer.ColorDelegator, 'recolorize_main') 

332 def test_recolorize(self, mock_recmain): 

333 text = self.text 

334 color = self.color 

335 eq = self.assertEqual 

336 # Call recolorize manually and not scheduled. 

337 text.after_cancel(color.after_id) 

338 

339 # No delegate. 

340 save_delegate = color.delegate 

341 color.delegate = None 

342 color.recolorize() 

343 mock_recmain.assert_not_called() 

344 color.delegate = save_delegate 

345 

346 # Toggle off colorizing. 

347 color.allow_colorizing = False 

348 color.recolorize() 

349 mock_recmain.assert_not_called() 

350 color.allow_colorizing = True 

351 

352 # Colorizing in progress. 

353 color.colorizing = True 

354 color.recolorize() 

355 mock_recmain.assert_not_called() 

356 color.colorizing = False 

357 

358 # Colorizing is done, but not completed, so rescheduled. 

359 color.recolorize() 

360 self.assertFalse(color.stop_colorizing) 

361 self.assertFalse(color.colorizing) 

362 mock_recmain.assert_called() 

363 eq(mock_recmain.call_count, 1) 

364 # Rescheduled when TODO tag still exists. 

365 eq(self.root.tk.call('after', 'info', color.after_id)[1], 'timer') 

366 

367 # No changes to text, so no scheduling added. 

368 text.tag_remove('TODO', '1.0', 'end') 

369 color.recolorize() 

370 self.assertFalse(color.stop_colorizing) 

371 self.assertFalse(color.colorizing) 

372 mock_recmain.assert_called() 

373 eq(mock_recmain.call_count, 2) 

374 self.assertIsNone(color.after_id) 

375 

376 @mock.patch.object(colorizer.ColorDelegator, 'notify_range') 

377 def test_recolorize_main(self, mock_notify): 

378 text = self.text 

379 color = self.color 

380 eq = self.assertEqual 

381 

382 text.insert('insert', source) 

383 expected = (('1.0', ('KEYWORD',)), ('1.2', ()), ('1.3', ('KEYWORD',)), 

384 ('1.7', ()), ('1.9', ('BUILTIN',)), ('1.14', ('STRING',)), 

385 ('1.19', ('COMMENT',)), 

386 ('2.1', ('KEYWORD',)), ('2.18', ()), ('2.25', ('COMMENT',)), 

387 ('3.6', ('BUILTIN',)), ('3.12', ('KEYWORD',)), ('3.21', ('COMMENT',)), 

388 ('4.0', ('KEYWORD',)), ('4.3', ()), ('4.6', ()), 

389 ('5.2', ('STRING',)), ('5.8', ('KEYWORD',)), ('5.10', ('STRING',)), 

390 ('6.0', ('KEYWORD',)), ('6.10', ('DEFINITION',)), ('6.11', ()), 

391 ('8.0', ('STRING',)), ('8.4', ()), ('8.5', ('STRING',)), 

392 ('8.12', ()), ('8.14', ('STRING',)), 

393 ('19.0', ('KEYWORD',)), 

394 ('20.4', ('KEYWORD',)), ('20.16', ('KEYWORD',)),# ('20.19', ('KEYWORD',)), 

395 #('22.4', ('KEYWORD',)), ('22.10', ('KEYWORD',)), ('22.14', ('KEYWORD',)), ('22.19', ('STRING',)), 

396 #('23.12', ('KEYWORD',)), 

397 ('24.8', ('KEYWORD',)), 

398 ('25.4', ('KEYWORD',)), ('25.9', ('KEYWORD',)), 

399 ('25.11', ('KEYWORD',)), ('25.15', ('STRING',)), 

400 ('25.19', ('KEYWORD',)), ('25.22', ()), 

401 ('25.24', ('KEYWORD',)), ('25.29', ('BUILTIN',)), ('25.37', ('KEYWORD',)), 

402 ('26.4', ('KEYWORD',)), ('26.9', ('KEYWORD',)),# ('26.11', ('KEYWORD',)), ('26.14', (),), 

403 ('27.25', ('STRING',)), ('27.38', ('STRING',)), 

404 ('29.0', ('STRING',)), 

405 ('30.1', ('STRING',)), 

406 # SYNC at the end of every line. 

407 ('1.55', ('SYNC',)), ('2.50', ('SYNC',)), ('3.34', ('SYNC',)), 

408 ) 

409 

410 # Nothing marked to do therefore no tags in text. 

411 text.tag_remove('TODO', '1.0', 'end') 

412 color.recolorize_main() 

413 for tag in text.tag_names(): 

414 with self.subTest(tag=tag): 

415 eq(text.tag_ranges(tag), ()) 

416 

417 # Source marked for processing. 

418 text.tag_add('TODO', '1.0', 'end') 

419 # Check some indexes. 

420 color.recolorize_main() 

421 for index, expected_tags in expected: 

422 with self.subTest(index=index): 

423 eq(text.tag_names(index), expected_tags) 

424 

425 # Check for some tags for ranges. 

426 eq(text.tag_nextrange('TODO', '1.0'), ()) 

427 eq(text.tag_nextrange('KEYWORD', '1.0'), ('1.0', '1.2')) 

428 eq(text.tag_nextrange('COMMENT', '2.0'), ('2.22', '2.43')) 

429 eq(text.tag_nextrange('SYNC', '2.0'), ('2.43', '3.0')) 

430 eq(text.tag_nextrange('STRING', '2.0'), ('4.17', '4.53')) 

431 eq(text.tag_nextrange('STRING', '8.0'), ('8.0', '8.3')) 

432 eq(text.tag_nextrange('STRING', '8.3'), ('8.5', '8.12')) 

433 eq(text.tag_nextrange('STRING', '8.12'), ('8.14', '8.17')) 

434 eq(text.tag_nextrange('STRING', '8.17'), ('8.19', '8.26')) 

435 eq(text.tag_nextrange('SYNC', '8.0'), ('8.26', '9.0')) 

436 eq(text.tag_nextrange('SYNC', '30.0'), ('30.10', '32.0')) 

437 

438 def _assert_highlighting(self, source, tag_ranges): 

439 """Check highlighting of a given piece of code. 

440 

441 This inserts just this code into the Text widget. It will then 

442 check that the resulting highlighting tag ranges exactly match 

443 those described in the given `tag_ranges` dict. 

444 

445 Note that the irrelevant tags 'sel', 'TODO' and 'SYNC' are 

446 ignored. 

447 """ 

448 text = self.text 

449 

450 with mock.patch.object(colorizer.ColorDelegator, 'notify_range'): 

451 text.delete('1.0', 'end-1c') 

452 text.insert('insert', source) 

453 text.tag_add('TODO', '1.0', 'end-1c') 

454 self.color.recolorize_main() 

455 

456 # Make a dict with highlighting tag ranges in the Text widget. 

457 text_tag_ranges = {} 

458 for tag in set(text.tag_names()) - {'sel', 'TODO', 'SYNC'}: 

459 indexes = [rng.string for rng in text.tag_ranges(tag)] 

460 for index_pair in zip(indexes[::2], indexes[1::2]): 

461 text_tag_ranges.setdefault(tag, []).append(index_pair) 

462 

463 self.assertEqual(text_tag_ranges, tag_ranges) 

464 

465 with mock.patch.object(colorizer.ColorDelegator, 'notify_range'): 

466 text.delete('1.0', 'end-1c') 

467 

468 def test_def_statement(self): 

469 # empty def 

470 self._assert_highlighting('def', {'KEYWORD': [('1.0', '1.3')]}) 

471 

472 # def followed by identifier 

473 self._assert_highlighting('def foo:', {'KEYWORD': [('1.0', '1.3')], 

474 'DEFINITION': [('1.4', '1.7')]}) 

475 

476 # def followed by partial identifier 

477 self._assert_highlighting('def fo', {'KEYWORD': [('1.0', '1.3')], 

478 'DEFINITION': [('1.4', '1.6')]}) 

479 

480 # def followed by non-keyword 

481 self._assert_highlighting('def ++', {'KEYWORD': [('1.0', '1.3')]}) 

482 

483 def test_match_soft_keyword(self): 

484 # empty match 

485 self._assert_highlighting('match', {'KEYWORD': [('1.0', '1.5')]}) 

486 

487 # match followed by partial identifier 

488 self._assert_highlighting('match fo', {'KEYWORD': [('1.0', '1.5')]}) 

489 

490 # match followed by identifier and colon 

491 self._assert_highlighting('match foo:', {'KEYWORD': [('1.0', '1.5')]}) 

492 

493 # match followed by keyword 

494 self._assert_highlighting('match and', {'KEYWORD': [('1.6', '1.9')]}) 

495 

496 # match followed by builtin with keyword prefix 

497 self._assert_highlighting('match int:', {'KEYWORD': [('1.0', '1.5')], 

498 'BUILTIN': [('1.6', '1.9')]}) 

499 

500 # match followed by non-text operator 

501 self._assert_highlighting('match^', {}) 

502 self._assert_highlighting('match @', {}) 

503 

504 # match followed by colon 

505 self._assert_highlighting('match :', {}) 

506 

507 # match followed by comma 

508 self._assert_highlighting('match\t,', {}) 

509 

510 # match followed by a lone underscore 

511 self._assert_highlighting('match _:', {'KEYWORD': [('1.0', '1.5')]}) 

512 

513 def test_case_soft_keyword(self): 

514 # empty case 

515 self._assert_highlighting('case', {'KEYWORD': [('1.0', '1.4')]}) 

516 

517 # case followed by partial identifier 

518 self._assert_highlighting('case fo', {'KEYWORD': [('1.0', '1.4')]}) 

519 

520 # case followed by identifier and colon 

521 self._assert_highlighting('case foo:', {'KEYWORD': [('1.0', '1.4')]}) 

522 

523 # case followed by keyword 

524 self._assert_highlighting('case and', {'KEYWORD': [('1.5', '1.8')]}) 

525 

526 # case followed by builtin with keyword prefix 

527 self._assert_highlighting('case int:', {'KEYWORD': [('1.0', '1.4')], 

528 'BUILTIN': [('1.5', '1.8')]}) 

529 

530 # case followed by non-text operator 

531 self._assert_highlighting('case^', {}) 

532 self._assert_highlighting('case @', {}) 

533 

534 # case followed by colon 

535 self._assert_highlighting('case :', {}) 

536 

537 # case followed by comma 

538 self._assert_highlighting('case\t,', {}) 

539 

540 # case followed by a lone underscore 

541 self._assert_highlighting('case _:', {'KEYWORD': [('1.0', '1.4'), 

542 ('1.5', '1.6')]}) 

543 

544 def test_long_multiline_string(self): 

545 source = textwrap.dedent('''\ 

546 """a 

547 b 

548 c 

549 d 

550 e""" 

551 ''') 

552 self._assert_highlighting(source, {'STRING': [('1.0', '5.4')]}) 

553 

554 @run_in_tk_mainloop(delay=50) 

555 def test_incremental_editing(self): 

556 text = self.text 

557 eq = self.assertEqual 

558 

559 # Simulate typing 'inte'. During this, the highlighting should 

560 # change from normal to keyword to builtin to normal. 

561 text.insert('insert', 'i') 

562 yield 

563 eq(text.tag_nextrange('BUILTIN', '1.0'), ()) 

564 eq(text.tag_nextrange('KEYWORD', '1.0'), ()) 

565 

566 text.insert('insert', 'n') 

567 yield 

568 eq(text.tag_nextrange('BUILTIN', '1.0'), ()) 

569 eq(text.tag_nextrange('KEYWORD', '1.0'), ('1.0', '1.2')) 

570 

571 text.insert('insert', 't') 

572 yield 

573 eq(text.tag_nextrange('BUILTIN', '1.0'), ('1.0', '1.3')) 

574 eq(text.tag_nextrange('KEYWORD', '1.0'), ()) 

575 

576 text.insert('insert', 'e') 

577 yield 

578 eq(text.tag_nextrange('BUILTIN', '1.0'), ()) 

579 eq(text.tag_nextrange('KEYWORD', '1.0'), ()) 

580 

581 # Simulate deleting three characters from the end of 'inte'. 

582 # During this, the highlighting should change from normal to 

583 # builtin to keyword to normal. 

584 text.delete('insert-1c', 'insert') 

585 yield 

586 eq(text.tag_nextrange('BUILTIN', '1.0'), ('1.0', '1.3')) 

587 eq(text.tag_nextrange('KEYWORD', '1.0'), ()) 

588 

589 text.delete('insert-1c', 'insert') 

590 yield 

591 eq(text.tag_nextrange('BUILTIN', '1.0'), ()) 

592 eq(text.tag_nextrange('KEYWORD', '1.0'), ('1.0', '1.2')) 

593 

594 text.delete('insert-1c', 'insert') 

595 yield 

596 eq(text.tag_nextrange('BUILTIN', '1.0'), ()) 

597 eq(text.tag_nextrange('KEYWORD', '1.0'), ()) 

598 

599 @mock.patch.object(colorizer.ColorDelegator, 'recolorize') 

600 @mock.patch.object(colorizer.ColorDelegator, 'notify_range') 

601 def test_removecolors(self, mock_notify, mock_recolorize): 

602 text = self.text 

603 color = self.color 

604 text.insert('insert', source) 

605 

606 color.recolorize_main() 

607 # recolorize_main doesn't add these tags. 

608 text.tag_add("ERROR", "1.0") 

609 text.tag_add("TODO", "1.0") 

610 text.tag_add("hit", "1.0") 

611 for tag in color.tagdefs: 

612 with self.subTest(tag=tag): 

613 self.assertNotEqual(text.tag_ranges(tag), ()) 

614 

615 color.removecolors() 

616 for tag in color.tagdefs: 

617 with self.subTest(tag=tag): 

618 self.assertEqual(text.tag_ranges(tag), ()) 

619 

620 

621if __name__ == '__main__': 621 ↛ 622line 621 didn't jump to line 622, because the condition on line 621 was never true

622 unittest.main(verbosity=2)