Coverage for idle_test/test_configdialog.py: 1%

1152 statements  

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

1"""Test configdialog, coverage 94%. 

2 

3Half the class creates dialog, half works with user customizations. 

4""" 

5from idlelib import configdialog 

6from test.support import requires 

7requires('gui') 

8import unittest 

9from unittest import mock 

10from idlelib.idle_test.mock_idle import Func 

11from tkinter import (Tk, StringVar, IntVar, BooleanVar, DISABLED, NORMAL) 

12from idlelib import config 

13from idlelib.configdialog import idleConf, changes, tracers 

14 

15# Tests should not depend on fortuitous user configurations. 

16# They must not affect actual user .cfg files. 

17# Use solution from test_config: empty parsers with no filename. 

18usercfg = idleConf.userCfg 

19testcfg = { 

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

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

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

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

24} 

25 

26root = None 

27dialog = None 

28mainpage = changes['main'] 

29highpage = changes['highlight'] 

30keyspage = changes['keys'] 

31extpage = changes['extensions'] 

32 

33 

34def setUpModule(): 

35 global root, dialog 

36 idleConf.userCfg = testcfg 

37 root = Tk() 

38 # root.withdraw() # Comment out, see issue 30870 

39 dialog = configdialog.ConfigDialog(root, 'Test', _utest=True) 

40 

41 

42def tearDownModule(): 

43 global root, dialog 

44 idleConf.userCfg = usercfg 

45 tracers.detach() 

46 tracers.clear() 

47 changes.clear() 

48 root.update_idletasks() 

49 root.destroy() 

50 root = dialog = None 

51 

52 

53class ConfigDialogTest(unittest.TestCase): 

54 

55 def test_deactivate_current_config(self): 

56 pass 

57 

58 def activate_config_changes(self): 

59 pass 

60 

61 

62class ButtonTest(unittest.TestCase): 

63 

64 def test_click_ok(self): 

65 d = dialog 

66 apply = d.apply = mock.Mock() 

67 destroy = d.destroy = mock.Mock() 

68 d.buttons['Ok'].invoke() 

69 apply.assert_called_once() 

70 destroy.assert_called_once() 

71 del d.destroy, d.apply 

72 

73 def test_click_apply(self): 

74 d = dialog 

75 deactivate = d.deactivate_current_config = mock.Mock() 

76 save_ext = d.extpage.save_all_changed_extensions = mock.Mock() 

77 activate = d.activate_config_changes = mock.Mock() 

78 d.buttons['Apply'].invoke() 

79 deactivate.assert_called_once() 

80 save_ext.assert_called_once() 

81 activate.assert_called_once() 

82 del d.extpage.save_all_changed_extensions 

83 del d.activate_config_changes, d.deactivate_current_config 

84 

85 def test_click_cancel(self): 

86 d = dialog 

87 d.destroy = Func() 

88 changes['main']['something'] = 1 

89 d.buttons['Cancel'].invoke() 

90 self.assertEqual(changes['main'], {}) 

91 self.assertEqual(d.destroy.called, 1) 

92 del d.destroy 

93 

94 def test_click_help(self): 

95 dialog.note.select(dialog.keyspage) 

96 with mock.patch.object(configdialog, 'view_text', 

97 new_callable=Func) as view: 

98 dialog.buttons['Help'].invoke() 

99 title, contents = view.kwds['title'], view.kwds['contents'] 

100 self.assertEqual(title, 'Help for IDLE preferences') 

101 self.assertTrue(contents.startswith('When you click') and 

102 contents.endswith('a different name.\n')) 

103 

104 

105class FontPageTest(unittest.TestCase): 

106 """Test that font widgets enable users to make font changes. 

107 

108 Test that widget actions set vars, that var changes add three 

109 options to changes and call set_samples, and that set_samples 

110 changes the font of both sample boxes. 

111 """ 

112 @classmethod 

113 def setUpClass(cls): 

114 page = cls.page = dialog.fontpage 

115 dialog.note.select(page) 

116 page.set_samples = Func() # Mask instance method. 

117 page.update() 

118 

119 @classmethod 

120 def tearDownClass(cls): 

121 del cls.page.set_samples # Unmask instance method. 

122 

123 def setUp(self): 

124 changes.clear() 

125 

126 def test_load_font_cfg(self): 

127 # Leave widget load test to human visual check. 

128 # TODO Improve checks when add IdleConf.get_font_values. 

129 tracers.detach() 

130 d = self.page 

131 d.font_name.set('Fake') 

132 d.font_size.set('1') 

133 d.font_bold.set(True) 

134 d.set_samples.called = 0 

135 d.load_font_cfg() 

136 self.assertNotEqual(d.font_name.get(), 'Fake') 

137 self.assertNotEqual(d.font_size.get(), '1') 

138 self.assertFalse(d.font_bold.get()) 

139 self.assertEqual(d.set_samples.called, 1) 

140 tracers.attach() 

141 

142 def test_fontlist_key(self): 

143 # Up and Down keys should select a new font. 

144 d = self.page 

145 if d.fontlist.size() < 2: 

146 self.skipTest('need at least 2 fonts') 

147 fontlist = d.fontlist 

148 fontlist.activate(0) 

149 font = d.fontlist.get('active') 

150 

151 # Test Down key. 

152 fontlist.focus_force() 

153 fontlist.update() 

154 fontlist.event_generate('<Key-Down>') 

155 fontlist.event_generate('<KeyRelease-Down>') 

156 

157 down_font = fontlist.get('active') 

158 self.assertNotEqual(down_font, font) 

159 self.assertIn(d.font_name.get(), down_font.lower()) 

160 

161 # Test Up key. 

162 fontlist.focus_force() 

163 fontlist.update() 

164 fontlist.event_generate('<Key-Up>') 

165 fontlist.event_generate('<KeyRelease-Up>') 

166 

167 up_font = fontlist.get('active') 

168 self.assertEqual(up_font, font) 

169 self.assertIn(d.font_name.get(), up_font.lower()) 

170 

171 def test_fontlist_mouse(self): 

172 # Click on item should select that item. 

173 d = self.page 

174 if d.fontlist.size() < 2: 

175 self.skipTest('need at least 2 fonts') 

176 fontlist = d.fontlist 

177 fontlist.activate(0) 

178 

179 # Select next item in listbox 

180 fontlist.focus_force() 

181 fontlist.see(1) 

182 fontlist.update() 

183 x, y, dx, dy = fontlist.bbox(1) 

184 x += dx // 2 

185 y += dy // 2 

186 fontlist.event_generate('<Button-1>', x=x, y=y) 

187 fontlist.event_generate('<ButtonRelease-1>', x=x, y=y) 

188 

189 font1 = fontlist.get(1) 

190 select_font = fontlist.get('anchor') 

191 self.assertEqual(select_font, font1) 

192 self.assertIn(d.font_name.get(), font1.lower()) 

193 

194 def test_sizelist(self): 

195 # Click on number should select that number 

196 d = self.page 

197 d.sizelist.variable.set(40) 

198 self.assertEqual(d.font_size.get(), '40') 

199 

200 def test_bold_toggle(self): 

201 # Click on checkbutton should invert it. 

202 d = self.page 

203 d.font_bold.set(False) 

204 d.bold_toggle.invoke() 

205 self.assertTrue(d.font_bold.get()) 

206 d.bold_toggle.invoke() 

207 self.assertFalse(d.font_bold.get()) 

208 

209 def test_font_set(self): 

210 # Test that setting a font Variable results in 3 provisional 

211 # change entries and a call to set_samples. Use values sure to 

212 # not be defaults. 

213 

214 default_font = idleConf.GetFont(root, 'main', 'EditorWindow') 

215 default_size = str(default_font[1]) 

216 default_bold = default_font[2] == 'bold' 

217 d = self.page 

218 d.font_size.set(default_size) 

219 d.font_bold.set(default_bold) 

220 d.set_samples.called = 0 

221 

222 d.font_name.set('Test Font') 

223 expected = {'EditorWindow': {'font': 'Test Font', 

224 'font-size': default_size, 

225 'font-bold': str(default_bold)}} 

226 self.assertEqual(mainpage, expected) 

227 self.assertEqual(d.set_samples.called, 1) 

228 changes.clear() 

229 

230 d.font_size.set('20') 

231 expected = {'EditorWindow': {'font': 'Test Font', 

232 'font-size': '20', 

233 'font-bold': str(default_bold)}} 

