123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- '''Run human tests of Idle's window, dialog, and popup widgets.
- run(*tests)
- Create a master Tk window. Within that, run each callable in tests
- after finding the matching test spec in this file. If tests is empty,
- run an htest for each spec dict in this file after finding the matching
- callable in the module named in the spec. Close the window to skip or
- end the test.
- In a tested module, let X be a global name bound to a callable (class
- or function) whose .__name__ attribute is also X (the usual situation).
- The first parameter of X must be 'parent'. When called, the parent
- argument will be the root window. X must create a child Toplevel
- window (or subclass thereof). The Toplevel may be a test widget or
- dialog, in which case the callable is the corresponding class. Or the
- Toplevel may contain the widget to be tested or set up a context in
- which a test widget is invoked. In this latter case, the callable is a
- wrapper function that sets up the Toplevel and other objects. Wrapper
- function names, such as _editor_window', should start with '_'.
- End the module with
- if __name__ == '__main__':
- <unittest, if there is one>
- from idlelib.idle_test.htest import run
- run(X)
- To have wrapper functions and test invocation code ignored by coveragepy
- reports, put '# htest #' on the def statement header line.
- def _wrapper(parent): # htest #
- Also make sure that the 'if __name__' line matches the above. Then have
- make sure that .coveragerc includes the following.
- [report]
- exclude_lines =
- .*# htest #
- if __name__ == .__main__.:
- (The "." instead of "'" is intentional and necessary.)
- To run any X, this file must contain a matching instance of the
- following template, with X.__name__ prepended to '_spec'.
- When all tests are run, the prefix is use to get X.
- _spec = {
- 'file': '',
- 'kwds': {'title': ''},
- 'msg': ""
- }
- file (no .py): run() imports file.py.
- kwds: augmented with {'parent':root} and passed to X as **kwds.
- title: an example kwd; some widgets need this, delete if not.
- msg: master window hints about testing the widget.
- Modules and classes not being tested at the moment:
- pyshell.PyShellEditorWindow
- debugger.Debugger
- autocomplete_w.AutoCompleteWindow
- outwin.OutputWindow (indirectly being tested with grep test)
- '''
- import idlelib.pyshell # Set Windows DPI awareness before Tk().
- from importlib import import_module
- import textwrap
- import tkinter as tk
- from tkinter.ttk import Scrollbar
- tk.NoDefaultRoot()
- AboutDialog_spec = {
- 'file': 'help_about',
- 'kwds': {'title': 'help_about test',
- '_htest': True,
- },
- 'msg': "Click on URL to open in default browser.\n"
- "Verify x.y.z versions and test each button, including Close.\n "
- }
- # TODO implement ^\; adding '<Control-Key-\\>' to function does not work.
- _calltip_window_spec = {
- 'file': 'calltip_w',
- 'kwds': {},
- 'msg': "Typing '(' should display a calltip.\n"
- "Typing ') should hide the calltip.\n"
- "So should moving cursor out of argument area.\n"
- "Force-open-calltip does not work here.\n"
- }
- _module_browser_spec = {
- 'file': 'browser',
- 'kwds': {},
- 'msg': "Inspect names of module, class(with superclass if "
- "applicable), methods and functions.\nToggle nested items.\n"
- "Double clicking on items prints a traceback for an exception "
- "that is ignored."
- }
- _color_delegator_spec = {
- 'file': 'colorizer',
- 'kwds': {},
- 'msg': "The text is sample Python code.\n"
- "Ensure components like comments, keywords, builtins,\n"
- "string, definitions, and break are correctly colored.\n"
- "The default color scheme is in idlelib/config-highlight.def"
- }
- CustomRun_spec = {
- 'file': 'query',
- 'kwds': {'title': 'Customize query.py Run',
- '_htest': True},
- 'msg': "Enter with <Return> or [Run]. Print valid entry to Shell\n"
- "Arguments are parsed into a list\n"
- "Mode is currently restart True or False\n"
- "Close dialog with valid entry, <Escape>, [Cancel], [X]"
- }
- ConfigDialog_spec = {
- 'file': 'configdialog',
- 'kwds': {'title': 'ConfigDialogTest',
- '_htest': True,},
- 'msg': "IDLE preferences dialog.\n"
- "In the 'Fonts/Tabs' tab, changing font face, should update the "
- "font face of the text in the area below it.\nIn the "
- "'Highlighting' tab, try different color schemes. Clicking "
- "items in the sample program should update the choices above it."
- "\nIn the 'Keys', 'General' and 'Extensions' tabs, test settings "
- "of interest."
- "\n[Ok] to close the dialog.[Apply] to apply the settings and "
- "and [Cancel] to revert all changes.\nRe-run the test to ensure "
- "changes made have persisted."
- }
- # TODO Improve message
- _dyn_option_menu_spec = {
- 'file': 'dynoption',
- 'kwds': {},
- 'msg': "Select one of the many options in the 'old option set'.\n"
- "Click the button to change the option set.\n"
- "Select one of the many options in the 'new option set'."
- }
- # TODO edit wrapper
- _editor_window_spec = {
- 'file': 'editor',
- 'kwds': {},
- 'msg': "Test editor functions of interest.\n"
- "Best to close editor first."
- }
- GetKeysDialog_spec = {
- 'file': 'config_key',
- 'kwds': {'title': 'Test keybindings',
- 'action': 'find-again',
- 'current_key_sequences': [['<Control-Key-g>', '<Key-F3>', '<Control-Key-G>']],
- '_htest': True,
- },
- 'msg': "Test for different key modifier sequences.\n"
- "<nothing> is invalid.\n"
- "No modifier key is invalid.\n"
- "Shift key with [a-z],[0-9], function key, move key, tab, space "
- "is invalid.\nNo validity checking if advanced key binding "
- "entry is used."
- }
- _grep_dialog_spec = {
- 'file': 'grep',
- 'kwds': {},
- 'msg': "Click the 'Show GrepDialog' button.\n"
- "Test the various 'Find-in-files' functions.\n"
- "The results should be displayed in a new '*Output*' window.\n"
- "'Right-click'->'Go to file/line' anywhere in the search results "
- "should open that file \nin a new EditorWindow."
- }
- HelpSource_spec = {
- 'file': 'query',
- 'kwds': {'title': 'Help name and source',
- 'menuitem': 'test',
- 'filepath': __file__,
- 'used_names': {'abc'},
- '_htest': True},
- 'msg': "Enter menu item name and help file path\n"
- "'', > than 30 chars, and 'abc' are invalid menu item names.\n"
- "'' and file does not exist are invalid path items.\n"
- "Any url ('www...', 'http...') is accepted.\n"
- "Test Browse with and without path, as cannot unittest.\n"
- "[Ok] or <Return> prints valid entry to shell\n"
- "[Cancel] or <Escape> prints None to shell"
- }
- _io_binding_spec = {
- 'file': 'iomenu',
- 'kwds': {},
- 'msg': "Test the following bindings.\n"
- "<Control-o> to open file from dialog.\n"
- "Edit the file.\n"
- "<Control-p> to print the file.\n"
- "<Control-s> to save the file.\n"
- "<Alt-s> to save-as another file.\n"
- "<Control-c> to save-copy-as another file.\n"
- "Check that changes were saved by opening the file elsewhere."
- }
- _linenumbers_drag_scrolling_spec = {
- 'file': 'sidebar',
- 'kwds': {},
- 'msg': textwrap.dedent("""\
- 1. Click on the line numbers and drag down below the edge of the
- window, moving the mouse a bit and then leaving it there for a while.
- The text and line numbers should gradually scroll down, with the
- selection updated continuously.
- 2. With the lines still selected, click on a line number above the
- selected lines. Only the line whose number was clicked should be
- selected.
- 3. Repeat step #1, dragging to above the window. The text and line
- numbers should gradually scroll up, with the selection updated
- continuously.
- 4. Repeat step #2, clicking a line number below the selection."""),
- }
- _multi_call_spec = {
- 'file': 'multicall',
- 'kwds': {},
- 'msg': "The following actions should trigger a print to console or IDLE"
- " Shell.\nEntering and leaving the text area, key entry, "
- "<Control-Key>,\n<Alt-Key-a>, <Control-Key-a>, "
- "<Alt-Control-Key-a>, \n<Control-Button-1>, <Alt-Button-1> and "
- "focusing out of the window\nare sequences to be tested."
- }
- _multistatus_bar_spec = {
- 'file': 'statusbar',
- 'kwds': {},
- 'msg': "Ensure presence of multi-status bar below text area.\n"
- "Click 'Update Status' to change the multi-status text"
- }
- _object_browser_spec = {
- 'file': 'debugobj',
- 'kwds': {},
- 'msg': "Double click on items up to the lowest level.\n"
- "Attributes of the objects and related information "
- "will be displayed side-by-side at each level."
- }
- _path_browser_spec = {
- 'file': 'pathbrowser',
- 'kwds': {},
- 'msg': "Test for correct display of all paths in sys.path.\n"
- "Toggle nested items up to the lowest level.\n"
- "Double clicking on an item prints a traceback\n"
- "for an exception that is ignored."
- }
- _percolator_spec = {
- 'file': 'percolator',
- 'kwds': {},
- 'msg': "There are two tracers which can be toggled using a checkbox.\n"
- "Toggling a tracer 'on' by checking it should print tracer "
- "output to the console or to the IDLE shell.\n"
- "If both the tracers are 'on', the output from the tracer which "
- "was switched 'on' later, should be printed first\n"
- "Test for actions like text entry, and removal."
- }
- Query_spec = {
- 'file': 'query',
- 'kwds': {'title': 'Query',
- 'message': 'Enter something',
- 'text0': 'Go',
- '_htest': True},
- 'msg': "Enter with <Return> or [Ok]. Print valid entry to Shell\n"
- "Blank line, after stripping, is ignored\n"
- "Close dialog with valid entry, <Escape>, [Cancel], [X]"
- }
- _replace_dialog_spec = {
- 'file': 'replace',
- 'kwds': {},
- 'msg': "Click the 'Replace' button.\n"
- "Test various replace options in the 'Replace dialog'.\n"
- "Click [Close] or [X] to close the 'Replace Dialog'."
- }
- _search_dialog_spec = {
- 'file': 'search',
- 'kwds': {},
- 'msg': "Click the 'Search' button.\n"
- "Test various search options in the 'Search dialog'.\n"
- "Click [Close] or [X] to close the 'Search Dialog'."
- }
- _searchbase_spec = {
- 'file': 'searchbase',
- 'kwds': {},
- 'msg': "Check the appearance of the base search dialog\n"
- "Its only action is to close."
- }
- _scrolled_list_spec = {
- 'file': 'scrolledlist',
- 'kwds': {},
- 'msg': "You should see a scrollable list of items\n"
- "Selecting (clicking) or double clicking an item "
- "prints the name to the console or Idle shell.\n"
- "Right clicking an item will display a popup."
- }
- show_idlehelp_spec = {
- 'file': 'help',
- 'kwds': {},
- 'msg': "If the help text displays, this works.\n"
- "Text is selectable. Window is scrollable."
- }
- _stack_viewer_spec = {
- 'file': 'stackviewer',
- 'kwds': {},
- 'msg': "A stacktrace for a NameError exception.\n"
- "Expand 'idlelib ...' and '<locals>'.\n"
- "Check that exc_value, exc_tb, and exc_type are correct.\n"
- }
- _tooltip_spec = {
- 'file': 'tooltip',
- 'kwds': {},
- 'msg': "Place mouse cursor over both the buttons\n"
- "A tooltip should appear with some text."
- }
- _tree_widget_spec = {
- 'file': 'tree',
- 'kwds': {},
- 'msg': "The canvas is scrollable.\n"
- "Click on folders up to to the lowest level."
- }
- _undo_delegator_spec = {
- 'file': 'undo',
- 'kwds': {},
- 'msg': "Click [Undo] to undo any action.\n"
- "Click [Redo] to redo any action.\n"
- "Click [Dump] to dump the current state "
- "by printing to the console or the IDLE shell.\n"
- }
- ViewWindow_spec = {
- 'file': 'textview',
- 'kwds': {'title': 'Test textview',
- 'contents': 'The quick brown fox jumps over the lazy dog.\n'*35,
- '_htest': True},
- 'msg': "Test for read-only property of text.\n"
- "Select text, scroll window, close"
- }
- _widget_redirector_spec = {
- 'file': 'redirector',
- 'kwds': {},
- 'msg': "Every text insert should be printed to the console "
- "or the IDLE shell."
- }
- def run(*tests):
- root = tk.Tk()
- root.title('IDLE htest')
- root.resizable(0, 0)
- # a scrollable Label like constant width text widget.
- frameLabel = tk.Frame(root, padx=10)
- frameLabel.pack()
- text = tk.Text(frameLabel, wrap='word')
- text.configure(bg=root.cget('bg'), relief='flat', height=4, width=70)
- scrollbar = Scrollbar(frameLabel, command=text.yview)
- text.config(yscrollcommand=scrollbar.set)
- scrollbar.pack(side='right', fill='y', expand=False)
- text.pack(side='left', fill='both', expand=True)
- test_list = [] # List of tuples of the form (spec, callable widget)
- if tests:
- for test in tests:
- test_spec = globals()[test.__name__ + '_spec']
- test_spec['name'] = test.__name__
- test_list.append((test_spec, test))
- else:
- for k, d in globals().items():
- if k.endswith('_spec'):
- test_name = k[:-5]
- test_spec = d
- test_spec['name'] = test_name
- mod = import_module('idlelib.' + test_spec['file'])
- test = getattr(mod, test_name)
- test_list.append((test_spec, test))
- test_name = tk.StringVar(root)
- callable_object = None
- test_kwds = None
- def next_test():
- nonlocal test_name, callable_object, test_kwds
- if len(test_list) == 1:
- next_button.pack_forget()
- test_spec, callable_object = test_list.pop()
- test_kwds = test_spec['kwds']
- test_kwds['parent'] = root
- test_name.set('Test ' + test_spec['name'])
- text.configure(state='normal') # enable text editing
- text.delete('1.0','end')
- text.insert("1.0",test_spec['msg'])
- text.configure(state='disabled') # preserve read-only property
- def run_test(_=None):
- widget = callable_object(**test_kwds)
- try:
- print(widget.result)
- except AttributeError:
- pass
- def close(_=None):
- root.destroy()
- button = tk.Button(root, textvariable=test_name,
- default='active', command=run_test)
- next_button = tk.Button(root, text="Next", command=next_test)
- button.pack()
- next_button.pack()
- next_button.focus_set()
- root.bind('<Key-Return>', run_test)
- root.bind('<Key-Escape>', close)
- next_test()
- root.mainloop()
- if __name__ == '__main__':
- run()
|