Home | History | Annotate | Download | only in idle_test
      1 '''Run human tests of Idle's window, dialog, and popup widgets.
      2 
      3 run(*tests)
      4 Create a master Tk window.  Within that, run each callable in tests
      5 after finding the matching test spec in this file.  If tests is empty,
      6 run an htest for each spec dict in this file after finding the matching
      7 callable in the module named in the spec.  Close the window to skip or
      8 end the test.
      9 
     10 In a tested module, let X be a global name bound to a callable (class
     11 or function) whose .__name__ attrubute is also X (the usual situation).
     12 The first parameter of X must be 'parent'.  When called, the parent
     13 argument will be the root window.  X must create a child Toplevel
     14 window (or subclass thereof).  The Toplevel may be a test widget or
     15 dialog, in which case the callable is the corresonding class.  Or the
     16 Toplevel may contain the widget to be tested or set up a context in
     17 which a test widget is invoked.  In this latter case, the callable is a
     18 wrapper function that sets up the Toplevel and other objects.  Wrapper
     19 function names, such as _editor_window', should start with '_'.
     20 
     21 
     22 End the module with
     23 
     24 if __name__ == '__main__':
     25     <unittest, if there is one>
     26     from idlelib.idle_test.htest import run
     27     run(X)
     28 
     29 To have wrapper functions and test invocation code ignored by coveragepy
     30 reports, put '# htest #' on the def statement header line.
     31 
     32 def _wrapper(parent):  # htest #
     33 
     34 Also make sure that the 'if __name__' line matches the above.  Then have
     35 make sure that .coveragerc includes the following.
     36 
     37 [report]
     38 exclude_lines =
     39     .*# htest #
     40     if __name__ == .__main__.:
     41 
     42 (The "." instead of "'" is intentional and necessary.)
     43 
     44 
     45 To run any X, this file must contain a matching instance of the
     46 following template, with X.__name__ prepended to '_spec'.
     47 When all tests are run, the prefix is use to get X.
     48 
     49 _spec = {
     50     'file': '',
     51     'kwds': {'title': ''},
     52     'msg': ""
     53     }
     54 
     55 file (no .py): run() imports file.py.
     56 kwds: augmented with {'parent':root} and passed to X as **kwds.
     57 title: an example kwd; some widgets need this, delete if not.
     58 msg: master window hints about testing the widget.
     59 
     60 
     61 Modules and classes not being tested at the moment:
     62 PyShell.PyShellEditorWindow
     63 Debugger.Debugger
     64 AutoCompleteWindow.AutoCompleteWindow
     65 OutputWindow.OutputWindow (indirectly being tested with grep test)
     66 '''
     67 
     68 from importlib import import_module
     69 from idlelib.macosxSupport import _initializeTkVariantTests
     70 import Tkinter as tk
     71 
     72 AboutDialog_spec = {
     73     'file': 'aboutDialog',
     74     'kwds': {'title': 'aboutDialog test',
     75              '_htest': True,
     76              },
     77     'msg': "Test every button. Ensure Python, TK and IDLE versions "
     78            "are correctly displayed.\n [Close] to exit.",
     79     }
     80 
     81 _calltip_window_spec = {
     82     'file': 'CallTipWindow',
     83     'kwds': {},
     84     'msg': "Typing '(' should display a calltip.\n"
     85            "Typing ') should hide the calltip.\n"
     86     }
     87 
     88 _class_browser_spec = {
     89     'file': 'ClassBrowser',
     90     'kwds': {},
     91     'msg': "Inspect names of module, class(with superclass if "
     92            "applicable), methods and functions.\nToggle nested items.\n"
     93            "Double clicking on items prints a traceback for an exception "
     94            "that is ignored."
     95     }
     96 
     97 _color_delegator_spec = {
     98     'file': 'ColorDelegator',
     99     'kwds': {},
    100     'msg': "The text is sample Python code.\n"
    101            "Ensure components like comments, keywords, builtins,\n"
    102            "string, definitions, and break are correctly colored.\n"
    103            "The default color scheme is in idlelib/config-highlight.def"
    104     }
    105 
    106 ConfigDialog_spec = {
    107     'file': 'configDialog',
    108     'kwds': {'title': 'ConfigDialogTest',
    109              '_htest': True,},
    110     'msg': "IDLE preferences dialog.\n"
    111            "In the 'Fonts/Tabs' tab, changing font face, should update the "
    112            "font face of the text in the area below it.\nIn the "
    113            "'Highlighting' tab, try different color schemes. Clicking "
    114            "items in the sample program should update the choices above it."
    115            "\nIn the 'Keys', 'General' and 'Extensions' tabs, test settings"
    116            "of interest."
    117            "\n[Ok] to close the dialog.[Apply] to apply the settings and "
    118            "and [Cancel] to revert all changes.\nRe-run the test to ensure "
    119            "changes made have persisted."
    120     }
    121 
    122 # TODO Improve message
    123 _dyn_option_menu_spec = {
    124     'file': 'dynOptionMenuWidget',
    125     'kwds': {},
    126     'msg': "Select one of the many options in the 'old option set'.\n"
    127            "Click the button to change the option set.\n"
    128            "Select one of the many options in the 'new option set'."
    129     }
    130 
    131 # TODO edit wrapper
    132 _editor_window_spec = {
    133    'file': 'EditorWindow',
    134     'kwds': {},
    135     'msg': "Test editor functions of interest.\n"
    136            "Best to close editor first."
    137     }
    138 
    139 GetCfgSectionNameDialog_spec = {
    140     'file': 'configSectionNameDialog',
    141     'kwds': {'title':'Get Name',
    142              'message':'Enter something',
    143              'used_names': {'abc'},
    144              '_htest': True},
    145     'msg': "After the text entered with [Ok] is stripped, <nothing>, "
    146            "'abc', or more that 30 chars are errors.\n"
    147            "Close 'Get Name' with a valid entry (printed to Shell), "
    148            "[Cancel], or [X]",
    149     }
    150 
    151 GetHelpSourceDialog_spec = {
    152     'file': 'configHelpSourceEdit',
    153     'kwds': {'title': 'Get helpsource',
    154              '_htest': True},
    155     'msg': "Enter menu item name and help file path\n "
    156            "<nothing> and more than 30 chars are invalid menu item names.\n"
    157            "<nothing>, file does not exist are invalid path items.\n"
    158            "Test for incomplete web address for help file path.\n"
    159            "A valid entry will be printed to shell with [0k].\n"
    160            "[Cancel] will print None to shell",
    161     }
    162 
    163 # Update once issue21519 is resolved.
    164 GetKeysDialog_spec = {
    165     'file': 'keybindingDialog',
    166     'kwds': {'title': 'Test keybindings',
    167              'action': 'find-again',
    168              'currentKeySequences': [''] ,
    169              '_htest': True,
    170              },
    171     'msg': "Test for different key modifier sequences.\n"
    172            "<nothing> is invalid.\n"
    173            "No modifier key is invalid.\n"
    174            "Shift key with [a-z],[0-9], function key, move key, tab, space"
    175            "is invalid.\nNo validitity checking if advanced key binding "
    176            "entry is used."
    177     }
    178 
    179 _grep_dialog_spec = {
    180     'file': 'GrepDialog',
    181     'kwds': {},
    182     'msg': "Click the 'Show GrepDialog' button.\n"
    183            "Test the various 'Find-in-files' functions.\n"
    184            "The results should be displayed in a new '*Output*' window.\n"
    185            "'Right-click'->'Goto file/line' anywhere in the search results "
    186            "should open that file \nin a new EditorWindow."
    187     }
    188 
    189 _io_binding_spec = {
    190     'file': 'IOBinding',
    191     'kwds': {},
    192     'msg': "Test the following bindings.\n"
    193            "<Control-o> to open file from dialog.\n"
    194            "Edit the file.\n"
    195            "<Control-p> to print the file.\n"
    196            "<Control-s> to save the file.\n"
    197            "<Alt-s> to save-as another file.\n"
    198            "<Control-c> to save-copy-as another file.\n"
    199            "Check that changes were saved by opening the file elsewhere."
    200     }
    201 
    202 _multi_call_spec = {
    203     'file': 'MultiCall',
    204     'kwds': {},
    205     'msg': "The following actions should trigger a print to console or IDLE"
    206            " Shell.\nEntering and leaving the text area, key entry, "
    207            "<Control-Key>,\n<Alt-Key-a>, <Control-Key-a>, "
    208            "<Alt-Control-Key-a>, \n<Control-Button-1>, <Alt-Button-1> and "
    209            "focusing out of the window\nare sequences to be tested."
    210     }
    211 
    212 _multistatus_bar_spec = {
    213     'file': 'MultiStatusBar',
    214     'kwds': {},
    215     'msg': "Ensure presence of multi-status bar below text area.\n"
    216            "Click 'Update Status' to change the multi-status text"
    217     }
    218 
    219 _object_browser_spec = {
    220     'file': 'ObjectBrowser',
    221     'kwds': {},
    222     'msg': "Double click on items upto the lowest level.\n"
    223            "Attributes of the objects and related information "
    224            "will be displayed side-by-side at each level."
    225     }
    226 
    227 _path_browser_spec = {
    228     'file': 'PathBrowser',
    229     'kwds': {},
    230     'msg': "Test for correct display of all paths in sys.path.\n"
    231            "Toggle nested items upto the lowest level.\n"
    232            "Double clicking on an item prints a traceback\n"
    233            "for an exception that is ignored."
    234     }
    235 
    236 _percolator_spec = {
    237     'file': 'Percolator',
    238     'kwds': {},
    239     'msg': "There are two tracers which can be toggled using a checkbox.\n"
    240            "Toggling a tracer 'on' by checking it should print tracer"
    241            "output to the console or to the IDLE shell.\n"
    242            "If both the tracers are 'on', the output from the tracer which "
    243            "was switched 'on' later, should be printed first\n"
    244            "Test for actions like text entry, and removal."
    245     }
    246 
    247 _replace_dialog_spec = {
    248     'file': 'ReplaceDialog',
    249     'kwds': {},
    250     'msg': "Click the 'Replace' button.\n"
    251            "Test various replace options in the 'Replace dialog'.\n"
    252            "Click [Close] or [X] to close the 'Replace Dialog'."
    253     }
    254 
    255 _search_dialog_spec = {
    256     'file': 'SearchDialog',
    257     'kwds': {},
    258     'msg': "Click the 'Search' button.\n"
    259            "Test various search options in the 'Search dialog'.\n"
    260            "Click [Close] or [X] to close the 'Search Dialog'."
    261     }
    262 
    263 _scrolled_list_spec = {
    264     'file': 'ScrolledList',
    265     'kwds': {},
    266     'msg': "You should see a scrollable list of items\n"
    267            "Selecting (clicking) or double clicking an item "
    268            "prints the name to the console or Idle shell.\n"
    269            "Right clicking an item will display a popup."
    270     }
    271 
    272 show_idlehelp_spec = {
    273     'file': 'help',
    274     'kwds': {},
    275     'msg': "If the help text displays, this works.\n"
    276            "Text is selectable. Window is scrollable."
    277     }
    278 
    279 _stack_viewer_spec = {
    280     'file': 'StackViewer',
    281     'kwds': {},
    282     'msg': "A stacktrace for a NameError exception.\n"
    283            "Expand 'idlelib ...' and '<locals>'.\n"
    284            "Check that exc_value, exc_tb, and exc_type are correct.\n"
    285     }
    286 
    287 _tabbed_pages_spec = {
    288     'file': 'tabbedpages',
    289     'kwds': {},
    290     'msg': "Toggle between the two tabs 'foo' and 'bar'\n"
    291            "Add a tab by entering a suitable name for it.\n"
    292            "Remove an existing tab by entering its name.\n"
    293            "Remove all existing tabs.\n"
    294            "<nothing> is an invalid add page and remove page name.\n"
    295     }
    296 
    297 TextViewer_spec = {
    298     'file': 'textView',
    299     'kwds': {'title': 'Test textView',
    300              'text':'The quick brown fox jumps over the lazy dog.\n'*35,
    301              '_htest': True},
    302     'msg': "Test for read-only property of text.\n"
    303            "Text is selectable. Window is scrollable.",
    304      }
    305 
    306 _tooltip_spec = {
    307     'file': 'ToolTip',
    308     'kwds': {},
    309     'msg': "Place mouse cursor over both the buttons\n"
    310            "A tooltip should appear with some text."
    311     }
    312 
    313 _tree_widget_spec = {
    314     'file': 'TreeWidget',
    315     'kwds': {},
    316     'msg': "The canvas is scrollable.\n"
    317            "Click on folders upto to the lowest level."
    318     }
    319 
    320 _undo_delegator_spec = {
    321     'file': 'UndoDelegator',
    322     'kwds': {},
    323     'msg': "Click [Undo] to undo any action.\n"
    324            "Click [Redo] to redo any action.\n"
    325            "Click [Dump] to dump the current state "
    326            "by printing to the console or the IDLE shell.\n"
    327     }
    328 
    329 _widget_redirector_spec = {
    330     'file': 'WidgetRedirector',
    331     'kwds': {},
    332     'msg': "Every text insert should be printed to the console."
    333            "or the IDLE shell."
    334     }
    335 
    336 def run(*tests):
    337     root = tk.Tk()
    338     root.title('IDLE htest')
    339     root.resizable(0, 0)
    340     _initializeTkVariantTests(root)
    341 
    342     # a scrollable Label like constant width text widget.
    343     frameLabel = tk.Frame(root, padx=10)
    344     frameLabel.pack()
    345     text = tk.Text(frameLabel, wrap='word')
    346     text.configure(bg=root.cget('bg'), relief='flat', height=4, width=70)
    347     scrollbar = tk.Scrollbar(frameLabel, command=text.yview)
    348     text.config(yscrollcommand=scrollbar.set)
    349     scrollbar.pack(side='right', fill='y', expand=False)
    350     text.pack(side='left', fill='both', expand=True)
    351 
    352     test_list = [] # List of tuples of the form (spec, callable widget)
    353     if tests:
    354         for test in tests:
    355             test_spec = globals()[test.__name__ + '_spec']
    356             test_spec['name'] = test.__name__
    357             test_list.append((test_spec,  test))
    358     else:
    359         for k, d in globals().items():
    360             if k.endswith('_spec'):
    361                 test_name = k[:-5]
    362                 test_spec = d
    363                 test_spec['name'] = test_name
    364                 mod = import_module('idlelib.' + test_spec['file'])
    365                 test = getattr(mod, test_name)
    366                 test_list.append((test_spec, test))
    367 
    368     test_name = [tk.StringVar('')]
    369     callable_object = [None]
    370     test_kwds = [None]
    371 
    372 
    373     def next():
    374         if len(test_list) == 1:
    375             next_button.pack_forget()
    376         test_spec, callable_object[0] = test_list.pop()
    377         test_kwds[0] = test_spec['kwds']
    378         test_kwds[0]['parent'] = root
    379         test_name[0].set('Test ' + test_spec['name'])
    380 
    381         text.configure(state='normal') # enable text editing
    382         text.delete('1.0','end')
    383         text.insert("1.0",test_spec['msg'])
    384         text.configure(state='disabled') # preserve read-only property
    385 
    386     def run_test():
    387         widget = callable_object[0](**test_kwds[0])
    388         try:
    389             print(widget.result)
    390         except AttributeError:
    391             pass
    392 
    393     button = tk.Button(root, textvariable=test_name[0], command=run_test)
    394     button.pack()
    395     next_button = tk.Button(root, text="Next", command=next)
    396     next_button.pack()
    397 
    398     next()
    399 
    400     root.mainloop()
    401 
    402 if __name__ == '__main__':
    403     run()
    404