234 self.assertEqual(mainpage, expected) 

235 self.assertEqual(d.set_samples.called, 2) 

236 changes.clear() 

237 

238 d.font_bold.set(not default_bold) 

239 expected = {'EditorWindow': {'font': 'Test Font', 

240 'font-size': '20', 

241 'font-bold': str(not default_bold)}} 

242 self.assertEqual(mainpage, expected) 

243 self.assertEqual(d.set_samples.called, 3) 

244 

245 def test_set_samples(self): 

246 d = self.page 

247 del d.set_samples # Unmask method for test 

248 orig_samples = d.font_sample, d.highlight_sample 

249 d.font_sample, d.highlight_sample = {}, {} 

250 d.font_name.set('test') 

251 d.font_size.set('5') 

252 d.font_bold.set(1) 

253 expected = {'font': ('test', '5', 'bold')} 

254 

255 # Test set_samples. 

256 d.set_samples() 

257 self.assertTrue(d.font_sample == d.highlight_sample == expected) 

258 

259 d.font_sample, d.highlight_sample = orig_samples 

260 d.set_samples = Func() # Re-mask for other tests. 

261 

262 

263class HighPageTest(unittest.TestCase): 

264 """Test that highlight tab widgets enable users to make changes. 

265 

266 Test that widget actions set vars, that var changes add 

267 options to changes and that themes work correctly. 

268 """ 

269 

270 @classmethod 

271 def setUpClass(cls): 

272 page = cls.page = dialog.highpage 

273 dialog.note.select(page) 

274 page.set_theme_type = Func() 

275 page.paint_theme_sample = Func() 

276 page.set_highlight_target = Func() 

277 page.set_color_sample = Func() 

278 page.update() 

279 

280 @classmethod 

281 def tearDownClass(cls): 

282 d = cls.page 

283 del d.set_theme_type, d.paint_theme_sample 

284 del d.set_highlight_target, d.set_color_sample 

285 

286 def setUp(self): 

287 d = self.page 

288 # The following is needed for test_load_key_cfg, _delete_custom_keys. 

289 # This may indicate a defect in some test or function. 

290 for section in idleConf.GetSectionList('user', 'highlight'): 

291 idleConf.userCfg['highlight'].remove_section(section) 

292 changes.clear() 

293 d.set_theme_type.called = 0 

294 d.paint_theme_sample.called = 0 

295 d.set_highlight_target.called = 0 

296 d.set_color_sample.called = 0 

297 

298 def test_load_theme_cfg(self): 

299 tracers.detach() 

300 d = self.page 

301 eq = self.assertEqual 

302 

303 # Use builtin theme with no user themes created. 

304 idleConf.CurrentTheme = mock.Mock(return_value='IDLE Classic') 

305 d.load_theme_cfg() 

306 self.assertTrue(d.theme_source.get()) 

307 # builtinlist sets variable builtin_name to the CurrentTheme default. 

308 eq(d.builtin_name.get(), 'IDLE Classic') 

309 eq(d.custom_name.get(), '- no custom themes -') 

310 eq(d.custom_theme_on.state(), ('disabled',)) 

311 eq(d.set_theme_type.called, 1) 

312 eq(d.paint_theme_sample.called, 1) 

313 eq(d.set_highlight_target.called, 1) 

314 

315 # Builtin theme with non-empty user theme list. 

316 idleConf.SetOption('highlight', 'test1', 'option', 'value') 

317 idleConf.SetOption('highlight', 'test2', 'option2', 'value2') 

318 d.load_theme_cfg() 

319 eq(d.builtin_name.get(), 'IDLE Classic') 

320 eq(d.custom_name.get(), 'test1') 

321 eq(d.set_theme_type.called, 2) 

322 eq(d.paint_theme_sample.called, 2) 

323 eq(d.set_highlight_target.called, 2) 

324 

325 # Use custom theme. 

326 idleConf.CurrentTheme = mock.Mock(return_value='test2') 

327 idleConf.SetOption('main', 'Theme', 'default', '0') 

328 d.load_theme_cfg() 

329 self.assertFalse(d.theme_source.get()) 

330 eq(d.builtin_name.get(), 'IDLE Classic') 

331 eq(d.custom_name.get(), 'test2') 

332 eq(d.set_theme_type.called, 3) 

333 eq(d.paint_theme_sample.called, 3) 

334 eq(d.set_highlight_target.called, 3) 

335 

336 del idleConf.CurrentTheme 

337 tracers.attach() 

338 

339 def test_theme_source(self): 

340 eq = self.assertEqual 

341 d = self.page 

342 # Test these separately. 

343 d.var_changed_builtin_name = Func() 

344 d.var_changed_custom_name = Func() 

345 # Builtin selected. 

346 d.builtin_theme_on.invoke() 

347 eq(mainpage, {'Theme': {'default': 'True'}}) 

348 eq(d.var_changed_builtin_name.called, 1) 

349 eq(d.var_changed_custom_name.called, 0) 

350 changes.clear() 

351 

352 # Custom selected. 

353 d.custom_theme_on.state(('!disabled',)) 

354 d.custom_theme_on.invoke() 

355 self.assertEqual(mainpage, {'Theme': {'default': 'False'}}) 

356 eq(d.var_changed_builtin_name.called, 1) 

357 eq(d.var_changed_custom_name.called, 1) 

358 del d.var_changed_builtin_name, d.var_changed_custom_name 

359 

360 def test_builtin_name(self): 

361 eq = self.assertEqual 

362 d = self.page 

363 item_list = ['IDLE Classic', 'IDLE Dark', 'IDLE New'] 

364 

365 # Not in old_themes, defaults name to first item. 

366 idleConf.SetOption('main', 'Theme', 'name', 'spam') 

367 d.builtinlist.SetMenu(item_list, 'IDLE Dark') 

368 eq(mainpage, {'Theme': {'name': 'IDLE Classic', 

369 'name2': 'IDLE Dark'}}) 

370 eq(d.theme_message['text'], 'New theme, see Help') 

371 eq(d.paint_theme_sample.called, 1) 

372 

373 # Not in old themes - uses name2. 

374 changes.clear() 

375 idleConf.SetOption('main', 'Theme', 'name', 'IDLE New') 

376 d.builtinlist.SetMenu(item_list, 'IDLE Dark') 

377 eq(mainpage, {'Theme': {'name2': 'IDLE Dark'}}) 

378 eq(d.theme_message['text'], 'New theme, see Help') 

379 eq(d.paint_theme_sample.called, 2) 

380 

381 # Builtin name in old_themes. 

382 changes.clear() 

383 d.builtinlist.SetMenu(item_list, 'IDLE Classic') 

384 eq(mainpage, {'Theme': {'name': 'IDLE Classic', 'name2': ''}}) 

385 eq(d.theme_message['text'], '') 

386 eq(d.paint_theme_sample.called, 3) 

387 

388 def test_custom_name(self): 

389 d = self.page 

390 

391 # If no selections, doesn't get added. 

392 d.customlist.SetMenu([], '- no custom themes -') 

393 self.assertNotIn('Theme', mainpage) 

394 self.assertEqual(d.paint_theme_sample.called, 0) 

395 

396 # Custom name selected. 

397 changes.clear() 

398 d.customlist.SetMenu(['a', 'b', 'c'], 'c') 

399 self.assertEqual(mainpage, {'Theme': {'name': 'c'}}) 

400 self.assertEqual(d.paint_theme_sample.called, 1) 

401 

402 def test_color(self): 

403 d = self.page 

404 d.on_new_color_set = Func() 

405 # self.color is only set in get_color through colorchooser. 

406 d.color.set('green') 

407 self.assertEqual(d.on_new_color_set.called, 1) 

408 del d.on_new_color_set 

409 

410 def test_highlight_target_list_mouse(self): 

411 # Set highlight_target through targetlist. 

412 eq = self.assertEqual 

413 d = self.page 

414 

415 d.targetlist.SetMenu(['a', 'b', 'c'], 'c') 

416 eq(d.highlight_target.get(), 'c') 

417 eq(d.set_highlight_target.called, 1) 

418 

419 def test_highlight_target_text_mouse(self): 

420 # Set highlight_target through clicking highlight_sample. 

421 eq = self.assertEqual 

422 d = self.page 

423 

424 elem = {} 

425 count = 0 

426 hs = d.highlight_sample 

427 hs.focus_force() 

428 hs.see(1.0) 

429 hs.update_idletasks() 

430 

431 def tag_to_element(elem): 

432 for element, tag in d.theme_elements.items(): 

433 elem[tag[0]] = element 

434 

435 def click_it(start): 

436 x, y, dx, dy = hs.bbox(start) 

437 x += dx // 2 

438 y += dy // 2 

439 hs.event_generate('<Enter>', x=0, y=0) 

440 hs.event_generate('<Motion>', x=x, y=y) 

441 hs.event_generate('<ButtonPress-1>', x=x, y=y) 

442 hs.event_generate('<ButtonRelease-1>', x=x, y=y) 

443 

444 # Flip theme_elements to make the tag the key. 

445 tag_to_element(elem) 

446 

447 # If highlight_sample has a tag that isn't in theme_elements, there 

448 # will be a KeyError in the test run. 

449 for tag in hs.tag_names(): 

450 for start_index in hs.tag_ranges(tag)[0::2]: 

451 count += 1 

452 click_it(start_index) 

453 eq(d.highlight_target.get(), elem[tag]) 

454 eq(d.set_highlight_target.called, count) 

455 

456 def test_highlight_sample_double_click(self): 

457 # Test double click on highlight_sample. 

458 eq = self.assertEqual 

459 d = self.page 

460 

461 hs = d.highlight_sample 

462 hs.focus_force() 

463 hs.see(1.0) 

464 hs.update_idletasks() 

465 

466 # Test binding from configdialog. 

467 hs.event_generate('<Enter>', x=0, y=0) 

468 hs.event_generate('<Motion>', x=0, y=0) 

469 # Double click is a sequence of two clicks in a row. 

470 for _ in range(2): 

471 hs.event_generate('<ButtonPress-1>', x=0, y=0) 

472 hs.event_generate('<ButtonRelease-1>', x=0, y=0) 

473 

474 eq(hs.tag_ranges('sel'), ()) 

475 

476 def test_highlight_sample_b1_motion(self): 

477 # Test button motion on highlight_sample. 

478 eq = self.assertEqual 

479 d = self.page 

480 

481 hs = d.highlight_sample 

482 hs.focus_force() 

483 hs.see(1.0) 

484 hs.update_idletasks() 

485 

486 x, y, dx, dy, offset = hs.dlineinfo('1.0') 

487 

488 # Test binding from configdialog. 

489 hs.event_generate('<Leave>') 

490 hs.event_generate('<Enter>') 

491 hs.event_generate('<Motion>', x=x, y=y) 

492 hs.event_generate('<ButtonPress-1>', x=x, y=y) 

493 hs.event_generate('<B1-Motion>', x=dx, y=dy) 

494 hs.event_generate('<ButtonRelease-1>', x=dx, y=dy) 

495 

496 eq(hs.tag_ranges('sel'), ()) 

497 

498 def test_set_theme_type(self): 

499 eq = self.assertEqual 

500 d = self.page 

501 del d.set_theme_type 

502 

503 # Builtin theme selected. 

504 d.theme_source.set(True) 

505 d.set_theme_type() 

506 eq(d.builtinlist['state'], NORMAL) 

507 eq(d.customlist['state'], DISABLED) 

508 eq(d.button_delete_custom.state(), ('disabled',)) 

509 

510 # Custom theme selected. 

511 d.theme_source.set(False) 

512 d.set_theme_type() 

513 eq(d.builtinlist['state'], DISABLED) 

514 eq(d.custom_theme_on.state(), ('selected',)) 

515 eq(d.customlist['state'], NORMAL) 

516 eq(d.button_delete_custom.state(), ()) 

517 d.set_theme_type = Func() 

518 

519 def test_get_color(self): 

520 eq = self.assertEqual 

521 d = self.page 

522 orig_chooser = configdialog.colorchooser.askcolor 

523 chooser = configdialog.colorchooser.askcolor = Func() 

524 gntn = d.get_new_theme_name = Func() 

525 

526 d.highlight_target.set('Editor Breakpoint') 

527 d.color.set('#ffffff') 

528 

529 # Nothing selected. 

530 chooser.result = (None, None) 

531 d.button_set_color.invoke() 

532 eq(d.color.get(), '#ffffff') 

533 

534 # Selection same as previous color. 

535 chooser.result = ('', d.style.lookup(d.frame_color_set['style'], 'background')) 

536 d.button_set_color.invoke() 

537 eq(d.color.get(), '#ffffff') 

538 

539 # Select different color. 

540 chooser.result = ((222.8671875, 0.0, 0.0), '#de0000') 

541 

542 # Default theme. 

543 d.color.set('#ffffff') 

544 d.theme_source.set(True) 

545 

546 # No theme name selected therefore color not saved. 

547 gntn.result = '' 

548 d.button_set_color.invoke() 

549 eq(gntn.called, 1) 

550 eq(d.color.get(), '#ffffff') 

551 # Theme name selected. 

552 gntn.result = 'My New Theme' 

553 d.button_set_color.invoke() 

554 eq(d.custom_name.get(), gntn.result) 

555 eq(d.color.get(), '#de0000') 

556 

557 # Custom theme. 

558 d.color.set('#ffffff') 

559 d.theme_source.set(False) 

560 d.button_set_color.invoke() 

561 eq(d.color.get(), '#de0000') 

562 

563 del d.get_new_theme_name 

564 configdialog.colorchooser.askcolor = orig_chooser 

565 

566 def test_on_new_color_set(self): 

567 d = self.page 

568 color = '#3f7cae' 

569 d.custom_name.set('Python') 

570 d.highlight_target.set('Selected Text') 

571 d.fg_bg_toggle.set(True) 

572 

573 d.color.set(color) 

574 self.assertEqual(d.style.lookup(d.frame_color_set['style'], 'background'), color) 

575 self.assertEqual(d.highlight_sample.tag_cget('hilite', 'foreground'), color) 

576 self.assertEqual(highpage, 

577 {'Python': {'hilite-foreground': color}}) 

578 

579 def test_get_new_theme_name(self): 

580 orig_sectionname = configdialog.SectionName 

581 sn = configdialog.SectionName = Func(return_self=True) 

582 d = self.page 

583 

584 sn.result = 'New Theme' 

585 self.assertEqual(d.get_new_theme_name(''), 'New Theme') 

586 

587 configdialog.SectionName = orig_sectionname 

588 

589 def test_save_as_new_theme(self): 

590 d = self.page 

591 gntn = d.get_new_theme_name = Func() 

592 d.theme_source.set(True) 

593 

594 # No name entered. 

595 gntn.result = '' 

596 d.button_save_custom.invoke() 

597 self.assertNotIn(gntn.result, idleConf.userCfg['highlight']) 

598 

599 # Name entered. 

600 gntn.result = 'my new theme' 

601 gntn.called = 0 

602 self.assertNotIn(gntn.result, idleConf.userCfg['highlight']) 

603 d.button_save_custom.invoke() 

604 self.assertIn(gntn.result, idleConf.userCfg['highlight']) 

605 

606 del d.get_new_theme_name 

607 

608 def test_create_new_and_save_new(self): 

609 eq = self.assertEqual 

610 d = self.page 

611 

612 # Use default as previously active theme. 

613 d.theme_source.set(True) 

614 d.builtin_name.set('IDLE Classic') 

615 first_new = 'my new custom theme' 

616 second_new = 'my second custom theme' 

617 

618 # No changes, so themes are an exact copy. 

619 self.assertNotIn(first_new, idleConf.userCfg) 

620 d.create_new(first_new) 

621 eq(idleConf.GetSectionList('user', 'highlight'), [first_new]) 

622 eq(idleConf.GetThemeDict('default', 'IDLE Classic'), 

623 idleConf.GetThemeDict('user', first_new)) 

624 eq(d.custom_name.get(), first_new) 

625 self.assertFalse(d.theme_source.get()) # Use custom set. 

626 eq(d.set_theme_type.called, 1) 

627 

628 # Test that changed targets are in new theme. 

629 changes.add_option('highlight', first_new, 'hit-background', 'yellow') 

630 self.assertNotIn(second_new, idleConf.userCfg) 

631 d.create_new(second_new) 

632 eq(idleConf.GetSectionList('user', 'highlight'), [first_new, second_new]) 

633 self.assertNotEqual(idleConf.GetThemeDict('user', first_new), 

634 idleConf.GetThemeDict('user', second_new)) 

635 # Check that difference in themes was in `hit-background` from `changes`. 

636 idleConf.SetOption('highlight', first_new, 'hit-background', 'yellow') 

637 eq(idleConf.GetThemeDict('user', first_new), 

638 idleConf.GetThemeDict('user', second_new)) 

639 

640 def test_set_highlight_target(self): 

641 eq = self.assertEqual 

642 d = self.page 

643 del d.set_highlight_target 

644 

645 # Target is cursor. 

646 d.highlight_target.set('Cursor') 

647 eq(d.fg_on.state(), ('disabled', 'selected')) 

648 eq(d.bg_on.state(), ('disabled',)) 

649 self.assertTrue(d.fg_bg_toggle) 

650 eq(d.set_color_sample.called, 1) 

651 

652 # Target is not cursor. 

653 d.highlight_target.set('Comment') 

654 eq(d.fg_on.state(), ('selected',)) 

655 eq(d.bg_on.state(), ()) 

656 self.assertTrue(d.fg_bg_toggle) 

657 eq(d.set_color_sample.called, 2) 

658 

659 d.set_highlight_target = Func() 

660 

661 def test_set_color_sample_binding(self): 

662 d = self.page 

663 scs = d.set_color_sample 

664 

665 d.fg_on.invoke() 

666 self.assertEqual(scs.called, 1) 

667 

668 d.bg_on.invoke() 

669 self.assertEqual(scs.called, 2) 

670 

671 def test_set_color_sample(self): 

672 d = self.page 

673 del d.set_color_sample 

674 d.highlight_target.set('Selected Text') 

675 d.fg_bg_toggle.set(True) 

676 d.set_color_sample() 

677 self.assertEqual( 

678 d.style.lookup(d.frame_color_set['style'], 'background'), 

679 d.highlight_sample.tag_cget('hilite', 'foreground')) 

680 d.set_color_sample = Func() 

681 

682 def test_paint_theme_sample(self): 

683 eq = self.assertEqual 

684 page = self.page 

685 del page.paint_theme_sample # Delete masking mock. 

686 hs_tag = page.highlight_sample.tag_cget 

687 gh = idleConf.GetHighlight 

688 

689 # Create custom theme based on IDLE Dark. 

690 page.theme_source.set(True) 

691 page.builtin_name.set('IDLE Dark') 

692 theme = 'IDLE Test' 

693 page.create_new(theme) 

694 page.set_color_sample.called = 0 

695 

696 # Base theme with nothing in `changes`. 

697 page.paint_theme_sample() 

698 new_console = {'foreground': 'blue', 

699 'background': 'yellow',} 

700 for key, value in new_console.items(): 

701 self.assertNotEqual(hs_tag('console', key), value) 

702 eq(page.set_color_sample.called, 1) 

703 

704 # Apply changes. 

705 for key, value in new_console.items(): 

706 changes.add_option('highlight', theme, 'console-'+key, value) 

707 page.paint_theme_sample() 

708 for key, value in new_console.items(): 

709 eq(hs_tag('console', key), value) 

710 eq(page.set_color_sample.called, 2) 

711 

712 page.paint_theme_sample = Func() 

713 

714 def test_delete_custom(self): 

715 eq = self.assertEqual 

716 d = self.page 

717 d.button_delete_custom.state(('!disabled',)) 

718 yesno = d.askyesno = Func() 

719 dialog.deactivate_current_config = Func() 

720 dialog.activate_config_changes = Func() 

721 

722 theme_name = 'spam theme' 

723 idleConf.userCfg['highlight'].SetOption(theme_name, 'name', 'value') 

724 highpage[theme_name] = {'option': 'True'} 

725 

726 theme_name2 = 'other theme' 

727 idleConf.userCfg['highlight'].SetOption(theme_name2, 'name', 'value') 

728 highpage[theme_name2] = {'option': 'False'} 

729 

730 # Force custom theme. 

731 d.custom_theme_on.state(('!disabled',)) 

732 d.custom_theme_on.invoke() 

733 d.custom_name.set(theme_name) 

734 

735 # Cancel deletion. 

736 yesno.result = False 

737 d.button_delete_custom.invoke() 

738 eq(yesno.called, 1) 

739 eq(highpage[theme_name], {'option': 'True'}) 

740 eq(idleConf.GetSectionList('user', 'highlight'), [theme_name, theme_name2]) 

741 eq(dialog.deactivate_current_config.called, 0) 

742 eq(dialog.activate_config_changes.called, 0) 

743 eq(d.set_theme_type.called, 0) 

744 

745 # Confirm deletion. 

746 yesno.result = True 

747 d.button_delete_custom.invoke() 

748 eq(yesno.called, 2) 

749 self.assertNotIn(theme_name, highpage) 

750 eq(idleConf.GetSectionList('user', 'highlight'), [theme_name2]) 

751 eq(d.custom_theme_on.state(), ()) 

752 eq(d.custom_name.get(), theme_name2) 

753 eq(dialog.deactivate_current_config.called, 1) 

754 eq(dialog.activate_config_changes.called, 1) 

755 eq(d.set_theme_type.called, 1) 

756 

757 # Confirm deletion of second theme - empties list. 

758 d.custom_name.set(theme_name2) 

759 yesno.result = True 

760 d.button_delete_custom.invoke() 

761 eq(yesno.called, 3) 

762 self.assertNotIn(theme_name, highpage) 

763 eq(idleConf.GetSectionList('user', 'highlight'), []) 

764 eq(d.custom_theme_on.state(), ('disabled',)) 

765 eq(d.custom_name.get(), '- no custom themes -') 

766 eq(dialog.deactivate_current_config.called, 2) 

767 eq(dialog.activate_config_changes.called, 2) 

768 eq(d.set_theme_type.called, 2) 

769 

770 del dialog.activate_config_changes, dialog.deactivate_current_config 

771 del d.askyesno 

772 

773 

774class KeysPageTest(unittest.TestCase): 

775 """Test that keys tab widgets enable users to make changes. 

776 

777 Test that widget actions set vars, that var changes add 

778 options to changes and that key sets works correctly. 

779 """ 

780 

781 @classmethod 

782 def setUpClass(cls): 

783 page = cls.page = dialog.keyspage 

784 dialog.note.select(page) 

785 page.set_keys_type = Func() 

786 page.load_keys_list = Func() 

787 

788 @classmethod 

789 def tearDownClass(cls): 

790 page = cls.page 

791 del page.set_keys_type, page.load_keys_list 

792 

793 def setUp(self): 

794 d = self.page 

795 # The following is needed for test_load_key_cfg, _delete_custom_keys. 

796 # This may indicate a defect in some test or function. 

797 for section in idleConf.GetSectionList('user', 'keys'): 

798 idleConf.userCfg['keys'].remove_section(section) 

799 changes.clear() 

800 d.set_keys_type.called = 0 

801 d.load_keys_list.called = 0 

802 

803 def test_load_key_cfg(self): 

804 tracers.detach() 

805 d = self.page 

806 eq = self.assertEqual 

807 

808 # Use builtin keyset with no user keysets created. 

809 idleConf.CurrentKeys = mock.Mock(return_value='IDLE Classic OSX') 

810 d.load_key_cfg() 

811 self.assertTrue(d.keyset_source.get()) 

812 # builtinlist sets variable builtin_name to the CurrentKeys default. 

813 eq(d.builtin_name.get(), 'IDLE Classic OSX') 

814 eq(d.custom_name.get(), '- no custom keys -') 

815 eq(d.custom_keyset_on.state(), ('disabled',)) 

816 eq(d.set_keys_type.called, 1) 

817 eq(d.load_keys_list.called, 1) 

818 eq(d.load_keys_list.args, ('IDLE Classic OSX', )) 

819 

820 # Builtin keyset with non-empty user keyset list. 

821 idleConf.SetOption('keys', 'test1', 'option', 'value') 

822 idleConf.SetOption('keys', 'test2', 'option2', 'value2') 

823 d.load_key_cfg() 

824 eq(d.builtin_name.get(), 'IDLE Classic OSX') 

825 eq(d.custom_name.get(), 'test1') 

826 eq(d.set_keys_type.called, 2) 

827 eq(d.load_keys_list.called, 2) 

828 eq(d.load_keys_list.args, ('IDLE Classic OSX', )) 

829 

830 # Use custom keyset. 

831 idleConf.CurrentKeys = mock.Mock(return_value='test2') 

832 idleConf.default_keys = mock.Mock(return_value='IDLE Modern Unix') 

833 idleConf.SetOption('main', 'Keys', 'default', '0') 

834 d.load_key_cfg() 

835 self.assertFalse(d.keyset_source.get()) 

836 eq(d.builtin_name.get(), 'IDLE Modern Unix') 

837 eq(d.custom_name.get(), 'test2') 

838 eq(d.set_keys_type.called, 3) 

839 eq(d.load_keys_list.called, 3) 

840 eq(d.load_keys_list.args, ('test2', )) 

841 

842 del idleConf.CurrentKeys, idleConf.default_keys 

843 tracers.attach() 

844 

845 def test_keyset_source(self): 

846 eq = self.assertEqual 

847 d = self.page 

848 # Test these separately. 

849 d.var_changed_builtin_name = Func() 

850 d.var_changed_custom_name = Func() 

851 # Builtin selected. 

852 d.builtin_keyset_on.invoke() 

853 eq(mainpage, {'Keys': {'default': 'True'}}) 

854 eq(d.var_changed_builtin_name.called, 1) 

855 eq(d.var_changed_custom_name.called, 0) 

856 changes.clear() 

857 

858 # Custom selected. 

859 d.custom_keyset_on.state(('!disabled',)) 

860 d.custom_keyset_on.invoke() 

861 self.assertEqual(mainpage, {'Keys': {'default': 'False'}}) 

862 eq(d.var_changed_builtin_name.called, 1) 

863 eq(d.var_changed_custom_name.called, 1) 

864 del d.var_changed_builtin_name, d.var_changed_custom_name 

865 

866 def test_builtin_name(self): 

867 eq = self.assertEqual 

868 d = self.page 

869 idleConf.userCfg['main'].remove_section('Keys') 

870 item_list = ['IDLE Classic Windows', 'IDLE Classic OSX', 

871 'IDLE Modern UNIX'] 

872 

873 # Not in old_keys, defaults name to first item. 

874 d.builtinlist.SetMenu(item_list, 'IDLE Modern UNIX') 

875 eq(mainpage, {'Keys': {'name': 'IDLE Classic Windows', 

876 'name2': 'IDLE Modern UNIX'}}) 

877 eq(d.keys_message['text'], 'New key set, see Help') 

878 eq(d.load_keys_list.called, 1) 

879 eq(d.load_keys_list.args, ('IDLE Modern UNIX', )) 

880 

881 # Not in old keys - uses name2. 

882 changes.clear() 

883 idleConf.SetOption('main', 'Keys', 'name', 'IDLE Classic Unix') 

884 d.builtinlist.SetMenu(item_list, 'IDLE Modern UNIX') 

885 eq(mainpage, {'Keys': {'name2': 'IDLE Modern UNIX'}}) 

886 eq(d.keys_message['text'], 'New key set, see Help') 

887 eq(d.load_keys_list.called, 2) 

888 eq(d.load_keys_list.args, ('IDLE Modern UNIX', )) 

889 

890 # Builtin name in old_keys. 

891 changes.clear() 

892 d.builtinlist.SetMenu(item_list, 'IDLE Classic OSX') 

893 eq(mainpage, {'Keys': {'name': 'IDLE Classic OSX', 'name2': ''}}) 

894 eq(d.keys_message['text'], '') 

895 eq(d.load_keys_list.called, 3) 

896 eq(d.load_keys_list.args, ('IDLE Classic OSX', )) 

897 

898 def test_custom_name(self): 

899 d = self.page 

900 

901 # If no selections, doesn't get added. 

902 d.customlist.SetMenu([], '- no custom keys -') 

903 self.assertNotIn('Keys', mainpage) 

904 self.assertEqual(d.load_keys_list.called, 0) 

905 

906 # Custom name selected. 

907 changes.clear() 

908 d.customlist.SetMenu(['a', 'b', 'c'], 'c') 

909 self.assertEqual(mainpage, {'Keys': {'name': 'c'}}) 

910 self.assertEqual(d.load_keys_list.called, 1) 

911 

912 def test_keybinding(self): 

913 idleConf.SetOption('extensions', 'ZzDummy', 'enable', 'True') 

914 d = self.page 

915 d.custom_name.set('my custom keys') 

916 d.bindingslist.delete(0, 'end') 

917 d.bindingslist.insert(0, 'copy') 

918 d.bindingslist.insert(1, 'z-in') 

919 d.bindingslist.selection_set(0) 

920 d.bindingslist.selection_anchor(0) 

921 # Core binding - adds to keys. 

922 d.keybinding.set('<Key-F11>') 

923 self.assertEqual(keyspage, 

924 {'my custom keys': {'copy': '<Key-F11>'}}) 

925 

926 # Not a core binding - adds to extensions. 

927 d.bindingslist.selection_set(1) 

928 d.bindingslist.selection_anchor(1) 

929 d.keybinding.set('<Key-F11>') 

930 self.assertEqual(extpage, 

931 {'ZzDummy_cfgBindings': {'z-in': '<Key-F11>'}}) 

932 

933 def test_set_keys_type(self): 

934 eq = self.assertEqual 

935 d = self.page 

936 del d.set_keys_type 

937 

938 # Builtin keyset selected. 

939 d.keyset_source.set(True) 

940 d.set_keys_type() 

941 eq(d.builtinlist['state'], NORMAL) 

942 eq(d.customlist['state'], DISABLED) 

943 eq(d.button_delete_custom_keys.state(), ('disabled',)) 

944 

945 # Custom keyset selected. 

946 d.keyset_source.set(False) 

947 d.set_keys_type() 

948 eq(d.builtinlist['state'], DISABLED) 

949 eq(d.custom_keyset_on.state(), ('selected',)) 

950 eq(d.customlist['state'], NORMAL) 

951 eq(d.button_delete_custom_keys.state(), ()) 

952 d.set_keys_type = Func() 

953 

954 def test_get_new_keys(self): 

955 eq = self.assertEqual 

956 d = self.page 

957 orig_getkeysdialog = configdialog.GetKeysWindow 

958 gkd = configdialog.GetKeysWindow = Func(return_self=True) 

959 gnkn = d.get_new_keys_name = Func() 

960 

961 d.button_new_keys.state(('!disabled',)) 

962 d.bindingslist.delete(0, 'end') 

963 d.bindingslist.insert(0, 'copy - <Control-Shift-Key-C>') 

964 d.bindingslist.selection_set(0) 

965 d.bindingslist.selection_anchor(0) 

966 d.keybinding.set('Key-a') 

967 d.keyset_source.set(True) # Default keyset. 

968 

969 # Default keyset; no change to binding. 

970 gkd.result = '' 

971 d.button_new_keys.invoke() 

972 eq(d.bindingslist.get('anchor'), 'copy - <Control-Shift-Key-C>') 

973 # Keybinding isn't changed when there isn't a change entered. 

974 eq(d.keybinding.get(), 'Key-a') 

975 

976 # Default keyset; binding changed. 

977 gkd.result = '<Key-F11>' 

978 # No keyset name selected therefore binding not saved. 

979 gnkn.result = '' 

980 d.button_new_keys.invoke() 

981 eq(gnkn.called, 1) 

982 eq(d.bindingslist.get('anchor'), 'copy - <Control-Shift-Key-C>') 

983 # Keyset name selected. 

984 gnkn.result = 'My New Key Set' 

985 d.button_new_keys.invoke() 

986 eq(d.custom_name.get(), gnkn.result) 

987 eq(d.bindingslist.get('anchor'), 'copy - <Key-F11>') 

988 eq(d.keybinding.get(), '<Key-F11>') 

989 

990 # User keyset; binding changed. 

991 d.keyset_source.set(False) # Custom keyset. 

992 gnkn.called = 0 

993 gkd.result = '<Key-p>' 

994 d.button_new_keys.invoke() 

995 eq(gnkn.called, 0) 

996 eq(d.bindingslist.get('anchor'), 'copy - <Key-p>') 

997 eq(d.keybinding.get(), '<Key-p>') 

998 

999 del d.get_new_keys_name 

1000 configdialog.GetKeysWindow = orig_getkeysdialog 

1001 

1002 def test_get_new_keys_name(self): 

1003 orig_sectionname = configdialog.SectionName 

1004 sn = configdialog.SectionName = Func(return_self=True) 

1005 d = self.page 

1006 

1007 sn.result = 'New Keys' 

1008 self.assertEqual(d.get_new_keys_name(''), 'New Keys') 

1009 

1010 configdialog.SectionName = orig_sectionname 

1011 

1012 def test_save_as_new_key_set(self): 

1013 d = self.page 

1014 gnkn = d.get_new_keys_name = Func() 

1015 d.keyset_source.set(True) 

1016 

1017 # No name entered. 

1018 gnkn.result = '' 

1019 d.button_save_custom_keys.invoke() 

1020 

1021 # Name entered. 

1022 gnkn.result = 'my new key set' 

1023 gnkn.called = 0 

1024 self.assertNotIn(gnkn.result, idleConf.userCfg['keys']) 

1025 d.button_save_custom_keys.invoke() 

1026 self.assertIn(gnkn.result, idleConf.userCfg['keys']) 

1027 

1028 del d.get_new_keys_name 

1029 

1030 def test_on_bindingslist_select(self): 

1031 d = self.page 

1032 b = d.bindingslist 

1033 b.delete(0, 'end') 

1034 b.insert(0, 'copy') 

1035 b.insert(1, 'find') 

1036 b.activate(0) 

1037 

1038 b.focus_force() 

1039 b.see(1) 

1040 b.update() 

1041 x, y, dx, dy = b.bbox(1) 

1042 x += dx // 2 

1043 y += dy // 2 

1044 b.event_generate('<Enter>', x=0, y=0) 

1045 b.event_generate('<Motion>', x=x, y=y) 

1046 b.event_generate('<Button-1>', x=x, y=y) 

1047 b.event_generate('<ButtonRelease-1>', x=x, y=y) 

1048 self.assertEqual(b.get('anchor'), 'find') 

1049 self.assertEqual(d.button_new_keys.state(), ()) 

1050 

1051 def test_create_new_key_set_and_save_new_key_set(self): 

1052 eq = self.assertEqual 

1053 d = self.page 

1054 

1055 # Use default as previously active keyset. 

1056 d.keyset_source.set(True) 

1057 d.builtin_name.set('IDLE Classic Windows') 

1058 first_new = 'my new custom key set' 

1059 second_new = 'my second custom keyset' 

1060 

1061 # No changes, so keysets are an exact copy. 

1062 self.assertNotIn(first_new, idleConf.userCfg) 

1063 d.create_new_key_set(first_new) 

1064 eq(idleConf.GetSectionList('user', 'keys'), [first_new]) 

1065 eq(idleConf.GetKeySet('IDLE Classic Windows'), 

1066 idleConf.GetKeySet(first_new)) 

1067 eq(d.custom_name.get(), first_new) 

1068 self.assertFalse(d.keyset_source.get()) # Use custom set. 

1069 eq(d.set_keys_type.called, 1) 

1070 

1071 # Test that changed keybindings are in new keyset. 

1072 changes.add_option('keys', first_new, 'copy', '<Key-F11>') 

1073 self.assertNotIn(second_new, idleConf.userCfg) 

1074 d.create_new_key_set(second_new) 

1075 eq(idleConf.GetSectionList('user', 'keys'), [first_new, second_new]) 

1076 self.assertNotEqual(idleConf.GetKeySet(first_new), 

1077 idleConf.GetKeySet(second_new)) 

1078 # Check that difference in keysets was in option `copy` from `changes`. 

1079 idleConf.SetOption('keys', first_new, 'copy', '<Key-F11>') 

1080 eq(idleConf.GetKeySet(first_new), idleConf.GetKeySet(second_new)) 

1081 

1082 def test_load_keys_list(self): 

1083 eq = self.assertEqual 

1084 d = self.page 

1085 gks = idleConf.GetKeySet = Func() 

1086 del d.load_keys_list 

1087 b = d.bindingslist 

1088 

1089 b.delete(0, 'end') 

1090 b.insert(0, '<<find>>') 

1091 b.insert(1, '<<help>>') 

1092 gks.result = {'<<copy>>': ['<Control-Key-c>', '<Control-Key-C>'], 

1093 '<<force-open-completions>>': ['<Control-Key-space>'], 

1094 '<<spam>>': ['<Key-F11>']} 

1095 changes.add_option('keys', 'my keys', 'spam', '<Shift-Key-a>') 

1096 expected = ('copy - <Control-Key-c> <Control-Key-C>', 

1097 'force-open-completions - <Control-Key-space>', 

1098 'spam - <Shift-Key-a>') 

1099 

1100 # No current selection. 

1101 d.load_keys_list('my keys') 

1102 eq(b.get(0, 'end'), expected) 

1103 eq(b.get('anchor'), '') 

1104 eq(b.curselection(), ()) 

1105 

1106 # Check selection. 

1107 b.selection_set(1) 

1108 b.selection_anchor(1) 

1109 d.load_keys_list('my keys') 

1110 eq(b.get(0, 'end'), expected) 

1111 eq(b.get('anchor'), 'force-open-completions - <Control-Key-space>') 

1112 eq(b.curselection(), (1, )) 

1113 

1114 # Change selection. 

1115 b.selection_set(2) 

1116 b.selection_anchor(2) 

1117 d.load_keys_list('my keys') 

1118 eq(b.get(0, 'end'), expected) 

1119 eq(b.get('anchor'), 'spam - <Shift-Key-a>') 

1120 eq(b.curselection(), (2, )) 

1121 d.load_keys_list = Func() 

1122 

1123 del idleConf.GetKeySet 

1124 

1125 def test_delete_custom_keys(self): 

1126 eq = self.assertEqual 

1127 d = self.page 

1128 d.button_delete_custom_keys.state(('!disabled',)) 

1129 yesno = d.askyesno = Func() 

1130 dialog.deactivate_current_config = Func() 

1131 dialog.activate_config_changes = Func() 

1132 

1133 keyset_name = 'spam key set' 

1134 idleConf.userCfg['keys'].SetOption(keyset_name, 'name', 'value') 

1135 keyspage[keyset_name] = {'option': 'True'} 

1136 

1137 keyset_name2 = 'other key set' 

1138 idleConf.userCfg['keys'].SetOption(keyset_name2, 'name', 'value') 

1139 keyspage[keyset_name2] = {'option': 'False'} 

1140 

1141 # Force custom keyset. 

1142 d.custom_keyset_on.state(('!disabled',)) 

1143 d.custom_keyset_on.invoke() 

1144 d.custom_name.set(keyset_name) 

1145 

1146 # Cancel deletion. 

1147 yesno.result = False 

1148 d.button_delete_custom_keys.invoke() 

1149 eq(yesno.called, 1) 

1150 eq(keyspage[keyset_name], {'option': 'True'}) 

1151 eq(idleConf.GetSectionList('user', 'keys'), [keyset_name, keyset_name2]) 

1152 eq(dialog.deactivate_current_config.called, 0) 

1153 eq(dialog.activate_config_changes.called, 0) 

1154 eq(d.set_keys_type.called, 0) 

1155 

1156 # Confirm deletion. 

1157 yesno.result = True 

1158 d.button_delete_custom_keys.invoke() 

1159 eq(yesno.called, 2) 

1160 self.assertNotIn(keyset_name, keyspage) 

1161 eq(idleConf.GetSectionList('user', 'keys'), [keyset_name2]) 

1162 eq(d.custom_keyset_on.state(), ()) 

1163 eq(d.custom_name.get(), keyset_name2) 

1164 eq(dialog.deactivate_current_config.called, 1) 

1165 eq(dialog.activate_config_changes.called, 1) 

1166 eq(d.set_keys_type.called, 1) 

1167 

1168 # Confirm deletion of second keyset - empties list. 

1169 d.custom_name.set(keyset_name2) 

1170 yesno.result = True 

1171 d.button_delete_custom_keys.invoke() 

1172 eq(yesno.called, 3) 

1173 self.assertNotIn(keyset_name, keyspage) 

1174 eq(idleConf.GetSectionList('user', 'keys'), []) 

1175 eq(d.custom_keyset_on.state(), ('disabled',)) 

1176 eq(d.custom_name.get(), '- no custom keys -') 

1177 eq(dialog.deactivate_current_config.called, 2) 

1178 eq(dialog.activate_config_changes.called, 2) 

1179 eq(d.set_keys_type.called, 2) 

1180 

1181 del dialog.activate_config_changes, dialog.deactivate_current_config 

1182 del d.askyesno 

1183 

1184 

1185class WinPageTest(unittest.TestCase): 

1186 """Test that general tab widgets enable users to make changes. 

1187 

1188 Test that widget actions set vars, that var changes add 

1189 options to changes. 

1190 """ 

1191 @classmethod 

1192 def setUpClass(cls): 

1193 page = cls.page = dialog.winpage 

1194 dialog.note.select(page) 

1195 page.update() 

1196 

1197 def setUp(self): 

1198 changes.clear() 

1199 

1200 def test_load_windows_cfg(self): 

1201 # Set to wrong values, load, check right values. 

1202 eq = self.assertEqual 

1203 d = self.page 

1204 d.startup_edit.set(1) 

1205 d.win_width.set(1) 

1206 d.win_height.set(1) 

1207 d.load_windows_cfg() 

1208 eq(d.startup_edit.get(), 0) 

1209 eq(d.win_width.get(), '80') 

1210 eq(d.win_height.get(), '40') 

1211 

1212 def test_startup(self): 

1213 d = self.page 

1214 d.startup_editor_on.invoke() 

1215 self.assertEqual(mainpage, 

1216 {'General': {'editor-on-startup': '1'}}) 

1217 changes.clear() 

1218 d.startup_shell_on.invoke() 

1219 self.assertEqual(mainpage, 

1220 {'General': {'editor-on-startup': '0'}}) 

1221 

1222 def test_editor_size(self): 

1223 d = self.page 

1224 d.win_height_int.delete(0, 'end') 

1225 d.win_height_int.insert(0, '11') 

1226 self.assertEqual(mainpage, {'EditorWindow': {'height': '11'}}) 

1227 changes.clear() 

1228 d.win_width_int.delete(0, 'end') 

1229 d.win_width_int.insert(0, '11') 

1230 self.assertEqual(mainpage, {'EditorWindow': {'width': '11'}}) 

1231 

1232 def test_indent_spaces(self): 

1233 d = self.page 

1234 d.indent_chooser.set(6) 

1235 self.assertEqual(d.indent_spaces.get(), '6') 

1236 self.assertEqual(mainpage, {'Indent': {'num-spaces': '6'}}) 

1237 

1238 def test_cursor_blink(self): 

1239 self.page.cursor_blink_bool.invoke() 

1240 self.assertEqual(mainpage, {'EditorWindow': {'cursor-blink': 'False'}}) 

1241 

1242 def test_autocomplete_wait(self): 

1243 self.page.auto_wait_int.delete(0, 'end') 

1244 self.page.auto_wait_int.insert(0, '11') 

1245 self.assertEqual(extpage, {'AutoComplete': {'popupwait': '11'}}) 

1246 

1247 def test_parenmatch(self): 

1248 d = self.page 

1249 eq = self.assertEqual 

1250 d.paren_style_type['menu'].invoke(0) 

1251 eq(extpage, {'ParenMatch': {'style': 'opener'}}) 

1252 changes.clear() 

1253 d.paren_flash_time.delete(0, 'end') 

1254 d.paren_flash_time.insert(0, '11') 

1255 eq(extpage, {'ParenMatch': {'flash-delay': '11'}}) 

1256 changes.clear() 

1257 d.bell_on.invoke() 

1258 eq(extpage, {'ParenMatch': {'bell': 'False'}}) 

1259 

1260 def test_paragraph(self): 

1261 self.page.format_width_int.delete(0, 'end') 

1262 self.page.format_width_int.insert(0, '11') 

1263 self.assertEqual(extpage, {'FormatParagraph': {'max-width': '11'}}) 

1264 

1265 

1266class ShedPageTest(unittest.TestCase): 

1267 """Test that shed tab widgets enable users to make changes. 

1268 

1269 Test that widget actions set vars, that var changes add 

1270 options to changes. 

1271 """ 

1272 @classmethod 

1273 def setUpClass(cls): 

1274 page = cls.page = dialog.shedpage 

1275 dialog.note.select(page) 

1276 page.update() 

1277 

1278 def setUp(self): 

1279 changes.clear() 

1280 

1281 def test_load_shelled_cfg(self): 

1282 # Set to wrong values, load, check right values. 

1283 eq = self.assertEqual 

1284 d = self.page 

1285 d.autosave.set(1) 

1286 d.load_shelled_cfg() 

1287 eq(d.autosave.get(), 0) 

1288 

1289 def test_autosave(self): 

1290 d = self.page 

1291 d.save_auto_on.invoke() 

1292 self.assertEqual(mainpage, {'General': {'autosave': '1'}}) 

1293 d.save_ask_on.invoke() 

1294 self.assertEqual(mainpage, {'General': {'autosave': '0'}}) 

1295 

1296 def test_context(self): 

1297 self.page.context_int.delete(0, 'end') 

1298 self.page.context_int.insert(0, '1') 

1299 self.assertEqual(extpage, {'CodeContext': {'maxlines': '1'}}) 

1300 

1301 

1302#unittest.skip("Nothing here yet TODO") 

1303class ExtPageTest(unittest.TestCase): 

1304 """Test that the help source list works correctly.""" 

1305 @classmethod 

1306 def setUpClass(cls): 

1307 page = dialog.extpage 

1308 dialog.note.select(page) 

1309 

1310 

1311class HelpSourceTest(unittest.TestCase): 

1312 """Test that the help source list works correctly.""" 

1313 @classmethod 

1314 def setUpClass(cls): 

1315 page = dialog.extpage 

1316 dialog.note.select(page) 

1317 frame = cls.frame = page.frame_help 

1318 frame.set = frame.set_add_delete_state = Func() 

1319 frame.upc = frame.update_help_changes = Func() 

1320 frame.update() 

1321 

1322 @classmethod 

1323 def tearDownClass(cls): 

1324 frame = cls.frame 

1325 del frame.set, frame.set_add_delete_state 

1326 del frame.upc, frame.update_help_changes 

1327 frame.helplist.delete(0, 'end') 

1328 frame.user_helplist.clear() 

1329 

1330 def setUp(self): 

1331 changes.clear() 

1332 

1333 def test_load_helplist(self): 

1334 eq = self.assertEqual 

1335 fr = self.frame 

1336 fr.helplist.insert('end', 'bad') 

1337 fr.user_helplist = ['bad', 'worse'] 

1338 idleConf.SetOption('main', 'HelpFiles', '1', 'name;file') 

1339 fr.load_helplist() 

1340 eq(fr.helplist.get(0, 'end'), ('name',)) 

1341 eq(fr.user_helplist, [('name', 'file', '1')]) 

1342 

1343 def test_source_selected(self): 

1344 fr = self.frame 

1345 fr.set = fr.set_add_delete_state 

1346 fr.upc = fr.update_help_changes 

1347 helplist = fr.helplist 

1348 dex = 'end' 

1349 helplist.insert(dex, 'source') 

1350 helplist.activate(dex) 

1351 

1352 helplist.focus_force() 

1353 helplist.see(dex) 

1354 helplist.update() 

1355 x, y, dx, dy = helplist.bbox(dex) 

1356 x += dx // 2 

1357 y += dy // 2 

1358 fr.set.called = fr.upc.called = 0 

1359 helplist.event_generate('<Enter>', x=0, y=0) 

1360 helplist.event_generate('<Motion>', x=x, y=y) 

1361 helplist.event_generate('<Button-1>', x=x, y=y) 

1362 helplist.event_generate('<ButtonRelease-1>', x=x, y=y) 

1363 self.assertEqual(helplist.get('anchor'), 'source') 

1364 self.assertTrue(fr.set.called) 

1365 self.assertFalse(fr.upc.called) 

1366 

1367 def test_set_add_delete_state(self): 

1368 # Call with 0 items, 1 unselected item, 1 selected item. 

1369 eq = self.assertEqual 

1370 fr = self.frame 

1371 del fr.set_add_delete_state # Unmask method. 

1372 sad = fr.set_add_delete_state 

1373 h = fr.helplist 

1374 

1375 h.delete(0, 'end') 

1376 sad() 

1377 eq(fr.button_helplist_edit.state(), ('disabled',)) 

1378 eq(fr.button_helplist_remove.state(), ('disabled',)) 

1379 

1380 h.insert(0, 'source') 

1381 sad() 

1382 eq(fr.button_helplist_edit.state(), ('disabled',)) 

1383 eq(fr.button_helplist_remove.state(), ('disabled',)) 

1384 

1385 h.selection_set(0) 

1386 sad() 

1387 eq(fr.button_helplist_edit.state(), ()) 

1388 eq(fr.button_helplist_remove.state(), ()) 

1389 fr.set_add_delete_state = Func() # Mask method. 

1390 

1391 def test_helplist_item_add(self): 

1392 # Call without and twice with HelpSource result. 

1393 # Double call enables check on order. 

1394 eq = self.assertEqual 

1395 orig_helpsource = configdialog.HelpSource 

1396 hs = configdialog.HelpSource = Func(return_self=True) 

1397 fr = self.frame 

1398 fr.helplist.delete(0, 'end') 

1399 fr.user_helplist.clear() 

1400 fr.set.called = fr.upc.called = 0 

1401 

1402 hs.result = '' 

1403 fr.helplist_item_add() 

1404 self.assertTrue(list(fr.helplist.get(0, 'end')) == 

1405 fr.user_helplist == []) 

1406 self.assertFalse(fr.upc.called) 

1407 

1408 hs.result = ('name1', 'file1') 

1409 fr.helplist_item_add() 

1410 hs.result = ('name2', 'file2') 

1411 fr.helplist_item_add() 

1412 eq(fr.helplist.get(0, 'end'), ('name1', 'name2')) 

1413 eq(fr.user_helplist, [('name1', 'file1'), ('name2', 'file2')]) 

1414 eq(fr.upc.called, 2) 

1415 self.assertFalse(fr.set.called) 

1416 

1417 configdialog.HelpSource = orig_helpsource 

1418 

1419 def test_helplist_item_edit(self): 

1420 # Call without and with HelpSource change. 

1421 eq = self.assertEqual 

1422 orig_helpsource = configdialog.HelpSource 

1423 hs = configdialog.HelpSource = Func(return_self=True) 

1424 fr = self.frame 

1425 fr.helplist.delete(0, 'end') 

1426 fr.helplist.insert(0, 'name1') 

1427 fr.helplist.selection_set(0) 

1428 fr.helplist.selection_anchor(0) 

1429 fr.user_helplist.clear() 

1430 fr.user_helplist.append(('name1', 'file1')) 

1431 fr.set.called = fr.upc.called = 0 

1432 

1433 hs.result = '' 

1434 fr.helplist_item_edit() 

1435 hs.result = ('name1', 'file1') 

1436 fr.helplist_item_edit() 

1437 eq(fr.helplist.get(0, 'end'), ('name1',)) 

1438 eq(fr.user_helplist, [('name1', 'file1')]) 

1439 self.assertFalse(fr.upc.called) 

1440 

1441 hs.result = ('name2', 'file2') 

1442 fr.helplist_item_edit() 

1443 eq(fr.helplist.get(0, 'end'), ('name2',)) 

1444 eq(fr.user_helplist, [('name2', 'file2')]) 

1445 self.assertTrue(fr.upc.called == fr.set.called == 1) 

1446 

1447 configdialog.HelpSource = orig_helpsource 

1448 

1449 def test_helplist_item_remove(self): 

1450 eq = self.assertEqual 

1451 fr = self.frame 

1452 fr.helplist.delete(0, 'end') 

1453 fr.helplist.insert(0, 'name1') 

1454 fr.helplist.selection_set(0) 

1455 fr.helplist.selection_anchor(0) 

1456 fr.user_helplist.clear() 

1457 fr.user_helplist.append(('name1', 'file1')) 

1458 fr.set.called = fr.upc.called = 0 

1459 

1460 fr.helplist_item_remove() 

1461 eq(fr.helplist.get(0, 'end'), ()) 

1462 eq(fr.user_helplist, []) 

1463 self.assertTrue(fr.upc.called == fr.set.called == 1) 

1464 

1465 def test_update_help_changes(self): 

1466 fr = self.frame 

1467 del fr.update_help_changes 

1468 fr.user_helplist.clear() 

1469 fr.user_helplist.append(('name1', 'file1')) 

1470 fr.user_helplist.append(('name2', 'file2')) 

1471 

1472 fr.update_help_changes() 

1473 self.assertEqual(mainpage['HelpFiles'], 

1474 {'1': 'name1;file1', '2': 'name2;file2'}) 

1475 fr.update_help_changes = Func() 

1476 

1477 

1478class VarTraceTest(unittest.TestCase): 

1479 

1480 @classmethod 

1481 def setUpClass(cls): 

1482 cls.tracers = configdialog.VarTrace() 

1483 cls.iv = IntVar(root) 

1484 cls.bv = BooleanVar(root) 

1485 

1486 @classmethod 

1487 def tearDownClass(cls): 

1488 del cls.tracers, cls.iv, cls.bv 

1489 

1490 def setUp(self): 

1491 self.tracers.clear() 

1492 self.called = 0 

1493 

1494 def var_changed_increment(self, *params): 

1495 self.called += 13 

1496 

1497 def var_changed_boolean(self, *params): 

1498 pass 

1499 

1500 def test_init(self): 

1501 tr = self.tracers 

1502 tr.__init__() 

1503 self.assertEqual(tr.untraced, []) 

1504 self.assertEqual(tr.traced, []) 

1505 

1506 def test_clear(self): 

1507 tr = self.tracers 

1508 tr.untraced.append(0) 

1509 tr.traced.append(1) 

1510 tr.clear() 

1511 self.assertEqual(tr.untraced, []) 

1512 self.assertEqual(tr.traced, []) 

1513 

1514 def test_add(self): 

1515 tr = self.tracers 

1516 func = Func() 

1517 cb = tr.make_callback = mock.Mock(return_value=func) 

1518 

1519 iv = tr.add(self.iv, self.var_changed_increment) 

1520 self.assertIs(iv, self.iv) 

1521 bv = tr.add(self.bv, self.var_changed_boolean) 

1522 self.assertIs(bv, self.bv) 

1523 

1524 sv = StringVar(root) 

1525 sv2 = tr.add(sv, ('main', 'section', 'option')) 

1526 self.assertIs(sv2, sv) 

1527 cb.assert_called_once() 

1528 cb.assert_called_with(sv, ('main', 'section', 'option')) 

1529 

1530 expected = [(iv, self.var_changed_increment), 

1531 (bv, self.var_changed_boolean), 

1532 (sv, func)] 

1533 self.assertEqual(tr.traced, []) 

1534 self.assertEqual(tr.untraced, expected) 

1535 

1536 del tr.make_callback 

1537 

1538 def test_make_callback(self): 

1539 cb = self.tracers.make_callback(self.iv, ('main', 'section', 'option')) 

1540 self.assertTrue(callable(cb)) 

1541 self.iv.set(42) 

1542 # Not attached, so set didn't invoke the callback. 

1543 self.assertNotIn('section', changes['main']) 

1544 # Invoke callback manually. 

1545 cb() 

1546 self.assertIn('section', changes['main']) 

1547 self.assertEqual(changes['main']['section']['option'], '42') 

1548 changes.clear() 

1549 

1550 def test_attach_detach(self): 

1551 tr = self.tracers 

1552 iv = tr.add(self.iv, self.var_changed_increment) 

1553 bv = tr.add(self.bv, self.var_changed_boolean) 

1554 expected = [(iv, self.var_changed_increment), 

1555 (bv, self.var_changed_boolean)] 

1556 

1557 # Attach callbacks and test call increment. 

1558 tr.attach() 

1559 self.assertEqual(tr.untraced, []) 

1560 self.assertCountEqual(tr.traced, expected) 

1561 iv.set(1) 

1562 self.assertEqual(iv.get(), 1) 

1563 self.assertEqual(self.called, 13) 

1564 

1565 # Check that only one callback is attached to a variable. 

1566 # If more than one callback were attached, then var_changed_increment 

1567 # would be called twice and the counter would be 2. 

1568 self.called = 0 

1569 tr.attach() 

1570 iv.set(1) 

1571 self.assertEqual(self.called, 13) 

1572 

1573 # Detach callbacks. 

1574 self.called = 0 

1575 tr.detach() 

1576 self.assertEqual(tr.traced, []) 

1577 self.assertCountEqual(tr.untraced, expected) 

1578 iv.set(1) 

1579 self.assertEqual(self.called, 0) 

1580 

1581 

1582if __name__ == '__main__': 

1583 unittest.main(verbosity=2)