mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-10-27 13:21:58 -06:00
Removed the Requirement to Install Python and NodeJS (Now Bundled with Borealis)
This commit is contained in:
241
Dependencies/Python/Lib/idlelib/idle_test/README.txt
vendored
Normal file
241
Dependencies/Python/Lib/idlelib/idle_test/README.txt
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
README FOR IDLE TESTS IN IDLELIB.IDLE_TEST
|
||||
|
||||
0. Quick Start
|
||||
|
||||
Automated unit tests were added in 3.3 for Python 3.x.
|
||||
To run the tests from a command line:
|
||||
|
||||
python -m test.test_idle
|
||||
|
||||
Human-mediated tests were added later in 3.4.
|
||||
|
||||
python -m idlelib.idle_test.htest
|
||||
|
||||
|
||||
1. Test Files
|
||||
|
||||
The idle directory, idlelib, has over 60 xyz.py files. The idle_test
|
||||
subdirectory contains test_xyz.py for each implementation file xyz.py.
|
||||
To add a test for abc.py, open idle_test/template.py and immediately
|
||||
Save As test_abc.py. Insert 'abc' on the first line, and replace
|
||||
'zzdummy' with 'abc.
|
||||
|
||||
Remove the imports of requires and tkinter if not needed. Otherwise,
|
||||
add to the tkinter imports as needed.
|
||||
|
||||
Add a prefix to 'Test' for the initial test class. The template class
|
||||
contains code needed or possibly needed for gui tests. See the next
|
||||
section if doing gui tests. If not, and not needed for further classes,
|
||||
this code can be removed.
|
||||
|
||||
Add the following at the end of abc.py. If an htest was added first,
|
||||
insert the import and main lines before the htest lines.
|
||||
|
||||
if __name__ == "__main__":
|
||||
from unittest import main
|
||||
main('idlelib.idle_test.test_abc', verbosity=2, exit=False)
|
||||
|
||||
The ', exit=False' is only needed if an htest follows.
|
||||
|
||||
|
||||
|
||||
2. GUI Tests
|
||||
|
||||
When run as part of the Python test suite, Idle GUI tests need to run
|
||||
test.support.requires('gui'). A test is a GUI test if it creates a
|
||||
tkinter.Tk root or master object either directly or indirectly by
|
||||
instantiating a tkinter or idle class. GUI tests cannot run in test
|
||||
processes that either have no graphical environment available or are not
|
||||
allowed to use it.
|
||||
|
||||
To guard a module consisting entirely of GUI tests, start with
|
||||
|
||||
from test.support import requires
|
||||
requires('gui')
|
||||
|
||||
To guard a test class, put "requires('gui')" in its setUpClass function.
|
||||
The template.py file does this.
|
||||
|
||||
To avoid interfering with other GUI tests, all GUI objects must be
|
||||
destroyed and deleted by the end of the test. The Tk root created in a
|
||||
setUpX function should be destroyed in the corresponding tearDownX and
|
||||
the module or class attribute deleted. Others widgets should descend
|
||||
from the single root and the attributes deleted BEFORE root is
|
||||
destroyed. See https://bugs.python.org/issue20567.
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = tk.Tk()
|
||||
cls.text = tk.Text(root)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
The update_idletasks call is sometimes needed to prevent the following
|
||||
warning either when running a test alone or as part of the test suite
|
||||
(#27196). It should not hurt if not needed.
|
||||
|
||||
can't invoke "event" command: application has been destroyed
|
||||
...
|
||||
"ttk::ThemeChanged"
|
||||
|
||||
If a test creates instance 'e' of EditorWindow, call 'e._close()' before
|
||||
or as the first part of teardown. The effect of omitting this depends
|
||||
on the later shutdown. Then enable the after_cancel loop in the
|
||||
template. This prevents messages like the following.
|
||||
|
||||
bgerror failed to handle background error.
|
||||
Original error: invalid command name "106096696timer_event"
|
||||
Error in bgerror: can't invoke "tk" command: application has been destroyed
|
||||
|
||||
Requires('gui') causes the test(s) it guards to be skipped if any of
|
||||
these conditions are met:
|
||||
|
||||
- The tests are being run by regrtest.py, and it was started without
|
||||
enabling the "gui" resource with the "-u" command line option.
|
||||
|
||||
- The tests are being run on Windows by a service that is not allowed
|
||||
to interact with the graphical environment.
|
||||
|
||||
- The tests are being run on Linux and X Windows is not available.
|
||||
|
||||
- The tests are being run on Mac OSX in a process that cannot make a
|
||||
window manager connection.
|
||||
|
||||
- tkinter.Tk cannot be successfully instantiated for some reason.
|
||||
|
||||
- test.support.use_resources has been set by something other than
|
||||
regrtest.py and does not contain "gui".
|
||||
|
||||
Tests of non-GUI operations should avoid creating tk widgets. Incidental
|
||||
uses of tk variables and messageboxes can be replaced by the mock
|
||||
classes in idle_test/mock_tk.py. The mock text handles some uses of the
|
||||
tk Text widget.
|
||||
|
||||
|
||||
3. Running Unit Tests
|
||||
|
||||
Assume that xyz.py and test_xyz.py both end with a unittest.main() call.
|
||||
Running either from an Idle editor runs all tests in the test_xyz file
|
||||
with the version of Python running Idle. Test output appears in the
|
||||
Shell window. The 'verbosity=2' option lists all test methods in the
|
||||
file, which is appropriate when developing tests. The 'exit=False'
|
||||
option is needed in xyx.py files when an htest follows.
|
||||
|
||||
The following command lines also run all test methods, including
|
||||
GUI tests, in test_xyz.py. (Both '-m idlelib' and '-m idlelib.idle'
|
||||
start Idle and so cannot run tests.)
|
||||
|
||||
python -m idlelib.xyz
|
||||
python -m idlelib.idle_test.test_xyz
|
||||
|
||||
The following runs all idle_test/test_*.py tests interactively.
|
||||
|
||||
>>> import unittest
|
||||
>>> unittest.main('idlelib.idle_test', verbosity=2)
|
||||
|
||||
The following run all Idle tests at a command line. Option '-v' is the
|
||||
same as 'verbosity=2'.
|
||||
|
||||
python -m unittest -v idlelib.idle_test
|
||||
python -m test -v -ugui test_idle
|
||||
python -m test.test_idle
|
||||
|
||||
IDLE tests are 'discovered' by idlelib.idle_test.__init__.load_tests
|
||||
when this is imported into test.test_idle. Normally, neither file
|
||||
should be changed when working on individual test modules. The third
|
||||
command runs unittest indirectly through regrtest. The same happens when
|
||||
the entire test suite is run with 'python -m test'. So that command must
|
||||
work for buildbots to stay green. IDLE tests must not disturb the
|
||||
environment in a way that makes other tests fail (GH-62281).
|
||||
|
||||
To test subsets of modules, see idlelib.idle_test.__init__. This
|
||||
can be used to find refleaks or possible sources of "Theme changed"
|
||||
tcl messages (GH-71383).
|
||||
|
||||
To run an individual Testcase or test method, extend the dotted name
|
||||
given to unittest on the command line or use the test -m option. The
|
||||
latter allows use of other regrtest options. When using the latter,
|
||||
all components of the pattern must be present, but any can be replaced
|
||||
by '*'.
|
||||
|
||||
python -m unittest -v idlelib.idle_test.test_xyz.Test_case.test_meth
|
||||
python -m test -m idlelib.idle_test.text_xyz.Test_case.test_meth test_idle
|
||||
|
||||
The test suite can be run in an IDLE user process from Shell.
|
||||
>>> import test.autotest # Issue 25588, 2017/10/13, 3.6.4, 3.7.0a2.
|
||||
There are currently failures not usually present, and this does not
|
||||
work when run from the editor.
|
||||
|
||||
|
||||
4. Human-mediated Tests
|
||||
|
||||
Human-mediated tests are widget tests that cannot be automated but need
|
||||
human verification. They are contained in idlelib/idle_test/htest.py,
|
||||
which has instructions. (Some modules need an auxiliary function,
|
||||
identified with "# htest # on the header line.) The set is about
|
||||
complete, though some tests need improvement. To run all htests, run the
|
||||
htest file from an editor or from the command line with:
|
||||
|
||||
python -m idlelib.idle_test.htest
|
||||
|
||||
|
||||
5. Test Coverage
|
||||
|
||||
Install the coverage package into your Python 3.6 site-packages
|
||||
directory. (Its exact location depends on the OS).
|
||||
> python3 -m pip install coverage
|
||||
(On Windows, replace 'python3 with 'py -3.6' or perhaps just 'python'.)
|
||||
|
||||
The problem with running coverage with repository python is that
|
||||
coverage uses absolute imports for its submodules, hence it needs to be
|
||||
in a directory in sys.path. One solution: copy the package to the
|
||||
directory containing the cpython repository. Call it 'dev'. Then run
|
||||
coverage either directly or from a script in that directory so that
|
||||
'dev' is prepended to sys.path.
|
||||
|
||||
Either edit or add dev/.coveragerc so it looks something like this.
|
||||
---
|
||||
# .coveragerc sets coverage options.
|
||||
[run]
|
||||
branch = True
|
||||
|
||||
[report]
|
||||
# Regexes for lines to exclude from consideration
|
||||
exclude_lines =
|
||||
# Don't complain if non-runnable code isn't run:
|
||||
if 0:
|
||||
if __name__ == .__main__.:
|
||||
|
||||
.*# htest #
|
||||
if not _utest:
|
||||
if _htest:
|
||||
---
|
||||
The additions for IDLE are 'branch = True', to test coverage both ways,
|
||||
and the last three exclude lines, to exclude things peculiar to IDLE
|
||||
that are not executed during tests.
|
||||
|
||||
A script like the following cover.bat (for Windows) is very handy.
|
||||
---
|
||||
@echo off
|
||||
rem Usage: cover filename [test_ suffix] # proper case required by coverage
|
||||
rem filename without .py, 2nd parameter if test is not test_filename
|
||||
setlocal
|
||||
set py=f:\dev\3x\pcbuild\win32\python_d.exe
|
||||
set src=idlelib.%1
|
||||
if "%2" EQU "" set tst=f:/dev/3x/Lib/idlelib/idle_test/test_%1.py
|
||||
if "%2" NEQ "" set tst=f:/dev/ex/Lib/idlelib/idle_test/test_%2.py
|
||||
|
||||
%py% -m coverage run --pylib --source=%src% %tst%
|
||||
%py% -m coverage report --show-missing
|
||||
%py% -m coverage html
|
||||
start htmlcov\3x_Lib_idlelib_%1_py.html
|
||||
rem Above opens new report; htmlcov\index.html displays report index
|
||||
---
|
||||
The second parameter was added for tests of module x not named test_x.
|
||||
(There were several before modules were renamed, now only one is left.)
|
||||
27
Dependencies/Python/Lib/idlelib/idle_test/__init__.py
vendored
Normal file
27
Dependencies/Python/Lib/idlelib/idle_test/__init__.py
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
"""idlelib.idle_test implements test.test_idle, which tests the IDLE
|
||||
application as part of the stdlib test suite.
|
||||
Run IDLE tests alone with "python -m test.test_idle (-v)".
|
||||
|
||||
This package and its contained modules are subject to change and
|
||||
any direct use is at your own risk.
|
||||
"""
|
||||
from os.path import dirname
|
||||
|
||||
# test_idle imports load_tests for test discovery (default all).
|
||||
# To run subsets of idlelib module tests, insert '[<chars>]' after '_'.
|
||||
# Example: insert '[ac]' for modules beginning with 'a' or 'c'.
|
||||
# Additional .discover/.addTest pairs with separate inserts work.
|
||||
# Example: pairs with 'c' and 'g' test c* files and grep.
|
||||
|
||||
def load_tests(loader, standard_tests, pattern):
|
||||
this_dir = dirname(__file__)
|
||||
top_dir = dirname(dirname(this_dir))
|
||||
module_tests = loader.discover(start_dir=this_dir,
|
||||
pattern='test_*.py', # Insert here.
|
||||
top_level_dir=top_dir)
|
||||
standard_tests.addTests(module_tests)
|
||||
## module_tests = loader.discover(start_dir=this_dir,
|
||||
## pattern='test_*.py', # Insert here.
|
||||
## top_level_dir=top_dir)
|
||||
## standard_tests.addTests(module_tests)
|
||||
return standard_tests
|
||||
4
Dependencies/Python/Lib/idlelib/idle_test/example_noext
vendored
Normal file
4
Dependencies/Python/Lib/idlelib/idle_test/example_noext
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
#!usr/bin/env python
|
||||
|
||||
def example_function(some_argument):
|
||||
pass
|
||||
4
Dependencies/Python/Lib/idlelib/idle_test/example_stub.pyi
vendored
Normal file
4
Dependencies/Python/Lib/idlelib/idle_test/example_stub.pyi
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# An example file to test recognition of a .pyi file as Python source code.
|
||||
|
||||
class Example:
|
||||
def method(self, argument1: str, argument2: list[int]) -> None: ...
|
||||
442
Dependencies/Python/Lib/idlelib/idle_test/htest.py
vendored
Normal file
442
Dependencies/Python/Lib/idlelib/idle_test/htest.py
vendored
Normal file
@@ -0,0 +1,442 @@
|
||||
"""Run human tests of Idle's window, dialog, and popup widgets.
|
||||
|
||||
run(*tests) Create a master Tk() htest 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 master window to end testing.
|
||||
|
||||
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' or 'master'. When called, the
|
||||
first argument will be the root window. X must create a child
|
||||
Toplevel(parent/master) (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 '_' and be lowercase.
|
||||
|
||||
|
||||
End the module with
|
||||
|
||||
if __name__ == '__main__':
|
||||
<run unittest.main with 'exit=False'>
|
||||
from idlelib.idle_test.htest import run
|
||||
run(callable) # There could be multiple comma-separated callables.
|
||||
|
||||
To have wrapper functions ignored by coverage reports, tag the def
|
||||
header like so: "def _wrapper(parent): # htest #". Use the same tag
|
||||
for htest lines in widget code. 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.
|
||||
|
||||
callable_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 line if not.
|
||||
msg: master window hints about testing the widget.
|
||||
|
||||
|
||||
TODO test these modules and classes:
|
||||
autocomplete_w.AutoCompleteWindow
|
||||
debugger.Debugger
|
||||
outwin.OutputWindow (indirectly being tested with grep test)
|
||||
pyshell.PyShellEditorWindow
|
||||
"""
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
_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"
|
||||
}
|
||||
|
||||
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."
|
||||
}
|
||||
|
||||
CustomRun_spec = {
|
||||
'file': 'query',
|
||||
'kwds': {'title': 'Customize query.py Run',
|
||||
'_htest': True},
|
||||
'msg': "Enter with <Return> or [OK]. 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]"
|
||||
}
|
||||
|
||||
_debug_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."
|
||||
}
|
||||
|
||||
# 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."
|
||||
}
|
||||
|
||||
GetKeysWindow_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' in the search results\n "
|
||||
"should open that file in 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"
|
||||
"<Escape>, [Cancel], or [X] 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."
|
||||
}
|
||||
|
||||
_multi_call_spec = {
|
||||
'file': 'multicall',
|
||||
'kwds': {},
|
||||
'msg': "The following should trigger a print to console or IDLE Shell.\n"
|
||||
"Entering 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 elsewhere."
|
||||
}
|
||||
|
||||
_module_browser_spec = {
|
||||
'file': 'browser',
|
||||
'kwds': {},
|
||||
'msg': textwrap.dedent("""
|
||||
"Inspect names of module, class(with superclass if applicable),
|
||||
"methods and functions. Toggle nested items. Double clicking
|
||||
"on items prints a traceback for an exception that is ignored.""")
|
||||
}
|
||||
|
||||
_multistatus_bar_spec = {
|
||||
'file': 'statusbar',
|
||||
'kwds': {},
|
||||
'msg': "Ensure presence of multi-status bar below text area.\n"
|
||||
"Click 'Update Status' to change the status text"
|
||||
}
|
||||
|
||||
PathBrowser_spec = {
|
||||
'file': 'pathbrowser',
|
||||
'kwds': {'_htest': True},
|
||||
'msg': "Test for correct display of all paths in sys.path.\n"
|
||||
"Toggle nested items out 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'."
|
||||
}
|
||||
|
||||
_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."
|
||||
}
|
||||
|
||||
_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."
|
||||
}
|
||||
|
||||
show_idlehelp_spec = {
|
||||
'file': 'help',
|
||||
'kwds': {},
|
||||
'msg': "If the help text displays, this works.\n"
|
||||
"Text is selectable. Window is scrollable."
|
||||
}
|
||||
|
||||
_sidebar_number_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
|
||||
or below 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."""),
|
||||
}
|
||||
|
||||
_stackbrowser_spec = {
|
||||
'file': 'stackviewer',
|
||||
'kwds': {},
|
||||
'msg': "A stacktrace for a NameError exception.\n"
|
||||
"Should have NameError and 1 traceback line."
|
||||
}
|
||||
|
||||
_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):
|
||||
"Run callables in 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 = [] # Make list of (spec, callable) tuples.
|
||||
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 key, dic in globals().items():
|
||||
if key.endswith('_spec'):
|
||||
test_name = key[:-5]
|
||||
test_spec = dic
|
||||
test_spec['name'] = test_name
|
||||
mod = import_module('idlelib.' + test_spec['file'])
|
||||
test = getattr(mod, test_name)
|
||||
test_list.append((test_spec, test))
|
||||
test_list.reverse() # So can pop in proper order in next_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_name.set('Test ' + test_spec['name'])
|
||||
|
||||
text['state'] = 'normal' # Enable text replacement.
|
||||
text.delete('1.0', 'end')
|
||||
text.insert("1.0", test_spec['msg'])
|
||||
text['state'] = 'disabled' # Restore read-only property.
|
||||
|
||||
def run_test(_=None):
|
||||
widget = callable_object(root, **test_kwds)
|
||||
try:
|
||||
print(widget.result) # Only true for query classes(?).
|
||||
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()
|
||||
61
Dependencies/Python/Lib/idlelib/idle_test/mock_idle.py
vendored
Normal file
61
Dependencies/Python/Lib/idlelib/idle_test/mock_idle.py
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
'''Mock classes that imitate idlelib modules or classes.
|
||||
|
||||
Attributes and methods will be added as needed for tests.
|
||||
'''
|
||||
|
||||
from idlelib.idle_test.mock_tk import Text
|
||||
|
||||
class Func:
|
||||
'''Record call, capture args, return/raise result set by test.
|
||||
|
||||
When mock function is called, set or use attributes:
|
||||
self.called - increment call number even if no args, kwds passed.
|
||||
self.args - capture positional arguments.
|
||||
self.kwds - capture keyword arguments.
|
||||
self.result - return or raise value set in __init__.
|
||||
self.return_self - return self instead, to mock query class return.
|
||||
|
||||
Most common use will probably be to mock instance methods.
|
||||
Given class instance, can set and delete as instance attribute.
|
||||
Mock_tk.Var and Mbox_func are special variants of this.
|
||||
'''
|
||||
def __init__(self, result=None, return_self=False):
|
||||
self.called = 0
|
||||
self.result = result
|
||||
self.return_self = return_self
|
||||
self.args = None
|
||||
self.kwds = None
|
||||
def __call__(self, *args, **kwds):
|
||||
self.called += 1
|
||||
self.args = args
|
||||
self.kwds = kwds
|
||||
if isinstance(self.result, BaseException):
|
||||
raise self.result
|
||||
elif self.return_self:
|
||||
return self
|
||||
else:
|
||||
return self.result
|
||||
|
||||
|
||||
class Editor:
|
||||
'''Minimally imitate editor.EditorWindow class.
|
||||
'''
|
||||
def __init__(self, flist=None, filename=None, key=None, root=None,
|
||||
text=None): # Allow real Text with mock Editor.
|
||||
self.text = text or Text()
|
||||
self.undo = UndoDelegator()
|
||||
|
||||
def get_selection_indices(self):
|
||||
first = self.text.index('1.0')
|
||||
last = self.text.index('end')
|
||||
return first, last
|
||||
|
||||
|
||||
class UndoDelegator:
|
||||
'''Minimally imitate undo.UndoDelegator class.
|
||||
'''
|
||||
# A real undo block is only needed for user interaction.
|
||||
def undo_block_start(*args):
|
||||
pass
|
||||
def undo_block_stop(*args):
|
||||
pass
|
||||
307
Dependencies/Python/Lib/idlelib/idle_test/mock_tk.py
vendored
Normal file
307
Dependencies/Python/Lib/idlelib/idle_test/mock_tk.py
vendored
Normal file
@@ -0,0 +1,307 @@
|
||||
"""Classes that replace tkinter gui objects used by an object being tested.
|
||||
|
||||
A gui object is anything with a master or parent parameter, which is
|
||||
typically required in spite of what the doc strings say.
|
||||
"""
|
||||
import re
|
||||
from _tkinter import TclError
|
||||
|
||||
|
||||
class Event:
|
||||
'''Minimal mock with attributes for testing event handlers.
|
||||
|
||||
This is not a gui object, but is used as an argument for callbacks
|
||||
that access attributes of the event passed. If a callback ignores
|
||||
the event, other than the fact that is happened, pass 'event'.
|
||||
|
||||
Keyboard, mouse, window, and other sources generate Event instances.
|
||||
Event instances have the following attributes: serial (number of
|
||||
event), time (of event), type (of event as number), widget (in which
|
||||
event occurred), and x,y (position of mouse). There are other
|
||||
attributes for specific events, such as keycode for key events.
|
||||
tkinter.Event.__doc__ has more but is still not complete.
|
||||
'''
|
||||
def __init__(self, **kwds):
|
||||
"Create event with attributes needed for test"
|
||||
self.__dict__.update(kwds)
|
||||
|
||||
|
||||
class Var:
|
||||
"Use for String/Int/BooleanVar: incomplete"
|
||||
def __init__(self, master=None, value=None, name=None):
|
||||
self.master = master
|
||||
self.value = value
|
||||
self.name = name
|
||||
def set(self, value):
|
||||
self.value = value
|
||||
def get(self):
|
||||
return self.value
|
||||
|
||||
|
||||
class Mbox_func:
|
||||
"""Generic mock for messagebox functions, which all have the same signature.
|
||||
|
||||
Instead of displaying a message box, the mock's call method saves the
|
||||
arguments as instance attributes, which test functions can then examine.
|
||||
The test can set the result returned to ask function
|
||||
"""
|
||||
def __init__(self, result=None):
|
||||
self.result = result # Return None for all show funcs
|
||||
def __call__(self, title, message, *args, **kwds):
|
||||
# Save all args for possible examination by tester
|
||||
self.title = title
|
||||
self.message = message
|
||||
self.args = args
|
||||
self.kwds = kwds
|
||||
return self.result # Set by tester for ask functions
|
||||
|
||||
|
||||
class Mbox:
|
||||
"""Mock for tkinter.messagebox with an Mbox_func for each function.
|
||||
|
||||
Example usage in test_module.py for testing functions in module.py:
|
||||
---
|
||||
from idlelib.idle_test.mock_tk import Mbox
|
||||
import module
|
||||
|
||||
orig_mbox = module.messagebox
|
||||
showerror = Mbox.showerror # example, for attribute access in test methods
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
module.messagebox = Mbox
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
module.messagebox = orig_mbox
|
||||
---
|
||||
For 'ask' functions, set func.result return value before calling the method
|
||||
that uses the message function. When messagebox functions are the
|
||||
only GUI calls in a method, this replacement makes the method GUI-free,
|
||||
"""
|
||||
askokcancel = Mbox_func() # True or False
|
||||
askquestion = Mbox_func() # 'yes' or 'no'
|
||||
askretrycancel = Mbox_func() # True or False
|
||||
askyesno = Mbox_func() # True or False
|
||||
askyesnocancel = Mbox_func() # True, False, or None
|
||||
showerror = Mbox_func() # None
|
||||
showinfo = Mbox_func() # None
|
||||
showwarning = Mbox_func() # None
|
||||
|
||||
|
||||
class Text:
|
||||
"""A semi-functional non-gui replacement for tkinter.Text text editors.
|
||||
|
||||
The mock's data model is that a text is a list of \n-terminated lines.
|
||||
The mock adds an empty string at the beginning of the list so that the
|
||||
index of actual lines start at 1, as with Tk. The methods never see this.
|
||||
Tk initializes files with a terminal \n that cannot be deleted. It is
|
||||
invisible in the sense that one cannot move the cursor beyond it.
|
||||
|
||||
This class is only tested (and valid) with strings of ascii chars.
|
||||
For testing, we are not concerned with Tk Text's treatment of,
|
||||
for instance, 0-width characters or character + accent.
|
||||
"""
|
||||
def __init__(self, master=None, cnf={}, **kw):
|
||||
'''Initialize mock, non-gui, text-only Text widget.
|
||||
|
||||
At present, all args are ignored. Almost all affect visual behavior.
|
||||
There are just a few Text-only options that affect text behavior.
|
||||
'''
|
||||
self.data = ['', '\n']
|
||||
|
||||
def index(self, index):
|
||||
"Return string version of index decoded according to current text."
|
||||
return "%s.%s" % self._decode(index, endflag=1)
|
||||
|
||||
def _decode(self, index, endflag=0):
|
||||
"""Return a (line, char) tuple of int indexes into self.data.
|
||||
|
||||
This implements .index without converting the result back to a string.
|
||||
The result is constrained by the number of lines and linelengths of
|
||||
self.data. For many indexes, the result is initially (1, 0).
|
||||
|
||||
The input index may have any of several possible forms:
|
||||
* line.char float: converted to 'line.char' string;
|
||||
* 'line.char' string, where line and char are decimal integers;
|
||||
* 'line.char lineend', where lineend='lineend' (and char is ignored);
|
||||
* 'line.end', where end='end' (same as above);
|
||||
* 'insert', the positions before terminal \n;
|
||||
* 'end', whose meaning depends on the endflag passed to ._endex.
|
||||
* 'sel.first' or 'sel.last', where sel is a tag -- not implemented.
|
||||
"""
|
||||
if isinstance(index, (float, bytes)):
|
||||
index = str(index)
|
||||
try:
|
||||
index=index.lower()
|
||||
except AttributeError:
|
||||
raise TclError('bad text index "%s"' % index) from None
|
||||
|
||||
lastline = len(self.data) - 1 # same as number of text lines
|
||||
if index == 'insert':
|
||||
return lastline, len(self.data[lastline]) - 1
|
||||
elif index == 'end':
|
||||
return self._endex(endflag)
|
||||
|
||||
line, char = index.split('.')
|
||||
line = int(line)
|
||||
|
||||
# Out of bounds line becomes first or last ('end') index
|
||||
if line < 1:
|
||||
return 1, 0
|
||||
elif line > lastline:
|
||||
return self._endex(endflag)
|
||||
|
||||
linelength = len(self.data[line]) -1 # position before/at \n
|
||||
if char.endswith(' lineend') or char == 'end':
|
||||
return line, linelength
|
||||
# Tk requires that ignored chars before ' lineend' be valid int
|
||||
if m := re.fullmatch(r'end-(\d*)c', char, re.A): # Used by hyperparser.
|
||||
return line, linelength - int(m.group(1))
|
||||
|
||||
# Out of bounds char becomes first or last index of line
|
||||
char = int(char)
|
||||
if char < 0:
|
||||
char = 0
|
||||
elif char > linelength:
|
||||
char = linelength
|
||||
return line, char
|
||||
|
||||
def _endex(self, endflag):
|
||||
'''Return position for 'end' or line overflow corresponding to endflag.
|
||||
|
||||
-1: position before terminal \n; for .insert(), .delete
|
||||
0: position after terminal \n; for .get, .delete index 1
|
||||
1: same viewed as beginning of non-existent next line (for .index)
|
||||
'''
|
||||
n = len(self.data)
|
||||
if endflag == 1:
|
||||
return n, 0
|
||||
else:
|
||||
n -= 1
|
||||
return n, len(self.data[n]) + endflag
|
||||
|
||||
def insert(self, index, chars):
|
||||
"Insert chars before the character at index."
|
||||
|
||||
if not chars: # ''.splitlines() is [], not ['']
|
||||
return
|
||||
chars = chars.splitlines(True)
|
||||
if chars[-1][-1] == '\n':
|
||||
chars.append('')
|
||||
line, char = self._decode(index, -1)
|
||||
before = self.data[line][:char]
|
||||
after = self.data[line][char:]
|
||||
self.data[line] = before + chars[0]
|
||||
self.data[line+1:line+1] = chars[1:]
|
||||
self.data[line+len(chars)-1] += after
|
||||
|
||||
def get(self, index1, index2=None):
|
||||
"Return slice from index1 to index2 (default is 'index1+1')."
|
||||
|
||||
startline, startchar = self._decode(index1)
|
||||
if index2 is None:
|
||||
endline, endchar = startline, startchar+1
|
||||
else:
|
||||
endline, endchar = self._decode(index2)
|
||||
|
||||
if startline == endline:
|
||||
return self.data[startline][startchar:endchar]
|
||||
else:
|
||||
lines = [self.data[startline][startchar:]]
|
||||
for i in range(startline+1, endline):
|
||||
lines.append(self.data[i])
|
||||
lines.append(self.data[endline][:endchar])
|
||||
return ''.join(lines)
|
||||
|
||||
def delete(self, index1, index2=None):
|
||||
'''Delete slice from index1 to index2 (default is 'index1+1').
|
||||
|
||||
Adjust default index2 ('index+1) for line ends.
|
||||
Do not delete the terminal \n at the very end of self.data ([-1][-1]).
|
||||
'''
|
||||
startline, startchar = self._decode(index1, -1)
|
||||
if index2 is None:
|
||||
if startchar < len(self.data[startline])-1:
|
||||
# not deleting \n
|
||||
endline, endchar = startline, startchar+1
|
||||
elif startline < len(self.data) - 1:
|
||||
# deleting non-terminal \n, convert 'index1+1 to start of next line
|
||||
endline, endchar = startline+1, 0
|
||||
else:
|
||||
# do not delete terminal \n if index1 == 'insert'
|
||||
return
|
||||
else:
|
||||
endline, endchar = self._decode(index2, -1)
|
||||
# restricting end position to insert position excludes terminal \n
|
||||
|
||||
if startline == endline and startchar < endchar:
|
||||
self.data[startline] = self.data[startline][:startchar] + \
|
||||
self.data[startline][endchar:]
|
||||
elif startline < endline:
|
||||
self.data[startline] = self.data[startline][:startchar] + \
|
||||
self.data[endline][endchar:]
|
||||
startline += 1
|
||||
for i in range(startline, endline+1):
|
||||
del self.data[startline]
|
||||
|
||||
def compare(self, index1, op, index2):
|
||||
line1, char1 = self._decode(index1)
|
||||
line2, char2 = self._decode(index2)
|
||||
if op == '<':
|
||||
return line1 < line2 or line1 == line2 and char1 < char2
|
||||
elif op == '<=':
|
||||
return line1 < line2 or line1 == line2 and char1 <= char2
|
||||
elif op == '>':
|
||||
return line1 > line2 or line1 == line2 and char1 > char2
|
||||
elif op == '>=':
|
||||
return line1 > line2 or line1 == line2 and char1 >= char2
|
||||
elif op == '==':
|
||||
return line1 == line2 and char1 == char2
|
||||
elif op == '!=':
|
||||
return line1 != line2 or char1 != char2
|
||||
else:
|
||||
raise TclError('''bad comparison operator "%s": '''
|
||||
'''must be <, <=, ==, >=, >, or !=''' % op)
|
||||
|
||||
# The following Text methods normally do something and return None.
|
||||
# Whether doing nothing is sufficient for a test will depend on the test.
|
||||
|
||||
def mark_set(self, name, index):
|
||||
"Set mark *name* before the character at index."
|
||||
pass
|
||||
|
||||
def mark_unset(self, *markNames):
|
||||
"Delete all marks in markNames."
|
||||
|
||||
def tag_remove(self, tagName, index1, index2=None):
|
||||
"Remove tag tagName from all characters between index1 and index2."
|
||||
pass
|
||||
|
||||
# The following Text methods affect the graphics screen and return None.
|
||||
# Doing nothing should always be sufficient for tests.
|
||||
|
||||
def scan_dragto(self, x, y):
|
||||
"Adjust the view of the text according to scan_mark"
|
||||
|
||||
def scan_mark(self, x, y):
|
||||
"Remember the current X, Y coordinates."
|
||||
|
||||
def see(self, index):
|
||||
"Scroll screen to make the character at INDEX is visible."
|
||||
pass
|
||||
|
||||
# The following is a Misc method inherited by Text.
|
||||
# It should properly go in a Misc mock, but is included here for now.
|
||||
|
||||
def bind(sequence=None, func=None, add=None):
|
||||
"Bind to this widget at event sequence a call to function func."
|
||||
pass
|
||||
|
||||
|
||||
class Entry:
|
||||
"Mock for tkinter.Entry."
|
||||
def focus_set(self):
|
||||
pass
|
||||
30
Dependencies/Python/Lib/idlelib/idle_test/template.py
vendored
Normal file
30
Dependencies/Python/Lib/idlelib/idle_test/template.py
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
"Test , coverage %."
|
||||
|
||||
from idlelib import zzdummy
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk
|
||||
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.update_idletasks()
|
||||
## for id in cls.root.tk.call('after', 'info'):
|
||||
## cls.root.after_cancel(id) # Need for EditorWindow.
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_init(self):
|
||||
self.assertTrue(True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
305
Dependencies/Python/Lib/idlelib/idle_test/test_autocomplete.py
vendored
Normal file
305
Dependencies/Python/Lib/idlelib/idle_test/test_autocomplete.py
vendored
Normal file
@@ -0,0 +1,305 @@
|
||||
"Test autocomplete, coverage 93%."
|
||||
|
||||
import unittest
|
||||
from unittest.mock import Mock, patch
|
||||
from test.support import requires
|
||||
from tkinter import Tk, Text
|
||||
import os
|
||||
import __main__
|
||||
|
||||
import idlelib.autocomplete as ac
|
||||
import idlelib.autocomplete_w as acw
|
||||
from idlelib.idle_test.mock_idle import Func
|
||||
from idlelib.idle_test.mock_tk import Event
|
||||
|
||||
|
||||
class DummyEditwin:
|
||||
def __init__(self, root, text):
|
||||
self.root = root
|
||||
self.text = text
|
||||
self.indentwidth = 8
|
||||
self.tabwidth = 8
|
||||
self.prompt_last_line = '>>>' # Currently not used by autocomplete.
|
||||
|
||||
|
||||
class AutoCompleteTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.text = Text(cls.root)
|
||||
cls.editor = DummyEditwin(cls.root, cls.text)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.editor, cls.text
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.text.delete('1.0', 'end')
|
||||
self.autocomplete = ac.AutoComplete(self.editor)
|
||||
|
||||
def test_init(self):
|
||||
self.assertEqual(self.autocomplete.editwin, self.editor)
|
||||
self.assertEqual(self.autocomplete.text, self.text)
|
||||
|
||||
def test_make_autocomplete_window(self):
|
||||
testwin = self.autocomplete._make_autocomplete_window()
|
||||
self.assertIsInstance(testwin, acw.AutoCompleteWindow)
|
||||
|
||||
def test_remove_autocomplete_window(self):
|
||||
acp = self.autocomplete
|
||||
acp.autocompletewindow = m = Mock()
|
||||
acp._remove_autocomplete_window()
|
||||
m.hide_window.assert_called_once()
|
||||
self.assertIsNone(acp.autocompletewindow)
|
||||
|
||||
def test_force_open_completions_event(self):
|
||||
# Call _open_completions and break.
|
||||
acp = self.autocomplete
|
||||
open_c = Func()
|
||||
acp.open_completions = open_c
|
||||
self.assertEqual(acp.force_open_completions_event('event'), 'break')
|
||||
self.assertEqual(open_c.args[0], ac.FORCE)
|
||||
|
||||
def test_autocomplete_event(self):
|
||||
Equal = self.assertEqual
|
||||
acp = self.autocomplete
|
||||
|
||||
# Result of autocomplete event: If modified tab, None.
|
||||
ev = Event(mc_state=True)
|
||||
self.assertIsNone(acp.autocomplete_event(ev))
|
||||
del ev.mc_state
|
||||
|
||||
# If tab after whitespace, None.
|
||||
self.text.insert('1.0', ' """Docstring.\n ')
|
||||
self.assertIsNone(acp.autocomplete_event(ev))
|
||||
self.text.delete('1.0', 'end')
|
||||
|
||||
# If active autocomplete window, complete() and 'break'.
|
||||
self.text.insert('1.0', 're.')
|
||||
acp.autocompletewindow = mock = Mock()
|
||||
mock.is_active = Mock(return_value=True)
|
||||
Equal(acp.autocomplete_event(ev), 'break')
|
||||
mock.complete.assert_called_once()
|
||||
acp.autocompletewindow = None
|
||||
|
||||
# If no active autocomplete window, open_completions(), None/break.
|
||||
open_c = Func(result=False)
|
||||
acp.open_completions = open_c
|
||||
Equal(acp.autocomplete_event(ev), None)
|
||||
Equal(open_c.args[0], ac.TAB)
|
||||
open_c.result = True
|
||||
Equal(acp.autocomplete_event(ev), 'break')
|
||||
Equal(open_c.args[0], ac.TAB)
|
||||
|
||||
def test_try_open_completions_event(self):
|
||||
Equal = self.assertEqual
|
||||
text = self.text
|
||||
acp = self.autocomplete
|
||||
trycompletions = acp.try_open_completions_event
|
||||
after = Func(result='after1')
|
||||
acp.text.after = after
|
||||
|
||||
# If no text or trigger, after not called.
|
||||
trycompletions()
|
||||
Equal(after.called, 0)
|
||||
text.insert('1.0', 're')
|
||||
trycompletions()
|
||||
Equal(after.called, 0)
|
||||
|
||||
# Attribute needed, no existing callback.
|
||||
text.insert('insert', ' re.')
|
||||
acp._delayed_completion_id = None
|
||||
trycompletions()
|
||||
Equal(acp._delayed_completion_index, text.index('insert'))
|
||||
Equal(after.args,
|
||||
(acp.popupwait, acp._delayed_open_completions, ac.TRY_A))
|
||||
cb1 = acp._delayed_completion_id
|
||||
Equal(cb1, 'after1')
|
||||
|
||||
# File needed, existing callback cancelled.
|
||||
text.insert('insert', ' "./Lib/')
|
||||
after.result = 'after2'
|
||||
cancel = Func()
|
||||
acp.text.after_cancel = cancel
|
||||
trycompletions()
|
||||
Equal(acp._delayed_completion_index, text.index('insert'))
|
||||
Equal(cancel.args, (cb1,))
|
||||
Equal(after.args,
|
||||
(acp.popupwait, acp._delayed_open_completions, ac.TRY_F))
|
||||
Equal(acp._delayed_completion_id, 'after2')
|
||||
|
||||
def test_delayed_open_completions(self):
|
||||
Equal = self.assertEqual
|
||||
acp = self.autocomplete
|
||||
open_c = Func()
|
||||
acp.open_completions = open_c
|
||||
self.text.insert('1.0', '"dict.')
|
||||
|
||||
# Set autocomplete._delayed_completion_id to None.
|
||||
# Text index changed, don't call open_completions.
|
||||
acp._delayed_completion_id = 'after'
|
||||
acp._delayed_completion_index = self.text.index('insert+1c')
|
||||
acp._delayed_open_completions('dummy')
|
||||
self.assertIsNone(acp._delayed_completion_id)
|
||||
Equal(open_c.called, 0)
|
||||
|
||||
# Text index unchanged, call open_completions.
|
||||
acp._delayed_completion_index = self.text.index('insert')
|
||||
acp._delayed_open_completions((1, 2, 3, ac.FILES))
|
||||
self.assertEqual(open_c.args[0], (1, 2, 3, ac.FILES))
|
||||
|
||||
def test_oc_cancel_comment(self):
|
||||
none = self.assertIsNone
|
||||
acp = self.autocomplete
|
||||
|
||||
# Comment is in neither code or string.
|
||||
acp._delayed_completion_id = 'after'
|
||||
after = Func(result='after')
|
||||
acp.text.after_cancel = after
|
||||
self.text.insert(1.0, '# comment')
|
||||
none(acp.open_completions(ac.TAB)) # From 'else' after 'elif'.
|
||||
none(acp._delayed_completion_id)
|
||||
|
||||
def test_oc_no_list(self):
|
||||
acp = self.autocomplete
|
||||
fetch = Func(result=([],[]))
|
||||
acp.fetch_completions = fetch
|
||||
self.text.insert('1.0', 'object')
|
||||
self.assertIsNone(acp.open_completions(ac.TAB))
|
||||
self.text.insert('insert', '.')
|
||||
self.assertIsNone(acp.open_completions(ac.TAB))
|
||||
self.assertEqual(fetch.called, 2)
|
||||
|
||||
|
||||
def test_open_completions_none(self):
|
||||
# Test other two None returns.
|
||||
none = self.assertIsNone
|
||||
acp = self.autocomplete
|
||||
|
||||
# No object for attributes or need call not allowed.
|
||||
self.text.insert(1.0, '.')
|
||||
none(acp.open_completions(ac.TAB))
|
||||
self.text.insert('insert', ' int().')
|
||||
none(acp.open_completions(ac.TAB))
|
||||
|
||||
# Blank or quote trigger 'if complete ...'.
|
||||
self.text.delete(1.0, 'end')
|
||||
self.assertFalse(acp.open_completions(ac.TAB))
|
||||
self.text.insert('1.0', '"')
|
||||
self.assertFalse(acp.open_completions(ac.TAB))
|
||||
self.text.delete('1.0', 'end')
|
||||
|
||||
class dummy_acw:
|
||||
__init__ = Func()
|
||||
show_window = Func(result=False)
|
||||
hide_window = Func()
|
||||
|
||||
def test_open_completions(self):
|
||||
# Test completions of files and attributes.
|
||||
acp = self.autocomplete
|
||||
fetch = Func(result=(['tem'],['tem', '_tem']))
|
||||
acp.fetch_completions = fetch
|
||||
def make_acw(): return self.dummy_acw()
|
||||
acp._make_autocomplete_window = make_acw
|
||||
|
||||
self.text.insert('1.0', 'int.')
|
||||
acp.open_completions(ac.TAB)
|
||||
self.assertIsInstance(acp.autocompletewindow, self.dummy_acw)
|
||||
self.text.delete('1.0', 'end')
|
||||
|
||||
# Test files.
|
||||
self.text.insert('1.0', '"t')
|
||||
self.assertTrue(acp.open_completions(ac.TAB))
|
||||
self.text.delete('1.0', 'end')
|
||||
|
||||
def test_completion_kwds(self):
|
||||
self.assertIn('and', ac.completion_kwds)
|
||||
self.assertIn('case', ac.completion_kwds)
|
||||
self.assertNotIn('None', ac.completion_kwds)
|
||||
|
||||
def test_fetch_completions(self):
|
||||
# Test that fetch_completions returns 2 lists:
|
||||
# For attribute completion, a large list containing all variables, and
|
||||
# a small list containing non-private variables.
|
||||
# For file completion, a large list containing all files in the path,
|
||||
# and a small list containing files that do not start with '.'.
|
||||
acp = self.autocomplete
|
||||
small, large = acp.fetch_completions(
|
||||
'', ac.ATTRS)
|
||||
if hasattr(__main__, '__file__') and __main__.__file__ != ac.__file__:
|
||||
self.assertNotIn('AutoComplete', small) # See issue 36405.
|
||||
|
||||
# Test attributes
|
||||
s, b = acp.fetch_completions('', ac.ATTRS)
|
||||
self.assertLess(len(small), len(large))
|
||||
self.assertTrue(all(filter(lambda x: x.startswith('_'), s)))
|
||||
self.assertTrue(any(filter(lambda x: x.startswith('_'), b)))
|
||||
|
||||
# Test smalll should respect to __all__.
|
||||
with patch.dict('__main__.__dict__', {'__all__': ['a', 'b']}):
|
||||
s, b = acp.fetch_completions('', ac.ATTRS)
|
||||
self.assertEqual(s, ['a', 'b'])
|
||||
self.assertIn('__name__', b) # From __main__.__dict__.
|
||||
self.assertIn('sum', b) # From __main__.__builtins__.__dict__.
|
||||
self.assertIn('nonlocal', b) # From keyword.kwlist.
|
||||
pos = b.index('False') # Test False not included twice.
|
||||
self.assertNotEqual(b[pos+1], 'False')
|
||||
|
||||
# Test attributes with name entity.
|
||||
mock = Mock()
|
||||
mock._private = Mock()
|
||||
with patch.dict('__main__.__dict__', {'foo': mock}):
|
||||
s, b = acp.fetch_completions('foo', ac.ATTRS)
|
||||
self.assertNotIn('_private', s)
|
||||
self.assertIn('_private', b)
|
||||
self.assertEqual(s, [i for i in sorted(dir(mock)) if i[:1] != '_'])
|
||||
self.assertEqual(b, sorted(dir(mock)))
|
||||
|
||||
# Test files
|
||||
def _listdir(path):
|
||||
# This will be patch and used in fetch_completions.
|
||||
if path == '.':
|
||||
return ['foo', 'bar', '.hidden']
|
||||
return ['monty', 'python', '.hidden']
|
||||
|
||||
with patch.object(os, 'listdir', _listdir):
|
||||
s, b = acp.fetch_completions('', ac.FILES)
|
||||
self.assertEqual(s, ['bar', 'foo'])
|
||||
self.assertEqual(b, ['.hidden', 'bar', 'foo'])
|
||||
|
||||
s, b = acp.fetch_completions('~', ac.FILES)
|
||||
self.assertEqual(s, ['monty', 'python'])
|
||||
self.assertEqual(b, ['.hidden', 'monty', 'python'])
|
||||
|
||||
def test_get_entity(self):
|
||||
# Test that a name is in the namespace of sys.modules and
|
||||
# __main__.__dict__.
|
||||
acp = self.autocomplete
|
||||
Equal = self.assertEqual
|
||||
|
||||
Equal(acp.get_entity('int'), int)
|
||||
|
||||
# Test name from sys.modules.
|
||||
mock = Mock()
|
||||
with patch.dict('sys.modules', {'tempfile': mock}):
|
||||
Equal(acp.get_entity('tempfile'), mock)
|
||||
|
||||
# Test name from __main__.__dict__.
|
||||
di = {'foo': 10, 'bar': 20}
|
||||
with patch.dict('__main__.__dict__', {'d': di}):
|
||||
Equal(acp.get_entity('d'), di)
|
||||
|
||||
# Test name not in namespace.
|
||||
with patch.dict('__main__.__dict__', {}):
|
||||
with self.assertRaises(NameError):
|
||||
acp.get_entity('not_exist')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
32
Dependencies/Python/Lib/idlelib/idle_test/test_autocomplete_w.py
vendored
Normal file
32
Dependencies/Python/Lib/idlelib/idle_test/test_autocomplete_w.py
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
"Test autocomplete_w, coverage 11%."
|
||||
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk, Text
|
||||
|
||||
import idlelib.autocomplete_w as acw
|
||||
|
||||
|
||||
class AutoCompleteWindowTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.text = Text(cls.root)
|
||||
cls.acw = acw.AutoCompleteWindow(cls.text, tags=None)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text, cls.acw
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_init(self):
|
||||
self.assertEqual(self.acw.widget, self.text)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
155
Dependencies/Python/Lib/idlelib/idle_test/test_autoexpand.py
vendored
Normal file
155
Dependencies/Python/Lib/idlelib/idle_test/test_autoexpand.py
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
"Test autoexpand, coverage 100%."
|
||||
|
||||
from idlelib.autoexpand import AutoExpand
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Text, Tk
|
||||
|
||||
|
||||
class DummyEditwin:
|
||||
# AutoExpand.__init__ only needs .text
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
|
||||
class AutoExpandTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.tk = Tk()
|
||||
cls.text = Text(cls.tk)
|
||||
cls.auto_expand = AutoExpand(DummyEditwin(cls.text))
|
||||
cls.auto_expand.bell = lambda: None
|
||||
|
||||
# If mock_tk.Text._decode understood indexes 'insert' with suffixed 'linestart',
|
||||
# 'wordstart', and 'lineend', used by autoexpand, we could use the following
|
||||
# to run these test on non-gui machines (but check bell).
|
||||
## try:
|
||||
## requires('gui')
|
||||
## #raise ResourceDenied() # Uncomment to test mock.
|
||||
## except ResourceDenied:
|
||||
## from idlelib.idle_test.mock_tk import Text
|
||||
## cls.text = Text()
|
||||
## cls.text.bell = lambda: None
|
||||
## else:
|
||||
## from tkinter import Tk, Text
|
||||
## cls.tk = Tk()
|
||||
## cls.text = Text(cls.tk)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text, cls.auto_expand
|
||||
if hasattr(cls, 'tk'):
|
||||
cls.tk.destroy()
|
||||
del cls.tk
|
||||
|
||||
def tearDown(self):
|
||||
self.text.delete('1.0', 'end')
|
||||
|
||||
def test_get_prevword(self):
|
||||
text = self.text
|
||||
previous = self.auto_expand.getprevword
|
||||
equal = self.assertEqual
|
||||
|
||||
equal(previous(), '')
|
||||
|
||||
text.insert('insert', 't')
|
||||
equal(previous(), 't')
|
||||
|
||||
text.insert('insert', 'his')
|
||||
equal(previous(), 'this')
|
||||
|
||||
text.insert('insert', ' ')
|
||||
equal(previous(), '')
|
||||
|
||||
text.insert('insert', 'is')
|
||||
equal(previous(), 'is')
|
||||
|
||||
text.insert('insert', '\nsample\nstring')
|
||||
equal(previous(), 'string')
|
||||
|
||||
text.delete('3.0', 'insert')
|
||||
equal(previous(), '')
|
||||
|
||||
text.delete('1.0', 'end')
|
||||
equal(previous(), '')
|
||||
|
||||
def test_before_only(self):
|
||||
previous = self.auto_expand.getprevword
|
||||
expand = self.auto_expand.expand_word_event
|
||||
equal = self.assertEqual
|
||||
|
||||
self.text.insert('insert', 'ab ac bx ad ab a')
|
||||
equal(self.auto_expand.getwords(), ['ab', 'ad', 'ac', 'a'])
|
||||
expand('event')
|
||||
equal(previous(), 'ab')
|
||||
expand('event')
|
||||
equal(previous(), 'ad')
|
||||
expand('event')
|
||||
equal(previous(), 'ac')
|
||||
expand('event')
|
||||
equal(previous(), 'a')
|
||||
|
||||
def test_after_only(self):
|
||||
# Also add punctuation 'noise' that should be ignored.
|
||||
text = self.text
|
||||
previous = self.auto_expand.getprevword
|
||||
expand = self.auto_expand.expand_word_event
|
||||
equal = self.assertEqual
|
||||
|
||||
text.insert('insert', 'a, [ab] ac: () bx"" cd ac= ad ya')
|
||||
text.mark_set('insert', '1.1')
|
||||
equal(self.auto_expand.getwords(), ['ab', 'ac', 'ad', 'a'])
|
||||
expand('event')
|
||||
equal(previous(), 'ab')
|
||||
expand('event')
|
||||
equal(previous(), 'ac')
|
||||
expand('event')
|
||||
equal(previous(), 'ad')
|
||||
expand('event')
|
||||
equal(previous(), 'a')
|
||||
|
||||
def test_both_before_after(self):
|
||||
text = self.text
|
||||
previous = self.auto_expand.getprevword
|
||||
expand = self.auto_expand.expand_word_event
|
||||
equal = self.assertEqual
|
||||
|
||||
text.insert('insert', 'ab xy yz\n')
|
||||
text.insert('insert', 'a ac by ac')
|
||||
|
||||
text.mark_set('insert', '2.1')
|
||||
equal(self.auto_expand.getwords(), ['ab', 'ac', 'a'])
|
||||
expand('event')
|
||||
equal(previous(), 'ab')
|
||||
expand('event')
|
||||
equal(previous(), 'ac')
|
||||
expand('event')
|
||||
equal(previous(), 'a')
|
||||
|
||||
def test_other_expand_cases(self):
|
||||
text = self.text
|
||||
expand = self.auto_expand.expand_word_event
|
||||
equal = self.assertEqual
|
||||
|
||||
# no expansion candidate found
|
||||
equal(self.auto_expand.getwords(), [])
|
||||
equal(expand('event'), 'break')
|
||||
|
||||
text.insert('insert', 'bx cy dz a')
|
||||
equal(self.auto_expand.getwords(), [])
|
||||
|
||||
# reset state by successfully expanding once
|
||||
# move cursor to another position and expand again
|
||||
text.insert('insert', 'ac xy a ac ad a')
|
||||
text.mark_set('insert', '1.7')
|
||||
expand('event')
|
||||
initial_state = self.auto_expand.state
|
||||
text.mark_set('insert', '1.end')
|
||||
expand('event')
|
||||
new_state = self.auto_expand.state
|
||||
self.assertNotEqual(initial_state, new_state)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
257
Dependencies/Python/Lib/idlelib/idle_test/test_browser.py
vendored
Normal file
257
Dependencies/Python/Lib/idlelib/idle_test/test_browser.py
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
"Test browser, coverage 90%."
|
||||
|
||||
from idlelib import browser
|
||||
from test.support import requires
|
||||
import unittest
|
||||
from unittest import mock
|
||||
from idlelib.idle_test.mock_idle import Func
|
||||
from idlelib.util import py_extensions
|
||||
|
||||
from collections import deque
|
||||
import os.path
|
||||
import pyclbr
|
||||
from tkinter import Tk
|
||||
|
||||
from idlelib.tree import TreeNode
|
||||
|
||||
|
||||
class ModuleBrowserTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.mb = browser.ModuleBrowser(cls.root, __file__, _utest=True)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.mb.close()
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root, cls.mb
|
||||
|
||||
def test_init(self):
|
||||
mb = self.mb
|
||||
eq = self.assertEqual
|
||||
eq(mb.path, __file__)
|
||||
eq(pyclbr._modules, {})
|
||||
self.assertIsInstance(mb.node, TreeNode)
|
||||
self.assertIsNotNone(browser.file_open)
|
||||
|
||||
def test_settitle(self):
|
||||
mb = self.mb
|
||||
self.assertIn(os.path.basename(__file__), mb.top.title())
|
||||
self.assertEqual(mb.top.iconname(), 'Module Browser')
|
||||
|
||||
def test_rootnode(self):
|
||||
mb = self.mb
|
||||
rn = mb.rootnode()
|
||||
self.assertIsInstance(rn, browser.ModuleBrowserTreeItem)
|
||||
|
||||
def test_close(self):
|
||||
mb = self.mb
|
||||
mb.top.destroy = Func()
|
||||
mb.node.destroy = Func()
|
||||
mb.close()
|
||||
self.assertTrue(mb.top.destroy.called)
|
||||
self.assertTrue(mb.node.destroy.called)
|
||||
del mb.top.destroy, mb.node.destroy
|
||||
|
||||
def test_is_browseable_extension(self):
|
||||
path = "/path/to/file"
|
||||
for ext in py_extensions:
|
||||
with self.subTest(ext=ext):
|
||||
filename = f'{path}{ext}'
|
||||
actual = browser.is_browseable_extension(filename)
|
||||
expected = ext not in browser.browseable_extension_blocklist
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
|
||||
# Nested tree same as in test_pyclbr.py except for supers on C0. C1.
|
||||
mb = pyclbr
|
||||
module, fname = 'test', 'test.py'
|
||||
C0 = mb.Class(module, 'C0', ['base'], fname, 1, end_lineno=9)
|
||||
F1 = mb._nest_function(C0, 'F1', 3, 5)
|
||||
C1 = mb._nest_class(C0, 'C1', 6, 9, [''])
|
||||
C2 = mb._nest_class(C1, 'C2', 7, 9)
|
||||
F3 = mb._nest_function(C2, 'F3', 9, 9)
|
||||
f0 = mb.Function(module, 'f0', fname, 11, end_lineno=15)
|
||||
f1 = mb._nest_function(f0, 'f1', 12, 14)
|
||||
f2 = mb._nest_function(f1, 'f2', 13, 13)
|
||||
c1 = mb._nest_class(f0, 'c1', 15, 15)
|
||||
mock_pyclbr_tree = {'C0': C0, 'f0': f0}
|
||||
|
||||
# Adjust C0.name, C1.name so tests do not depend on order.
|
||||
browser.transform_children(mock_pyclbr_tree, 'test') # C0(base)
|
||||
browser.transform_children(C0.children) # C1()
|
||||
|
||||
# The class below checks that the calls above are correct
|
||||
# and that duplicate calls have no effect.
|
||||
|
||||
|
||||
class TransformChildrenTest(unittest.TestCase):
|
||||
|
||||
def test_transform_module_children(self):
|
||||
eq = self.assertEqual
|
||||
transform = browser.transform_children
|
||||
# Parameter matches tree module.
|
||||
tcl = list(transform(mock_pyclbr_tree, 'test'))
|
||||
eq(tcl, [C0, f0])
|
||||
eq(tcl[0].name, 'C0(base)')
|
||||
eq(tcl[1].name, 'f0')
|
||||
# Check that second call does not change suffix.
|
||||
tcl = list(transform(mock_pyclbr_tree, 'test'))
|
||||
eq(tcl[0].name, 'C0(base)')
|
||||
# Nothing to traverse if parameter name isn't same as tree module.
|
||||
tcl = list(transform(mock_pyclbr_tree, 'different name'))
|
||||
eq(tcl, [])
|
||||
|
||||
def test_transform_node_children(self):
|
||||
eq = self.assertEqual
|
||||
transform = browser.transform_children
|
||||
# Class with two children, one name altered.
|
||||
tcl = list(transform(C0.children))
|
||||
eq(tcl, [F1, C1])
|
||||
eq(tcl[0].name, 'F1')
|
||||
eq(tcl[1].name, 'C1()')
|
||||
tcl = list(transform(C0.children))
|
||||
eq(tcl[1].name, 'C1()')
|
||||
# Function with two children.
|
||||
eq(list(transform(f0.children)), [f1, c1])
|
||||
|
||||
|
||||
class ModuleBrowserTreeItemTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.mbt = browser.ModuleBrowserTreeItem(fname)
|
||||
|
||||
def test_init(self):
|
||||
self.assertEqual(self.mbt.file, fname)
|
||||
|
||||
def test_gettext(self):
|
||||
self.assertEqual(self.mbt.GetText(), fname)
|
||||
|
||||
def test_geticonname(self):
|
||||
self.assertEqual(self.mbt.GetIconName(), 'python')
|
||||
|
||||
def test_isexpandable(self):
|
||||
self.assertTrue(self.mbt.IsExpandable())
|
||||
|
||||
def test_listchildren(self):
|
||||
save_rex = browser.pyclbr.readmodule_ex
|
||||
save_tc = browser.transform_children
|
||||
browser.pyclbr.readmodule_ex = Func(result=mock_pyclbr_tree)
|
||||
browser.transform_children = Func(result=[f0, C0])
|
||||
try:
|
||||
self.assertEqual(self.mbt.listchildren(), [f0, C0])
|
||||
finally:
|
||||
browser.pyclbr.readmodule_ex = save_rex
|
||||
browser.transform_children = save_tc
|
||||
|
||||
def test_getsublist(self):
|
||||
mbt = self.mbt
|
||||
mbt.listchildren = Func(result=[f0, C0])
|
||||
sub0, sub1 = mbt.GetSubList()
|
||||
del mbt.listchildren
|
||||
self.assertIsInstance(sub0, browser.ChildBrowserTreeItem)
|
||||
self.assertIsInstance(sub1, browser.ChildBrowserTreeItem)
|
||||
self.assertEqual(sub0.name, 'f0')
|
||||
self.assertEqual(sub1.name, 'C0(base)')
|
||||
|
||||
@mock.patch('idlelib.browser.file_open')
|
||||
def test_ondoubleclick(self, fopen):
|
||||
mbt = self.mbt
|
||||
|
||||
with mock.patch('os.path.exists', return_value=False):
|
||||
mbt.OnDoubleClick()
|
||||
fopen.assert_not_called()
|
||||
|
||||
with mock.patch('os.path.exists', return_value=True):
|
||||
mbt.OnDoubleClick()
|
||||
fopen.assert_called_once_with(fname)
|
||||
|
||||
|
||||
class ChildBrowserTreeItemTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
CBT = browser.ChildBrowserTreeItem
|
||||
cls.cbt_f1 = CBT(f1)
|
||||
cls.cbt_C1 = CBT(C1)
|
||||
cls.cbt_F1 = CBT(F1)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.cbt_C1, cls.cbt_f1, cls.cbt_F1
|
||||
|
||||
def test_init(self):
|
||||
eq = self.assertEqual
|
||||
eq(self.cbt_C1.name, 'C1()')
|
||||
self.assertFalse(self.cbt_C1.isfunction)
|
||||
eq(self.cbt_f1.name, 'f1')
|
||||
self.assertTrue(self.cbt_f1.isfunction)
|
||||
|
||||
def test_gettext(self):
|
||||
self.assertEqual(self.cbt_C1.GetText(), 'class C1()')
|
||||
self.assertEqual(self.cbt_f1.GetText(), 'def f1(...)')
|
||||
|
||||
def test_geticonname(self):
|
||||
self.assertEqual(self.cbt_C1.GetIconName(), 'folder')
|
||||
self.assertEqual(self.cbt_f1.GetIconName(), 'python')
|
||||
|
||||
def test_isexpandable(self):
|
||||
self.assertTrue(self.cbt_C1.IsExpandable())
|
||||
self.assertTrue(self.cbt_f1.IsExpandable())
|
||||
self.assertFalse(self.cbt_F1.IsExpandable())
|
||||
|
||||
def test_getsublist(self):
|
||||
eq = self.assertEqual
|
||||
CBT = browser.ChildBrowserTreeItem
|
||||
|
||||
f1sublist = self.cbt_f1.GetSubList()
|
||||
self.assertIsInstance(f1sublist[0], CBT)
|
||||
eq(len(f1sublist), 1)
|
||||
eq(f1sublist[0].name, 'f2')
|
||||
|
||||
eq(self.cbt_F1.GetSubList(), [])
|
||||
|
||||
@mock.patch('idlelib.browser.file_open')
|
||||
def test_ondoubleclick(self, fopen):
|
||||
goto = fopen.return_value.gotoline = mock.Mock()
|
||||
self.cbt_F1.OnDoubleClick()
|
||||
fopen.assert_called()
|
||||
goto.assert_called()
|
||||
goto.assert_called_with(self.cbt_F1.obj.lineno)
|
||||
# Failure test would have to raise OSError or AttributeError.
|
||||
|
||||
|
||||
class NestedChildrenTest(unittest.TestCase):
|
||||
"Test that all the nodes in a nested tree are added to the BrowserTree."
|
||||
|
||||
def test_nested(self):
|
||||
queue = deque()
|
||||
actual_names = []
|
||||
# The tree items are processed in breadth first order.
|
||||
# Verify that processing each sublist hits every node and
|
||||
# in the right order.
|
||||
expected_names = ['f0', 'C0(base)',
|
||||
'f1', 'c1', 'F1', 'C1()',
|
||||
'f2', 'C2',
|
||||
'F3']
|
||||
CBT = browser.ChildBrowserTreeItem
|
||||
queue.extend((CBT(f0), CBT(C0)))
|
||||
while queue:
|
||||
cb = queue.popleft()
|
||||
sublist = cb.GetSubList()
|
||||
queue.extend(sublist)
|
||||
self.assertIn(cb.name, cb.GetText())
|
||||
self.assertIn(cb.GetIconName(), ('python', 'folder'))
|
||||
self.assertIs(cb.IsExpandable(), sublist != [])
|
||||
actual_names.append(cb.name)
|
||||
self.assertEqual(actual_names, expected_names)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
372
Dependencies/Python/Lib/idlelib/idle_test/test_calltip.py
vendored
Normal file
372
Dependencies/Python/Lib/idlelib/idle_test/test_calltip.py
vendored
Normal file
@@ -0,0 +1,372 @@
|
||||
"Test calltip, coverage 76%"
|
||||
|
||||
from idlelib import calltip
|
||||
import unittest
|
||||
from unittest.mock import Mock
|
||||
import textwrap
|
||||
import types
|
||||
import re
|
||||
from idlelib.idle_test.mock_tk import Text
|
||||
from test.support import MISSING_C_DOCSTRINGS
|
||||
|
||||
|
||||
# Test Class TC is used in multiple get_argspec test methods
|
||||
class TC:
|
||||
'doc'
|
||||
tip = "(ai=None, *b)"
|
||||
def __init__(self, ai=None, *b): 'doc'
|
||||
__init__.tip = "(self, ai=None, *b)"
|
||||
def t1(self): 'doc'
|
||||
t1.tip = "(self)"
|
||||
def t2(self, ai, b=None): 'doc'
|
||||
t2.tip = "(self, ai, b=None)"
|
||||
def t3(self, ai, *args): 'doc'
|
||||
t3.tip = "(self, ai, *args)"
|
||||
def t4(self, *args): 'doc'
|
||||
t4.tip = "(self, *args)"
|
||||
def t5(self, ai, b=None, *args, **kw): 'doc'
|
||||
t5.tip = "(self, ai, b=None, *args, **kw)"
|
||||
def t6(no, self): 'doc'
|
||||
t6.tip = "(no, self)"
|
||||
def __call__(self, ci): 'doc'
|
||||
__call__.tip = "(self, ci)"
|
||||
def nd(self): pass # No doc.
|
||||
# attaching .tip to wrapped methods does not work
|
||||
@classmethod
|
||||
def cm(cls, a): 'doc'
|
||||
@staticmethod
|
||||
def sm(b): 'doc'
|
||||
|
||||
|
||||
tc = TC()
|
||||
default_tip = calltip._default_callable_argspec
|
||||
get_spec = calltip.get_argspec
|
||||
|
||||
|
||||
class Get_argspecTest(unittest.TestCase):
|
||||
# The get_spec function must return a string, even if blank.
|
||||
# Test a variety of objects to be sure that none cause it to raise
|
||||
# (quite aside from getting as correct an answer as possible).
|
||||
# The tests of builtins may break if inspect or the docstrings change,
|
||||
# but a red buildbot is better than a user crash (as has happened).
|
||||
# For a simple mismatch, change the expected output to the actual.
|
||||
|
||||
@unittest.skipIf(MISSING_C_DOCSTRINGS,
|
||||
"Signature information for builtins requires docstrings")
|
||||
def test_builtins(self):
|
||||
|
||||
def tiptest(obj, out):
|
||||
self.assertEqual(get_spec(obj), out)
|
||||
|
||||
# Python class that inherits builtin methods
|
||||
class List(list): "List() doc"
|
||||
|
||||
# Simulate builtin with no docstring for default tip test
|
||||
class SB: __call__ = None
|
||||
|
||||
if List.__doc__ is not None:
|
||||
tiptest(List,
|
||||
f'(iterable=(), /)'
|
||||
f'\n{List.__doc__}')
|
||||
tiptest(list.__new__,
|
||||
'(*args, **kwargs)\n'
|
||||
'Create and return a new object. '
|
||||
'See help(type) for accurate signature.')
|
||||
tiptest(list.__init__,
|
||||
'(self, /, *args, **kwargs)\n'
|
||||
'Initialize self. See help(type(self)) for accurate signature.')
|
||||
append_doc = "\nAppend object to the end of the list."
|
||||
tiptest(list.append, '(self, object, /)' + append_doc)
|
||||
tiptest(List.append, '(self, object, /)' + append_doc)
|
||||
tiptest([].append, '(object, /)' + append_doc)
|
||||
# The use of 'object' above matches the signature text.
|
||||
|
||||
tiptest(types.MethodType,
|
||||
'(function, instance, /)\n'
|
||||
'Create a bound instance method object.')
|
||||
tiptest(SB(), default_tip)
|
||||
|
||||
p = re.compile('')
|
||||
tiptest(re.sub, '''\
|
||||
(pattern, repl, string, count=0, flags=0)
|
||||
Return the string obtained by replacing the leftmost
|
||||
non-overlapping occurrences of the pattern in string by the
|
||||
replacement repl. repl can be either a string or a callable;
|
||||
if a string, backslash escapes in it are processed. If it is
|
||||
a callable, it's passed the Match object and must return''')
|
||||
tiptest(p.sub, '''\
|
||||
(repl, string, count=0)
|
||||
Return the string obtained by replacing the leftmost \
|
||||
non-overlapping occurrences o...''')
|
||||
|
||||
def test_signature_wrap(self):
|
||||
if textwrap.TextWrapper.__doc__ is not None:
|
||||
self.assertEqual(get_spec(textwrap.TextWrapper), '''\
|
||||
(width=70, initial_indent='', subsequent_indent='', expand_tabs=True,
|
||||
replace_whitespace=True, fix_sentence_endings=False, break_long_words=True,
|
||||
drop_whitespace=True, break_on_hyphens=True, tabsize=8, *, max_lines=None,
|
||||
placeholder=' [...]')
|
||||
Object for wrapping/filling text. The public interface consists of
|
||||
the wrap() and fill() methods; the other methods are just there for
|
||||
subclasses to override in order to tweak the default behaviour.
|
||||
If you want to completely replace the main wrapping algorithm,
|
||||
you\'ll probably have to override _wrap_chunks().''')
|
||||
|
||||
def test_properly_formatted(self):
|
||||
|
||||
def foo(s='a'*100):
|
||||
pass
|
||||
|
||||
def bar(s='a'*100):
|
||||
"""Hello Guido"""
|
||||
pass
|
||||
|
||||
def baz(s='a'*100, z='b'*100):
|
||||
pass
|
||||
|
||||
indent = calltip._INDENT
|
||||
|
||||
sfoo = "(s='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"\
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + indent + "aaaaaaaaa"\
|
||||
"aaaaaaaaaa')"
|
||||
sbar = "(s='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"\
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + indent + "aaaaaaaaa"\
|
||||
"aaaaaaaaaa')\nHello Guido"
|
||||
sbaz = "(s='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"\
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + indent + "aaaaaaaaa"\
|
||||
"aaaaaaaaaa', z='bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"\
|
||||
"bbbbbbbbbbbbbbbbb\n" + indent + "bbbbbbbbbbbbbbbbbbbbbb"\
|
||||
"bbbbbbbbbbbbbbbbbbbbbb')"
|
||||
|
||||
for func,doc in [(foo, sfoo), (bar, sbar), (baz, sbaz)]:
|
||||
with self.subTest(func=func, doc=doc):
|
||||
self.assertEqual(get_spec(func), doc)
|
||||
|
||||
def test_docline_truncation(self):
|
||||
def f(): pass
|
||||
f.__doc__ = 'a'*300
|
||||
self.assertEqual(get_spec(f), f"()\n{'a'*(calltip._MAX_COLS-3) + '...'}")
|
||||
|
||||
@unittest.skipIf(MISSING_C_DOCSTRINGS,
|
||||
"Signature information for builtins requires docstrings")
|
||||
def test_multiline_docstring(self):
|
||||
# Test fewer lines than max.
|
||||
self.assertEqual(get_spec(range),
|
||||
"range(stop) -> range object\n"
|
||||
"range(start, stop[, step]) -> range object")
|
||||
|
||||
# Test max lines
|
||||
self.assertEqual(get_spec(bytes), '''\
|
||||
bytes(iterable_of_ints) -> bytes
|
||||
bytes(string, encoding[, errors]) -> bytes
|
||||
bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer
|
||||
bytes(int) -> bytes object of size given by the parameter initialized with null bytes
|
||||
bytes() -> empty bytes object''')
|
||||
|
||||
def test_multiline_docstring_2(self):
|
||||
# Test more than max lines
|
||||
def f(): pass
|
||||
f.__doc__ = 'a\n' * 15
|
||||
self.assertEqual(get_spec(f), '()' + '\na' * calltip._MAX_LINES)
|
||||
|
||||
def test_functions(self):
|
||||
def t1(): 'doc'
|
||||
t1.tip = "()"
|
||||
def t2(a, b=None): 'doc'
|
||||
t2.tip = "(a, b=None)"
|
||||
def t3(a, *args): 'doc'
|
||||
t3.tip = "(a, *args)"
|
||||
def t4(*args): 'doc'
|
||||
t4.tip = "(*args)"
|
||||
def t5(a, b=None, *args, **kw): 'doc'
|
||||
t5.tip = "(a, b=None, *args, **kw)"
|
||||
|
||||
doc = '\ndoc' if t1.__doc__ is not None else ''
|
||||
for func in (t1, t2, t3, t4, t5, TC):
|
||||
with self.subTest(func=func):
|
||||
self.assertEqual(get_spec(func), func.tip + doc)
|
||||
|
||||
def test_methods(self):
|
||||
doc = '\ndoc' if TC.__doc__ is not None else ''
|
||||
for meth in (TC.t1, TC.t2, TC.t3, TC.t4, TC.t5, TC.t6, TC.__call__):
|
||||
with self.subTest(meth=meth):
|
||||
self.assertEqual(get_spec(meth), meth.tip + doc)
|
||||
self.assertEqual(get_spec(TC.cm), "(a)" + doc)
|
||||
self.assertEqual(get_spec(TC.sm), "(b)" + doc)
|
||||
|
||||
def test_bound_methods(self):
|
||||
# test that first parameter is correctly removed from argspec
|
||||
doc = '\ndoc' if TC.__doc__ is not None else ''
|
||||
for meth, mtip in ((tc.t1, "()"), (tc.t4, "(*args)"),
|
||||
(tc.t6, "(self)"), (tc.__call__, '(ci)'),
|
||||
(tc, '(ci)'), (TC.cm, "(a)"),):
|
||||
with self.subTest(meth=meth, mtip=mtip):
|
||||
self.assertEqual(get_spec(meth), mtip + doc)
|
||||
|
||||
def test_starred_parameter(self):
|
||||
# test that starred first parameter is *not* removed from argspec
|
||||
class C:
|
||||
def m1(*args): pass
|
||||
c = C()
|
||||
for meth, mtip in ((C.m1, '(*args)'), (c.m1, "(*args)"),):
|
||||
with self.subTest(meth=meth, mtip=mtip):
|
||||
self.assertEqual(get_spec(meth), mtip)
|
||||
|
||||
def test_invalid_method_get_spec(self):
|
||||
class C:
|
||||
def m2(**kwargs): pass
|
||||
class Test:
|
||||
def __call__(*, a): pass
|
||||
|
||||
mtip = calltip._invalid_method
|
||||
self.assertEqual(get_spec(C().m2), mtip)
|
||||
self.assertEqual(get_spec(Test()), mtip)
|
||||
|
||||
def test_non_ascii_name(self):
|
||||
# test that re works to delete a first parameter name that
|
||||
# includes non-ascii chars, such as various forms of A.
|
||||
uni = "(A\u0391\u0410\u05d0\u0627\u0905\u1e00\u3042, a)"
|
||||
assert calltip._first_param.sub('', uni) == '(a)'
|
||||
|
||||
def test_no_docstring(self):
|
||||
for meth, mtip in ((TC.nd, "(self)"), (tc.nd, "()")):
|
||||
with self.subTest(meth=meth, mtip=mtip):
|
||||
self.assertEqual(get_spec(meth), mtip)
|
||||
|
||||
def test_buggy_getattr_class(self):
|
||||
class NoCall:
|
||||
def __getattr__(self, name): # Not invoked for class attribute.
|
||||
raise IndexError # Bug.
|
||||
class CallA(NoCall):
|
||||
def __call__(self, ci): # Bug does not matter.
|
||||
pass
|
||||
class CallB(NoCall):
|
||||
def __call__(oui, a, b, c): # Non-standard 'self'.
|
||||
pass
|
||||
|
||||
for meth, mtip in ((NoCall, default_tip), (CallA, default_tip),
|
||||
(NoCall(), ''), (CallA(), '(ci)'),
|
||||
(CallB(), '(a, b, c)')):
|
||||
with self.subTest(meth=meth, mtip=mtip):
|
||||
self.assertEqual(get_spec(meth), mtip)
|
||||
|
||||
def test_metaclass_class(self): # Failure case for issue 38689.
|
||||
class Type(type): # Type() requires 3 type args, returns class.
|
||||
__class__ = property({}.__getitem__, {}.__setitem__)
|
||||
class Object(metaclass=Type):
|
||||
__slots__ = '__class__'
|
||||
for meth, mtip in ((Type, get_spec(type)), (Object, default_tip),
|
||||
(Object(), '')):
|
||||
with self.subTest(meth=meth, mtip=mtip):
|
||||
self.assertEqual(get_spec(meth), mtip)
|
||||
|
||||
def test_non_callables(self):
|
||||
for obj in (0, 0.0, '0', b'0', [], {}):
|
||||
with self.subTest(obj=obj):
|
||||
self.assertEqual(get_spec(obj), '')
|
||||
|
||||
|
||||
class Get_entityTest(unittest.TestCase):
|
||||
def test_bad_entity(self):
|
||||
self.assertIsNone(calltip.get_entity('1/0'))
|
||||
def test_good_entity(self):
|
||||
self.assertIs(calltip.get_entity('int'), int)
|
||||
|
||||
|
||||
# Test the 9 Calltip methods.
|
||||
# open_calltip is about half the code; the others are fairly trivial.
|
||||
# The default mocks are what are needed for open_calltip.
|
||||
|
||||
class mock_Shell:
|
||||
"Return mock sufficient to pass to hyperparser."
|
||||
def __init__(self, text):
|
||||
text.tag_prevrange = Mock(return_value=None)
|
||||
self.text = text
|
||||
self.prompt_last_line = ">>> "
|
||||
self.indentwidth = 4
|
||||
self.tabwidth = 8
|
||||
|
||||
|
||||
class mock_TipWindow:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def showtip(self, text, parenleft, parenright):
|
||||
self.args = parenleft, parenright
|
||||
self.parenline, self.parencol = map(int, parenleft.split('.'))
|
||||
|
||||
|
||||
class WrappedCalltip(calltip.Calltip):
|
||||
def _make_tk_calltip_window(self):
|
||||
return mock_TipWindow()
|
||||
|
||||
def remove_calltip_window(self, event=None):
|
||||
if self.active_calltip: # Setup to None.
|
||||
self.active_calltip = None
|
||||
self.tips_removed += 1 # Setup to 0.
|
||||
|
||||
def fetch_tip(self, expression):
|
||||
return 'tip'
|
||||
|
||||
|
||||
class CalltipTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.text = Text()
|
||||
cls.ct = WrappedCalltip(mock_Shell(cls.text))
|
||||
|
||||
def setUp(self):
|
||||
self.text.delete('1.0', 'end') # Insert and call
|
||||
self.ct.active_calltip = None
|
||||
# Test .active_calltip, +args
|
||||
self.ct.tips_removed = 0
|
||||
|
||||
def open_close(self, testfunc):
|
||||
# Open-close template with testfunc called in between.
|
||||
opentip = self.ct.open_calltip
|
||||
self.text.insert(1.0, 'f(')
|
||||
opentip(False)
|
||||
self.tip = self.ct.active_calltip
|
||||
testfunc(self) ###
|
||||
self.text.insert('insert', ')')
|
||||
opentip(False)
|
||||
self.assertIsNone(self.ct.active_calltip, None)
|
||||
|
||||
def test_open_close(self):
|
||||
def args(self):
|
||||
self.assertEqual(self.tip.args, ('1.1', '1.end'))
|
||||
self.open_close(args)
|
||||
|
||||
def test_repeated_force(self):
|
||||
def force(self):
|
||||
for char in 'abc':
|
||||
self.text.insert('insert', 'a')
|
||||
self.ct.open_calltip(True)
|
||||
self.ct.open_calltip(True)
|
||||
self.assertIs(self.ct.active_calltip, self.tip)
|
||||
self.open_close(force)
|
||||
|
||||
def test_repeated_parens(self):
|
||||
def parens(self):
|
||||
for context in "a", "'":
|
||||
with self.subTest(context=context):
|
||||
self.text.insert('insert', context)
|
||||
for char in '(()())':
|
||||
self.text.insert('insert', char)
|
||||
self.assertIs(self.ct.active_calltip, self.tip)
|
||||
self.text.insert('insert', "'")
|
||||
self.open_close(parens)
|
||||
|
||||
def test_comment_parens(self):
|
||||
def comment(self):
|
||||
self.text.insert('insert', "# ")
|
||||
for char in '(()())':
|
||||
self.text.insert('insert', char)
|
||||
self.assertIs(self.ct.active_calltip, self.tip)
|
||||
self.text.insert('insert', "\n")
|
||||
self.open_close(comment)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
29
Dependencies/Python/Lib/idlelib/idle_test/test_calltip_w.py
vendored
Normal file
29
Dependencies/Python/Lib/idlelib/idle_test/test_calltip_w.py
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
"Test calltip_w, coverage 18%."
|
||||
|
||||
from idlelib import calltip_w
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk, Text
|
||||
|
||||
|
||||
class CallTipWindowTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.text = Text(cls.root)
|
||||
cls.calltip = calltip_w.CalltipWindow(cls.text)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.text, cls.root
|
||||
|
||||
def test_init(self):
|
||||
self.assertEqual(self.calltip.anchor_widget, self.text)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
455
Dependencies/Python/Lib/idlelib/idle_test/test_codecontext.py
vendored
Normal file
455
Dependencies/Python/Lib/idlelib/idle_test/test_codecontext.py
vendored
Normal file
@@ -0,0 +1,455 @@
|
||||
"Test codecontext, coverage 100%"
|
||||
|
||||
from idlelib import codecontext
|
||||
import unittest
|
||||
import unittest.mock
|
||||
from test.support import requires
|
||||
from tkinter import NSEW, Tk, Frame, Text, TclError
|
||||
|
||||
from unittest import mock
|
||||
import re
|
||||
from idlelib import config
|
||||
|
||||
|
||||
usercfg = codecontext.idleConf.userCfg
|
||||
testcfg = {
|
||||
'main': config.IdleUserConfParser(''),
|
||||
'highlight': config.IdleUserConfParser(''),
|
||||
'keys': config.IdleUserConfParser(''),
|
||||
'extensions': config.IdleUserConfParser(''),
|
||||
}
|
||||
code_sample = """\
|
||||
|
||||
class C1:
|
||||
# Class comment.
|
||||
def __init__(self, a, b):
|
||||
self.a = a
|
||||
self.b = b
|
||||
def compare(self):
|
||||
if a > b:
|
||||
return a
|
||||
elif a < b:
|
||||
return b
|
||||
else:
|
||||
return None
|
||||
"""
|
||||
|
||||
|
||||
class DummyEditwin:
|
||||
def __init__(self, root, frame, text):
|
||||
self.root = root
|
||||
self.top = root
|
||||
self.text_frame = frame
|
||||
self.text = text
|
||||
self.label = ''
|
||||
|
||||
def getlineno(self, index):
|
||||
return int(float(self.text.index(index)))
|
||||
|
||||
def update_menu_label(self, **kwargs):
|
||||
self.label = kwargs['label']
|
||||
|
||||
|
||||
class CodeContextTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
root = cls.root = Tk()
|
||||
root.withdraw()
|
||||
frame = cls.frame = Frame(root)
|
||||
text = cls.text = Text(frame)
|
||||
text.insert('1.0', code_sample)
|
||||
# Need to pack for creation of code context text widget.
|
||||
frame.pack(side='left', fill='both', expand=1)
|
||||
text.grid(row=1, column=1, sticky=NSEW)
|
||||
cls.editor = DummyEditwin(root, frame, text)
|
||||
codecontext.idleConf.userCfg = testcfg
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
codecontext.idleConf.userCfg = usercfg
|
||||
cls.editor.text.delete('1.0', 'end')
|
||||
del cls.editor, cls.frame, cls.text
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.text.yview(0)
|
||||
self.text['font'] = 'TkFixedFont'
|
||||
self.cc = codecontext.CodeContext(self.editor)
|
||||
|
||||
self.highlight_cfg = {"background": '#abcdef',
|
||||
"foreground": '#123456'}
|
||||
orig_idleConf_GetHighlight = codecontext.idleConf.GetHighlight
|
||||
def mock_idleconf_GetHighlight(theme, element):
|
||||
if element == 'context':
|
||||
return self.highlight_cfg
|
||||
return orig_idleConf_GetHighlight(theme, element)
|
||||
GetHighlight_patcher = unittest.mock.patch.object(
|
||||
codecontext.idleConf, 'GetHighlight', mock_idleconf_GetHighlight)
|
||||
GetHighlight_patcher.start()
|
||||
self.addCleanup(GetHighlight_patcher.stop)
|
||||
|
||||
self.font_override = 'TkFixedFont'
|
||||
def mock_idleconf_GetFont(root, configType, section):
|
||||
return self.font_override
|
||||
GetFont_patcher = unittest.mock.patch.object(
|
||||
codecontext.idleConf, 'GetFont', mock_idleconf_GetFont)
|
||||
GetFont_patcher.start()
|
||||
self.addCleanup(GetFont_patcher.stop)
|
||||
|
||||
def tearDown(self):
|
||||
if self.cc.context:
|
||||
self.cc.context.destroy()
|
||||
# Explicitly call __del__ to remove scheduled scripts.
|
||||
self.cc.__del__()
|
||||
del self.cc.context, self.cc
|
||||
|
||||
def test_init(self):
|
||||
eq = self.assertEqual
|
||||
ed = self.editor
|
||||
cc = self.cc
|
||||
|
||||
eq(cc.editwin, ed)
|
||||
eq(cc.text, ed.text)
|
||||
eq(cc.text['font'], ed.text['font'])
|
||||
self.assertIsNone(cc.context)
|
||||
eq(cc.info, [(0, -1, '', False)])
|
||||
eq(cc.topvisible, 1)
|
||||
self.assertIsNone(self.cc.t1)
|
||||
|
||||
def test_del(self):
|
||||
self.cc.__del__()
|
||||
|
||||
def test_del_with_timer(self):
|
||||
timer = self.cc.t1 = self.text.after(10000, lambda: None)
|
||||
self.cc.__del__()
|
||||
with self.assertRaises(TclError) as cm:
|
||||
self.root.tk.call('after', 'info', timer)
|
||||
self.assertIn("doesn't exist", str(cm.exception))
|
||||
|
||||
def test_reload(self):
|
||||
codecontext.CodeContext.reload()
|
||||
self.assertEqual(self.cc.context_depth, 15)
|
||||
|
||||
def test_toggle_code_context_event(self):
|
||||
eq = self.assertEqual
|
||||
cc = self.cc
|
||||
toggle = cc.toggle_code_context_event
|
||||
|
||||
# Make sure code context is off.
|
||||
if cc.context:
|
||||
toggle()
|
||||
|
||||
# Toggle on.
|
||||
toggle()
|
||||
self.assertIsNotNone(cc.context)
|
||||
eq(cc.context['font'], self.text['font'])
|
||||
eq(cc.context['fg'], self.highlight_cfg['foreground'])
|
||||
eq(cc.context['bg'], self.highlight_cfg['background'])
|
||||
eq(cc.context.get('1.0', 'end-1c'), '')
|
||||
eq(cc.editwin.label, 'Hide Code Context')
|
||||
eq(self.root.tk.call('after', 'info', self.cc.t1)[1], 'timer')
|
||||
|
||||
# Toggle off.
|
||||
toggle()
|
||||
self.assertIsNone(cc.context)
|
||||
eq(cc.editwin.label, 'Show Code Context')
|
||||
self.assertIsNone(self.cc.t1)
|
||||
|
||||
# Scroll down and toggle back on.
|
||||
line11_context = '\n'.join(x[2] for x in cc.get_context(11)[0])
|
||||
cc.text.yview(11)
|
||||
toggle()
|
||||
eq(cc.context.get('1.0', 'end-1c'), line11_context)
|
||||
|
||||
# Toggle off and on again.
|
||||
toggle()
|
||||
toggle()
|
||||
eq(cc.context.get('1.0', 'end-1c'), line11_context)
|
||||
|
||||
def test_get_context(self):
|
||||
eq = self.assertEqual
|
||||
gc = self.cc.get_context
|
||||
|
||||
# stopline must be greater than 0.
|
||||
with self.assertRaises(AssertionError):
|
||||
gc(1, stopline=0)
|
||||
|
||||
eq(gc(3), ([(2, 0, 'class C1:', 'class')], 0))
|
||||
|
||||
# Don't return comment.
|
||||
eq(gc(4), ([(2, 0, 'class C1:', 'class')], 0))
|
||||
|
||||
# Two indentation levels and no comment.
|
||||
eq(gc(5), ([(2, 0, 'class C1:', 'class'),
|
||||
(4, 4, ' def __init__(self, a, b):', 'def')], 0))
|
||||
|
||||
# Only one 'def' is returned, not both at the same indent level.
|
||||
eq(gc(10), ([(2, 0, 'class C1:', 'class'),
|
||||
(7, 4, ' def compare(self):', 'def'),
|
||||
(8, 8, ' if a > b:', 'if')], 0))
|
||||
|
||||
# With 'elif', also show the 'if' even though it's at the same level.
|
||||
eq(gc(11), ([(2, 0, 'class C1:', 'class'),
|
||||
(7, 4, ' def compare(self):', 'def'),
|
||||
(8, 8, ' if a > b:', 'if'),
|
||||
(10, 8, ' elif a < b:', 'elif')], 0))
|
||||
|
||||
# Set stop_line to not go back to first line in source code.
|
||||
# Return includes stop_line.
|
||||
eq(gc(11, stopline=2), ([(2, 0, 'class C1:', 'class'),
|
||||
(7, 4, ' def compare(self):', 'def'),
|
||||
(8, 8, ' if a > b:', 'if'),
|
||||
(10, 8, ' elif a < b:', 'elif')], 0))
|
||||
eq(gc(11, stopline=3), ([(7, 4, ' def compare(self):', 'def'),
|
||||
(8, 8, ' if a > b:', 'if'),
|
||||
(10, 8, ' elif a < b:', 'elif')], 4))
|
||||
eq(gc(11, stopline=8), ([(8, 8, ' if a > b:', 'if'),
|
||||
(10, 8, ' elif a < b:', 'elif')], 8))
|
||||
|
||||
# Set stop_indent to test indent level to stop at.
|
||||
eq(gc(11, stopindent=4), ([(7, 4, ' def compare(self):', 'def'),
|
||||
(8, 8, ' if a > b:', 'if'),
|
||||
(10, 8, ' elif a < b:', 'elif')], 4))
|
||||
# Check that the 'if' is included.
|
||||
eq(gc(11, stopindent=8), ([(8, 8, ' if a > b:', 'if'),
|
||||
(10, 8, ' elif a < b:', 'elif')], 8))
|
||||
|
||||
def test_update_code_context(self):
|
||||
eq = self.assertEqual
|
||||
cc = self.cc
|
||||
# Ensure code context is active.
|
||||
if not cc.context:
|
||||
cc.toggle_code_context_event()
|
||||
|
||||
# Invoke update_code_context without scrolling - nothing happens.
|
||||
self.assertIsNone(cc.update_code_context())
|
||||
eq(cc.info, [(0, -1, '', False)])
|
||||
eq(cc.topvisible, 1)
|
||||
|
||||
# Scroll down to line 1.
|
||||
cc.text.yview(1)
|
||||
cc.update_code_context()
|
||||
eq(cc.info, [(0, -1, '', False)])
|
||||
eq(cc.topvisible, 2)
|
||||
eq(cc.context.get('1.0', 'end-1c'), '')
|
||||
|
||||
# Scroll down to line 2.
|
||||
cc.text.yview(2)
|
||||
cc.update_code_context()
|
||||
eq(cc.info, [(0, -1, '', False), (2, 0, 'class C1:', 'class')])
|
||||
eq(cc.topvisible, 3)
|
||||
eq(cc.context.get('1.0', 'end-1c'), 'class C1:')
|
||||
|
||||
# Scroll down to line 3. Since it's a comment, nothing changes.
|
||||
cc.text.yview(3)
|
||||
cc.update_code_context()
|
||||
eq(cc.info, [(0, -1, '', False), (2, 0, 'class C1:', 'class')])
|
||||
eq(cc.topvisible, 4)
|
||||
eq(cc.context.get('1.0', 'end-1c'), 'class C1:')
|
||||
|
||||
# Scroll down to line 4.
|
||||
cc.text.yview(4)
|
||||
cc.update_code_context()
|
||||
eq(cc.info, [(0, -1, '', False),
|
||||
(2, 0, 'class C1:', 'class'),
|
||||
(4, 4, ' def __init__(self, a, b):', 'def')])
|
||||
eq(cc.topvisible, 5)
|
||||
eq(cc.context.get('1.0', 'end-1c'), 'class C1:\n'
|
||||
' def __init__(self, a, b):')
|
||||
|
||||
# Scroll down to line 11. Last 'def' is removed.
|
||||
cc.text.yview(11)
|
||||
cc.update_code_context()
|
||||
eq(cc.info, [(0, -1, '', False),
|
||||
(2, 0, 'class C1:', 'class'),
|
||||
(7, 4, ' def compare(self):', 'def'),
|
||||
(8, 8, ' if a > b:', 'if'),
|
||||
(10, 8, ' elif a < b:', 'elif')])
|
||||
eq(cc.topvisible, 12)
|
||||
eq(cc.context.get('1.0', 'end-1c'), 'class C1:\n'
|
||||
' def compare(self):\n'
|
||||
' if a > b:\n'
|
||||
' elif a < b:')
|
||||
|
||||
# No scroll. No update, even though context_depth changed.
|
||||
cc.update_code_context()
|
||||
cc.context_depth = 1
|
||||
eq(cc.info, [(0, -1, '', False),
|
||||
(2, 0, 'class C1:', 'class'),
|
||||
(7, 4, ' def compare(self):', 'def'),
|
||||
(8, 8, ' if a > b:', 'if'),
|
||||
(10, 8, ' elif a < b:', 'elif')])
|
||||
eq(cc.topvisible, 12)
|
||||
eq(cc.context.get('1.0', 'end-1c'), 'class C1:\n'
|
||||
' def compare(self):\n'
|
||||
' if a > b:\n'
|
||||
' elif a < b:')
|
||||
|
||||
# Scroll up.
|
||||
cc.text.yview(5)
|
||||
cc.update_code_context()
|
||||
eq(cc.info, [(0, -1, '', False),
|
||||
(2, 0, 'class C1:', 'class'),
|
||||
(4, 4, ' def __init__(self, a, b):', 'def')])
|
||||
eq(cc.topvisible, 6)
|
||||
# context_depth is 1.
|
||||
eq(cc.context.get('1.0', 'end-1c'), ' def __init__(self, a, b):')
|
||||
|
||||
def test_jumptoline(self):
|
||||
eq = self.assertEqual
|
||||
cc = self.cc
|
||||
jump = cc.jumptoline
|
||||
|
||||
if not cc.context:
|
||||
cc.toggle_code_context_event()
|
||||
|
||||
# Empty context.
|
||||
cc.text.yview('2.0')
|
||||
cc.update_code_context()
|
||||
eq(cc.topvisible, 2)
|
||||
cc.context.mark_set('insert', '1.5')
|
||||
jump()
|
||||
eq(cc.topvisible, 1)
|
||||
|
||||
# 4 lines of context showing.
|
||||
cc.text.yview('12.0')
|
||||
cc.update_code_context()
|
||||
eq(cc.topvisible, 12)
|
||||
cc.context.mark_set('insert', '3.0')
|
||||
jump()
|
||||
eq(cc.topvisible, 8)
|
||||
|
||||
# More context lines than limit.
|
||||
cc.context_depth = 2
|
||||
cc.text.yview('12.0')
|
||||
cc.update_code_context()
|
||||
eq(cc.topvisible, 12)
|
||||
cc.context.mark_set('insert', '1.0')
|
||||
jump()
|
||||
eq(cc.topvisible, 8)
|
||||
|
||||
# Context selection stops jump.
|
||||
cc.text.yview('5.0')
|
||||
cc.update_code_context()
|
||||
cc.context.tag_add('sel', '1.0', '2.0')
|
||||
cc.context.mark_set('insert', '1.0')
|
||||
jump() # Without selection, to line 2.
|
||||
eq(cc.topvisible, 5)
|
||||
|
||||
@mock.patch.object(codecontext.CodeContext, 'update_code_context')
|
||||
def test_timer_event(self, mock_update):
|
||||
# Ensure code context is not active.
|
||||
if self.cc.context:
|
||||
self.cc.toggle_code_context_event()
|
||||
self.cc.timer_event()
|
||||
mock_update.assert_not_called()
|
||||
|
||||
# Activate code context.
|
||||
self.cc.toggle_code_context_event()
|
||||
self.cc.timer_event()
|
||||
mock_update.assert_called()
|
||||
|
||||
def test_font(self):
|
||||
eq = self.assertEqual
|
||||
cc = self.cc
|
||||
|
||||
orig_font = cc.text['font']
|
||||
test_font = 'TkTextFont'
|
||||
self.assertNotEqual(orig_font, test_font)
|
||||
|
||||
# Ensure code context is not active.
|
||||
if cc.context is not None:
|
||||
cc.toggle_code_context_event()
|
||||
|
||||
self.font_override = test_font
|
||||
# Nothing breaks or changes with inactive code context.
|
||||
cc.update_font()
|
||||
|
||||
# Activate code context, previous font change is immediately effective.
|
||||
cc.toggle_code_context_event()
|
||||
eq(cc.context['font'], test_font)
|
||||
|
||||
# Call the font update, change is picked up.
|
||||
self.font_override = orig_font
|
||||
cc.update_font()
|
||||
eq(cc.context['font'], orig_font)
|
||||
|
||||
def test_highlight_colors(self):
|
||||
eq = self.assertEqual
|
||||
cc = self.cc
|
||||
|
||||
orig_colors = dict(self.highlight_cfg)
|
||||
test_colors = {'background': '#222222', 'foreground': '#ffff00'}
|
||||
|
||||
def assert_colors_are_equal(colors):
|
||||
eq(cc.context['background'], colors['background'])
|
||||
eq(cc.context['foreground'], colors['foreground'])
|
||||
|
||||
# Ensure code context is not active.
|
||||
if cc.context:
|
||||
cc.toggle_code_context_event()
|
||||
|
||||
self.highlight_cfg = test_colors
|
||||
# Nothing breaks with inactive code context.
|
||||
cc.update_highlight_colors()
|
||||
|
||||
# Activate code context, previous colors change is immediately effective.
|
||||
cc.toggle_code_context_event()
|
||||
assert_colors_are_equal(test_colors)
|
||||
|
||||
# Call colors update with no change to the configured colors.
|
||||
cc.update_highlight_colors()
|
||||
assert_colors_are_equal(test_colors)
|
||||
|
||||
# Call the colors update with code context active, change is picked up.
|
||||
self.highlight_cfg = orig_colors
|
||||
cc.update_highlight_colors()
|
||||
assert_colors_are_equal(orig_colors)
|
||||
|
||||
|
||||
class HelperFunctionText(unittest.TestCase):
|
||||
|
||||
def test_get_spaces_firstword(self):
|
||||
get = codecontext.get_spaces_firstword
|
||||
test_lines = (
|
||||
(' first word', (' ', 'first')),
|
||||
('\tfirst word', ('\t', 'first')),
|
||||
(' \u19D4\u19D2: ', (' ', '\u19D4\u19D2')),
|
||||
('no spaces', ('', 'no')),
|
||||
('', ('', '')),
|
||||
('# TEST COMMENT', ('', '')),
|
||||
(' (continuation)', (' ', ''))
|
||||
)
|
||||
for line, expected_output in test_lines:
|
||||
self.assertEqual(get(line), expected_output)
|
||||
|
||||
# Send the pattern in the call.
|
||||
self.assertEqual(get(' (continuation)',
|
||||
c=re.compile(r'^(\s*)([^\s]*)')),
|
||||
(' ', '(continuation)'))
|
||||
|
||||
def test_get_line_info(self):
|
||||
eq = self.assertEqual
|
||||
gli = codecontext.get_line_info
|
||||
lines = code_sample.splitlines()
|
||||
|
||||
# Line 1 is not a BLOCKOPENER.
|
||||
eq(gli(lines[0]), (codecontext.INFINITY, '', False))
|
||||
# Line 2 is a BLOCKOPENER without an indent.
|
||||
eq(gli(lines[1]), (0, 'class C1:', 'class'))
|
||||
# Line 3 is not a BLOCKOPENER and does not return the indent level.
|
||||
eq(gli(lines[2]), (codecontext.INFINITY, ' # Class comment.', False))
|
||||
# Line 4 is a BLOCKOPENER and is indented.
|
||||
eq(gli(lines[3]), (4, ' def __init__(self, a, b):', 'def'))
|
||||
# Line 8 is a different BLOCKOPENER and is indented.
|
||||
eq(gli(lines[7]), (8, ' if a > b:', 'if'))
|
||||
# Test tab.
|
||||
eq(gli('\tif a == b:'), (1, '\tif a == b:', 'if'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
622
Dependencies/Python/Lib/idlelib/idle_test/test_colorizer.py
vendored
Normal file
622
Dependencies/Python/Lib/idlelib/idle_test/test_colorizer.py
vendored
Normal file
@@ -0,0 +1,622 @@
|
||||
"Test colorizer, coverage 99%."
|
||||
from idlelib import colorizer
|
||||
from test.support import requires
|
||||
import unittest
|
||||
from unittest import mock
|
||||
from idlelib.idle_test.tkinter_testing_utils import run_in_tk_mainloop
|
||||
|
||||
from functools import partial
|
||||
import textwrap
|
||||
from tkinter import Tk, Text
|
||||
from idlelib import config
|
||||
from idlelib.percolator import Percolator
|
||||
|
||||
|
||||
usercfg = colorizer.idleConf.userCfg
|
||||
testcfg = {
|
||||
'main': config.IdleUserConfParser(''),
|
||||
'highlight': config.IdleUserConfParser(''),
|
||||
'keys': config.IdleUserConfParser(''),
|
||||
'extensions': config.IdleUserConfParser(''),
|
||||
}
|
||||
|
||||
source = textwrap.dedent("""\
|
||||
if True: int ('1') # keyword, builtin, string, comment
|
||||
elif False: print(0) # 'string' in comment
|
||||
else: float(None) # if in comment
|
||||
if iF + If + IF: 'keyword matching must respect case'
|
||||
if'': x or'' # valid keyword-string no-space combinations
|
||||
async def f(): await g()
|
||||
# Strings should be entirely colored, including quotes.
|
||||
'x', '''x''', "x", \"""x\"""
|
||||
'abc\\
|
||||
def'
|
||||
'''abc\\
|
||||
def'''
|
||||
# All valid prefixes for unicode and byte strings should be colored.
|
||||
r'x', u'x', R'x', U'x', f'x', F'x'
|
||||
fr'x', Fr'x', fR'x', FR'x', rf'x', rF'x', Rf'x', RF'x'
|
||||
b'x',B'x', br'x',Br'x',bR'x',BR'x', rb'x', rB'x',Rb'x',RB'x'
|
||||
# Invalid combinations of legal characters should be half colored.
|
||||
ur'x', ru'x', uf'x', fu'x', UR'x', ufr'x', rfu'x', xf'x', fx'x'
|
||||
match point:
|
||||
case (x, 0) as _:
|
||||
print(f"X={x}")
|
||||
case [_, [_], "_",
|
||||
_]:
|
||||
pass
|
||||
case _ if ("a" if _ else set()): pass
|
||||
case _:
|
||||
raise ValueError("Not a point _")
|
||||
'''
|
||||
case _:'''
|
||||
"match x:"
|
||||
""")
|
||||
|
||||
|
||||
def setUpModule():
|
||||
colorizer.idleConf.userCfg = testcfg
|
||||
|
||||
|
||||
def tearDownModule():
|
||||
colorizer.idleConf.userCfg = usercfg
|
||||
|
||||
|
||||
class FunctionTest(unittest.TestCase):
|
||||
|
||||
def test_any(self):
|
||||
self.assertEqual(colorizer.any('test', ('a', 'b', 'cd')),
|
||||
'(?P<test>a|b|cd)')
|
||||
|
||||
def test_make_pat(self):
|
||||
# Tested in more detail by testing prog.
|
||||
self.assertTrue(colorizer.make_pat())
|
||||
|
||||
def test_prog(self):
|
||||
prog = colorizer.prog
|
||||
eq = self.assertEqual
|
||||
line = 'def f():\n print("hello")\n'
|
||||
m = prog.search(line)
|
||||
eq(m.groupdict()['KEYWORD'], 'def')
|
||||
m = prog.search(line, m.end())
|
||||
eq(m.groupdict()['SYNC'], '\n')
|
||||
m = prog.search(line, m.end())
|
||||
eq(m.groupdict()['BUILTIN'], 'print')
|
||||
m = prog.search(line, m.end())
|
||||
eq(m.groupdict()['STRING'], '"hello"')
|
||||
m = prog.search(line, m.end())
|
||||
eq(m.groupdict()['SYNC'], '\n')
|
||||
|
||||
def test_idprog(self):
|
||||
idprog = colorizer.idprog
|
||||
m = idprog.match('nospace')
|
||||
self.assertIsNone(m)
|
||||
m = idprog.match(' space')
|
||||
self.assertEqual(m.group(0), ' space')
|
||||
|
||||
|
||||
class ColorConfigTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
root = cls.root = Tk()
|
||||
root.withdraw()
|
||||
cls.text = Text(root)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_color_config(self):
|
||||
text = self.text
|
||||
eq = self.assertEqual
|
||||
colorizer.color_config(text)
|
||||
# Uses IDLE Classic theme as default.
|
||||
eq(text['background'], '#ffffff')
|
||||
eq(text['foreground'], '#000000')
|
||||
eq(text['selectbackground'], 'gray')
|
||||
eq(text['selectforeground'], '#000000')
|
||||
eq(text['insertbackground'], 'black')
|
||||
eq(text['inactiveselectbackground'], 'gray')
|
||||
|
||||
|
||||
class ColorDelegatorInstantiationTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
root = cls.root = Tk()
|
||||
root.withdraw()
|
||||
cls.text = Text(root)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.color = colorizer.ColorDelegator()
|
||||
|
||||
def tearDown(self):
|
||||
self.color.close()
|
||||
self.text.delete('1.0', 'end')
|
||||
self.color.resetcache()
|
||||
del self.color
|
||||
|
||||
def test_init(self):
|
||||
color = self.color
|
||||
self.assertIsInstance(color, colorizer.ColorDelegator)
|
||||
|
||||
def test_init_state(self):
|
||||
# init_state() is called during the instantiation of
|
||||
# ColorDelegator in setUp().
|
||||
color = self.color
|
||||
self.assertIsNone(color.after_id)
|
||||
self.assertTrue(color.allow_colorizing)
|
||||
self.assertFalse(color.colorizing)
|
||||
self.assertFalse(color.stop_colorizing)
|
||||
|
||||
|
||||
class ColorDelegatorTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
root = cls.root = Tk()
|
||||
root.withdraw()
|
||||
text = cls.text = Text(root)
|
||||
cls.percolator = Percolator(text)
|
||||
# Delegator stack = [Delegator(text)]
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.percolator.close()
|
||||
del cls.percolator, cls.text
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.color = colorizer.ColorDelegator()
|
||||
self.percolator.insertfilter(self.color)
|
||||
# Calls color.setdelegate(Delegator(text)).
|
||||
|
||||
def tearDown(self):
|
||||
self.color.close()
|
||||
self.percolator.removefilter(self.color)
|
||||
self.text.delete('1.0', 'end')
|
||||
self.color.resetcache()
|
||||
del self.color
|
||||
|
||||
def test_setdelegate(self):
|
||||
# Called in setUp when filter is attached to percolator.
|
||||
color = self.color
|
||||
self.assertIsInstance(color.delegate, colorizer.Delegator)
|
||||
# It is too late to mock notify_range, so test side effect.
|
||||
self.assertEqual(self.root.tk.call(
|
||||
'after', 'info', color.after_id)[1], 'timer')
|
||||
|
||||
def test_LoadTagDefs(self):
|
||||
highlight = partial(config.idleConf.GetHighlight, theme='IDLE Classic')
|
||||
for tag, colors in self.color.tagdefs.items():
|
||||
with self.subTest(tag=tag):
|
||||
self.assertIn('background', colors)
|
||||
self.assertIn('foreground', colors)
|
||||
if tag not in ('SYNC', 'TODO'):
|
||||
self.assertEqual(colors, highlight(element=tag.lower()))
|
||||
|
||||
def test_config_colors(self):
|
||||
text = self.text
|
||||
highlight = partial(config.idleConf.GetHighlight, theme='IDLE Classic')
|
||||
for tag in self.color.tagdefs:
|
||||
for plane in ('background', 'foreground'):
|
||||
with self.subTest(tag=tag, plane=plane):
|
||||
if tag in ('SYNC', 'TODO'):
|
||||
self.assertEqual(text.tag_cget(tag, plane), '')
|
||||
else:
|
||||
self.assertEqual(text.tag_cget(tag, plane),
|
||||
highlight(element=tag.lower())[plane])
|
||||
# 'sel' is marked as the highest priority.
|
||||
self.assertEqual(text.tag_names()[-1], 'sel')
|
||||
|
||||
@mock.patch.object(colorizer.ColorDelegator, 'notify_range')
|
||||
def test_insert(self, mock_notify):
|
||||
text = self.text
|
||||
# Initial text.
|
||||
text.insert('insert', 'foo')
|
||||
self.assertEqual(text.get('1.0', 'end'), 'foo\n')
|
||||
mock_notify.assert_called_with('1.0', '1.0+3c')
|
||||
# Additional text.
|
||||
text.insert('insert', 'barbaz')
|
||||
self.assertEqual(text.get('1.0', 'end'), 'foobarbaz\n')
|
||||
mock_notify.assert_called_with('1.3', '1.3+6c')
|
||||
|
||||
@mock.patch.object(colorizer.ColorDelegator, 'notify_range')
|
||||
def test_delete(self, mock_notify):
|
||||
text = self.text
|
||||
# Initialize text.
|
||||
text.insert('insert', 'abcdefghi')
|
||||
self.assertEqual(text.get('1.0', 'end'), 'abcdefghi\n')
|
||||
# Delete single character.
|
||||
text.delete('1.7')
|
||||
self.assertEqual(text.get('1.0', 'end'), 'abcdefgi\n')
|
||||
mock_notify.assert_called_with('1.7')
|
||||
# Delete multiple characters.
|
||||
text.delete('1.3', '1.6')
|
||||
self.assertEqual(text.get('1.0', 'end'), 'abcgi\n')
|
||||
mock_notify.assert_called_with('1.3')
|
||||
|
||||
def test_notify_range(self):
|
||||
text = self.text
|
||||
color = self.color
|
||||
eq = self.assertEqual
|
||||
|
||||
# Colorizing already scheduled.
|
||||
save_id = color.after_id
|
||||
eq(self.root.tk.call('after', 'info', save_id)[1], 'timer')
|
||||
self.assertFalse(color.colorizing)
|
||||
self.assertFalse(color.stop_colorizing)
|
||||
self.assertTrue(color.allow_colorizing)
|
||||
|
||||
# Coloring scheduled and colorizing in progress.
|
||||
color.colorizing = True
|
||||
color.notify_range('1.0', 'end')
|
||||
self.assertFalse(color.stop_colorizing)
|
||||
eq(color.after_id, save_id)
|
||||
|
||||
# No colorizing scheduled and colorizing in progress.
|
||||
text.after_cancel(save_id)
|
||||
color.after_id = None
|
||||
color.notify_range('1.0', '1.0+3c')
|
||||
self.assertTrue(color.stop_colorizing)
|
||||
self.assertIsNotNone(color.after_id)
|
||||
eq(self.root.tk.call('after', 'info', color.after_id)[1], 'timer')
|
||||
# New event scheduled.
|
||||
self.assertNotEqual(color.after_id, save_id)
|
||||
|
||||
# No colorizing scheduled and colorizing off.
|
||||
text.after_cancel(color.after_id)
|
||||
color.after_id = None
|
||||
color.allow_colorizing = False
|
||||
color.notify_range('1.4', '1.4+10c')
|
||||
# Nothing scheduled when colorizing is off.
|
||||
self.assertIsNone(color.after_id)
|
||||
|
||||
def test_toggle_colorize_event(self):
|
||||
color = self.color
|
||||
eq = self.assertEqual
|
||||
|
||||
# Starts with colorizing allowed and scheduled.
|
||||
self.assertFalse(color.colorizing)
|
||||
self.assertFalse(color.stop_colorizing)
|
||||
self.assertTrue(color.allow_colorizing)
|
||||
eq(self.root.tk.call('after', 'info', color.after_id)[1], 'timer')
|
||||
|
||||
# Toggle colorizing off.
|
||||
color.toggle_colorize_event()
|
||||
self.assertIsNone(color.after_id)
|
||||
self.assertFalse(color.colorizing)
|
||||
self.assertFalse(color.stop_colorizing)
|
||||
self.assertFalse(color.allow_colorizing)
|
||||
|
||||
# Toggle on while colorizing in progress (doesn't add timer).
|
||||
color.colorizing = True
|
||||
color.toggle_colorize_event()
|
||||
self.assertIsNone(color.after_id)
|
||||
self.assertTrue(color.colorizing)
|
||||
self.assertFalse(color.stop_colorizing)
|
||||
self.assertTrue(color.allow_colorizing)
|
||||
|
||||
# Toggle off while colorizing in progress.
|
||||
color.toggle_colorize_event()
|
||||
self.assertIsNone(color.after_id)
|
||||
self.assertTrue(color.colorizing)
|
||||
self.assertTrue(color.stop_colorizing)
|
||||
self.assertFalse(color.allow_colorizing)
|
||||
|
||||
# Toggle on while colorizing not in progress.
|
||||
color.colorizing = False
|
||||
color.toggle_colorize_event()
|
||||
eq(self.root.tk.call('after', 'info', color.after_id)[1], 'timer')
|
||||
self.assertFalse(color.colorizing)
|
||||
self.assertTrue(color.stop_colorizing)
|
||||
self.assertTrue(color.allow_colorizing)
|
||||
|
||||
@mock.patch.object(colorizer.ColorDelegator, 'recolorize_main')
|
||||
def test_recolorize(self, mock_recmain):
|
||||
text = self.text
|
||||
color = self.color
|
||||
eq = self.assertEqual
|
||||
# Call recolorize manually and not scheduled.
|
||||
text.after_cancel(color.after_id)
|
||||
|
||||
# No delegate.
|
||||
save_delegate = color.delegate
|
||||
color.delegate = None
|
||||
color.recolorize()
|
||||
mock_recmain.assert_not_called()
|
||||
color.delegate = save_delegate
|
||||
|
||||
# Toggle off colorizing.
|
||||
color.allow_colorizing = False
|
||||
color.recolorize()
|
||||
mock_recmain.assert_not_called()
|
||||
color.allow_colorizing = True
|
||||
|
||||
# Colorizing in progress.
|
||||
color.colorizing = True
|
||||
color.recolorize()
|
||||
mock_recmain.assert_not_called()
|
||||
color.colorizing = False
|
||||
|
||||
# Colorizing is done, but not completed, so rescheduled.
|
||||
color.recolorize()
|
||||
self.assertFalse(color.stop_colorizing)
|
||||
self.assertFalse(color.colorizing)
|
||||
mock_recmain.assert_called()
|
||||
eq(mock_recmain.call_count, 1)
|
||||
# Rescheduled when TODO tag still exists.
|
||||
eq(self.root.tk.call('after', 'info', color.after_id)[1], 'timer')
|
||||
|
||||
# No changes to text, so no scheduling added.
|
||||
text.tag_remove('TODO', '1.0', 'end')
|
||||
color.recolorize()
|
||||
self.assertFalse(color.stop_colorizing)
|
||||
self.assertFalse(color.colorizing)
|
||||
mock_recmain.assert_called()
|
||||
eq(mock_recmain.call_count, 2)
|
||||
self.assertIsNone(color.after_id)
|
||||
|
||||
@mock.patch.object(colorizer.ColorDelegator, 'notify_range')
|
||||
def test_recolorize_main(self, mock_notify):
|
||||
text = self.text
|
||||
color = self.color
|
||||
eq = self.assertEqual
|
||||
|
||||
text.insert('insert', source)
|
||||
expected = (('1.0', ('KEYWORD',)), ('1.2', ()), ('1.3', ('KEYWORD',)),
|
||||
('1.7', ()), ('1.9', ('BUILTIN',)), ('1.14', ('STRING',)),
|
||||
('1.19', ('COMMENT',)),
|
||||
('2.1', ('KEYWORD',)), ('2.18', ()), ('2.25', ('COMMENT',)),
|
||||
('3.6', ('BUILTIN',)), ('3.12', ('KEYWORD',)), ('3.21', ('COMMENT',)),
|
||||
('4.0', ('KEYWORD',)), ('4.3', ()), ('4.6', ()),
|
||||
('5.2', ('STRING',)), ('5.8', ('KEYWORD',)), ('5.10', ('STRING',)),
|
||||
('6.0', ('KEYWORD',)), ('6.10', ('DEFINITION',)), ('6.11', ()),
|
||||
('8.0', ('STRING',)), ('8.4', ()), ('8.5', ('STRING',)),
|
||||
('8.12', ()), ('8.14', ('STRING',)),
|
||||
('19.0', ('KEYWORD',)),
|
||||
('20.4', ('KEYWORD',)), ('20.16', ('KEYWORD',)),# ('20.19', ('KEYWORD',)),
|
||||
#('22.4', ('KEYWORD',)), ('22.10', ('KEYWORD',)), ('22.14', ('KEYWORD',)), ('22.19', ('STRING',)),
|
||||
#('23.12', ('KEYWORD',)),
|
||||
('24.8', ('KEYWORD',)),
|
||||
('25.4', ('KEYWORD',)), ('25.9', ('KEYWORD',)),
|
||||
('25.11', ('KEYWORD',)), ('25.15', ('STRING',)),
|
||||
('25.19', ('KEYWORD',)), ('25.22', ()),
|
||||
('25.24', ('KEYWORD',)), ('25.29', ('BUILTIN',)), ('25.37', ('KEYWORD',)),
|
||||
('26.4', ('KEYWORD',)), ('26.9', ('KEYWORD',)),# ('26.11', ('KEYWORD',)), ('26.14', (),),
|
||||
('27.25', ('STRING',)), ('27.38', ('STRING',)),
|
||||
('29.0', ('STRING',)),
|
||||
('30.1', ('STRING',)),
|
||||
# SYNC at the end of every line.
|
||||
('1.55', ('SYNC',)), ('2.50', ('SYNC',)), ('3.34', ('SYNC',)),
|
||||
)
|
||||
|
||||
# Nothing marked to do therefore no tags in text.
|
||||
text.tag_remove('TODO', '1.0', 'end')
|
||||
color.recolorize_main()
|
||||
for tag in text.tag_names():
|
||||
with self.subTest(tag=tag):
|
||||
eq(text.tag_ranges(tag), ())
|
||||
|
||||
# Source marked for processing.
|
||||
text.tag_add('TODO', '1.0', 'end')
|
||||
# Check some indexes.
|
||||
color.recolorize_main()
|
||||
for index, expected_tags in expected:
|
||||
with self.subTest(index=index):
|
||||
eq(text.tag_names(index), expected_tags)
|
||||
|
||||
# Check for some tags for ranges.
|
||||
eq(text.tag_nextrange('TODO', '1.0'), ())
|
||||
eq(text.tag_nextrange('KEYWORD', '1.0'), ('1.0', '1.2'))
|
||||
eq(text.tag_nextrange('COMMENT', '2.0'), ('2.22', '2.43'))
|
||||
eq(text.tag_nextrange('SYNC', '2.0'), ('2.43', '3.0'))
|
||||
eq(text.tag_nextrange('STRING', '2.0'), ('4.17', '4.53'))
|
||||
eq(text.tag_nextrange('STRING', '8.0'), ('8.0', '8.3'))
|
||||
eq(text.tag_nextrange('STRING', '8.3'), ('8.5', '8.12'))
|
||||
eq(text.tag_nextrange('STRING', '8.12'), ('8.14', '8.17'))
|
||||
eq(text.tag_nextrange('STRING', '8.17'), ('8.19', '8.26'))
|
||||
eq(text.tag_nextrange('SYNC', '8.0'), ('8.26', '9.0'))
|
||||
eq(text.tag_nextrange('SYNC', '30.0'), ('30.10', '32.0'))
|
||||
|
||||
def _assert_highlighting(self, source, tag_ranges):
|
||||
"""Check highlighting of a given piece of code.
|
||||
|
||||
This inserts just this code into the Text widget. It will then
|
||||
check that the resulting highlighting tag ranges exactly match
|
||||
those described in the given `tag_ranges` dict.
|
||||
|
||||
Note that the irrelevant tags 'sel', 'TODO' and 'SYNC' are
|
||||
ignored.
|
||||
"""
|
||||
text = self.text
|
||||
|
||||
with mock.patch.object(colorizer.ColorDelegator, 'notify_range'):
|
||||
text.delete('1.0', 'end-1c')
|
||||
text.insert('insert', source)
|
||||
text.tag_add('TODO', '1.0', 'end-1c')
|
||||
self.color.recolorize_main()
|
||||
|
||||
# Make a dict with highlighting tag ranges in the Text widget.
|
||||
text_tag_ranges = {}
|
||||
for tag in set(text.tag_names()) - {'sel', 'TODO', 'SYNC'}:
|
||||
indexes = [rng.string for rng in text.tag_ranges(tag)]
|
||||
for index_pair in zip(indexes[::2], indexes[1::2]):
|
||||
text_tag_ranges.setdefault(tag, []).append(index_pair)
|
||||
|
||||
self.assertEqual(text_tag_ranges, tag_ranges)
|
||||
|
||||
with mock.patch.object(colorizer.ColorDelegator, 'notify_range'):
|
||||
text.delete('1.0', 'end-1c')
|
||||
|
||||
def test_def_statement(self):
|
||||
# empty def
|
||||
self._assert_highlighting('def', {'KEYWORD': [('1.0', '1.3')]})
|
||||
|
||||
# def followed by identifier
|
||||
self._assert_highlighting('def foo:', {'KEYWORD': [('1.0', '1.3')],
|
||||
'DEFINITION': [('1.4', '1.7')]})
|
||||
|
||||
# def followed by partial identifier
|
||||
self._assert_highlighting('def fo', {'KEYWORD': [('1.0', '1.3')],
|
||||
'DEFINITION': [('1.4', '1.6')]})
|
||||
|
||||
# def followed by non-keyword
|
||||
self._assert_highlighting('def ++', {'KEYWORD': [('1.0', '1.3')]})
|
||||
|
||||
def test_match_soft_keyword(self):
|
||||
# empty match
|
||||
self._assert_highlighting('match', {'KEYWORD': [('1.0', '1.5')]})
|
||||
|
||||
# match followed by partial identifier
|
||||
self._assert_highlighting('match fo', {'KEYWORD': [('1.0', '1.5')]})
|
||||
|
||||
# match followed by identifier and colon
|
||||
self._assert_highlighting('match foo:', {'KEYWORD': [('1.0', '1.5')]})
|
||||
|
||||
# match followed by keyword
|
||||
self._assert_highlighting('match and', {'KEYWORD': [('1.6', '1.9')]})
|
||||
|
||||
# match followed by builtin with keyword prefix
|
||||
self._assert_highlighting('match int:', {'KEYWORD': [('1.0', '1.5')],
|
||||
'BUILTIN': [('1.6', '1.9')]})
|
||||
|
||||
# match followed by non-text operator
|
||||
self._assert_highlighting('match^', {})
|
||||
self._assert_highlighting('match @', {})
|
||||
|
||||
# match followed by colon
|
||||
self._assert_highlighting('match :', {})
|
||||
|
||||
# match followed by comma
|
||||
self._assert_highlighting('match\t,', {})
|
||||
|
||||
# match followed by a lone underscore
|
||||
self._assert_highlighting('match _:', {'KEYWORD': [('1.0', '1.5')]})
|
||||
|
||||
def test_case_soft_keyword(self):
|
||||
# empty case
|
||||
self._assert_highlighting('case', {'KEYWORD': [('1.0', '1.4')]})
|
||||
|
||||
# case followed by partial identifier
|
||||
self._assert_highlighting('case fo', {'KEYWORD': [('1.0', '1.4')]})
|
||||
|
||||
# case followed by identifier and colon
|
||||
self._assert_highlighting('case foo:', {'KEYWORD': [('1.0', '1.4')]})
|
||||
|
||||
# case followed by keyword
|
||||
self._assert_highlighting('case and', {'KEYWORD': [('1.5', '1.8')]})
|
||||
|
||||
# case followed by builtin with keyword prefix
|
||||
self._assert_highlighting('case int:', {'KEYWORD': [('1.0', '1.4')],
|
||||
'BUILTIN': [('1.5', '1.8')]})
|
||||
|
||||
# case followed by non-text operator
|
||||
self._assert_highlighting('case^', {})
|
||||
self._assert_highlighting('case @', {})
|
||||
|
||||
# case followed by colon
|
||||
self._assert_highlighting('case :', {})
|
||||
|
||||
# case followed by comma
|
||||
self._assert_highlighting('case\t,', {})
|
||||
|
||||
# case followed by a lone underscore
|
||||
self._assert_highlighting('case _:', {'KEYWORD': [('1.0', '1.4'),
|
||||
('1.5', '1.6')]})
|
||||
|
||||
def test_long_multiline_string(self):
|
||||
source = textwrap.dedent('''\
|
||||
"""a
|
||||
b
|
||||
c
|
||||
d
|
||||
e"""
|
||||
''')
|
||||
self._assert_highlighting(source, {'STRING': [('1.0', '5.4')]})
|
||||
|
||||
@run_in_tk_mainloop(delay=50)
|
||||
def test_incremental_editing(self):
|
||||
text = self.text
|
||||
eq = self.assertEqual
|
||||
|
||||
# Simulate typing 'inte'. During this, the highlighting should
|
||||
# change from normal to keyword to builtin to normal.
|
||||
text.insert('insert', 'i')
|
||||
yield
|
||||
eq(text.tag_nextrange('BUILTIN', '1.0'), ())
|
||||
eq(text.tag_nextrange('KEYWORD', '1.0'), ())
|
||||
|
||||
text.insert('insert', 'n')
|
||||
yield
|
||||
eq(text.tag_nextrange('BUILTIN', '1.0'), ())
|
||||
eq(text.tag_nextrange('KEYWORD', '1.0'), ('1.0', '1.2'))
|
||||
|
||||
text.insert('insert', 't')
|
||||
yield
|
||||
eq(text.tag_nextrange('BUILTIN', '1.0'), ('1.0', '1.3'))
|
||||
eq(text.tag_nextrange('KEYWORD', '1.0'), ())
|
||||
|
||||
text.insert('insert', 'e')
|
||||
yield
|
||||
eq(text.tag_nextrange('BUILTIN', '1.0'), ())
|
||||
eq(text.tag_nextrange('KEYWORD', '1.0'), ())
|
||||
|
||||
# Simulate deleting three characters from the end of 'inte'.
|
||||
# During this, the highlighting should change from normal to
|
||||
# builtin to keyword to normal.
|
||||
text.delete('insert-1c', 'insert')
|
||||
yield
|
||||
eq(text.tag_nextrange('BUILTIN', '1.0'), ('1.0', '1.3'))
|
||||
eq(text.tag_nextrange('KEYWORD', '1.0'), ())
|
||||
|
||||
text.delete('insert-1c', 'insert')
|
||||
yield
|
||||
eq(text.tag_nextrange('BUILTIN', '1.0'), ())
|
||||
eq(text.tag_nextrange('KEYWORD', '1.0'), ('1.0', '1.2'))
|
||||
|
||||
text.delete('insert-1c', 'insert')
|
||||
yield
|
||||
eq(text.tag_nextrange('BUILTIN', '1.0'), ())
|
||||
eq(text.tag_nextrange('KEYWORD', '1.0'), ())
|
||||
|
||||
@mock.patch.object(colorizer.ColorDelegator, 'recolorize')
|
||||
@mock.patch.object(colorizer.ColorDelegator, 'notify_range')
|
||||
def test_removecolors(self, mock_notify, mock_recolorize):
|
||||
text = self.text
|
||||
color = self.color
|
||||
text.insert('insert', source)
|
||||
|
||||
color.recolorize_main()
|
||||
# recolorize_main doesn't add these tags.
|
||||
text.tag_add("ERROR", "1.0")
|
||||
text.tag_add("TODO", "1.0")
|
||||
text.tag_add("hit", "1.0")
|
||||
for tag in color.tagdefs:
|
||||
with self.subTest(tag=tag):
|
||||
self.assertNotEqual(text.tag_ranges(tag), ())
|
||||
|
||||
color.removecolors()
|
||||
for tag in color.tagdefs:
|
||||
with self.subTest(tag=tag):
|
||||
self.assertEqual(text.tag_ranges(tag), ())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
805
Dependencies/Python/Lib/idlelib/idle_test/test_config.py
vendored
Normal file
805
Dependencies/Python/Lib/idlelib/idle_test/test_config.py
vendored
Normal file
@@ -0,0 +1,805 @@
|
||||
"""Test config, coverage 93%.
|
||||
(100% for IdleConfParser, IdleUserConfParser*, ConfigChanges).
|
||||
* Exception is OSError clause in Save method.
|
||||
Much of IdleConf is also exercised by ConfigDialog and test_configdialog.
|
||||
"""
|
||||
from idlelib import config
|
||||
import sys
|
||||
import os
|
||||
import tempfile
|
||||
from test.support import captured_stderr, findfile
|
||||
import unittest
|
||||
from unittest import mock
|
||||
import idlelib
|
||||
from idlelib.idle_test.mock_idle import Func
|
||||
|
||||
# Tests should not depend on fortuitous user configurations.
|
||||
# They must not affect actual user .cfg files.
|
||||
# Replace user parsers with empty parsers that cannot be saved
|
||||
# due to getting '' as the filename when created.
|
||||
|
||||
idleConf = config.idleConf
|
||||
usercfg = idleConf.userCfg
|
||||
testcfg = {}
|
||||
usermain = testcfg['main'] = config.IdleUserConfParser('')
|
||||
userhigh = testcfg['highlight'] = config.IdleUserConfParser('')
|
||||
userkeys = testcfg['keys'] = config.IdleUserConfParser('')
|
||||
userextn = testcfg['extensions'] = config.IdleUserConfParser('')
|
||||
|
||||
def setUpModule():
|
||||
idleConf.userCfg = testcfg
|
||||
idlelib.testing = True
|
||||
|
||||
def tearDownModule():
|
||||
idleConf.userCfg = usercfg
|
||||
idlelib.testing = False
|
||||
|
||||
|
||||
class IdleConfParserTest(unittest.TestCase):
|
||||
"""Test that IdleConfParser works"""
|
||||
|
||||
config = """
|
||||
[one]
|
||||
one = false
|
||||
two = true
|
||||
three = 10
|
||||
|
||||
[two]
|
||||
one = a string
|
||||
two = true
|
||||
three = false
|
||||
"""
|
||||
|
||||
def test_get(self):
|
||||
parser = config.IdleConfParser('')
|
||||
parser.read_string(self.config)
|
||||
eq = self.assertEqual
|
||||
|
||||
# Test with type argument.
|
||||
self.assertIs(parser.Get('one', 'one', type='bool'), False)
|
||||
self.assertIs(parser.Get('one', 'two', type='bool'), True)
|
||||
eq(parser.Get('one', 'three', type='int'), 10)
|
||||
eq(parser.Get('two', 'one'), 'a string')
|
||||
self.assertIs(parser.Get('two', 'two', type='bool'), True)
|
||||
self.assertIs(parser.Get('two', 'three', type='bool'), False)
|
||||
|
||||
# Test without type should fallback to string.
|
||||
eq(parser.Get('two', 'two'), 'true')
|
||||
eq(parser.Get('two', 'three'), 'false')
|
||||
|
||||
# If option not exist, should return None, or default.
|
||||
self.assertIsNone(parser.Get('not', 'exist'))
|
||||
eq(parser.Get('not', 'exist', default='DEFAULT'), 'DEFAULT')
|
||||
|
||||
def test_get_option_list(self):
|
||||
parser = config.IdleConfParser('')
|
||||
parser.read_string(self.config)
|
||||
get_list = parser.GetOptionList
|
||||
self.assertCountEqual(get_list('one'), ['one', 'two', 'three'])
|
||||
self.assertCountEqual(get_list('two'), ['one', 'two', 'three'])
|
||||
self.assertEqual(get_list('not exist'), [])
|
||||
|
||||
def test_load_nothing(self):
|
||||
parser = config.IdleConfParser('')
|
||||
parser.Load()
|
||||
self.assertEqual(parser.sections(), [])
|
||||
|
||||
def test_load_file(self):
|
||||
# Borrow test/configdata/cfgparser.1 from test_configparser.
|
||||
config_path = findfile('cfgparser.1', subdir='configdata')
|
||||
parser = config.IdleConfParser(config_path)
|
||||
parser.Load()
|
||||
|
||||
self.assertEqual(parser.Get('Foo Bar', 'foo'), 'newbar')
|
||||
self.assertEqual(parser.GetOptionList('Foo Bar'), ['foo'])
|
||||
|
||||
|
||||
class IdleUserConfParserTest(unittest.TestCase):
|
||||
"""Test that IdleUserConfParser works"""
|
||||
|
||||
def new_parser(self, path=''):
|
||||
return config.IdleUserConfParser(path)
|
||||
|
||||
def test_set_option(self):
|
||||
parser = self.new_parser()
|
||||
parser.add_section('Foo')
|
||||
# Setting new option in existing section should return True.
|
||||
self.assertTrue(parser.SetOption('Foo', 'bar', 'true'))
|
||||
# Setting existing option with same value should return False.
|
||||
self.assertFalse(parser.SetOption('Foo', 'bar', 'true'))
|
||||
# Setting exiting option with new value should return True.
|
||||
self.assertTrue(parser.SetOption('Foo', 'bar', 'false'))
|
||||
self.assertEqual(parser.Get('Foo', 'bar'), 'false')
|
||||
|
||||
# Setting option in new section should create section and return True.
|
||||
self.assertTrue(parser.SetOption('Bar', 'bar', 'true'))
|
||||
self.assertCountEqual(parser.sections(), ['Bar', 'Foo'])
|
||||
self.assertEqual(parser.Get('Bar', 'bar'), 'true')
|
||||
|
||||
def test_remove_option(self):
|
||||
parser = self.new_parser()
|
||||
parser.AddSection('Foo')
|
||||
parser.SetOption('Foo', 'bar', 'true')
|
||||
|
||||
self.assertTrue(parser.RemoveOption('Foo', 'bar'))
|
||||
self.assertFalse(parser.RemoveOption('Foo', 'bar'))
|
||||
self.assertFalse(parser.RemoveOption('Not', 'Exist'))
|
||||
|
||||
def test_add_section(self):
|
||||
parser = self.new_parser()
|
||||
self.assertEqual(parser.sections(), [])
|
||||
|
||||
# Should not add duplicate section.
|
||||
# Configparser raises DuplicateError, IdleParser not.
|
||||
parser.AddSection('Foo')
|
||||
parser.AddSection('Foo')
|
||||
parser.AddSection('Bar')
|
||||
self.assertCountEqual(parser.sections(), ['Bar', 'Foo'])
|
||||
|
||||
def test_remove_empty_sections(self):
|
||||
parser = self.new_parser()
|
||||
|
||||
parser.AddSection('Foo')
|
||||
parser.AddSection('Bar')
|
||||
parser.SetOption('Idle', 'name', 'val')
|
||||
self.assertCountEqual(parser.sections(), ['Bar', 'Foo', 'Idle'])
|
||||
parser.RemoveEmptySections()
|
||||
self.assertEqual(parser.sections(), ['Idle'])
|
||||
|
||||
def test_is_empty(self):
|
||||
parser = self.new_parser()
|
||||
|
||||
parser.AddSection('Foo')
|
||||
parser.AddSection('Bar')
|
||||
self.assertTrue(parser.IsEmpty())
|
||||
self.assertEqual(parser.sections(), [])
|
||||
|
||||
parser.SetOption('Foo', 'bar', 'false')
|
||||
parser.AddSection('Bar')
|
||||
self.assertFalse(parser.IsEmpty())
|
||||
self.assertCountEqual(parser.sections(), ['Foo'])
|
||||
|
||||
def test_save(self):
|
||||
with tempfile.TemporaryDirectory() as tdir:
|
||||
path = os.path.join(tdir, 'test.cfg')
|
||||
parser = self.new_parser(path)
|
||||
parser.AddSection('Foo')
|
||||
parser.SetOption('Foo', 'bar', 'true')
|
||||
|
||||
# Should save to path when config is not empty.
|
||||
self.assertFalse(os.path.exists(path))
|
||||
parser.Save()
|
||||
self.assertTrue(os.path.exists(path))
|
||||
|
||||
# Should remove the file from disk when config is empty.
|
||||
parser.remove_section('Foo')
|
||||
parser.Save()
|
||||
self.assertFalse(os.path.exists(path))
|
||||
|
||||
|
||||
class IdleConfTest(unittest.TestCase):
|
||||
"""Test for idleConf"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.config_string = {}
|
||||
|
||||
conf = config.IdleConf(_utest=True)
|
||||
if __name__ != '__main__':
|
||||
idle_dir = os.path.dirname(__file__)
|
||||
else:
|
||||
idle_dir = os.path.abspath(sys.path[0])
|
||||
for ctype in conf.config_types:
|
||||
config_path = os.path.join(idle_dir, '../config-%s.def' % ctype)
|
||||
with open(config_path) as f:
|
||||
cls.config_string[ctype] = f.read()
|
||||
|
||||
cls.orig_warn = config._warn
|
||||
config._warn = Func()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
config._warn = cls.orig_warn
|
||||
|
||||
def new_config(self, _utest=False):
|
||||
return config.IdleConf(_utest=_utest)
|
||||
|
||||
def mock_config(self):
|
||||
"""Return a mocked idleConf
|
||||
|
||||
Both default and user config used the same config-*.def
|
||||
"""
|
||||
conf = config.IdleConf(_utest=True)
|
||||
for ctype in conf.config_types:
|
||||
conf.defaultCfg[ctype] = config.IdleConfParser('')
|
||||
conf.defaultCfg[ctype].read_string(self.config_string[ctype])
|
||||
conf.userCfg[ctype] = config.IdleUserConfParser('')
|
||||
conf.userCfg[ctype].read_string(self.config_string[ctype])
|
||||
|
||||
return conf
|
||||
|
||||
@unittest.skipIf(sys.platform.startswith('win'), 'this is test for unix system')
|
||||
def test_get_user_cfg_dir_unix(self):
|
||||
# Test to get user config directory under unix.
|
||||
conf = self.new_config(_utest=True)
|
||||
|
||||
# Check normal way should success
|
||||
with mock.patch('os.path.expanduser', return_value='/home/foo'):
|
||||
with mock.patch('os.path.exists', return_value=True):
|
||||
self.assertEqual(conf.GetUserCfgDir(), '/home/foo/.idlerc')
|
||||
|
||||
# Check os.getcwd should success
|
||||
with mock.patch('os.path.expanduser', return_value='~'):
|
||||
with mock.patch('os.getcwd', return_value='/home/foo/cpython'):
|
||||
with mock.patch('os.mkdir'):
|
||||
self.assertEqual(conf.GetUserCfgDir(),
|
||||
'/home/foo/cpython/.idlerc')
|
||||
|
||||
# Check user dir not exists and created failed should raise SystemExit
|
||||
with mock.patch('os.path.join', return_value='/path/not/exists'):
|
||||
with self.assertRaises(SystemExit):
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
conf.GetUserCfgDir()
|
||||
|
||||
@unittest.skipIf(not sys.platform.startswith('win'), 'this is test for Windows system')
|
||||
def test_get_user_cfg_dir_windows(self):
|
||||
# Test to get user config directory under Windows.
|
||||
conf = self.new_config(_utest=True)
|
||||
|
||||
# Check normal way should success
|
||||
with mock.patch('os.path.expanduser', return_value='C:\\foo'):
|
||||
with mock.patch('os.path.exists', return_value=True):
|
||||
self.assertEqual(conf.GetUserCfgDir(), 'C:\\foo\\.idlerc')
|
||||
|
||||
# Check os.getcwd should success
|
||||
with mock.patch('os.path.expanduser', return_value='~'):
|
||||
with mock.patch('os.getcwd', return_value='C:\\foo\\cpython'):
|
||||
with mock.patch('os.mkdir'):
|
||||
self.assertEqual(conf.GetUserCfgDir(),
|
||||
'C:\\foo\\cpython\\.idlerc')
|
||||
|
||||
# Check user dir not exists and created failed should raise SystemExit
|
||||
with mock.patch('os.path.join', return_value='/path/not/exists'):
|
||||
with self.assertRaises(SystemExit):
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
conf.GetUserCfgDir()
|
||||
|
||||
def test_create_config_handlers(self):
|
||||
conf = self.new_config(_utest=True)
|
||||
|
||||
# Mock out idle_dir
|
||||
idle_dir = '/home/foo'
|
||||
with mock.patch.dict({'__name__': '__foo__'}):
|
||||
with mock.patch('os.path.dirname', return_value=idle_dir):
|
||||
conf.CreateConfigHandlers()
|
||||
|
||||
# Check keys are equal
|
||||
self.assertCountEqual(conf.defaultCfg, conf.config_types)
|
||||
self.assertCountEqual(conf.userCfg, conf.config_types)
|
||||
|
||||
# Check conf parser are correct type
|
||||
for default_parser in conf.defaultCfg.values():
|
||||
self.assertIsInstance(default_parser, config.IdleConfParser)
|
||||
for user_parser in conf.userCfg.values():
|
||||
self.assertIsInstance(user_parser, config.IdleUserConfParser)
|
||||
|
||||
# Check config path are correct
|
||||
for cfg_type, parser in conf.defaultCfg.items():
|
||||
self.assertEqual(parser.file,
|
||||
os.path.join(idle_dir, f'config-{cfg_type}.def'))
|
||||
for cfg_type, parser in conf.userCfg.items():
|
||||
self.assertEqual(parser.file,
|
||||
os.path.join(conf.userdir or '#', f'config-{cfg_type}.cfg'))
|
||||
|
||||
def test_load_cfg_files(self):
|
||||
conf = self.new_config(_utest=True)
|
||||
|
||||
# Borrow test/configdata/cfgparser.1 from test_configparser.
|
||||
config_path = findfile('cfgparser.1', subdir='configdata')
|
||||
conf.defaultCfg['foo'] = config.IdleConfParser(config_path)
|
||||
conf.userCfg['foo'] = config.IdleUserConfParser(config_path)
|
||||
|
||||
# Load all config from path
|
||||
conf.LoadCfgFiles()
|
||||
|
||||
eq = self.assertEqual
|
||||
|
||||
# Check defaultCfg is loaded
|
||||
eq(conf.defaultCfg['foo'].Get('Foo Bar', 'foo'), 'newbar')
|
||||
eq(conf.defaultCfg['foo'].GetOptionList('Foo Bar'), ['foo'])
|
||||
|
||||
# Check userCfg is loaded
|
||||
eq(conf.userCfg['foo'].Get('Foo Bar', 'foo'), 'newbar')
|
||||
eq(conf.userCfg['foo'].GetOptionList('Foo Bar'), ['foo'])
|
||||
|
||||
def test_save_user_cfg_files(self):
|
||||
conf = self.mock_config()
|
||||
|
||||
with mock.patch('idlelib.config.IdleUserConfParser.Save') as m:
|
||||
conf.SaveUserCfgFiles()
|
||||
self.assertEqual(m.call_count, len(conf.userCfg))
|
||||
|
||||
def test_get_option(self):
|
||||
conf = self.mock_config()
|
||||
|
||||
eq = self.assertEqual
|
||||
eq(conf.GetOption('main', 'EditorWindow', 'width'), '80')
|
||||
eq(conf.GetOption('main', 'EditorWindow', 'width', type='int'), 80)
|
||||
with mock.patch('idlelib.config._warn') as _warn:
|
||||
eq(conf.GetOption('main', 'EditorWindow', 'font', type='int'), None)
|
||||
eq(conf.GetOption('main', 'EditorWindow', 'NotExists'), None)
|
||||
eq(conf.GetOption('main', 'EditorWindow', 'NotExists', default='NE'), 'NE')
|
||||
eq(_warn.call_count, 4)
|
||||
|
||||
def test_set_option(self):
|
||||
conf = self.mock_config()
|
||||
|
||||
conf.SetOption('main', 'Foo', 'bar', 'newbar')
|
||||
self.assertEqual(conf.GetOption('main', 'Foo', 'bar'), 'newbar')
|
||||
|
||||
def test_get_section_list(self):
|
||||
conf = self.mock_config()
|
||||
|
||||
self.assertCountEqual(
|
||||
conf.GetSectionList('default', 'main'),
|
||||
['General', 'EditorWindow', 'PyShell', 'Indent', 'Theme',
|
||||
'Keys', 'History', 'HelpFiles'])
|
||||
self.assertCountEqual(
|
||||
conf.GetSectionList('user', 'main'),
|
||||
['General', 'EditorWindow', 'PyShell', 'Indent', 'Theme',
|
||||
'Keys', 'History', 'HelpFiles'])
|
||||
|
||||
with self.assertRaises(config.InvalidConfigSet):
|
||||
conf.GetSectionList('foobar', 'main')
|
||||
with self.assertRaises(config.InvalidConfigType):
|
||||
conf.GetSectionList('default', 'notexists')
|
||||
|
||||
def test_get_highlight(self):
|
||||
conf = self.mock_config()
|
||||
|
||||
eq = self.assertEqual
|
||||
eq(conf.GetHighlight('IDLE Classic', 'normal'), {'foreground': '#000000',
|
||||
'background': '#ffffff'})
|
||||
|
||||
# Test cursor (this background should be normal-background)
|
||||
eq(conf.GetHighlight('IDLE Classic', 'cursor'), {'foreground': 'black',
|
||||
'background': '#ffffff'})
|
||||
|
||||
# Test get user themes
|
||||
conf.SetOption('highlight', 'Foobar', 'normal-foreground', '#747474')
|
||||
conf.SetOption('highlight', 'Foobar', 'normal-background', '#171717')
|
||||
with mock.patch('idlelib.config._warn'):
|
||||
eq(conf.GetHighlight('Foobar', 'normal'), {'foreground': '#747474',
|
||||
'background': '#171717'})
|
||||
|
||||
def test_get_theme_dict(self):
|
||||
# TODO: finish.
|
||||
conf = self.mock_config()
|
||||
|
||||
# These two should be the same
|
||||
self.assertEqual(
|
||||
conf.GetThemeDict('default', 'IDLE Classic'),
|
||||
conf.GetThemeDict('user', 'IDLE Classic'))
|
||||
|
||||
with self.assertRaises(config.InvalidTheme):
|
||||
conf.GetThemeDict('bad', 'IDLE Classic')
|
||||
|
||||
def test_get_current_theme_and_keys(self):
|
||||
conf = self.mock_config()
|
||||
|
||||
self.assertEqual(conf.CurrentTheme(), conf.current_colors_and_keys('Theme'))
|
||||
self.assertEqual(conf.CurrentKeys(), conf.current_colors_and_keys('Keys'))
|
||||
|
||||
def test_current_colors_and_keys(self):
|
||||
conf = self.mock_config()
|
||||
|
||||
self.assertEqual(conf.current_colors_and_keys('Theme'), 'IDLE Classic')
|
||||
|
||||
def test_default_keys(self):
|
||||
current_platform = sys.platform
|
||||
conf = self.new_config(_utest=True)
|
||||
|
||||
sys.platform = 'win32'
|
||||
self.assertEqual(conf.default_keys(), 'IDLE Classic Windows')
|
||||
|
||||
sys.platform = 'darwin'
|
||||
self.assertEqual(conf.default_keys(), 'IDLE Classic OSX')
|
||||
|
||||
sys.platform = 'some-linux'
|
||||
self.assertEqual(conf.default_keys(), 'IDLE Modern Unix')
|
||||
|
||||
# Restore platform
|
||||
sys.platform = current_platform
|
||||
|
||||
def test_get_extensions(self):
|
||||
userextn.read_string('''
|
||||
[ZzDummy]
|
||||
enable = True
|
||||
[DISABLE]
|
||||
enable = False
|
||||
''')
|
||||
eq = self.assertEqual
|
||||
iGE = idleConf.GetExtensions
|
||||
eq(iGE(shell_only=True), [])
|
||||
eq(iGE(), ['ZzDummy'])
|
||||
eq(iGE(editor_only=True), ['ZzDummy'])
|
||||
eq(iGE(active_only=False), ['ZzDummy', 'DISABLE'])
|
||||
eq(iGE(active_only=False, editor_only=True), ['ZzDummy', 'DISABLE'])
|
||||
userextn.remove_section('ZzDummy')
|
||||
userextn.remove_section('DISABLE')
|
||||
|
||||
|
||||
def test_remove_key_bind_names(self):
|
||||
conf = self.mock_config()
|
||||
|
||||
self.assertCountEqual(
|
||||
conf.RemoveKeyBindNames(conf.GetSectionList('default', 'extensions')),
|
||||
['AutoComplete', 'CodeContext', 'FormatParagraph', 'ParenMatch', 'ZzDummy'])
|
||||
|
||||
def test_get_extn_name_for_event(self):
|
||||
userextn.read_string('''
|
||||
[ZzDummy]
|
||||
enable = True
|
||||
''')
|
||||
eq = self.assertEqual
|
||||
eq(idleConf.GetExtnNameForEvent('z-in'), 'ZzDummy')
|
||||
eq(idleConf.GetExtnNameForEvent('z-out'), None)
|
||||
userextn.remove_section('ZzDummy')
|
||||
|
||||
def test_get_extension_keys(self):
|
||||
userextn.read_string('''
|
||||
[ZzDummy]
|
||||
enable = True
|
||||
''')
|
||||
self.assertEqual(idleConf.GetExtensionKeys('ZzDummy'),
|
||||
{'<<z-in>>': ['<Control-Shift-KeyRelease-Insert>']})
|
||||
userextn.remove_section('ZzDummy')
|
||||
# need option key test
|
||||
## key = ['<Option-Key-2>'] if sys.platform == 'darwin' else ['<Alt-Key-2>']
|
||||
## eq(conf.GetExtensionKeys('ZoomHeight'), {'<<zoom-height>>': key})
|
||||
|
||||
def test_get_extension_bindings(self):
|
||||
userextn.read_string('''
|
||||
[ZzDummy]
|
||||
enable = True
|
||||
''')
|
||||
eq = self.assertEqual
|
||||
iGEB = idleConf.GetExtensionBindings
|
||||
eq(iGEB('NotExists'), {})
|
||||
expect = {'<<z-in>>': ['<Control-Shift-KeyRelease-Insert>'],
|
||||
'<<z-out>>': ['<Control-Shift-KeyRelease-Delete>']}
|
||||
eq(iGEB('ZzDummy'), expect)
|
||||
userextn.remove_section('ZzDummy')
|
||||
|
||||
def test_get_keybinding(self):
|
||||
conf = self.mock_config()
|
||||
|
||||
eq = self.assertEqual
|
||||
eq(conf.GetKeyBinding('IDLE Modern Unix', '<<copy>>'),
|
||||
['<Control-Shift-Key-C>', '<Control-Key-Insert>'])
|
||||
eq(conf.GetKeyBinding('IDLE Classic Unix', '<<copy>>'),
|
||||
['<Alt-Key-w>', '<Meta-Key-w>'])
|
||||
eq(conf.GetKeyBinding('IDLE Classic Windows', '<<copy>>'),
|
||||
['<Control-Key-c>', '<Control-Key-C>'])
|
||||
eq(conf.GetKeyBinding('IDLE Classic Mac', '<<copy>>'), ['<Command-Key-c>'])
|
||||
eq(conf.GetKeyBinding('IDLE Classic OSX', '<<copy>>'), ['<Command-Key-c>'])
|
||||
|
||||
# Test keybinding not exists
|
||||
eq(conf.GetKeyBinding('NOT EXISTS', '<<copy>>'), [])
|
||||
eq(conf.GetKeyBinding('IDLE Modern Unix', 'NOT EXISTS'), [])
|
||||
|
||||
def test_get_current_keyset(self):
|
||||
current_platform = sys.platform
|
||||
conf = self.mock_config()
|
||||
|
||||
# Ensure that platform isn't darwin
|
||||
sys.platform = 'some-linux'
|
||||
self.assertEqual(conf.GetCurrentKeySet(), conf.GetKeySet(conf.CurrentKeys()))
|
||||
|
||||
# This should not be the same, since replace <Alt- to <Option-.
|
||||
# Above depended on config-extensions.def having Alt keys,
|
||||
# which is no longer true.
|
||||
# sys.platform = 'darwin'
|
||||
# self.assertNotEqual(conf.GetCurrentKeySet(), conf.GetKeySet(conf.CurrentKeys()))
|
||||
|
||||
# Restore platform
|
||||
sys.platform = current_platform
|
||||
|
||||
def test_get_keyset(self):
|
||||
conf = self.mock_config()
|
||||
|
||||
# Conflict with key set, should be disable to ''
|
||||
conf.defaultCfg['extensions'].add_section('Foobar')
|
||||
conf.defaultCfg['extensions'].add_section('Foobar_cfgBindings')
|
||||
conf.defaultCfg['extensions'].set('Foobar', 'enable', 'True')
|
||||
conf.defaultCfg['extensions'].set('Foobar_cfgBindings', 'newfoo', '<Key-F3>')
|
||||
self.assertEqual(conf.GetKeySet('IDLE Modern Unix')['<<newfoo>>'], '')
|
||||
|
||||
def test_is_core_binding(self):
|
||||
# XXX: Should move out the core keys to config file or other place
|
||||
conf = self.mock_config()
|
||||
|
||||
self.assertTrue(conf.IsCoreBinding('copy'))
|
||||
self.assertTrue(conf.IsCoreBinding('cut'))
|
||||
self.assertTrue(conf.IsCoreBinding('del-word-right'))
|
||||
self.assertFalse(conf.IsCoreBinding('not-exists'))
|
||||
|
||||
def test_extra_help_source_list(self):
|
||||
# Test GetExtraHelpSourceList and GetAllExtraHelpSourcesList in same
|
||||
# place to prevent prepare input data twice.
|
||||
conf = self.mock_config()
|
||||
|
||||
# Test default with no extra help source
|
||||
self.assertEqual(conf.GetExtraHelpSourceList('default'), [])
|
||||
self.assertEqual(conf.GetExtraHelpSourceList('user'), [])
|
||||
with self.assertRaises(config.InvalidConfigSet):
|
||||
self.assertEqual(conf.GetExtraHelpSourceList('bad'), [])
|
||||
self.assertCountEqual(
|
||||
conf.GetAllExtraHelpSourcesList(),
|
||||
conf.GetExtraHelpSourceList('default') + conf.GetExtraHelpSourceList('user'))
|
||||
|
||||
# Add help source to user config
|
||||
conf.userCfg['main'].SetOption('HelpFiles', '4', 'Python;https://python.org') # This is bad input
|
||||
conf.userCfg['main'].SetOption('HelpFiles', '3', 'Python:https://python.org') # This is bad input
|
||||
conf.userCfg['main'].SetOption('HelpFiles', '2', 'Pillow;https://pillow.readthedocs.io/en/latest/')
|
||||
conf.userCfg['main'].SetOption('HelpFiles', '1', 'IDLE;C:/Programs/Python36/Lib/idlelib/help.html')
|
||||
self.assertEqual(conf.GetExtraHelpSourceList('user'),
|
||||
[('IDLE', 'C:/Programs/Python36/Lib/idlelib/help.html', '1'),
|
||||
('Pillow', 'https://pillow.readthedocs.io/en/latest/', '2'),
|
||||
('Python', 'https://python.org', '4')])
|
||||
self.assertCountEqual(
|
||||
conf.GetAllExtraHelpSourcesList(),
|
||||
conf.GetExtraHelpSourceList('default') + conf.GetExtraHelpSourceList('user'))
|
||||
|
||||
def test_get_font(self):
|
||||
from test.support import requires
|
||||
from tkinter import Tk
|
||||
from tkinter.font import Font
|
||||
conf = self.mock_config()
|
||||
|
||||
requires('gui')
|
||||
root = Tk()
|
||||
root.withdraw()
|
||||
|
||||
f = Font.actual(Font(name='TkFixedFont', exists=True, root=root))
|
||||
self.assertEqual(
|
||||
conf.GetFont(root, 'main', 'EditorWindow'),
|
||||
(f['family'], 10 if f['size'] <= 0 else f['size'], f['weight']))
|
||||
|
||||
# Cleanup root
|
||||
root.destroy()
|
||||
del root
|
||||
|
||||
def test_get_core_keys(self):
|
||||
conf = self.mock_config()
|
||||
|
||||
eq = self.assertEqual
|
||||
eq(conf.GetCoreKeys()['<<center-insert>>'], ['<Control-l>'])
|
||||
eq(conf.GetCoreKeys()['<<copy>>'], ['<Control-c>', '<Control-C>'])
|
||||
eq(conf.GetCoreKeys()['<<history-next>>'], ['<Alt-n>'])
|
||||
eq(conf.GetCoreKeys('IDLE Classic Windows')['<<center-insert>>'],
|
||||
['<Control-Key-l>', '<Control-Key-L>'])
|
||||
eq(conf.GetCoreKeys('IDLE Classic OSX')['<<copy>>'], ['<Command-Key-c>'])
|
||||
eq(conf.GetCoreKeys('IDLE Classic Unix')['<<history-next>>'],
|
||||
['<Alt-Key-n>', '<Meta-Key-n>'])
|
||||
eq(conf.GetCoreKeys('IDLE Modern Unix')['<<history-next>>'],
|
||||
['<Alt-Key-n>', '<Meta-Key-n>'])
|
||||
|
||||
|
||||
class CurrentColorKeysTest(unittest.TestCase):
|
||||
""" Test colorkeys function with user config [Theme] and [Keys] patterns.
|
||||
|
||||
colorkeys = config.IdleConf.current_colors_and_keys
|
||||
Test all patterns written by IDLE and some errors
|
||||
Item 'default' should really be 'builtin' (versus 'custom).
|
||||
"""
|
||||
colorkeys = idleConf.current_colors_and_keys
|
||||
default_theme = 'IDLE Classic'
|
||||
default_keys = idleConf.default_keys()
|
||||
|
||||
def test_old_builtin_theme(self):
|
||||
# On initial installation, user main is blank.
|
||||
self.assertEqual(self.colorkeys('Theme'), self.default_theme)
|
||||
# For old default, name2 must be blank.
|
||||
usermain.read_string('''
|
||||
[Theme]
|
||||
default = True
|
||||
''')
|
||||
# IDLE omits 'name' for default old builtin theme.
|
||||
self.assertEqual(self.colorkeys('Theme'), self.default_theme)
|
||||
# IDLE adds 'name' for non-default old builtin theme.
|
||||
usermain['Theme']['name'] = 'IDLE New'
|
||||
self.assertEqual(self.colorkeys('Theme'), 'IDLE New')
|
||||
# Erroneous non-default old builtin reverts to default.
|
||||
usermain['Theme']['name'] = 'non-existent'
|
||||
self.assertEqual(self.colorkeys('Theme'), self.default_theme)
|
||||
usermain.remove_section('Theme')
|
||||
|
||||
def test_new_builtin_theme(self):
|
||||
# IDLE writes name2 for new builtins.
|
||||
usermain.read_string('''
|
||||
[Theme]
|
||||
default = True
|
||||
name2 = IDLE Dark
|
||||
''')
|
||||
self.assertEqual(self.colorkeys('Theme'), 'IDLE Dark')
|
||||
# Leftover 'name', not removed, is ignored.
|
||||
usermain['Theme']['name'] = 'IDLE New'
|
||||
self.assertEqual(self.colorkeys('Theme'), 'IDLE Dark')
|
||||
# Erroneous non-default new builtin reverts to default.
|
||||
usermain['Theme']['name2'] = 'non-existent'
|
||||
self.assertEqual(self.colorkeys('Theme'), self.default_theme)
|
||||
usermain.remove_section('Theme')
|
||||
|
||||
def test_user_override_theme(self):
|
||||
# Erroneous custom name (no definition) reverts to default.
|
||||
usermain.read_string('''
|
||||
[Theme]
|
||||
default = False
|
||||
name = Custom Dark
|
||||
''')
|
||||
self.assertEqual(self.colorkeys('Theme'), self.default_theme)
|
||||
# Custom name is valid with matching Section name.
|
||||
userhigh.read_string('[Custom Dark]\na=b')
|
||||
self.assertEqual(self.colorkeys('Theme'), 'Custom Dark')
|
||||
# Name2 is ignored.
|
||||
usermain['Theme']['name2'] = 'non-existent'
|
||||
self.assertEqual(self.colorkeys('Theme'), 'Custom Dark')
|
||||
usermain.remove_section('Theme')
|
||||
userhigh.remove_section('Custom Dark')
|
||||
|
||||
def test_old_builtin_keys(self):
|
||||
# On initial installation, user main is blank.
|
||||
self.assertEqual(self.colorkeys('Keys'), self.default_keys)
|
||||
# For old default, name2 must be blank, name is always used.
|
||||
usermain.read_string('''
|
||||
[Keys]
|
||||
default = True
|
||||
name = IDLE Classic Unix
|
||||
''')
|
||||
self.assertEqual(self.colorkeys('Keys'), 'IDLE Classic Unix')
|
||||
# Erroneous non-default old builtin reverts to default.
|
||||
usermain['Keys']['name'] = 'non-existent'
|
||||
self.assertEqual(self.colorkeys('Keys'), self.default_keys)
|
||||
usermain.remove_section('Keys')
|
||||
|
||||
def test_new_builtin_keys(self):
|
||||
# IDLE writes name2 for new builtins.
|
||||
usermain.read_string('''
|
||||
[Keys]
|
||||
default = True
|
||||
name2 = IDLE Modern Unix
|
||||
''')
|
||||
self.assertEqual(self.colorkeys('Keys'), 'IDLE Modern Unix')
|
||||
# Leftover 'name', not removed, is ignored.
|
||||
usermain['Keys']['name'] = 'IDLE Classic Unix'
|
||||
self.assertEqual(self.colorkeys('Keys'), 'IDLE Modern Unix')
|
||||
# Erroneous non-default new builtin reverts to default.
|
||||
usermain['Keys']['name2'] = 'non-existent'
|
||||
self.assertEqual(self.colorkeys('Keys'), self.default_keys)
|
||||
usermain.remove_section('Keys')
|
||||
|
||||
def test_user_override_keys(self):
|
||||
# Erroneous custom name (no definition) reverts to default.
|
||||
usermain.read_string('''
|
||||
[Keys]
|
||||
default = False
|
||||
name = Custom Keys
|
||||
''')
|
||||
self.assertEqual(self.colorkeys('Keys'), self.default_keys)
|
||||
# Custom name is valid with matching Section name.
|
||||
userkeys.read_string('[Custom Keys]\na=b')
|
||||
self.assertEqual(self.colorkeys('Keys'), 'Custom Keys')
|
||||
# Name2 is ignored.
|
||||
usermain['Keys']['name2'] = 'non-existent'
|
||||
self.assertEqual(self.colorkeys('Keys'), 'Custom Keys')
|
||||
usermain.remove_section('Keys')
|
||||
userkeys.remove_section('Custom Keys')
|
||||
|
||||
|
||||
class ChangesTest(unittest.TestCase):
|
||||
|
||||
empty = {'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}}
|
||||
|
||||
def load(self): # Test_add_option verifies that this works.
|
||||
changes = self.changes
|
||||
changes.add_option('main', 'Msec', 'mitem', 'mval')
|
||||
changes.add_option('highlight', 'Hsec', 'hitem', 'hval')
|
||||
changes.add_option('keys', 'Ksec', 'kitem', 'kval')
|
||||
return changes
|
||||
|
||||
loaded = {'main': {'Msec': {'mitem': 'mval'}},
|
||||
'highlight': {'Hsec': {'hitem': 'hval'}},
|
||||
'keys': {'Ksec': {'kitem':'kval'}},
|
||||
'extensions': {}}
|
||||
|
||||
def setUp(self):
|
||||
self.changes = config.ConfigChanges()
|
||||
|
||||
def test_init(self):
|
||||
self.assertEqual(self.changes, self.empty)
|
||||
|
||||
def test_add_option(self):
|
||||
changes = self.load()
|
||||
self.assertEqual(changes, self.loaded)
|
||||
changes.add_option('main', 'Msec', 'mitem', 'mval')
|
||||
self.assertEqual(changes, self.loaded)
|
||||
|
||||
def test_save_option(self): # Static function does not touch changes.
|
||||
save_option = self.changes.save_option
|
||||
self.assertTrue(save_option('main', 'Indent', 'what', '0'))
|
||||
self.assertFalse(save_option('main', 'Indent', 'what', '0'))
|
||||
self.assertEqual(usermain['Indent']['what'], '0')
|
||||
|
||||
self.assertTrue(save_option('main', 'Indent', 'use-spaces', '0'))
|
||||
self.assertEqual(usermain['Indent']['use-spaces'], '0')
|
||||
self.assertTrue(save_option('main', 'Indent', 'use-spaces', '1'))
|
||||
self.assertFalse(usermain.has_option('Indent', 'use-spaces'))
|
||||
usermain.remove_section('Indent')
|
||||
|
||||
def test_save_added(self):
|
||||
changes = self.load()
|
||||
self.assertTrue(changes.save_all())
|
||||
self.assertEqual(usermain['Msec']['mitem'], 'mval')
|
||||
self.assertEqual(userhigh['Hsec']['hitem'], 'hval')
|
||||
self.assertEqual(userkeys['Ksec']['kitem'], 'kval')
|
||||
changes.add_option('main', 'Msec', 'mitem', 'mval')
|
||||
self.assertFalse(changes.save_all())
|
||||
usermain.remove_section('Msec')
|
||||
userhigh.remove_section('Hsec')
|
||||
userkeys.remove_section('Ksec')
|
||||
|
||||
def test_save_help(self):
|
||||
# Any change to HelpFiles overwrites entire section.
|
||||
changes = self.changes
|
||||
changes.save_option('main', 'HelpFiles', 'IDLE', 'idledoc')
|
||||
changes.add_option('main', 'HelpFiles', 'ELDI', 'codeldi')
|
||||
changes.save_all()
|
||||
self.assertFalse(usermain.has_option('HelpFiles', 'IDLE'))
|
||||
self.assertTrue(usermain.has_option('HelpFiles', 'ELDI'))
|
||||
|
||||
def test_save_default(self): # Cover 2nd and 3rd false branches.
|
||||
changes = self.changes
|
||||
changes.add_option('main', 'Indent', 'use-spaces', '1')
|
||||
# save_option returns False; cfg_type_changed remains False.
|
||||
|
||||
# TODO: test that save_all calls usercfg Saves.
|
||||
|
||||
def test_delete_section(self):
|
||||
changes = self.load()
|
||||
changes.delete_section('main', 'fake') # Test no exception.
|
||||
self.assertEqual(changes, self.loaded) # Test nothing deleted.
|
||||
for cfgtype, section in (('main', 'Msec'), ('keys', 'Ksec')):
|
||||
testcfg[cfgtype].SetOption(section, 'name', 'value')
|
||||
changes.delete_section(cfgtype, section)
|
||||
with self.assertRaises(KeyError):
|
||||
changes[cfgtype][section] # Test section gone from changes
|
||||
testcfg[cfgtype][section] # and from mock userCfg.
|
||||
# TODO test for save call.
|
||||
|
||||
def test_clear(self):
|
||||
changes = self.load()
|
||||
changes.clear()
|
||||
self.assertEqual(changes, self.empty)
|
||||
|
||||
|
||||
class WarningTest(unittest.TestCase):
|
||||
|
||||
def test_warn(self):
|
||||
Equal = self.assertEqual
|
||||
config._warned = set()
|
||||
with captured_stderr() as stderr:
|
||||
config._warn('warning', 'key')
|
||||
Equal(config._warned, {('warning','key')})
|
||||
Equal(stderr.getvalue(), 'warning'+'\n')
|
||||
with captured_stderr() as stderr:
|
||||
config._warn('warning', 'key')
|
||||
Equal(stderr.getvalue(), '')
|
||||
with captured_stderr() as stderr:
|
||||
config._warn('warn2', 'yek')
|
||||
Equal(config._warned, {('warning','key'), ('warn2','yek')})
|
||||
Equal(stderr.getvalue(), 'warn2'+'\n')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
356
Dependencies/Python/Lib/idlelib/idle_test/test_config_key.py
vendored
Normal file
356
Dependencies/Python/Lib/idlelib/idle_test/test_config_key.py
vendored
Normal file
@@ -0,0 +1,356 @@
|
||||
"""Test config_key, coverage 98%.
|
||||
|
||||
Coverage is effectively 100%. Tkinter dialog is mocked, Mac-only line
|
||||
may be skipped, and dummy function in bind test should not be called.
|
||||
Not tested: exit with 'self.advanced or self.keys_ok(keys) ...' False.
|
||||
"""
|
||||
|
||||
from idlelib import config_key
|
||||
from test.support import requires
|
||||
import unittest
|
||||
from unittest import mock
|
||||
from tkinter import Tk, TclError
|
||||
from idlelib.idle_test.mock_idle import Func
|
||||
from idlelib.idle_test.mock_tk import Mbox_func
|
||||
|
||||
|
||||
class ValidationTest(unittest.TestCase):
|
||||
"Test validation methods: ok, keys_ok, bind_ok."
|
||||
|
||||
class Validator(config_key.GetKeysFrame):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
class list_keys_final:
|
||||
get = Func()
|
||||
self.list_keys_final = list_keys_final
|
||||
get_modifiers = Func()
|
||||
showerror = Mbox_func()
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
keylist = [['<Key-F12>'], ['<Control-Key-x>', '<Control-Key-X>']]
|
||||
cls.dialog = cls.Validator(cls.root, '<<Test>>', keylist)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.dialog
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.dialog.showerror.message = ''
|
||||
# A test that needs a particular final key value should set it.
|
||||
# A test that sets a non-blank modifier list should reset it to [].
|
||||
|
||||
def test_ok_empty(self):
|
||||
self.dialog.key_string.set(' ')
|
||||
self.dialog.ok()
|
||||
self.assertEqual(self.dialog.result, '')
|
||||
self.assertEqual(self.dialog.showerror.message, 'No key specified.')
|
||||
|
||||
def test_ok_good(self):
|
||||
self.dialog.key_string.set('<Key-F11>')
|
||||
self.dialog.list_keys_final.get.result = 'F11'
|
||||
self.dialog.ok()
|
||||
self.assertEqual(self.dialog.result, '<Key-F11>')
|
||||
self.assertEqual(self.dialog.showerror.message, '')
|
||||
|
||||
def test_keys_no_ending(self):
|
||||
self.assertFalse(self.dialog.keys_ok('<Control-Shift'))
|
||||
self.assertIn('Missing the final', self.dialog.showerror.message)
|
||||
|
||||
def test_keys_no_modifier_bad(self):
|
||||
self.dialog.list_keys_final.get.result = 'A'
|
||||
self.assertFalse(self.dialog.keys_ok('<Key-A>'))
|
||||
self.assertIn('No modifier', self.dialog.showerror.message)
|
||||
|
||||
def test_keys_no_modifier_ok(self):
|
||||
self.dialog.list_keys_final.get.result = 'F11'
|
||||
self.assertTrue(self.dialog.keys_ok('<Key-F11>'))
|
||||
self.assertEqual(self.dialog.showerror.message, '')
|
||||
|
||||
def test_keys_shift_bad(self):
|
||||
self.dialog.list_keys_final.get.result = 'a'
|
||||
self.dialog.get_modifiers.result = ['Shift']
|
||||
self.assertFalse(self.dialog.keys_ok('<a>'))
|
||||
self.assertIn('shift modifier', self.dialog.showerror.message)
|
||||
self.dialog.get_modifiers.result = []
|
||||
|
||||
def test_keys_dup(self):
|
||||
for mods, final, seq in (([], 'F12', '<Key-F12>'),
|
||||
(['Control'], 'x', '<Control-Key-x>'),
|
||||
(['Control'], 'X', '<Control-Key-X>')):
|
||||
with self.subTest(m=mods, f=final, s=seq):
|
||||
self.dialog.list_keys_final.get.result = final
|
||||
self.dialog.get_modifiers.result = mods
|
||||
self.assertFalse(self.dialog.keys_ok(seq))
|
||||
self.assertIn('already in use', self.dialog.showerror.message)
|
||||
self.dialog.get_modifiers.result = []
|
||||
|
||||
def test_bind_ok(self):
|
||||
self.assertTrue(self.dialog.bind_ok('<Control-Shift-Key-a>'))
|
||||
self.assertEqual(self.dialog.showerror.message, '')
|
||||
|
||||
def test_bind_not_ok(self):
|
||||
self.assertFalse(self.dialog.bind_ok('<Control-Shift>'))
|
||||
self.assertIn('not accepted', self.dialog.showerror.message)
|
||||
|
||||
|
||||
class ToggleLevelTest(unittest.TestCase):
|
||||
"Test toggle between Basic and Advanced frames."
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.dialog = config_key.GetKeysFrame(cls.root, '<<Test>>', [])
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.dialog
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_toggle_level(self):
|
||||
dialog = self.dialog
|
||||
|
||||
def stackorder():
|
||||
"""Get the stack order of the children of the frame.
|
||||
|
||||
winfo_children() stores the children in stack order, so
|
||||
this can be used to check whether a frame is above or
|
||||
below another one.
|
||||
"""
|
||||
for index, child in enumerate(dialog.winfo_children()):
|
||||
if child._name == 'keyseq_basic':
|
||||
basic = index
|
||||
if child._name == 'keyseq_advanced':
|
||||
advanced = index
|
||||
return basic, advanced
|
||||
|
||||
# New window starts at basic level.
|
||||
self.assertFalse(dialog.advanced)
|
||||
self.assertIn('Advanced', dialog.button_level['text'])
|
||||
basic, advanced = stackorder()
|
||||
self.assertGreater(basic, advanced)
|
||||
|
||||
# Toggle to advanced.
|
||||
dialog.toggle_level()
|
||||
self.assertTrue(dialog.advanced)
|
||||
self.assertIn('Basic', dialog.button_level['text'])
|
||||
basic, advanced = stackorder()
|
||||
self.assertGreater(advanced, basic)
|
||||
|
||||
# Toggle to basic.
|
||||
dialog.button_level.invoke()
|
||||
self.assertFalse(dialog.advanced)
|
||||
self.assertIn('Advanced', dialog.button_level['text'])
|
||||
basic, advanced = stackorder()
|
||||
self.assertGreater(basic, advanced)
|
||||
|
||||
|
||||
class KeySelectionTest(unittest.TestCase):
|
||||
"Test selecting key on Basic frames."
|
||||
|
||||
class Basic(config_key.GetKeysFrame):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
class list_keys_final:
|
||||
get = Func()
|
||||
select_clear = Func()
|
||||
yview = Func()
|
||||
self.list_keys_final = list_keys_final
|
||||
def set_modifiers_for_platform(self):
|
||||
self.modifiers = ['foo', 'bar', 'BAZ']
|
||||
self.modifier_label = {'BAZ': 'ZZZ'}
|
||||
showerror = Mbox_func()
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.dialog = cls.Basic(cls.root, '<<Test>>', [])
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.dialog
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.dialog.clear_key_seq()
|
||||
|
||||
def test_get_modifiers(self):
|
||||
dialog = self.dialog
|
||||
gm = dialog.get_modifiers
|
||||
eq = self.assertEqual
|
||||
|
||||
# Modifiers are set on/off by invoking the checkbutton.
|
||||
dialog.modifier_checkbuttons['foo'].invoke()
|
||||
eq(gm(), ['foo'])
|
||||
|
||||
dialog.modifier_checkbuttons['BAZ'].invoke()
|
||||
eq(gm(), ['foo', 'BAZ'])
|
||||
|
||||
dialog.modifier_checkbuttons['foo'].invoke()
|
||||
eq(gm(), ['BAZ'])
|
||||
|
||||
@mock.patch.object(config_key.GetKeysFrame, 'get_modifiers')
|
||||
def test_build_key_string(self, mock_modifiers):
|
||||
dialog = self.dialog
|
||||
key = dialog.list_keys_final
|
||||
string = dialog.key_string.get
|
||||
eq = self.assertEqual
|
||||
|
||||
key.get.result = 'a'
|
||||
mock_modifiers.return_value = []
|
||||
dialog.build_key_string()
|
||||
eq(string(), '<Key-a>')
|
||||
|
||||
mock_modifiers.return_value = ['mymod']
|
||||
dialog.build_key_string()
|
||||
eq(string(), '<mymod-Key-a>')
|
||||
|
||||
key.get.result = ''
|
||||
mock_modifiers.return_value = ['mymod', 'test']
|
||||
dialog.build_key_string()
|
||||
eq(string(), '<mymod-test>')
|
||||
|
||||
@mock.patch.object(config_key.GetKeysFrame, 'get_modifiers')
|
||||
def test_final_key_selected(self, mock_modifiers):
|
||||
dialog = self.dialog
|
||||
key = dialog.list_keys_final
|
||||
string = dialog.key_string.get
|
||||
eq = self.assertEqual
|
||||
|
||||
mock_modifiers.return_value = ['Shift']
|
||||
key.get.result = '{'
|
||||
dialog.final_key_selected()
|
||||
eq(string(), '<Shift-Key-braceleft>')
|
||||
|
||||
|
||||
class CancelWindowTest(unittest.TestCase):
|
||||
"Simulate user clicking [Cancel] button."
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.dialog = config_key.GetKeysWindow(
|
||||
cls.root, 'Title', '<<Test>>', [], _utest=True)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.dialog.cancel()
|
||||
del cls.dialog
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
@mock.patch.object(config_key.GetKeysFrame, 'ok')
|
||||
def test_cancel(self, mock_frame_ok):
|
||||
self.assertEqual(self.dialog.winfo_class(), 'Toplevel')
|
||||
self.dialog.button_cancel.invoke()
|
||||
with self.assertRaises(TclError):
|
||||
self.dialog.winfo_class()
|
||||
self.assertEqual(self.dialog.result, '')
|
||||
mock_frame_ok.assert_not_called()
|
||||
|
||||
|
||||
class OKWindowTest(unittest.TestCase):
|
||||
"Simulate user clicking [OK] button."
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.dialog = config_key.GetKeysWindow(
|
||||
cls.root, 'Title', '<<Test>>', [], _utest=True)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.dialog.cancel()
|
||||
del cls.dialog
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
@mock.patch.object(config_key.GetKeysFrame, 'ok')
|
||||
def test_ok(self, mock_frame_ok):
|
||||
self.assertEqual(self.dialog.winfo_class(), 'Toplevel')
|
||||
self.dialog.button_ok.invoke()
|
||||
with self.assertRaises(TclError):
|
||||
self.dialog.winfo_class()
|
||||
mock_frame_ok.assert_called()
|
||||
|
||||
|
||||
class WindowResultTest(unittest.TestCase):
|
||||
"Test window result get and set."
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.dialog = config_key.GetKeysWindow(
|
||||
cls.root, 'Title', '<<Test>>', [], _utest=True)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.dialog.cancel()
|
||||
del cls.dialog
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_result(self):
|
||||
dialog = self.dialog
|
||||
eq = self.assertEqual
|
||||
|
||||
dialog.result = ''
|
||||
eq(dialog.result, '')
|
||||
eq(dialog.frame.result,'')
|
||||
|
||||
dialog.result = 'bar'
|
||||
eq(dialog.result,'bar')
|
||||
eq(dialog.frame.result,'bar')
|
||||
|
||||
dialog.frame.result = 'foo'
|
||||
eq(dialog.result, 'foo')
|
||||
eq(dialog.frame.result,'foo')
|
||||
|
||||
|
||||
class HelperTest(unittest.TestCase):
|
||||
"Test module level helper functions."
|
||||
|
||||
def test_translate_key(self):
|
||||
tr = config_key.translate_key
|
||||
eq = self.assertEqual
|
||||
|
||||
# Letters return unchanged with no 'Shift'.
|
||||
eq(tr('q', []), 'Key-q')
|
||||
eq(tr('q', ['Control', 'Alt']), 'Key-q')
|
||||
|
||||
# 'Shift' uppercases single lowercase letters.
|
||||
eq(tr('q', ['Shift']), 'Key-Q')
|
||||
eq(tr('q', ['Control', 'Shift']), 'Key-Q')
|
||||
eq(tr('q', ['Control', 'Alt', 'Shift']), 'Key-Q')
|
||||
|
||||
# Convert key name to keysym.
|
||||
eq(tr('Page Up', []), 'Key-Prior')
|
||||
# 'Shift' doesn't change case when it's not a single char.
|
||||
eq(tr('*', ['Shift']), 'Key-asterisk')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
1581
Dependencies/Python/Lib/idlelib/idle_test/test_configdialog.py
vendored
Normal file
1581
Dependencies/Python/Lib/idlelib/idle_test/test_configdialog.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
298
Dependencies/Python/Lib/idlelib/idle_test/test_debugger.py
vendored
Normal file
298
Dependencies/Python/Lib/idlelib/idle_test/test_debugger.py
vendored
Normal file
@@ -0,0 +1,298 @@
|
||||
"""Test debugger, coverage 66%
|
||||
|
||||
Try to make tests pass with draft bdbx, which may replace bdb in 3.13+.
|
||||
"""
|
||||
|
||||
from idlelib import debugger
|
||||
from collections import namedtuple
|
||||
from textwrap import dedent
|
||||
from tkinter import Tk
|
||||
|
||||
from test.support import requires
|
||||
from test.support.testcase import ExtraAssertions
|
||||
import unittest
|
||||
from unittest import mock
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
"""A test python script for the debug tests."""
|
||||
TEST_CODE = dedent("""
|
||||
i = 1
|
||||
i += 2
|
||||
if i == 3:
|
||||
print(i)
|
||||
""")
|
||||
|
||||
|
||||
class MockFrame:
|
||||
"Minimal mock frame."
|
||||
|
||||
def __init__(self, code, lineno):
|
||||
self.f_code = code
|
||||
self.f_lineno = lineno
|
||||
|
||||
|
||||
class IdbTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.gui = Mock()
|
||||
cls.idb = debugger.Idb(cls.gui)
|
||||
|
||||
# Create test and code objects to simulate a debug session.
|
||||
code_obj = compile(TEST_CODE, 'idlelib/file.py', mode='exec')
|
||||
frame1 = MockFrame(code_obj, 1)
|
||||
frame1.f_back = None
|
||||
frame2 = MockFrame(code_obj, 2)
|
||||
frame2.f_back = frame1
|
||||
cls.frame = frame2
|
||||
cls.msg = 'file.py:2: <module>()'
|
||||
|
||||
def test_init(self):
|
||||
self.assertIs(self.idb.gui, self.gui)
|
||||
# Won't test super call since two Bdbs are very different.
|
||||
|
||||
def test_user_line(self):
|
||||
# Test that .user_line() creates a string message for a frame.
|
||||
self.gui.interaction = Mock()
|
||||
self.idb.user_line(self.frame)
|
||||
self.gui.interaction.assert_called_once_with(self.msg, self.frame)
|
||||
|
||||
def test_user_exception(self):
|
||||
# Test that .user_exception() creates a string message for a frame.
|
||||
exc_info = (type(ValueError), ValueError(), None)
|
||||
self.gui.interaction = Mock()
|
||||
self.idb.user_exception(self.frame, exc_info)
|
||||
self.gui.interaction.assert_called_once_with(
|
||||
self.msg, self.frame, exc_info)
|
||||
|
||||
|
||||
class FunctionTest(unittest.TestCase):
|
||||
# Test module functions together.
|
||||
|
||||
def test_functions(self):
|
||||
rpc_obj = compile(TEST_CODE,'rpc.py', mode='exec')
|
||||
rpc_frame = MockFrame(rpc_obj, 2)
|
||||
rpc_frame.f_back = rpc_frame
|
||||
self.assertTrue(debugger._in_rpc_code(rpc_frame))
|
||||
self.assertEqual(debugger._frame2message(rpc_frame),
|
||||
'rpc.py:2: <module>()')
|
||||
|
||||
code_obj = compile(TEST_CODE, 'idlelib/debugger.py', mode='exec')
|
||||
code_frame = MockFrame(code_obj, 1)
|
||||
code_frame.f_back = None
|
||||
self.assertFalse(debugger._in_rpc_code(code_frame))
|
||||
self.assertEqual(debugger._frame2message(code_frame),
|
||||
'debugger.py:1: <module>()')
|
||||
|
||||
code_frame.f_back = code_frame
|
||||
self.assertFalse(debugger._in_rpc_code(code_frame))
|
||||
code_frame.f_back = rpc_frame
|
||||
self.assertTrue(debugger._in_rpc_code(code_frame))
|
||||
|
||||
|
||||
class DebuggerTest(unittest.TestCase):
|
||||
"Tests for Debugger that do not need a real root."
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.pyshell = Mock()
|
||||
cls.pyshell.root = Mock()
|
||||
cls.idb = Mock()
|
||||
with patch.object(debugger.Debugger, 'make_gui'):
|
||||
cls.debugger = debugger.Debugger(cls.pyshell, cls.idb)
|
||||
cls.debugger.root = Mock()
|
||||
|
||||
def test_cont(self):
|
||||
self.debugger.cont()
|
||||
self.idb.set_continue.assert_called_once()
|
||||
|
||||
def test_step(self):
|
||||
self.debugger.step()
|
||||
self.idb.set_step.assert_called_once()
|
||||
|
||||
def test_quit(self):
|
||||
self.debugger.quit()
|
||||
self.idb.set_quit.assert_called_once()
|
||||
|
||||
def test_next(self):
|
||||
with patch.object(self.debugger, 'frame') as frame:
|
||||
self.debugger.next()
|
||||
self.idb.set_next.assert_called_once_with(frame)
|
||||
|
||||
def test_ret(self):
|
||||
with patch.object(self.debugger, 'frame') as frame:
|
||||
self.debugger.ret()
|
||||
self.idb.set_return.assert_called_once_with(frame)
|
||||
|
||||
def test_clear_breakpoint(self):
|
||||
self.debugger.clear_breakpoint('test.py', 4)
|
||||
self.idb.clear_break.assert_called_once_with('test.py', 4)
|
||||
|
||||
def test_clear_file_breaks(self):
|
||||
self.debugger.clear_file_breaks('test.py')
|
||||
self.idb.clear_all_file_breaks.assert_called_once_with('test.py')
|
||||
|
||||
def test_set_load_breakpoints(self):
|
||||
# Test the .load_breakpoints() method calls idb.
|
||||
FileIO = namedtuple('FileIO', 'filename')
|
||||
|
||||
class MockEditWindow(object):
|
||||
def __init__(self, fn, breakpoints):
|
||||
self.io = FileIO(fn)
|
||||
self.breakpoints = breakpoints
|
||||
|
||||
self.pyshell.flist = Mock()
|
||||
self.pyshell.flist.inversedict = (
|
||||
MockEditWindow('test1.py', [4, 4]),
|
||||
MockEditWindow('test2.py', [13, 44, 45]),
|
||||
)
|
||||
self.debugger.set_breakpoint('test0.py', 1)
|
||||
self.idb.set_break.assert_called_once_with('test0.py', 1)
|
||||
self.debugger.load_breakpoints() # Call set_breakpoint 5 times.
|
||||
self.idb.set_break.assert_has_calls(
|
||||
[mock.call('test0.py', 1),
|
||||
mock.call('test1.py', 4),
|
||||
mock.call('test1.py', 4),
|
||||
mock.call('test2.py', 13),
|
||||
mock.call('test2.py', 44),
|
||||
mock.call('test2.py', 45)])
|
||||
|
||||
def test_sync_source_line(self):
|
||||
# Test that .sync_source_line() will set the flist.gotofileline with fixed frame.
|
||||
test_code = compile(TEST_CODE, 'test_sync.py', 'exec')
|
||||
test_frame = MockFrame(test_code, 1)
|
||||
self.debugger.frame = test_frame
|
||||
|
||||
self.debugger.flist = Mock()
|
||||
with patch('idlelib.debugger.os.path.exists', return_value=True):
|
||||
self.debugger.sync_source_line()
|
||||
self.debugger.flist.gotofileline.assert_called_once_with('test_sync.py', 1)
|
||||
|
||||
|
||||
class DebuggerGuiTest(unittest.TestCase):
|
||||
"""Tests for debugger.Debugger that need tk root.
|
||||
|
||||
close needs debugger.top set in make_gui.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = root = Tk()
|
||||
root.withdraw()
|
||||
cls.pyshell = Mock()
|
||||
cls.pyshell.root = root
|
||||
cls.idb = Mock()
|
||||
# stack tests fail with debugger here.
|
||||
## cls.debugger = debugger.Debugger(cls.pyshell, cls.idb)
|
||||
## cls.debugger.root = root
|
||||
## # real root needed for real make_gui
|
||||
## # run, interacting, abort_loop
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.debugger = debugger.Debugger(self.pyshell, self.idb)
|
||||
self.debugger.root = self.root
|
||||
# real root needed for real make_gui
|
||||
# run, interacting, abort_loop
|
||||
|
||||
def test_run_debugger(self):
|
||||
self.debugger.run(1, 'two')
|
||||
self.idb.run.assert_called_once_with(1, 'two')
|
||||
self.assertEqual(self.debugger.interacting, 0)
|
||||
|
||||
def test_close(self):
|
||||
# Test closing the window in an idle state.
|
||||
self.debugger.close()
|
||||
self.pyshell.close_debugger.assert_called_once()
|
||||
|
||||
def test_show_stack(self):
|
||||
self.debugger.show_stack()
|
||||
self.assertEqual(self.debugger.stackviewer.gui, self.debugger)
|
||||
|
||||
def test_show_stack_with_frame(self):
|
||||
test_frame = MockFrame(None, None)
|
||||
self.debugger.frame = test_frame
|
||||
|
||||
# Reset the stackviewer to force it to be recreated.
|
||||
self.debugger.stackviewer = None
|
||||
self.idb.get_stack.return_value = ([], 0)
|
||||
self.debugger.show_stack()
|
||||
|
||||
# Check that the newly created stackviewer has the test gui as a field.
|
||||
self.assertEqual(self.debugger.stackviewer.gui, self.debugger)
|
||||
self.idb.get_stack.assert_called_once_with(test_frame, None)
|
||||
|
||||
|
||||
class StackViewerTest(unittest.TestCase, ExtraAssertions):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.code = compile(TEST_CODE, 'test_stackviewer.py', 'exec')
|
||||
self.stack = [
|
||||
(MockFrame(self.code, 1), 1),
|
||||
(MockFrame(self.code, 2), 2)
|
||||
]
|
||||
# Create a stackviewer and load the test stack.
|
||||
self.sv = debugger.StackViewer(self.root, None, None)
|
||||
self.sv.load_stack(self.stack)
|
||||
|
||||
def test_init(self):
|
||||
# Test creation of StackViewer.
|
||||
gui = None
|
||||
flist = None
|
||||
master_window = self.root
|
||||
sv = debugger.StackViewer(master_window, flist, gui)
|
||||
self.assertHasAttr(sv, 'stack')
|
||||
|
||||
def test_load_stack(self):
|
||||
# Test the .load_stack() method against a fixed test stack.
|
||||
# Check the test stack is assigned and the list contains the repr of them.
|
||||
self.assertEqual(self.sv.stack, self.stack)
|
||||
self.assertTrue('?.<module>(), line 1:' in self.sv.get(0))
|
||||
self.assertEqual(self.sv.get(1), '?.<module>(), line 2: ')
|
||||
|
||||
def test_show_source(self):
|
||||
# Test the .show_source() method against a fixed test stack.
|
||||
# Patch out the file list to monitor it
|
||||
self.sv.flist = Mock()
|
||||
# Patch out isfile to pretend file exists.
|
||||
with patch('idlelib.debugger.os.path.isfile', return_value=True) as isfile:
|
||||
self.sv.show_source(1)
|
||||
isfile.assert_called_once_with('test_stackviewer.py')
|
||||
self.sv.flist.open.assert_called_once_with('test_stackviewer.py')
|
||||
|
||||
|
||||
class NameSpaceTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_init(self):
|
||||
debugger.NamespaceViewer(self.root, 'Test')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
36
Dependencies/Python/Lib/idlelib/idle_test/test_debugger_r.py
vendored
Normal file
36
Dependencies/Python/Lib/idlelib/idle_test/test_debugger_r.py
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
"Test debugger_r, coverage 30%."
|
||||
|
||||
from idlelib import debugger_r
|
||||
import unittest
|
||||
|
||||
# Boilerplate likely to be needed for future test classes.
|
||||
##from test.support import requires
|
||||
##from tkinter import Tk
|
||||
##class Test(unittest.TestCase):
|
||||
## @classmethod
|
||||
## def setUpClass(cls):
|
||||
## requires('gui')
|
||||
## cls.root = Tk()
|
||||
## @classmethod
|
||||
## def tearDownClass(cls):
|
||||
## cls.root.destroy()
|
||||
|
||||
# GUIProxy, IdbAdapter, FrameProxy, CodeProxy, DictProxy,
|
||||
# GUIAdapter, IdbProxy, and 7 functions still need tests.
|
||||
|
||||
class IdbAdapterTest(unittest.TestCase):
|
||||
|
||||
def test_dict_item_noattr(self): # Issue 33065.
|
||||
|
||||
class BinData:
|
||||
def __repr__(self):
|
||||
return self.length
|
||||
|
||||
debugger_r.dicttable[0] = {'BinData': BinData()}
|
||||
idb = debugger_r.IdbAdapter(None)
|
||||
self.assertTrue(idb.dict_item(0, 'BinData'))
|
||||
debugger_r.dicttable.clear()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
57
Dependencies/Python/Lib/idlelib/idle_test/test_debugobj.py
vendored
Normal file
57
Dependencies/Python/Lib/idlelib/idle_test/test_debugobj.py
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
"Test debugobj, coverage 40%."
|
||||
|
||||
from idlelib import debugobj
|
||||
import unittest
|
||||
|
||||
|
||||
class ObjectTreeItemTest(unittest.TestCase):
|
||||
|
||||
def test_init(self):
|
||||
ti = debugobj.ObjectTreeItem('label', 22)
|
||||
self.assertEqual(ti.labeltext, 'label')
|
||||
self.assertEqual(ti.object, 22)
|
||||
self.assertEqual(ti.setfunction, None)
|
||||
|
||||
|
||||
class ClassTreeItemTest(unittest.TestCase):
|
||||
|
||||
def test_isexpandable(self):
|
||||
ti = debugobj.ClassTreeItem('label', 0)
|
||||
self.assertTrue(ti.IsExpandable())
|
||||
|
||||
|
||||
class AtomicObjectTreeItemTest(unittest.TestCase):
|
||||
|
||||
def test_isexpandable(self):
|
||||
ti = debugobj.AtomicObjectTreeItem('label', 0)
|
||||
self.assertFalse(ti.IsExpandable())
|
||||
|
||||
|
||||
class SequenceTreeItemTest(unittest.TestCase):
|
||||
|
||||
def test_isexpandable(self):
|
||||
ti = debugobj.SequenceTreeItem('label', ())
|
||||
self.assertFalse(ti.IsExpandable())
|
||||
ti = debugobj.SequenceTreeItem('label', (1,))
|
||||
self.assertTrue(ti.IsExpandable())
|
||||
|
||||
def test_keys(self):
|
||||
ti = debugobj.SequenceTreeItem('label', 'abc')
|
||||
self.assertEqual(list(ti.keys()), [0, 1, 2]) # keys() is a range.
|
||||
|
||||
|
||||
class DictTreeItemTest(unittest.TestCase):
|
||||
|
||||
def test_isexpandable(self):
|
||||
ti = debugobj.DictTreeItem('label', {})
|
||||
self.assertFalse(ti.IsExpandable())
|
||||
ti = debugobj.DictTreeItem('label', {1:1})
|
||||
self.assertTrue(ti.IsExpandable())
|
||||
|
||||
def test_keys(self):
|
||||
ti = debugobj.DictTreeItem('label', {1:1, 0:0, 2:2})
|
||||
self.assertEqual(ti.keys(), [0, 1, 2]) # keys() is a sorted list.
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
22
Dependencies/Python/Lib/idlelib/idle_test/test_debugobj_r.py
vendored
Normal file
22
Dependencies/Python/Lib/idlelib/idle_test/test_debugobj_r.py
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
"Test debugobj_r, coverage 56%."
|
||||
|
||||
from idlelib import debugobj_r
|
||||
import unittest
|
||||
|
||||
|
||||
class WrappedObjectTreeItemTest(unittest.TestCase):
|
||||
|
||||
def test_getattr(self):
|
||||
ti = debugobj_r.WrappedObjectTreeItem(list)
|
||||
self.assertEqual(ti.append, list.append)
|
||||
|
||||
class StubObjectTreeItemTest(unittest.TestCase):
|
||||
|
||||
def test_init(self):
|
||||
ti = debugobj_r.StubObjectTreeItem('socket', 1111)
|
||||
self.assertEqual(ti.sockio, 'socket')
|
||||
self.assertEqual(ti.oid, 1111)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
44
Dependencies/Python/Lib/idlelib/idle_test/test_delegator.py
vendored
Normal file
44
Dependencies/Python/Lib/idlelib/idle_test/test_delegator.py
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
"Test delegator, coverage 100%."
|
||||
|
||||
from idlelib.delegator import Delegator
|
||||
import unittest
|
||||
|
||||
|
||||
class DelegatorTest(unittest.TestCase):
|
||||
|
||||
def test_mydel(self):
|
||||
# Test a simple use scenario.
|
||||
|
||||
# Initialize an int delegator.
|
||||
mydel = Delegator(int)
|
||||
self.assertIs(mydel.delegate, int)
|
||||
self.assertEqual(mydel._Delegator__cache, set())
|
||||
# Trying to access a non-attribute of int fails.
|
||||
self.assertRaises(AttributeError, mydel.__getattr__, 'xyz')
|
||||
|
||||
# Add real int attribute 'bit_length' by accessing it.
|
||||
bl = mydel.bit_length
|
||||
self.assertIs(bl, int.bit_length)
|
||||
self.assertIs(mydel.__dict__['bit_length'], int.bit_length)
|
||||
self.assertEqual(mydel._Delegator__cache, {'bit_length'})
|
||||
|
||||
# Add attribute 'numerator'.
|
||||
mydel.numerator
|
||||
self.assertEqual(mydel._Delegator__cache, {'bit_length', 'numerator'})
|
||||
|
||||
# Delete 'numerator'.
|
||||
del mydel.numerator
|
||||
self.assertNotIn('numerator', mydel.__dict__)
|
||||
# The current implementation leaves it in the name cache.
|
||||
# self.assertIn('numerator', mydel._Delegator__cache)
|
||||
# However, this is not required and not part of the specification
|
||||
|
||||
# Change delegate to float, first resetting the attributes.
|
||||
mydel.setdelegate(float) # calls resetcache
|
||||
self.assertNotIn('bit_length', mydel.__dict__)
|
||||
self.assertEqual(mydel._Delegator__cache, set())
|
||||
self.assertIs(mydel.delegate, float)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=2)
|
||||
74
Dependencies/Python/Lib/idlelib/idle_test/test_editmenu.py
vendored
Normal file
74
Dependencies/Python/Lib/idlelib/idle_test/test_editmenu.py
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
'''Test (selected) IDLE Edit menu items.
|
||||
|
||||
Edit modules have their own test files
|
||||
'''
|
||||
from test.support import requires
|
||||
requires('gui')
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
import unittest
|
||||
from idlelib import pyshell
|
||||
|
||||
class PasteTest(unittest.TestCase):
|
||||
'''Test pasting into widgets that allow pasting.
|
||||
|
||||
On X11, replacing selections requires tk fix.
|
||||
'''
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.root = root = tk.Tk()
|
||||
cls.root.withdraw()
|
||||
pyshell.fix_x11_paste(root)
|
||||
cls.text = tk.Text(root)
|
||||
cls.entry = tk.Entry(root)
|
||||
cls.tentry = ttk.Entry(root)
|
||||
cls.spin = tk.Spinbox(root)
|
||||
root.clipboard_clear()
|
||||
root.clipboard_append('two')
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text, cls.entry, cls.tentry
|
||||
cls.root.clipboard_clear()
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_paste_text(self):
|
||||
"Test pasting into text with and without a selection."
|
||||
text = self.text
|
||||
for tag, ans in ('', 'onetwo\n'), ('sel', 'two\n'):
|
||||
with self.subTest(tag=tag, ans=ans):
|
||||
text.delete('1.0', 'end')
|
||||
text.insert('1.0', 'one', tag)
|
||||
text.event_generate('<<Paste>>')
|
||||
self.assertEqual(text.get('1.0', 'end'), ans)
|
||||
|
||||
def test_paste_entry(self):
|
||||
"Test pasting into an entry with and without a selection."
|
||||
# Generated <<Paste>> fails for tk entry without empty select
|
||||
# range for 'no selection'. Live widget works fine.
|
||||
for entry in self.entry, self.tentry:
|
||||
for end, ans in (0, 'onetwo'), ('end', 'two'):
|
||||
with self.subTest(entry=entry, end=end, ans=ans):
|
||||
entry.delete(0, 'end')
|
||||
entry.insert(0, 'one')
|
||||
entry.select_range(0, end)
|
||||
entry.event_generate('<<Paste>>')
|
||||
self.assertEqual(entry.get(), ans)
|
||||
|
||||
def test_paste_spin(self):
|
||||
"Test pasting into a spinbox with and without a selection."
|
||||
# See note above for entry.
|
||||
spin = self.spin
|
||||
for end, ans in (0, 'onetwo'), ('end', 'two'):
|
||||
with self.subTest(end=end, ans=ans):
|
||||
spin.delete(0, 'end')
|
||||
spin.insert(0, 'one')
|
||||
spin.selection('range', 0, end) # see note
|
||||
spin.event_generate('<<Paste>>')
|
||||
self.assertEqual(spin.get(), ans)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
241
Dependencies/Python/Lib/idlelib/idle_test/test_editor.py
vendored
Normal file
241
Dependencies/Python/Lib/idlelib/idle_test/test_editor.py
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
"Test editor, coverage 53%."
|
||||
|
||||
from idlelib import editor
|
||||
import unittest
|
||||
from collections import namedtuple
|
||||
from test.support import requires
|
||||
from tkinter import Tk, Text
|
||||
|
||||
Editor = editor.EditorWindow
|
||||
|
||||
|
||||
class EditorWindowTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.update_idletasks()
|
||||
for id in cls.root.tk.call('after', 'info'):
|
||||
cls.root.after_cancel(id)
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_init(self):
|
||||
e = Editor(root=self.root)
|
||||
self.assertEqual(e.root, self.root)
|
||||
e._close()
|
||||
|
||||
|
||||
class GetLineIndentTest(unittest.TestCase):
|
||||
def test_empty_lines(self):
|
||||
for tabwidth in [1, 2, 4, 6, 8]:
|
||||
for line in ['', '\n']:
|
||||
with self.subTest(line=line, tabwidth=tabwidth):
|
||||
self.assertEqual(
|
||||
editor.get_line_indent(line, tabwidth=tabwidth),
|
||||
(0, 0),
|
||||
)
|
||||
|
||||
def test_tabwidth_4(self):
|
||||
# (line, (raw, effective))
|
||||
tests = (('no spaces', (0, 0)),
|
||||
# Internal space isn't counted.
|
||||
(' space test', (4, 4)),
|
||||
('\ttab test', (1, 4)),
|
||||
('\t\tdouble tabs test', (2, 8)),
|
||||
# Different results when mixing tabs and spaces.
|
||||
(' \tmixed test', (5, 8)),
|
||||
(' \t mixed test', (5, 6)),
|
||||
('\t mixed test', (5, 8)),
|
||||
# Spaces not divisible by tabwidth.
|
||||
(' \tmixed test', (3, 4)),
|
||||
(' \t mixed test', (3, 5)),
|
||||
('\t mixed test', (3, 6)),
|
||||
# Only checks spaces and tabs.
|
||||
('\nnewline test', (0, 0)))
|
||||
|
||||
for line, expected in tests:
|
||||
with self.subTest(line=line):
|
||||
self.assertEqual(
|
||||
editor.get_line_indent(line, tabwidth=4),
|
||||
expected,
|
||||
)
|
||||
|
||||
def test_tabwidth_8(self):
|
||||
# (line, (raw, effective))
|
||||
tests = (('no spaces', (0, 0)),
|
||||
# Internal space isn't counted.
|
||||
(' space test', (8, 8)),
|
||||
('\ttab test', (1, 8)),
|
||||
('\t\tdouble tabs test', (2, 16)),
|
||||
# Different results when mixing tabs and spaces.
|
||||
(' \tmixed test', (9, 16)),
|
||||
(' \t mixed test', (9, 10)),
|
||||
('\t mixed test', (9, 16)),
|
||||
# Spaces not divisible by tabwidth.
|
||||
(' \tmixed test', (3, 8)),
|
||||
(' \t mixed test', (3, 9)),
|
||||
('\t mixed test', (3, 10)),
|
||||
# Only checks spaces and tabs.
|
||||
('\nnewline test', (0, 0)))
|
||||
|
||||
for line, expected in tests:
|
||||
with self.subTest(line=line):
|
||||
self.assertEqual(
|
||||
editor.get_line_indent(line, tabwidth=8),
|
||||
expected,
|
||||
)
|
||||
|
||||
|
||||
def insert(text, string):
|
||||
text.delete('1.0', 'end')
|
||||
text.insert('end', string)
|
||||
text.update_idletasks() # Force update for colorizer to finish.
|
||||
|
||||
|
||||
class IndentAndNewlineTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.window = Editor(root=cls.root)
|
||||
cls.window.indentwidth = 2
|
||||
cls.window.tabwidth = 2
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.window._close()
|
||||
del cls.window
|
||||
cls.root.update_idletasks()
|
||||
for id in cls.root.tk.call('after', 'info'):
|
||||
cls.root.after_cancel(id)
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_indent_and_newline_event(self):
|
||||
eq = self.assertEqual
|
||||
w = self.window
|
||||
text = w.text
|
||||
get = text.get
|
||||
nl = w.newline_and_indent_event
|
||||
|
||||
TestInfo = namedtuple('Tests', ['label', 'text', 'expected', 'mark'])
|
||||
|
||||
tests = (TestInfo('Empty line inserts with no indent.',
|
||||
' \n def __init__(self):',
|
||||
'\n \n def __init__(self):\n',
|
||||
'1.end'),
|
||||
TestInfo('Inside bracket before space, deletes space.',
|
||||
' def f1(self, a, b):',
|
||||
' def f1(self,\n a, b):\n',
|
||||
'1.14'),
|
||||
TestInfo('Inside bracket after space, deletes space.',
|
||||
' def f1(self, a, b):',
|
||||
' def f1(self,\n a, b):\n',
|
||||
'1.15'),
|
||||
TestInfo('Inside string with one line - no indent.',
|
||||
' """Docstring."""',
|
||||
' """Docstring.\n"""\n',
|
||||
'1.15'),
|
||||
TestInfo('Inside string with more than one line.',
|
||||
' """Docstring.\n Docstring Line 2"""',
|
||||
' """Docstring.\n Docstring Line 2\n """\n',
|
||||
'2.18'),
|
||||
TestInfo('Backslash with one line.',
|
||||
'a =\\',
|
||||
'a =\\\n \n',
|
||||
'1.end'),
|
||||
TestInfo('Backslash with more than one line.',
|
||||
'a =\\\n multiline\\',
|
||||
'a =\\\n multiline\\\n \n',
|
||||
'2.end'),
|
||||
TestInfo('Block opener - indents +1 level.',
|
||||
' def f1(self):\n pass',
|
||||
' def f1(self):\n \n pass\n',
|
||||
'1.end'),
|
||||
TestInfo('Block closer - dedents -1 level.',
|
||||
' def f1(self):\n pass',
|
||||
' def f1(self):\n pass\n \n',
|
||||
'2.end'),
|
||||
)
|
||||
|
||||
for test in tests:
|
||||
with self.subTest(label=test.label):
|
||||
insert(text, test.text)
|
||||
text.mark_set('insert', test.mark)
|
||||
nl(event=None)
|
||||
eq(get('1.0', 'end'), test.expected)
|
||||
|
||||
# Selected text.
|
||||
insert(text, ' def f1(self, a, b):\n return a + b')
|
||||
text.tag_add('sel', '1.17', '1.end')
|
||||
nl(None)
|
||||
# Deletes selected text before adding new line.
|
||||
eq(get('1.0', 'end'), ' def f1(self, a,\n \n return a + b\n')
|
||||
|
||||
|
||||
class IndentSearcherTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.text = Text(cls.root)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_searcher(self):
|
||||
text = self.text
|
||||
searcher = (self.text)
|
||||
test_info = (# text, (block, indent))
|
||||
("", (None, None)),
|
||||
("[1,", (None, None)), # TokenError
|
||||
("if 1:\n", ('if 1:\n', None)),
|
||||
("if 1:\n 2\n 3\n", ('if 1:\n', ' 2\n')),
|
||||
)
|
||||
for code, expected_pair in test_info:
|
||||
with self.subTest(code=code):
|
||||
insert(text, code)
|
||||
actual_pair = editor.IndentSearcher(text).run()
|
||||
self.assertEqual(actual_pair, expected_pair)
|
||||
|
||||
|
||||
class RMenuTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.window = Editor(root=cls.root)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.window._close()
|
||||
del cls.window
|
||||
cls.root.update_idletasks()
|
||||
for id in cls.root.tk.call('after', 'info'):
|
||||
cls.root.after_cancel(id)
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
class DummyRMenu:
|
||||
def tk_popup(x, y): pass
|
||||
|
||||
def test_rclick(self):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
33
Dependencies/Python/Lib/idlelib/idle_test/test_filelist.py
vendored
Normal file
33
Dependencies/Python/Lib/idlelib/idle_test/test_filelist.py
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
"Test filelist, coverage 19%."
|
||||
|
||||
from idlelib import filelist
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk
|
||||
|
||||
class FileListTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.update_idletasks()
|
||||
for id in cls.root.tk.call('after', 'info'):
|
||||
cls.root.after_cancel(id)
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_new_empty(self):
|
||||
flist = filelist.FileList(self.root)
|
||||
self.assertEqual(flist.root, self.root)
|
||||
e = flist.new()
|
||||
self.assertEqual(type(e), flist.EditorWindow)
|
||||
e._close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
668
Dependencies/Python/Lib/idlelib/idle_test/test_format.py
vendored
Normal file
668
Dependencies/Python/Lib/idlelib/idle_test/test_format.py
vendored
Normal file
@@ -0,0 +1,668 @@
|
||||
"Test format, coverage 99%."
|
||||
|
||||
from idlelib import format as ft
|
||||
import unittest
|
||||
from unittest import mock
|
||||
from test.support import requires
|
||||
from tkinter import Tk, Text
|
||||
from idlelib.editor import EditorWindow
|
||||
from idlelib.idle_test.mock_idle import Editor as MockEditor
|
||||
|
||||
|
||||
class Is_Get_Test(unittest.TestCase):
|
||||
"""Test the is_ and get_ functions"""
|
||||
test_comment = '# This is a comment'
|
||||
test_nocomment = 'This is not a comment'
|
||||
trailingws_comment = '# This is a comment '
|
||||
leadingws_comment = ' # This is a comment'
|
||||
leadingws_nocomment = ' This is not a comment'
|
||||
|
||||
def test_is_all_white(self):
|
||||
self.assertTrue(ft.is_all_white(''))
|
||||
self.assertTrue(ft.is_all_white('\t\n\r\f\v'))
|
||||
self.assertFalse(ft.is_all_white(self.test_comment))
|
||||
|
||||
def test_get_indent(self):
|
||||
Equal = self.assertEqual
|
||||
Equal(ft.get_indent(self.test_comment), '')
|
||||
Equal(ft.get_indent(self.trailingws_comment), '')
|
||||
Equal(ft.get_indent(self.leadingws_comment), ' ')
|
||||
Equal(ft.get_indent(self.leadingws_nocomment), ' ')
|
||||
|
||||
def test_get_comment_header(self):
|
||||
Equal = self.assertEqual
|
||||
# Test comment strings
|
||||
Equal(ft.get_comment_header(self.test_comment), '#')
|
||||
Equal(ft.get_comment_header(self.trailingws_comment), '#')
|
||||
Equal(ft.get_comment_header(self.leadingws_comment), ' #')
|
||||
# Test non-comment strings
|
||||
Equal(ft.get_comment_header(self.leadingws_nocomment), ' ')
|
||||
Equal(ft.get_comment_header(self.test_nocomment), '')
|
||||
|
||||
|
||||
class FindTest(unittest.TestCase):
|
||||
"""Test the find_paragraph function in paragraph module.
|
||||
|
||||
Using the runcase() function, find_paragraph() is called with 'mark' set at
|
||||
multiple indexes before and inside the test paragraph.
|
||||
|
||||
It appears that code with the same indentation as a quoted string is grouped
|
||||
as part of the same paragraph, which is probably incorrect behavior.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
from idlelib.idle_test.mock_tk import Text
|
||||
cls.text = Text()
|
||||
|
||||
def runcase(self, inserttext, stopline, expected):
|
||||
# Check that find_paragraph returns the expected paragraph when
|
||||
# the mark index is set to beginning, middle, end of each line
|
||||
# up to but not including the stop line
|
||||
text = self.text
|
||||
text.insert('1.0', inserttext)
|
||||
for line in range(1, stopline):
|
||||
linelength = int(text.index("%d.end" % line).split('.')[1])
|
||||
for col in (0, linelength//2, linelength):
|
||||
tempindex = "%d.%d" % (line, col)
|
||||
self.assertEqual(ft.find_paragraph(text, tempindex), expected)
|
||||
text.delete('1.0', 'end')
|
||||
|
||||
def test_find_comment(self):
|
||||
comment = (
|
||||
"# Comment block with no blank lines before\n"
|
||||
"# Comment line\n"
|
||||
"\n")
|
||||
self.runcase(comment, 3, ('1.0', '3.0', '#', comment[0:58]))
|
||||
|
||||
comment = (
|
||||
"\n"
|
||||
"# Comment block with whitespace line before and after\n"
|
||||
"# Comment line\n"
|
||||
"\n")
|
||||
self.runcase(comment, 4, ('2.0', '4.0', '#', comment[1:70]))
|
||||
|
||||
comment = (
|
||||
"\n"
|
||||
" # Indented comment block with whitespace before and after\n"
|
||||
" # Comment line\n"
|
||||
"\n")
|
||||
self.runcase(comment, 4, ('2.0', '4.0', ' #', comment[1:82]))
|
||||
|
||||
comment = (
|
||||
"\n"
|
||||
"# Single line comment\n"
|
||||
"\n")
|
||||
self.runcase(comment, 3, ('2.0', '3.0', '#', comment[1:23]))
|
||||
|
||||
comment = (
|
||||
"\n"
|
||||
" # Single line comment with leading whitespace\n"
|
||||
"\n")
|
||||
self.runcase(comment, 3, ('2.0', '3.0', ' #', comment[1:51]))
|
||||
|
||||
comment = (
|
||||
"\n"
|
||||
"# Comment immediately followed by code\n"
|
||||
"x = 42\n"
|
||||
"\n")
|
||||
self.runcase(comment, 3, ('2.0', '3.0', '#', comment[1:40]))
|
||||
|
||||
comment = (
|
||||
"\n"
|
||||
" # Indented comment immediately followed by code\n"
|
||||
"x = 42\n"
|
||||
"\n")
|
||||
self.runcase(comment, 3, ('2.0', '3.0', ' #', comment[1:53]))
|
||||
|
||||
comment = (
|
||||
"\n"
|
||||
"# Comment immediately followed by indented code\n"
|
||||
" x = 42\n"
|
||||
"\n")
|
||||
self.runcase(comment, 3, ('2.0', '3.0', '#', comment[1:49]))
|
||||
|
||||
def test_find_paragraph(self):
|
||||
teststring = (
|
||||
'"""String with no blank lines before\n'
|
||||
'String line\n'
|
||||
'"""\n'
|
||||
'\n')
|
||||
self.runcase(teststring, 4, ('1.0', '4.0', '', teststring[0:53]))
|
||||
|
||||
teststring = (
|
||||
"\n"
|
||||
'"""String with whitespace line before and after\n'
|
||||
'String line.\n'
|
||||
'"""\n'
|
||||
'\n')
|
||||
self.runcase(teststring, 5, ('2.0', '5.0', '', teststring[1:66]))
|
||||
|
||||
teststring = (
|
||||
'\n'
|
||||
' """Indented string with whitespace before and after\n'
|
||||
' Comment string.\n'
|
||||
' """\n'
|
||||
'\n')
|
||||
self.runcase(teststring, 5, ('2.0', '5.0', ' ', teststring[1:85]))
|
||||
|
||||
teststring = (
|
||||
'\n'
|
||||
'"""Single line string."""\n'
|
||||
'\n')
|
||||
self.runcase(teststring, 3, ('2.0', '3.0', '', teststring[1:27]))
|
||||
|
||||
teststring = (
|
||||
'\n'
|
||||
' """Single line string with leading whitespace."""\n'
|
||||
'\n')
|
||||
self.runcase(teststring, 3, ('2.0', '3.0', ' ', teststring[1:55]))
|
||||
|
||||
|
||||
class ReformatFunctionTest(unittest.TestCase):
|
||||
"""Test the reformat_paragraph function without the editor window."""
|
||||
|
||||
def test_reformat_paragraph(self):
|
||||
Equal = self.assertEqual
|
||||
reform = ft.reformat_paragraph
|
||||
hw = "O hello world"
|
||||
Equal(reform(' ', 1), ' ')
|
||||
Equal(reform("Hello world", 20), "Hello world")
|
||||
|
||||
# Test without leading newline
|
||||
Equal(reform(hw, 1), "O\nhello\nworld")
|
||||
Equal(reform(hw, 6), "O\nhello\nworld")
|
||||
Equal(reform(hw, 7), "O hello\nworld")
|
||||
Equal(reform(hw, 12), "O hello\nworld")
|
||||
Equal(reform(hw, 13), "O hello world")
|
||||
|
||||
# Test with leading newline
|
||||
hw = "\nO hello world"
|
||||
Equal(reform(hw, 1), "\nO\nhello\nworld")
|
||||
Equal(reform(hw, 6), "\nO\nhello\nworld")
|
||||
Equal(reform(hw, 7), "\nO hello\nworld")
|
||||
Equal(reform(hw, 12), "\nO hello\nworld")
|
||||
Equal(reform(hw, 13), "\nO hello world")
|
||||
|
||||
|
||||
class ReformatCommentTest(unittest.TestCase):
|
||||
"""Test the reformat_comment function without the editor window."""
|
||||
|
||||
def test_reformat_comment(self):
|
||||
Equal = self.assertEqual
|
||||
|
||||
# reformat_comment formats to a minimum of 20 characters
|
||||
test_string = (
|
||||
" \"\"\"this is a test of a reformat for a triple quoted string"
|
||||
" will it reformat to less than 70 characters for me?\"\"\"")
|
||||
result = ft.reformat_comment(test_string, 70, " ")
|
||||
expected = (
|
||||
" \"\"\"this is a test of a reformat for a triple quoted string will it\n"
|
||||
" reformat to less than 70 characters for me?\"\"\"")
|
||||
Equal(result, expected)
|
||||
|
||||
test_comment = (
|
||||
"# this is a test of a reformat for a triple quoted string will "
|
||||
"it reformat to less than 70 characters for me?")
|
||||
result = ft.reformat_comment(test_comment, 70, "#")
|
||||
expected = (
|
||||
"# this is a test of a reformat for a triple quoted string will it\n"
|
||||
"# reformat to less than 70 characters for me?")
|
||||
Equal(result, expected)
|
||||
|
||||
|
||||
class FormatClassTest(unittest.TestCase):
|
||||
def test_init_close(self):
|
||||
instance = ft.FormatParagraph('editor')
|
||||
self.assertEqual(instance.editwin, 'editor')
|
||||
instance.close()
|
||||
self.assertEqual(instance.editwin, None)
|
||||
|
||||
|
||||
# For testing format_paragraph_event, Initialize FormatParagraph with
|
||||
# a mock Editor with .text and .get_selection_indices. The text must
|
||||
# be a Text wrapper that adds two methods
|
||||
|
||||
# A real EditorWindow creates unneeded, time-consuming baggage and
|
||||
# sometimes emits shutdown warnings like this:
|
||||
# "warning: callback failed in WindowList <class '_tkinter.TclError'>
|
||||
# : invalid command name ".55131368.windows".
|
||||
# Calling EditorWindow._close in tearDownClass prevents this but causes
|
||||
# other problems (windows left open).
|
||||
|
||||
class TextWrapper:
|
||||
def __init__(self, master):
|
||||
self.text = Text(master=master)
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.text, name)
|
||||
def undo_block_start(self): pass
|
||||
def undo_block_stop(self): pass
|
||||
|
||||
class Editor:
|
||||
def __init__(self, root):
|
||||
self.text = TextWrapper(root)
|
||||
get_selection_indices = EditorWindow. get_selection_indices
|
||||
|
||||
class FormatEventTest(unittest.TestCase):
|
||||
"""Test the formatting of text inside a Text widget.
|
||||
|
||||
This is done with FormatParagraph.format.paragraph_event,
|
||||
which calls functions in the module as appropriate.
|
||||
"""
|
||||
test_string = (
|
||||
" '''this is a test of a reformat for a triple "
|
||||
"quoted string will it reformat to less than 70 "
|
||||
"characters for me?'''\n")
|
||||
multiline_test_string = (
|
||||
" '''The first line is under the max width.\n"
|
||||
" The second line's length is way over the max width. It goes "
|
||||
"on and on until it is over 100 characters long.\n"
|
||||
" Same thing with the third line. It is also way over the max "
|
||||
"width, but FormatParagraph will fix it.\n"
|
||||
" '''\n")
|
||||
multiline_test_comment = (
|
||||
"# The first line is under the max width.\n"
|
||||
"# The second line's length is way over the max width. It goes on "
|
||||
"and on until it is over 100 characters long.\n"
|
||||
"# Same thing with the third line. It is also way over the max "
|
||||
"width, but FormatParagraph will fix it.\n"
|
||||
"# The fourth line is short like the first line.")
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
editor = Editor(root=cls.root)
|
||||
cls.text = editor.text.text # Test code does not need the wrapper.
|
||||
cls.formatter = ft.FormatParagraph(editor).format_paragraph_event
|
||||
# Sets the insert mark just after the re-wrapped and inserted text.
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text, cls.formatter
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_short_line(self):
|
||||
self.text.insert('1.0', "Short line\n")
|
||||
self.formatter("Dummy")
|
||||
self.assertEqual(self.text.get('1.0', 'insert'), "Short line\n" )
|
||||
self.text.delete('1.0', 'end')
|
||||
|
||||
def test_long_line(self):
|
||||
text = self.text
|
||||
|
||||
# Set cursor ('insert' mark) to '1.0', within text.
|
||||
text.insert('1.0', self.test_string)
|
||||
text.mark_set('insert', '1.0')
|
||||
self.formatter('ParameterDoesNothing', limit=70)
|
||||
result = text.get('1.0', 'insert')
|
||||
# find function includes \n
|
||||
expected = (
|
||||
" '''this is a test of a reformat for a triple quoted string will it\n"
|
||||
" reformat to less than 70 characters for me?'''\n") # yes
|
||||
self.assertEqual(result, expected)
|
||||
text.delete('1.0', 'end')
|
||||
|
||||
# Select from 1.11 to line end.
|
||||
text.insert('1.0', self.test_string)
|
||||
text.tag_add('sel', '1.11', '1.end')
|
||||
self.formatter('ParameterDoesNothing', limit=70)
|
||||
result = text.get('1.0', 'insert')
|
||||
# selection excludes \n
|
||||
expected = (
|
||||
" '''this is a test of a reformat for a triple quoted string will it reformat\n"
|
||||
" to less than 70 characters for me?'''") # no
|
||||
self.assertEqual(result, expected)
|
||||
text.delete('1.0', 'end')
|
||||
|
||||
def test_multiple_lines(self):
|
||||
text = self.text
|
||||
# Select 2 long lines.
|
||||
text.insert('1.0', self.multiline_test_string)
|
||||
text.tag_add('sel', '2.0', '4.0')
|
||||
self.formatter('ParameterDoesNothing', limit=70)
|
||||
result = text.get('2.0', 'insert')
|
||||
expected = (
|
||||
" The second line's length is way over the max width. It goes on and\n"
|
||||
" on until it is over 100 characters long. Same thing with the third\n"
|
||||
" line. It is also way over the max width, but FormatParagraph will\n"
|
||||
" fix it.\n")
|
||||
self.assertEqual(result, expected)
|
||||
text.delete('1.0', 'end')
|
||||
|
||||
def test_comment_block(self):
|
||||
text = self.text
|
||||
|
||||
# Set cursor ('insert') to '1.0', within block.
|
||||
text.insert('1.0', self.multiline_test_comment)
|
||||
self.formatter('ParameterDoesNothing', limit=70)
|
||||
result = text.get('1.0', 'insert')
|
||||
expected = (
|
||||
"# The first line is under the max width. The second line's length is\n"
|
||||
"# way over the max width. It goes on and on until it is over 100\n"
|
||||
"# characters long. Same thing with the third line. It is also way over\n"
|
||||
"# the max width, but FormatParagraph will fix it. The fourth line is\n"
|
||||
"# short like the first line.\n")
|
||||
self.assertEqual(result, expected)
|
||||
text.delete('1.0', 'end')
|
||||
|
||||
# Select line 2, verify line 1 unaffected.
|
||||
text.insert('1.0', self.multiline_test_comment)
|
||||
text.tag_add('sel', '2.0', '3.0')
|
||||
self.formatter('ParameterDoesNothing', limit=70)
|
||||
result = text.get('1.0', 'insert')
|
||||
expected = (
|
||||
"# The first line is under the max width.\n"
|
||||
"# The second line's length is way over the max width. It goes on and\n"
|
||||
"# on until it is over 100 characters long.\n")
|
||||
self.assertEqual(result, expected)
|
||||
text.delete('1.0', 'end')
|
||||
|
||||
# The following block worked with EditorWindow but fails with the mock.
|
||||
# Lines 2 and 3 get pasted together even though the previous block left
|
||||
# the previous line alone. More investigation is needed.
|
||||
## # Select lines 3 and 4
|
||||
## text.insert('1.0', self.multiline_test_comment)
|
||||
## text.tag_add('sel', '3.0', '5.0')
|
||||
## self.formatter('ParameterDoesNothing')
|
||||
## result = text.get('3.0', 'insert')
|
||||
## expected = (
|
||||
##"# Same thing with the third line. It is also way over the max width,\n"
|
||||
##"# but FormatParagraph will fix it. The fourth line is short like the\n"
|
||||
##"# first line.\n")
|
||||
## self.assertEqual(result, expected)
|
||||
## text.delete('1.0', 'end')
|
||||
|
||||
|
||||
class DummyEditwin:
|
||||
def __init__(self, root, text):
|
||||
self.root = root
|
||||
self.text = text
|
||||
self.indentwidth = 4
|
||||
self.tabwidth = 4
|
||||
self.usetabs = False
|
||||
self.context_use_ps1 = True
|
||||
|
||||
_make_blanks = EditorWindow._make_blanks
|
||||
get_selection_indices = EditorWindow.get_selection_indices
|
||||
|
||||
|
||||
class FormatRegionTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.text = Text(cls.root)
|
||||
cls.text.undo_block_start = mock.Mock()
|
||||
cls.text.undo_block_stop = mock.Mock()
|
||||
cls.editor = DummyEditwin(cls.root, cls.text)
|
||||
cls.formatter = ft.FormatRegion(cls.editor)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text, cls.formatter, cls.editor
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.text.insert('1.0', self.code_sample)
|
||||
|
||||
def tearDown(self):
|
||||
self.text.delete('1.0', 'end')
|
||||
|
||||
code_sample = """\
|
||||
# WS line needed for test.
|
||||
class C1:
|
||||
# Class comment.
|
||||
def __init__(self, a, b):
|
||||
self.a = a
|
||||
self.b = b
|
||||
|
||||
def compare(self):
|
||||
if a > b:
|
||||
return a
|
||||
elif a < b:
|
||||
return b
|
||||
else:
|
||||
return None
|
||||
"""
|
||||
|
||||
def test_get_region(self):
|
||||
get = self.formatter.get_region
|
||||
text = self.text
|
||||
eq = self.assertEqual
|
||||
|
||||
# Add selection.
|
||||
text.tag_add('sel', '7.0', '10.0')
|
||||
expected_lines = ['',
|
||||
' def compare(self):',
|
||||
' if a > b:',
|
||||
'']
|
||||
eq(get(), ('7.0', '10.0', '\n'.join(expected_lines), expected_lines))
|
||||
|
||||
# Remove selection.
|
||||
text.tag_remove('sel', '1.0', 'end')
|
||||
eq(get(), ('15.0', '16.0', '\n', ['', '']))
|
||||
|
||||
def test_set_region(self):
|
||||
set_ = self.formatter.set_region
|
||||
text = self.text
|
||||
eq = self.assertEqual
|
||||
|
||||
save_bell = text.bell
|
||||
text.bell = mock.Mock()
|
||||
line6 = self.code_sample.splitlines()[5]
|
||||
line10 = self.code_sample.splitlines()[9]
|
||||
|
||||
text.tag_add('sel', '6.0', '11.0')
|
||||
head, tail, chars, lines = self.formatter.get_region()
|
||||
|
||||
# No changes.
|
||||
set_(head, tail, chars, lines)
|
||||
text.bell.assert_called_once()
|
||||
eq(text.get('6.0', '11.0'), chars)
|
||||
eq(text.get('sel.first', 'sel.last'), chars)
|
||||
text.tag_remove('sel', '1.0', 'end')
|
||||
|
||||
# Alter selected lines by changing lines and adding a newline.
|
||||
newstring = 'added line 1\n\n\n\n'
|
||||
newlines = newstring.split('\n')
|
||||
set_('7.0', '10.0', chars, newlines)
|
||||
# Selection changed.
|
||||
eq(text.get('sel.first', 'sel.last'), newstring)
|
||||
# Additional line added, so last index is changed.
|
||||
eq(text.get('7.0', '11.0'), newstring)
|
||||
# Before and after lines unchanged.
|
||||
eq(text.get('6.0', '7.0-1c'), line6)
|
||||
eq(text.get('11.0', '12.0-1c'), line10)
|
||||
text.tag_remove('sel', '1.0', 'end')
|
||||
|
||||
text.bell = save_bell
|
||||
|
||||
def test_indent_region_event(self):
|
||||
indent = self.formatter.indent_region_event
|
||||
text = self.text
|
||||
eq = self.assertEqual
|
||||
|
||||
text.tag_add('sel', '7.0', '10.0')
|
||||
indent()
|
||||
# Blank lines aren't affected by indent.
|
||||
eq(text.get('7.0', '10.0'), ('\n def compare(self):\n if a > b:\n'))
|
||||
|
||||
def test_dedent_region_event(self):
|
||||
dedent = self.formatter.dedent_region_event
|
||||
text = self.text
|
||||
eq = self.assertEqual
|
||||
|
||||
text.tag_add('sel', '7.0', '10.0')
|
||||
dedent()
|
||||
# Blank lines aren't affected by dedent.
|
||||
eq(text.get('7.0', '10.0'), ('\ndef compare(self):\n if a > b:\n'))
|
||||
|
||||
def test_comment_region_event(self):
|
||||
comment = self.formatter.comment_region_event
|
||||
text = self.text
|
||||
eq = self.assertEqual
|
||||
|
||||
text.tag_add('sel', '7.0', '10.0')
|
||||
comment()
|
||||
eq(text.get('7.0', '10.0'), ('##\n## def compare(self):\n## if a > b:\n'))
|
||||
|
||||
def test_uncomment_region_event(self):
|
||||
comment = self.formatter.comment_region_event
|
||||
uncomment = self.formatter.uncomment_region_event
|
||||
text = self.text
|
||||
eq = self.assertEqual
|
||||
|
||||
text.tag_add('sel', '7.0', '10.0')
|
||||
comment()
|
||||
uncomment()
|
||||
eq(text.get('7.0', '10.0'), ('\n def compare(self):\n if a > b:\n'))
|
||||
|
||||
# Only remove comments at the beginning of a line.
|
||||
text.tag_remove('sel', '1.0', 'end')
|
||||
text.tag_add('sel', '3.0', '4.0')
|
||||
uncomment()
|
||||
eq(text.get('3.0', '3.end'), (' # Class comment.'))
|
||||
|
||||
self.formatter.set_region('3.0', '4.0', '', ['# Class comment.', ''])
|
||||
uncomment()
|
||||
eq(text.get('3.0', '3.end'), (' Class comment.'))
|
||||
|
||||
@mock.patch.object(ft.FormatRegion, "_asktabwidth")
|
||||
def test_tabify_region_event(self, _asktabwidth):
|
||||
tabify = self.formatter.tabify_region_event
|
||||
text = self.text
|
||||
eq = self.assertEqual
|
||||
|
||||
text.tag_add('sel', '7.0', '10.0')
|
||||
# No tabwidth selected.
|
||||
_asktabwidth.return_value = None
|
||||
self.assertIsNone(tabify())
|
||||
|
||||
_asktabwidth.return_value = 3
|
||||
self.assertIsNotNone(tabify())
|
||||
eq(text.get('7.0', '10.0'), ('\n\t def compare(self):\n\t\t if a > b:\n'))
|
||||
|
||||
@mock.patch.object(ft.FormatRegion, "_asktabwidth")
|
||||
def test_untabify_region_event(self, _asktabwidth):
|
||||
untabify = self.formatter.untabify_region_event
|
||||
text = self.text
|
||||
eq = self.assertEqual
|
||||
|
||||
text.tag_add('sel', '7.0', '10.0')
|
||||
# No tabwidth selected.
|
||||
_asktabwidth.return_value = None
|
||||
self.assertIsNone(untabify())
|
||||
|
||||
_asktabwidth.return_value = 2
|
||||
self.formatter.tabify_region_event()
|
||||
_asktabwidth.return_value = 3
|
||||
self.assertIsNotNone(untabify())
|
||||
eq(text.get('7.0', '10.0'), ('\n def compare(self):\n if a > b:\n'))
|
||||
|
||||
@mock.patch.object(ft, "askinteger")
|
||||
def test_ask_tabwidth(self, askinteger):
|
||||
ask = self.formatter._asktabwidth
|
||||
askinteger.return_value = 10
|
||||
self.assertEqual(ask(), 10)
|
||||
|
||||
|
||||
class IndentsTest(unittest.TestCase):
|
||||
|
||||
@mock.patch.object(ft, "askyesno")
|
||||
def test_toggle_tabs(self, askyesno):
|
||||
editor = DummyEditwin(None, None) # usetabs == False.
|
||||
indents = ft.Indents(editor)
|
||||
askyesno.return_value = True
|
||||
|
||||
indents.toggle_tabs_event(None)
|
||||
self.assertEqual(editor.usetabs, True)
|
||||
self.assertEqual(editor.indentwidth, 8)
|
||||
|
||||
indents.toggle_tabs_event(None)
|
||||
self.assertEqual(editor.usetabs, False)
|
||||
self.assertEqual(editor.indentwidth, 8)
|
||||
|
||||
@mock.patch.object(ft, "askinteger")
|
||||
def test_change_indentwidth(self, askinteger):
|
||||
editor = DummyEditwin(None, None) # indentwidth == 4.
|
||||
indents = ft.Indents(editor)
|
||||
|
||||
askinteger.return_value = None
|
||||
indents.change_indentwidth_event(None)
|
||||
self.assertEqual(editor.indentwidth, 4)
|
||||
|
||||
askinteger.return_value = 3
|
||||
indents.change_indentwidth_event(None)
|
||||
self.assertEqual(editor.indentwidth, 3)
|
||||
|
||||
askinteger.return_value = 5
|
||||
editor.usetabs = True
|
||||
indents.change_indentwidth_event(None)
|
||||
self.assertEqual(editor.indentwidth, 3)
|
||||
|
||||
|
||||
class RstripTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.text = Text(cls.root)
|
||||
cls.editor = MockEditor(text=cls.text)
|
||||
cls.do_rstrip = ft.Rstrip(cls.editor).do_rstrip
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text, cls.do_rstrip, cls.editor
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def tearDown(self):
|
||||
self.text.delete('1.0', 'end-1c')
|
||||
|
||||
def test_rstrip_lines(self):
|
||||
original = (
|
||||
"Line with an ending tab \n"
|
||||
"Line ending in 5 spaces \n"
|
||||
"Linewithnospaces\n"
|
||||
" indented line\n"
|
||||
" indented line with trailing space \n"
|
||||
" \n")
|
||||
stripped = (
|
||||
"Line with an ending tab\n"
|
||||
"Line ending in 5 spaces\n"
|
||||
"Linewithnospaces\n"
|
||||
" indented line\n"
|
||||
" indented line with trailing space\n")
|
||||
|
||||
self.text.insert('1.0', original)
|
||||
self.do_rstrip()
|
||||
self.assertEqual(self.text.get('1.0', 'insert'), stripped)
|
||||
|
||||
def test_rstrip_end(self):
|
||||
text = self.text
|
||||
for code in ('', '\n', '\n\n\n'):
|
||||
with self.subTest(code=code):
|
||||
text.insert('1.0', code)
|
||||
self.do_rstrip()
|
||||
self.assertEqual(text.get('1.0','end-1c'), '')
|
||||
for code in ('a\n', 'a\n\n', 'a\n\n\n'):
|
||||
with self.subTest(code=code):
|
||||
text.delete('1.0', 'end-1c')
|
||||
text.insert('1.0', code)
|
||||
self.do_rstrip()
|
||||
self.assertEqual(text.get('1.0','end-1c'), 'a\n')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=2)
|
||||
157
Dependencies/Python/Lib/idlelib/idle_test/test_grep.py
vendored
Normal file
157
Dependencies/Python/Lib/idlelib/idle_test/test_grep.py
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
""" !Changing this line will break Test_findfile.test_found!
|
||||
Non-gui unit tests for grep.GrepDialog methods.
|
||||
dummy_command calls grep_it calls findfiles.
|
||||
An exception raised in one method will fail callers.
|
||||
Otherwise, tests are mostly independent.
|
||||
Currently only test grep_it, coverage 51%.
|
||||
"""
|
||||
from idlelib import grep
|
||||
import unittest
|
||||
from test.support import captured_stdout
|
||||
from test.support.testcase import ExtraAssertions
|
||||
from idlelib.idle_test.mock_tk import Var
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
class Dummy_searchengine:
|
||||
'''GrepDialog.__init__ calls parent SearchDiabolBase which attaches the
|
||||
passed in SearchEngine instance as attribute 'engine'. Only a few of the
|
||||
many possible self.engine.x attributes are needed here.
|
||||
'''
|
||||
def getpat(self):
|
||||
return self._pat
|
||||
|
||||
searchengine = Dummy_searchengine()
|
||||
|
||||
|
||||
class Dummy_grep:
|
||||
# Methods tested
|
||||
#default_command = GrepDialog.default_command
|
||||
grep_it = grep.GrepDialog.grep_it
|
||||
# Other stuff needed
|
||||
recvar = Var(False)
|
||||
engine = searchengine
|
||||
def close(self): # gui method
|
||||
pass
|
||||
|
||||
_grep = Dummy_grep()
|
||||
|
||||
|
||||
class FindfilesTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.realpath = os.path.realpath(__file__)
|
||||
cls.path = os.path.dirname(cls.realpath)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.realpath, cls.path
|
||||
|
||||
def test_invaliddir(self):
|
||||
with captured_stdout() as s:
|
||||
filelist = list(grep.findfiles('invaliddir', '*.*', False))
|
||||
self.assertEqual(filelist, [])
|
||||
self.assertIn('invalid', s.getvalue())
|
||||
|
||||
def test_curdir(self):
|
||||
# Test os.curdir.
|
||||
ff = grep.findfiles
|
||||
save_cwd = os.getcwd()
|
||||
os.chdir(self.path)
|
||||
filename = 'test_grep.py'
|
||||
filelist = list(ff(os.curdir, filename, False))
|
||||
self.assertIn(os.path.join(os.curdir, filename), filelist)
|
||||
os.chdir(save_cwd)
|
||||
|
||||
def test_base(self):
|
||||
ff = grep.findfiles
|
||||
readme = os.path.join(self.path, 'README.txt')
|
||||
|
||||
# Check for Python files in path where this file lives.
|
||||
filelist = list(ff(self.path, '*.py', False))
|
||||
# This directory has many Python files.
|
||||
self.assertGreater(len(filelist), 10)
|
||||
self.assertIn(self.realpath, filelist)
|
||||
self.assertNotIn(readme, filelist)
|
||||
|
||||
# Look for .txt files in path where this file lives.
|
||||
filelist = list(ff(self.path, '*.txt', False))
|
||||
self.assertNotEqual(len(filelist), 0)
|
||||
self.assertNotIn(self.realpath, filelist)
|
||||
self.assertIn(readme, filelist)
|
||||
|
||||
# Look for non-matching pattern.
|
||||
filelist = list(ff(self.path, 'grep.*', False))
|
||||
self.assertEqual(len(filelist), 0)
|
||||
self.assertNotIn(self.realpath, filelist)
|
||||
|
||||
def test_recurse(self):
|
||||
ff = grep.findfiles
|
||||
parent = os.path.dirname(self.path)
|
||||
grepfile = os.path.join(parent, 'grep.py')
|
||||
pat = '*.py'
|
||||
|
||||
# Get Python files only in parent directory.
|
||||
filelist = list(ff(parent, pat, False))
|
||||
parent_size = len(filelist)
|
||||
# Lots of Python files in idlelib.
|
||||
self.assertGreater(parent_size, 20)
|
||||
self.assertIn(grepfile, filelist)
|
||||
# Without subdirectories, this file isn't returned.
|
||||
self.assertNotIn(self.realpath, filelist)
|
||||
|
||||
# Include subdirectories.
|
||||
filelist = list(ff(parent, pat, True))
|
||||
# More files found now.
|
||||
self.assertGreater(len(filelist), parent_size)
|
||||
self.assertIn(grepfile, filelist)
|
||||
# This file exists in list now.
|
||||
self.assertIn(self.realpath, filelist)
|
||||
|
||||
# Check another level up the tree.
|
||||
parent = os.path.dirname(parent)
|
||||
filelist = list(ff(parent, '*.py', True))
|
||||
self.assertIn(self.realpath, filelist)
|
||||
|
||||
|
||||
class Grep_itTest(unittest.TestCase, ExtraAssertions):
|
||||
# Test captured reports with 0 and some hits.
|
||||
# Should test file names, but Windows reports have mixed / and \ separators
|
||||
# from incomplete replacement, so 'later'.
|
||||
|
||||
def report(self, pat):
|
||||
_grep.engine._pat = pat
|
||||
with captured_stdout() as s:
|
||||
_grep.grep_it(re.compile(pat), __file__)
|
||||
lines = s.getvalue().split('\n')
|
||||
lines.pop() # remove bogus '' after last \n
|
||||
return lines
|
||||
|
||||
def test_unfound(self):
|
||||
pat = 'xyz*'*7
|
||||
lines = self.report(pat)
|
||||
self.assertEqual(len(lines), 2)
|
||||
self.assertIn(pat, lines[0])
|
||||
self.assertEqual(lines[1], 'No hits.')
|
||||
|
||||
def test_found(self):
|
||||
|
||||
pat = '""" !Changing this line will break Test_findfile.test_found!'
|
||||
lines = self.report(pat)
|
||||
self.assertEqual(len(lines), 5)
|
||||
self.assertIn(pat, lines[0])
|
||||
self.assertIn('py: 1:', lines[1]) # line number 1
|
||||
self.assertIn('2', lines[3]) # hits found 2
|
||||
self.assertStartsWith(lines[4], '(Hint:')
|
||||
|
||||
|
||||
class Default_commandTest(unittest.TestCase):
|
||||
# To write this, move outwin import to top of GrepDialog
|
||||
# so it can be replaced by captured_stdout in class setup/teardown.
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
36
Dependencies/Python/Lib/idlelib/idle_test/test_help.py
vendored
Normal file
36
Dependencies/Python/Lib/idlelib/idle_test/test_help.py
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
"Test help, coverage 94%."
|
||||
|
||||
from idlelib import help
|
||||
import unittest
|
||||
from test.support import requires
|
||||
requires('gui')
|
||||
from os.path import abspath, dirname, join
|
||||
from tkinter import Tk
|
||||
|
||||
|
||||
class IdleDocTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"By itself, this tests that file parsed without exception."
|
||||
cls.root = root = Tk()
|
||||
root.withdraw()
|
||||
cls.window = help.show_idlehelp(root)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.window
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_1window(self):
|
||||
self.assertIn('IDLE Doc', self.window.wm_title())
|
||||
|
||||
def test_4text(self):
|
||||
text = self.window.frame.text
|
||||
self.assertEqual(text.get('1.0', '1.end'), ' IDLE — Python editor and shell ')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
182
Dependencies/Python/Lib/idlelib/idle_test/test_help_about.py
vendored
Normal file
182
Dependencies/Python/Lib/idlelib/idle_test/test_help_about.py
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
"""Test help_about, coverage 100%.
|
||||
help_about.build_bits branches on sys.platform='darwin'.
|
||||
'100% combines coverage on Mac and others.
|
||||
"""
|
||||
|
||||
from idlelib import help_about
|
||||
import unittest
|
||||
from test.support import requires, findfile
|
||||
from tkinter import Tk, TclError
|
||||
from idlelib.idle_test.mock_idle import Func
|
||||
from idlelib.idle_test.mock_tk import Mbox_func
|
||||
from idlelib import textview
|
||||
import os.path
|
||||
from platform import python_version
|
||||
|
||||
About = help_about.AboutDialog
|
||||
|
||||
|
||||
class LiveDialogTest(unittest.TestCase):
|
||||
"""Simulate user clicking buttons other than [Close].
|
||||
|
||||
Test that invoked textview has text from source.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.dialog = About(cls.root, 'About IDLE', _utest=True)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.dialog
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_build_bits(self):
|
||||
self.assertIn(help_about.bits, ('32', '64'))
|
||||
|
||||
def test_dialog_title(self):
|
||||
"""Test about dialog title"""
|
||||
self.assertEqual(self.dialog.title(), 'About IDLE')
|
||||
|
||||
def test_dialog_logo(self):
|
||||
"""Test about dialog logo."""
|
||||
path, file = os.path.split(self.dialog.icon_image['file'])
|
||||
fn, ext = os.path.splitext(file)
|
||||
self.assertEqual(fn, 'idle_48')
|
||||
|
||||
def test_printer_buttons(self):
|
||||
"""Test buttons whose commands use printer function."""
|
||||
dialog = self.dialog
|
||||
button_sources = [(dialog.py_license, license, 'license'),
|
||||
(dialog.py_copyright, copyright, 'copyright'),
|
||||
(dialog.py_credits, credits, 'credits')]
|
||||
|
||||
for button, printer, name in button_sources:
|
||||
with self.subTest(name=name):
|
||||
printer._Printer__setup()
|
||||
button.invoke()
|
||||
get = dialog._current_textview.viewframe.textframe.text.get
|
||||
lines = printer._Printer__lines
|
||||
if len(lines) < 2:
|
||||
self.fail(name + ' full text was not found')
|
||||
self.assertEqual(lines[0], get('1.0', '1.end'))
|
||||
self.assertEqual(lines[1], get('2.0', '2.end'))
|
||||
dialog._current_textview.destroy()
|
||||
|
||||
def test_file_buttons(self):
|
||||
"""Test buttons that display files."""
|
||||
dialog = self.dialog
|
||||
button_sources = [(self.dialog.readme, 'README.txt', 'readme'),
|
||||
(self.dialog.idle_news, 'News3.txt', 'news'),
|
||||
(self.dialog.idle_credits, 'CREDITS.txt', 'credits')]
|
||||
|
||||
for button, filename, name in button_sources:
|
||||
with self.subTest(name=name):
|
||||
button.invoke()
|
||||
fn = findfile(filename, subdir='idlelib')
|
||||
get = dialog._current_textview.viewframe.textframe.text.get
|
||||
with open(fn, encoding='utf-8') as f:
|
||||
self.assertEqual(f.readline().strip(), get('1.0', '1.end'))
|
||||
f.readline()
|
||||
self.assertEqual(f.readline().strip(), get('3.0', '3.end'))
|
||||
dialog._current_textview.destroy()
|
||||
|
||||
|
||||
class DefaultTitleTest(unittest.TestCase):
|
||||
"Test default title."
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.dialog = About(cls.root, _utest=True)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.dialog
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_dialog_title(self):
|
||||
"""Test about dialog title"""
|
||||
self.assertEqual(self.dialog.title(),
|
||||
f'About IDLE {python_version()}'
|
||||
f' ({help_about.bits} bit)')
|
||||
|
||||
|
||||
class CloseTest(unittest.TestCase):
|
||||
"""Simulate user clicking [Close] button"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.dialog = About(cls.root, 'About IDLE', _utest=True)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.dialog
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_close(self):
|
||||
self.assertEqual(self.dialog.winfo_class(), 'Toplevel')
|
||||
self.dialog.button_ok.invoke()
|
||||
with self.assertRaises(TclError):
|
||||
self.dialog.winfo_class()
|
||||
|
||||
|
||||
class Dummy_about_dialog:
|
||||
# Dummy class for testing file display functions.
|
||||
idle_credits = About.show_idle_credits
|
||||
idle_readme = About.show_readme
|
||||
idle_news = About.show_idle_news
|
||||
# Called by the above
|
||||
display_file_text = About.display_file_text
|
||||
_utest = True
|
||||
|
||||
|
||||
class DisplayFileTest(unittest.TestCase):
|
||||
"""Test functions that display files.
|
||||
|
||||
While somewhat redundant with gui-based test_file_dialog,
|
||||
these unit tests run on all buildbots, not just a few.
|
||||
"""
|
||||
dialog = Dummy_about_dialog()
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.orig_error = textview.showerror
|
||||
cls.orig_view = textview.view_text
|
||||
cls.error = Mbox_func()
|
||||
cls.view = Func()
|
||||
textview.showerror = cls.error
|
||||
textview.view_text = cls.view
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
textview.showerror = cls.orig_error
|
||||
textview.view_text = cls.orig_view
|
||||
|
||||
def test_file_display(self):
|
||||
for handler in (self.dialog.idle_credits,
|
||||
self.dialog.idle_readme,
|
||||
self.dialog.idle_news):
|
||||
self.error.message = ''
|
||||
self.view.called = False
|
||||
with self.subTest(handler=handler):
|
||||
handler()
|
||||
self.assertEqual(self.error.message, '')
|
||||
self.assertEqual(self.view.called, True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
172
Dependencies/Python/Lib/idlelib/idle_test/test_history.py
vendored
Normal file
172
Dependencies/Python/Lib/idlelib/idle_test/test_history.py
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
" Test history, coverage 100%."
|
||||
|
||||
from idlelib.history import History
|
||||
import unittest
|
||||
from test.support import requires
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import Text as tkText
|
||||
from idlelib.idle_test.mock_tk import Text as mkText
|
||||
from idlelib.config import idleConf
|
||||
|
||||
line1 = 'a = 7'
|
||||
line2 = 'b = a'
|
||||
|
||||
|
||||
class StoreTest(unittest.TestCase):
|
||||
'''Tests History.__init__ and History.store with mock Text'''
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.text = mkText()
|
||||
cls.history = History(cls.text)
|
||||
|
||||
def tearDown(self):
|
||||
self.text.delete('1.0', 'end')
|
||||
self.history.history = []
|
||||
|
||||
def test_init(self):
|
||||
self.assertIs(self.history.text, self.text)
|
||||
self.assertEqual(self.history.history, [])
|
||||
self.assertIsNone(self.history.prefix)
|
||||
self.assertIsNone(self.history.pointer)
|
||||
self.assertEqual(self.history.cyclic,
|
||||
idleConf.GetOption("main", "History", "cyclic", 1, "bool"))
|
||||
|
||||
def test_store_short(self):
|
||||
self.history.store('a')
|
||||
self.assertEqual(self.history.history, [])
|
||||
self.history.store(' a ')
|
||||
self.assertEqual(self.history.history, [])
|
||||
|
||||
def test_store_dup(self):
|
||||
self.history.store(line1)
|
||||
self.assertEqual(self.history.history, [line1])
|
||||
self.history.store(line2)
|
||||
self.assertEqual(self.history.history, [line1, line2])
|
||||
self.history.store(line1)
|
||||
self.assertEqual(self.history.history, [line2, line1])
|
||||
|
||||
def test_store_reset(self):
|
||||
self.history.prefix = line1
|
||||
self.history.pointer = 0
|
||||
self.history.store(line2)
|
||||
self.assertIsNone(self.history.prefix)
|
||||
self.assertIsNone(self.history.pointer)
|
||||
|
||||
|
||||
class TextWrapper:
|
||||
def __init__(self, master):
|
||||
self.text = tkText(master=master)
|
||||
self._bell = False
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.text, name)
|
||||
def bell(self):
|
||||
self._bell = True
|
||||
|
||||
|
||||
class FetchTest(unittest.TestCase):
|
||||
'''Test History.fetch with wrapped tk.Text.
|
||||
'''
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = tk.Tk()
|
||||
cls.root.withdraw()
|
||||
|
||||
def setUp(self):
|
||||
self.text = text = TextWrapper(self.root)
|
||||
text.insert('1.0', ">>> ")
|
||||
text.mark_set('iomark', '1.4')
|
||||
text.mark_gravity('iomark', 'left')
|
||||
self.history = History(text)
|
||||
self.history.history = [line1, line2]
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def fetch_test(self, reverse, line, prefix, index, *, bell=False):
|
||||
# Perform one fetch as invoked by Alt-N or Alt-P
|
||||
# Test the result. The line test is the most important.
|
||||
# The last two are diagnostic of fetch internals.
|
||||
History = self.history
|
||||
History.fetch(reverse)
|
||||
|
||||
Equal = self.assertEqual
|
||||
Equal(self.text.get('iomark', 'end-1c'), line)
|
||||
Equal(self.text._bell, bell)
|
||||
if bell:
|
||||
self.text._bell = False
|
||||
Equal(History.prefix, prefix)
|
||||
Equal(History.pointer, index)
|
||||
Equal(self.text.compare("insert", '==', "end-1c"), 1)
|
||||
|
||||
def test_fetch_prev_cyclic(self):
|
||||
prefix = ''
|
||||
test = self.fetch_test
|
||||
test(True, line2, prefix, 1)
|
||||
test(True, line1, prefix, 0)
|
||||
test(True, prefix, None, None, bell=True)
|
||||
|
||||
def test_fetch_next_cyclic(self):
|
||||
prefix = ''
|
||||
test = self.fetch_test
|
||||
test(False, line1, prefix, 0)
|
||||
test(False, line2, prefix, 1)
|
||||
test(False, prefix, None, None, bell=True)
|
||||
|
||||
# Prefix 'a' tests skip line2, which starts with 'b'
|
||||
def test_fetch_prev_prefix(self):
|
||||
prefix = 'a'
|
||||
self.text.insert('iomark', prefix)
|
||||
self.fetch_test(True, line1, prefix, 0)
|
||||
self.fetch_test(True, prefix, None, None, bell=True)
|
||||
|
||||
def test_fetch_next_prefix(self):
|
||||
prefix = 'a'
|
||||
self.text.insert('iomark', prefix)
|
||||
self.fetch_test(False, line1, prefix, 0)
|
||||
self.fetch_test(False, prefix, None, None, bell=True)
|
||||
|
||||
def test_fetch_prev_noncyclic(self):
|
||||
prefix = ''
|
||||
self.history.cyclic = False
|
||||
test = self.fetch_test
|
||||
test(True, line2, prefix, 1)
|
||||
test(True, line1, prefix, 0)
|
||||
test(True, line1, prefix, 0, bell=True)
|
||||
|
||||
def test_fetch_next_noncyclic(self):
|
||||
prefix = ''
|
||||
self.history.cyclic = False
|
||||
test = self.fetch_test
|
||||
test(False, prefix, None, None, bell=True)
|
||||
test(True, line2, prefix, 1)
|
||||
test(False, prefix, None, None, bell=True)
|
||||
test(False, prefix, None, None, bell=True)
|
||||
|
||||
def test_fetch_cursor_move(self):
|
||||
# Move cursor after fetch
|
||||
self.history.fetch(reverse=True) # initialization
|
||||
self.text.mark_set('insert', 'iomark')
|
||||
self.fetch_test(True, line2, None, None, bell=True)
|
||||
|
||||
def test_fetch_edit(self):
|
||||
# Edit after fetch
|
||||
self.history.fetch(reverse=True) # initialization
|
||||
self.text.delete('iomark', 'insert', )
|
||||
self.text.insert('iomark', 'a =')
|
||||
self.fetch_test(True, line1, 'a =', 0) # prefix is reset
|
||||
|
||||
def test_history_prev_next(self):
|
||||
# Minimally test functions bound to events
|
||||
self.history.history_prev('dummy event')
|
||||
self.assertEqual(self.history.pointer, 1)
|
||||
self.history.history_next('dummy event')
|
||||
self.assertEqual(self.history.pointer, None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=2)
|
||||
276
Dependencies/Python/Lib/idlelib/idle_test/test_hyperparser.py
vendored
Normal file
276
Dependencies/Python/Lib/idlelib/idle_test/test_hyperparser.py
vendored
Normal file
@@ -0,0 +1,276 @@
|
||||
"Test hyperparser, coverage 98%."
|
||||
|
||||
from idlelib.hyperparser import HyperParser
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk, Text
|
||||
from idlelib.editor import EditorWindow
|
||||
|
||||
class DummyEditwin:
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
self.indentwidth = 8
|
||||
self.tabwidth = 8
|
||||
self.prompt_last_line = '>>>'
|
||||
self.num_context_lines = 50, 500, 1000
|
||||
|
||||
_build_char_in_string_func = EditorWindow._build_char_in_string_func
|
||||
is_char_in_string = EditorWindow.is_char_in_string
|
||||
|
||||
|
||||
class HyperParserTest(unittest.TestCase):
|
||||
code = (
|
||||
'"""This is a module docstring"""\n'
|
||||
'# this line is a comment\n'
|
||||
'x = "this is a string"\n'
|
||||
"y = 'this is also a string'\n"
|
||||
'l = [i for i in range(10)]\n'
|
||||
'm = [py*py for # comment\n'
|
||||
' py in l]\n'
|
||||
'x.__len__\n'
|
||||
"z = ((r'asdf')+('a')))\n"
|
||||
'[x for x in\n'
|
||||
'for = False\n'
|
||||
'cliché = "this is a string with unicode, what a cliché"'
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.text = Text(cls.root)
|
||||
cls.editwin = DummyEditwin(cls.text)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text, cls.editwin
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.text.insert('insert', self.code)
|
||||
|
||||
def tearDown(self):
|
||||
self.text.delete('1.0', 'end')
|
||||
self.editwin.prompt_last_line = '>>>'
|
||||
|
||||
def get_parser(self, index):
|
||||
"""
|
||||
Return a parser object with index at 'index'
|
||||
"""
|
||||
return HyperParser(self.editwin, index)
|
||||
|
||||
def test_init(self):
|
||||
"""
|
||||
test corner cases in the init method
|
||||
"""
|
||||
with self.assertRaises(ValueError) as ve:
|
||||
self.text.tag_add('console', '1.0', '1.end')
|
||||
p = self.get_parser('1.5')
|
||||
self.assertIn('precedes', str(ve.exception))
|
||||
|
||||
# test without ps1
|
||||
self.editwin.prompt_last_line = ''
|
||||
|
||||
# number of lines lesser than 50
|
||||
p = self.get_parser('end')
|
||||
self.assertEqual(p.rawtext, self.text.get('1.0', 'end'))
|
||||
|
||||
# number of lines greater than 50
|
||||
self.text.insert('end', self.text.get('1.0', 'end')*4)
|
||||
p = self.get_parser('54.5')
|
||||
|
||||
def test_is_in_string(self):
|
||||
get = self.get_parser
|
||||
|
||||
p = get('1.0')
|
||||
self.assertFalse(p.is_in_string())
|
||||
p = get('1.4')
|
||||
self.assertTrue(p.is_in_string())
|
||||
p = get('2.3')
|
||||
self.assertFalse(p.is_in_string())
|
||||
p = get('3.3')
|
||||
self.assertFalse(p.is_in_string())
|
||||
p = get('3.7')
|
||||
self.assertTrue(p.is_in_string())
|
||||
p = get('4.6')
|
||||
self.assertTrue(p.is_in_string())
|
||||
p = get('12.54')
|
||||
self.assertTrue(p.is_in_string())
|
||||
|
||||
def test_is_in_code(self):
|
||||
get = self.get_parser
|
||||
|
||||
p = get('1.0')
|
||||
self.assertTrue(p.is_in_code())
|
||||
p = get('1.1')
|
||||
self.assertFalse(p.is_in_code())
|
||||
p = get('2.5')
|
||||
self.assertFalse(p.is_in_code())
|
||||
p = get('3.4')
|
||||
self.assertTrue(p.is_in_code())
|
||||
p = get('3.6')
|
||||
self.assertFalse(p.is_in_code())
|
||||
p = get('4.14')
|
||||
self.assertFalse(p.is_in_code())
|
||||
|
||||
def test_get_surrounding_bracket(self):
|
||||
get = self.get_parser
|
||||
|
||||
def without_mustclose(parser):
|
||||
# a utility function to get surrounding bracket
|
||||
# with mustclose=False
|
||||
return parser.get_surrounding_brackets(mustclose=False)
|
||||
|
||||
def with_mustclose(parser):
|
||||
# a utility function to get surrounding bracket
|
||||
# with mustclose=True
|
||||
return parser.get_surrounding_brackets(mustclose=True)
|
||||
|
||||
p = get('3.2')
|
||||
self.assertIsNone(with_mustclose(p))
|
||||
self.assertIsNone(without_mustclose(p))
|
||||
|
||||
p = get('5.6')
|
||||
self.assertTupleEqual(without_mustclose(p), ('5.4', '5.25'))
|
||||
self.assertTupleEqual(without_mustclose(p), with_mustclose(p))
|
||||
|
||||
p = get('5.23')
|
||||
self.assertTupleEqual(without_mustclose(p), ('5.21', '5.24'))
|
||||
self.assertTupleEqual(without_mustclose(p), with_mustclose(p))
|
||||
|
||||
p = get('6.15')
|
||||
self.assertTupleEqual(without_mustclose(p), ('6.4', '6.end'))
|
||||
self.assertIsNone(with_mustclose(p))
|
||||
|
||||
p = get('9.end')
|
||||
self.assertIsNone(with_mustclose(p))
|
||||
self.assertIsNone(without_mustclose(p))
|
||||
|
||||
def test_get_expression(self):
|
||||
get = self.get_parser
|
||||
|
||||
p = get('4.2')
|
||||
self.assertEqual(p.get_expression(), 'y ')
|
||||
|
||||
p = get('4.7')
|
||||
with self.assertRaises(ValueError) as ve:
|
||||
p.get_expression()
|
||||
self.assertIn('is inside a code', str(ve.exception))
|
||||
|
||||
p = get('5.25')
|
||||
self.assertEqual(p.get_expression(), 'range(10)')
|
||||
|
||||
p = get('6.7')
|
||||
self.assertEqual(p.get_expression(), 'py')
|
||||
|
||||
p = get('6.8')
|
||||
self.assertEqual(p.get_expression(), '')
|
||||
|
||||
p = get('7.9')
|
||||
self.assertEqual(p.get_expression(), 'py')
|
||||
|
||||
p = get('8.end')
|
||||
self.assertEqual(p.get_expression(), 'x.__len__')
|
||||
|
||||
p = get('9.13')
|
||||
self.assertEqual(p.get_expression(), "r'asdf'")
|
||||
|
||||
p = get('9.17')
|
||||
with self.assertRaises(ValueError) as ve:
|
||||
p.get_expression()
|
||||
self.assertIn('is inside a code', str(ve.exception))
|
||||
|
||||
p = get('10.0')
|
||||
self.assertEqual(p.get_expression(), '')
|
||||
|
||||
p = get('10.6')
|
||||
self.assertEqual(p.get_expression(), '')
|
||||
|
||||
p = get('10.11')
|
||||
self.assertEqual(p.get_expression(), '')
|
||||
|
||||
p = get('11.3')
|
||||
self.assertEqual(p.get_expression(), '')
|
||||
|
||||
p = get('11.11')
|
||||
self.assertEqual(p.get_expression(), 'False')
|
||||
|
||||
p = get('12.6')
|
||||
self.assertEqual(p.get_expression(), 'cliché')
|
||||
|
||||
def test_eat_identifier(self):
|
||||
def is_valid_id(candidate):
|
||||
result = HyperParser._eat_identifier(candidate, 0, len(candidate))
|
||||
if result == len(candidate):
|
||||
return True
|
||||
elif result == 0:
|
||||
return False
|
||||
else:
|
||||
err_msg = "Unexpected result: {} (expected 0 or {}".format(
|
||||
result, len(candidate)
|
||||
)
|
||||
raise Exception(err_msg)
|
||||
|
||||
# invalid first character which is valid elsewhere in an identifier
|
||||
self.assertFalse(is_valid_id('2notid'))
|
||||
|
||||
# ASCII-only valid identifiers
|
||||
self.assertTrue(is_valid_id('valid_id'))
|
||||
self.assertTrue(is_valid_id('_valid_id'))
|
||||
self.assertTrue(is_valid_id('valid_id_'))
|
||||
self.assertTrue(is_valid_id('_2valid_id'))
|
||||
|
||||
# keywords which should be "eaten"
|
||||
self.assertTrue(is_valid_id('True'))
|
||||
self.assertTrue(is_valid_id('False'))
|
||||
self.assertTrue(is_valid_id('None'))
|
||||
|
||||
# keywords which should not be "eaten"
|
||||
self.assertFalse(is_valid_id('for'))
|
||||
self.assertFalse(is_valid_id('import'))
|
||||
self.assertFalse(is_valid_id('return'))
|
||||
|
||||
# valid unicode identifiers
|
||||
self.assertTrue(is_valid_id('cliche'))
|
||||
self.assertTrue(is_valid_id('cliché'))
|
||||
self.assertTrue(is_valid_id('a٢'))
|
||||
|
||||
# invalid unicode identifiers
|
||||
self.assertFalse(is_valid_id('2a'))
|
||||
self.assertFalse(is_valid_id('٢a'))
|
||||
self.assertFalse(is_valid_id('a²'))
|
||||
|
||||
# valid identifier after "punctuation"
|
||||
self.assertEqual(HyperParser._eat_identifier('+ var', 0, 5), len('var'))
|
||||
self.assertEqual(HyperParser._eat_identifier('+var', 0, 4), len('var'))
|
||||
self.assertEqual(HyperParser._eat_identifier('.var', 0, 4), len('var'))
|
||||
|
||||
# invalid identifiers
|
||||
self.assertFalse(is_valid_id('+'))
|
||||
self.assertFalse(is_valid_id(' '))
|
||||
self.assertFalse(is_valid_id(':'))
|
||||
self.assertFalse(is_valid_id('?'))
|
||||
self.assertFalse(is_valid_id('^'))
|
||||
self.assertFalse(is_valid_id('\\'))
|
||||
self.assertFalse(is_valid_id('"'))
|
||||
self.assertFalse(is_valid_id('"a string"'))
|
||||
|
||||
def test_eat_identifier_various_lengths(self):
|
||||
eat_id = HyperParser._eat_identifier
|
||||
|
||||
for length in range(1, 21):
|
||||
self.assertEqual(eat_id('a' * length, 0, length), length)
|
||||
self.assertEqual(eat_id('é' * length, 0, length), length)
|
||||
self.assertEqual(eat_id('a' + '2' * (length - 1), 0, length), length)
|
||||
self.assertEqual(eat_id('é' + '2' * (length - 1), 0, length), length)
|
||||
self.assertEqual(eat_id('é' + 'a' * (length - 1), 0, length), length)
|
||||
self.assertEqual(eat_id('é' * (length - 1) + 'a', 0, length), length)
|
||||
self.assertEqual(eat_id('+' * length, 0, length), 0)
|
||||
self.assertEqual(eat_id('2' + 'a' * (length - 1), 0, length), 0)
|
||||
self.assertEqual(eat_id('2' + 'é' * (length - 1), 0, length), 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
84
Dependencies/Python/Lib/idlelib/idle_test/test_iomenu.py
vendored
Normal file
84
Dependencies/Python/Lib/idlelib/idle_test/test_iomenu.py
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
"Test , coverage 17%."
|
||||
|
||||
from idlelib import iomenu
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk
|
||||
from idlelib.editor import EditorWindow
|
||||
from idlelib import util
|
||||
from idlelib.idle_test.mock_idle import Func
|
||||
|
||||
# Fail if either tokenize.open and t.detect_encoding does not exist.
|
||||
# These are used in loadfile and encode.
|
||||
# Also used in pyshell.MI.execfile and runscript.tabnanny.
|
||||
from tokenize import open, detect_encoding
|
||||
# Remove when we have proper tests that use both.
|
||||
|
||||
|
||||
class IOBindingTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.editwin = EditorWindow(root=cls.root)
|
||||
cls.io = iomenu.IOBinding(cls.editwin)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.io.close()
|
||||
cls.editwin._close()
|
||||
del cls.editwin
|
||||
cls.root.update_idletasks()
|
||||
for id in cls.root.tk.call('after', 'info'):
|
||||
cls.root.after_cancel(id) # Need for EditorWindow.
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_init(self):
|
||||
self.assertIs(self.io.editwin, self.editwin)
|
||||
|
||||
def test_fixnewlines_end(self):
|
||||
eq = self.assertEqual
|
||||
io = self.io
|
||||
fix = io.fixnewlines
|
||||
text = io.editwin.text
|
||||
|
||||
# Make the editor temporarily look like Shell.
|
||||
self.editwin.interp = None
|
||||
shelltext = '>>> if 1'
|
||||
self.editwin.get_prompt_text = Func(result=shelltext)
|
||||
eq(fix(), shelltext) # Get... call and '\n' not added.
|
||||
del self.editwin.interp, self.editwin.get_prompt_text
|
||||
|
||||
text.insert(1.0, 'a')
|
||||
eq(fix(), 'a'+io.eol_convention)
|
||||
eq(text.get('1.0', 'end-1c'), 'a\n')
|
||||
eq(fix(), 'a'+io.eol_convention)
|
||||
|
||||
|
||||
def _extension_in_filetypes(extension):
|
||||
return any(
|
||||
f'*{extension}' in filetype_tuple[1]
|
||||
for filetype_tuple in iomenu.IOBinding.filetypes
|
||||
)
|
||||
|
||||
|
||||
class FiletypesTest(unittest.TestCase):
|
||||
def test_python_source_files(self):
|
||||
for extension in util.py_extensions:
|
||||
with self.subTest(extension=extension):
|
||||
self.assertTrue(
|
||||
_extension_in_filetypes(extension)
|
||||
)
|
||||
|
||||
def test_text_files(self):
|
||||
self.assertTrue(_extension_in_filetypes('.txt'))
|
||||
|
||||
def test_all_files(self):
|
||||
self.assertTrue(_extension_in_filetypes(''))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
113
Dependencies/Python/Lib/idlelib/idle_test/test_macosx.py
vendored
Normal file
113
Dependencies/Python/Lib/idlelib/idle_test/test_macosx.py
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
"Test macosx, coverage 45% on Windows."
|
||||
|
||||
from idlelib import macosx
|
||||
import unittest
|
||||
from test.support import requires
|
||||
import tkinter as tk
|
||||
import unittest.mock as mock
|
||||
from idlelib.filelist import FileList
|
||||
|
||||
mactypes = {'carbon', 'cocoa', 'xquartz'}
|
||||
nontypes = {'other'}
|
||||
alltypes = mactypes | nontypes
|
||||
|
||||
|
||||
def setUpModule():
|
||||
global orig_tktype
|
||||
orig_tktype = macosx._tk_type
|
||||
|
||||
|
||||
def tearDownModule():
|
||||
macosx._tk_type = orig_tktype
|
||||
|
||||
|
||||
class InitTktypeTest(unittest.TestCase):
|
||||
"Test _init_tk_type."
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = tk.Tk()
|
||||
cls.root.withdraw()
|
||||
cls.orig_platform = macosx.platform
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
macosx.platform = cls.orig_platform
|
||||
|
||||
def test_init_sets_tktype(self):
|
||||
"Test that _init_tk_type sets _tk_type according to platform."
|
||||
for platform, types in ('darwin', alltypes), ('other', nontypes):
|
||||
with self.subTest(platform=platform):
|
||||
macosx.platform = platform
|
||||
macosx._tk_type = None
|
||||
macosx._init_tk_type()
|
||||
self.assertIn(macosx._tk_type, types)
|
||||
|
||||
|
||||
class IsTypeTkTest(unittest.TestCase):
|
||||
"Test each of the four isTypeTk predecates."
|
||||
isfuncs = ((macosx.isAquaTk, ('carbon', 'cocoa')),
|
||||
(macosx.isCarbonTk, ('carbon')),
|
||||
(macosx.isCocoaTk, ('cocoa')),
|
||||
(macosx.isXQuartz, ('xquartz')),
|
||||
)
|
||||
|
||||
@mock.patch('idlelib.macosx._init_tk_type')
|
||||
def test_is_calls_init(self, mockinit):
|
||||
"Test that each isTypeTk calls _init_tk_type when _tk_type is None."
|
||||
macosx._tk_type = None
|
||||
for func, whentrue in self.isfuncs:
|
||||
with self.subTest(func=func):
|
||||
func()
|
||||
self.assertTrue(mockinit.called)
|
||||
mockinit.reset_mock()
|
||||
|
||||
def test_isfuncs(self):
|
||||
"Test that each isTypeTk return correct bool."
|
||||
for func, whentrue in self.isfuncs:
|
||||
for tktype in alltypes:
|
||||
with self.subTest(func=func, whentrue=whentrue, tktype=tktype):
|
||||
macosx._tk_type = tktype
|
||||
(self.assertTrue if tktype in whentrue else self.assertFalse)\
|
||||
(func())
|
||||
|
||||
|
||||
class SetupTest(unittest.TestCase):
|
||||
"Test setupApp."
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = tk.Tk()
|
||||
cls.root.withdraw()
|
||||
def cmd(tkpath, func):
|
||||
assert isinstance(tkpath, str)
|
||||
assert isinstance(func, type(cmd))
|
||||
cls.root.createcommand = cmd
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
@mock.patch('idlelib.macosx.overrideRootMenu') #27312
|
||||
def test_setupapp(self, overrideRootMenu):
|
||||
"Call setupApp with each possible graphics type."
|
||||
root = self.root
|
||||
flist = FileList(root)
|
||||
for tktype in alltypes:
|
||||
with self.subTest(tktype=tktype):
|
||||
macosx._tk_type = tktype
|
||||
macosx.setupApp(root, flist)
|
||||
if tktype in ('carbon', 'cocoa'):
|
||||
self.assertTrue(overrideRootMenu.called)
|
||||
overrideRootMenu.reset_mock()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
42
Dependencies/Python/Lib/idlelib/idle_test/test_mainmenu.py
vendored
Normal file
42
Dependencies/Python/Lib/idlelib/idle_test/test_mainmenu.py
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
"Test mainmenu, coverage 100%."
|
||||
# Reported as 88%; mocking turtledemo absence would have no point.
|
||||
|
||||
from idlelib import mainmenu
|
||||
import re
|
||||
import unittest
|
||||
|
||||
|
||||
class MainMenuTest(unittest.TestCase):
|
||||
|
||||
def test_menudefs(self):
|
||||
actual = [item[0] for item in mainmenu.menudefs]
|
||||
expect = ['file', 'edit', 'format', 'run', 'shell',
|
||||
'debug', 'options', 'window', 'help']
|
||||
self.assertEqual(actual, expect)
|
||||
|
||||
def test_default_keydefs(self):
|
||||
self.assertGreaterEqual(len(mainmenu.default_keydefs), 50)
|
||||
|
||||
def test_tcl_indexes(self):
|
||||
# Test tcl patterns used to find menuitem to alter.
|
||||
# On failure, change pattern here and in function(s).
|
||||
# Patterns here have '.*' for re instead of '*' for tcl.
|
||||
for menu, pattern in (
|
||||
('debug', '.*tack.*iewer'), # PyShell.debug_menu_postcommand
|
||||
('options', '.*ode.*ontext'), # EW.__init__, CodeContext.toggle...
|
||||
('options', '.*ine.*umbers'), # EW.__init__, EW.toggle...event.
|
||||
):
|
||||
with self.subTest(menu=menu, pattern=pattern):
|
||||
for menutup in mainmenu.menudefs:
|
||||
if menutup[0] == menu:
|
||||
break
|
||||
else:
|
||||
self.assertTrue(0, f"{menu} not in menudefs")
|
||||
self.assertTrue(any(re.search(pattern, menuitem[0])
|
||||
for menuitem in menutup[1]
|
||||
if menuitem is not None), # Separator.
|
||||
f"{pattern} not in {menu}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
49
Dependencies/Python/Lib/idlelib/idle_test/test_multicall.py
vendored
Normal file
49
Dependencies/Python/Lib/idlelib/idle_test/test_multicall.py
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
"Test multicall, coverage 33%."
|
||||
|
||||
from idlelib import multicall
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from test.support.testcase import ExtraAssertions
|
||||
from tkinter import Tk, Text
|
||||
|
||||
|
||||
class MultiCallTest(unittest.TestCase, ExtraAssertions):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.mc = multicall.MultiCallCreator(Text)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.mc
|
||||
cls.root.update_idletasks()
|
||||
## for id in cls.root.tk.call('after', 'info'):
|
||||
## cls.root.after_cancel(id) # Need for EditorWindow.
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_creator(self):
|
||||
mc = self.mc
|
||||
self.assertIs(multicall._multicall_dict[Text], mc)
|
||||
self.assertIsSubclass(mc, Text)
|
||||
mc2 = multicall.MultiCallCreator(Text)
|
||||
self.assertIs(mc, mc2)
|
||||
|
||||
def test_init(self):
|
||||
mctext = self.mc(self.root)
|
||||
self.assertIsInstance(mctext._MultiCall__binders, list)
|
||||
|
||||
def test_yview(self):
|
||||
# Added for tree.wheel_event
|
||||
# (it depends on yview to not be overridden)
|
||||
mc = self.mc
|
||||
self.assertIs(mc.yview, Text.yview)
|
||||
mctext = self.mc(self.root)
|
||||
self.assertIs(mctext.yview.__func__, Text.yview)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
171
Dependencies/Python/Lib/idlelib/idle_test/test_outwin.py
vendored
Normal file
171
Dependencies/Python/Lib/idlelib/idle_test/test_outwin.py
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
"Test outwin, coverage 76%."
|
||||
|
||||
from idlelib import outwin
|
||||
import sys
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk, Text
|
||||
from idlelib.idle_test.mock_tk import Mbox_func
|
||||
from idlelib.idle_test.mock_idle import Func
|
||||
from unittest import mock
|
||||
|
||||
|
||||
class OutputWindowTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
root = cls.root = Tk()
|
||||
root.withdraw()
|
||||
w = cls.window = outwin.OutputWindow(None, None, None, root)
|
||||
cls.text = w.text = Text(root)
|
||||
if sys.platform == 'darwin': # Issue 112938
|
||||
cls.text.update = cls.text.update_idletasks
|
||||
# Without this, test write, writelines, and goto... fail.
|
||||
# The reasons and why macOS-specific are unclear.
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.window.close()
|
||||
del cls.text, cls.window
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.text.delete('1.0', 'end')
|
||||
|
||||
def test_ispythonsource(self):
|
||||
# OutputWindow overrides ispythonsource to always return False.
|
||||
w = self.window
|
||||
self.assertFalse(w.ispythonsource('test.txt'))
|
||||
self.assertFalse(w.ispythonsource(__file__))
|
||||
|
||||
def test_window_title(self):
|
||||
self.assertEqual(self.window.top.title(), 'Output')
|
||||
|
||||
def test_maybesave(self):
|
||||
w = self.window
|
||||
eq = self.assertEqual
|
||||
w.get_saved = Func()
|
||||
|
||||
w.get_saved.result = False
|
||||
eq(w.maybesave(), 'no')
|
||||
eq(w.get_saved.called, 1)
|
||||
|
||||
w.get_saved.result = True
|
||||
eq(w.maybesave(), 'yes')
|
||||
eq(w.get_saved.called, 2)
|
||||
del w.get_saved
|
||||
|
||||
def test_write(self):
|
||||
eq = self.assertEqual
|
||||
delete = self.text.delete
|
||||
get = self.text.get
|
||||
write = self.window.write
|
||||
|
||||
# No new line - insert stays on same line.
|
||||
delete('1.0', 'end')
|
||||
test_text = 'test text'
|
||||
eq(write(test_text), len(test_text))
|
||||
eq(get('1.0', '1.end'), 'test text')
|
||||
eq(get('insert linestart', 'insert lineend'), 'test text')
|
||||
|
||||
# New line - insert moves to next line.
|
||||
delete('1.0', 'end')
|
||||
test_text = 'test text\n'
|
||||
eq(write(test_text), len(test_text))
|
||||
eq(get('1.0', '1.end'), 'test text')
|
||||
eq(get('insert linestart', 'insert lineend'), '')
|
||||
|
||||
# Text after new line is tagged for second line of Text widget.
|
||||
delete('1.0', 'end')
|
||||
test_text = 'test text\nLine 2'
|
||||
eq(write(test_text), len(test_text))
|
||||
eq(get('1.0', '1.end'), 'test text')
|
||||
eq(get('2.0', '2.end'), 'Line 2')
|
||||
eq(get('insert linestart', 'insert lineend'), 'Line 2')
|
||||
|
||||
# Test tags.
|
||||
delete('1.0', 'end')
|
||||
test_text = 'test text\n'
|
||||
test_text2 = 'Line 2\n'
|
||||
eq(write(test_text, tags='mytag'), len(test_text))
|
||||
eq(write(test_text2, tags='secondtag'), len(test_text2))
|
||||
eq(get('mytag.first', 'mytag.last'), test_text)
|
||||
eq(get('secondtag.first', 'secondtag.last'), test_text2)
|
||||
eq(get('1.0', '1.end'), test_text.rstrip('\n'))
|
||||
eq(get('2.0', '2.end'), test_text2.rstrip('\n'))
|
||||
|
||||
def test_writelines(self):
|
||||
eq = self.assertEqual
|
||||
get = self.text.get
|
||||
writelines = self.window.writelines
|
||||
|
||||
writelines(('Line 1\n', 'Line 2\n', 'Line 3\n'))
|
||||
eq(get('1.0', '1.end'), 'Line 1')
|
||||
eq(get('2.0', '2.end'), 'Line 2')
|
||||
eq(get('3.0', '3.end'), 'Line 3')
|
||||
eq(get('insert linestart', 'insert lineend'), '')
|
||||
|
||||
def test_goto_file_line(self):
|
||||
eq = self.assertEqual
|
||||
w = self.window
|
||||
text = self.text
|
||||
|
||||
w.flist = mock.Mock()
|
||||
gfl = w.flist.gotofileline = Func()
|
||||
showerror = w.showerror = Mbox_func()
|
||||
|
||||
# No file/line number.
|
||||
w.write('Not a file line')
|
||||
self.assertIsNone(w.goto_file_line())
|
||||
eq(gfl.called, 0)
|
||||
eq(showerror.title, 'No special line')
|
||||
|
||||
# Current file/line number.
|
||||
w.write(f'{str(__file__)}: 42: spam\n')
|
||||
w.write(f'{str(__file__)}: 21: spam')
|
||||
self.assertIsNone(w.goto_file_line())
|
||||
eq(gfl.args, (str(__file__), 21))
|
||||
|
||||
# Previous line has file/line number.
|
||||
text.delete('1.0', 'end')
|
||||
w.write(f'{str(__file__)}: 42: spam\n')
|
||||
w.write('Not a file line')
|
||||
self.assertIsNone(w.goto_file_line())
|
||||
eq(gfl.args, (str(__file__), 42))
|
||||
|
||||
del w.flist.gotofileline, w.showerror
|
||||
|
||||
|
||||
class ModuleFunctionTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUp(cls):
|
||||
outwin.file_line_progs = None
|
||||
|
||||
def test_compile_progs(self):
|
||||
outwin.compile_progs()
|
||||
for pat, regex in zip(outwin.file_line_pats, outwin.file_line_progs):
|
||||
self.assertEqual(regex.pattern, pat)
|
||||
|
||||
@mock.patch('builtins.open')
|
||||
def test_file_line_helper(self, mock_open):
|
||||
flh = outwin.file_line_helper
|
||||
test_lines = (
|
||||
(r'foo file "testfile1", line 42, bar', ('testfile1', 42)),
|
||||
(r'foo testfile2(21) bar', ('testfile2', 21)),
|
||||
(r' testfile3 : 42: foo bar\n', (' testfile3 ', 42)),
|
||||
(r'foo testfile4.py :1: ', ('foo testfile4.py ', 1)),
|
||||
('testfile5: \u19D4\u19D2: ', ('testfile5', 42)),
|
||||
(r'testfile6: 42', None), # only one `:`
|
||||
(r'testfile7 42 text', None) # no separators
|
||||
)
|
||||
for line, expected_output in test_lines:
|
||||
self.assertEqual(flh(line), expected_output)
|
||||
if expected_output:
|
||||
mock_open.assert_called_with(expected_output[0])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
112
Dependencies/Python/Lib/idlelib/idle_test/test_parenmatch.py
vendored
Normal file
112
Dependencies/Python/Lib/idlelib/idle_test/test_parenmatch.py
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
"""Test parenmatch, coverage 91%.
|
||||
|
||||
This must currently be a gui test because ParenMatch methods use
|
||||
several text methods not defined on idlelib.idle_test.mock_tk.Text.
|
||||
"""
|
||||
from idlelib.parenmatch import ParenMatch
|
||||
from test.support import requires
|
||||
requires('gui')
|
||||
|
||||
import unittest
|
||||
from unittest.mock import Mock
|
||||
from tkinter import Tk, Text
|
||||
|
||||
|
||||
class DummyEditwin:
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
self.indentwidth = 8
|
||||
self.tabwidth = 8
|
||||
self.prompt_last_line = '>>>' # Currently not used by parenmatch.
|
||||
|
||||
|
||||
class ParenMatchTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.text = Text(cls.root)
|
||||
cls.editwin = DummyEditwin(cls.text)
|
||||
cls.editwin.text_frame = Mock()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text, cls.editwin
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def tearDown(self):
|
||||
self.text.delete('1.0', 'end')
|
||||
|
||||
def get_parenmatch(self):
|
||||
pm = ParenMatch(self.editwin)
|
||||
pm.bell = lambda: None
|
||||
return pm
|
||||
|
||||
def test_paren_styles(self):
|
||||
"""
|
||||
Test ParenMatch with each style.
|
||||
"""
|
||||
text = self.text
|
||||
pm = self.get_parenmatch()
|
||||
for style, range1, range2 in (
|
||||
('opener', ('1.10', '1.11'), ('1.10', '1.11')),
|
||||
('default',('1.10', '1.11'),('1.10', '1.11')),
|
||||
('parens', ('1.14', '1.15'), ('1.15', '1.16')),
|
||||
('expression', ('1.10', '1.15'), ('1.10', '1.16'))):
|
||||
with self.subTest(style=style):
|
||||
text.delete('1.0', 'end')
|
||||
pm.STYLE = style
|
||||
text.insert('insert', 'def foobar(a, b')
|
||||
|
||||
pm.flash_paren_event('event')
|
||||
self.assertIn('<<parenmatch-check-restore>>', text.event_info())
|
||||
if style == 'parens':
|
||||
self.assertTupleEqual(text.tag_nextrange('paren', '1.0'),
|
||||
('1.10', '1.11'))
|
||||
self.assertTupleEqual(
|
||||
text.tag_prevrange('paren', 'end'), range1)
|
||||
|
||||
text.insert('insert', ')')
|
||||
pm.restore_event()
|
||||
self.assertNotIn('<<parenmatch-check-restore>>',
|
||||
text.event_info())
|
||||
self.assertEqual(text.tag_prevrange('paren', 'end'), ())
|
||||
|
||||
pm.paren_closed_event('event')
|
||||
self.assertTupleEqual(
|
||||
text.tag_prevrange('paren', 'end'), range2)
|
||||
|
||||
def test_paren_corner(self):
|
||||
"""
|
||||
Test corner cases in flash_paren_event and paren_closed_event.
|
||||
|
||||
Force execution of conditional expressions and alternate paths.
|
||||
"""
|
||||
text = self.text
|
||||
pm = self.get_parenmatch()
|
||||
|
||||
text.insert('insert', '# Comment.)')
|
||||
pm.paren_closed_event('event')
|
||||
|
||||
text.insert('insert', '\ndef')
|
||||
pm.flash_paren_event('event')
|
||||
pm.paren_closed_event('event')
|
||||
|
||||
text.insert('insert', ' a, *arg)')
|
||||
pm.paren_closed_event('event')
|
||||
|
||||
def test_handle_restore_timer(self):
|
||||
pm = self.get_parenmatch()
|
||||
pm.restore_event = Mock()
|
||||
pm.handle_restore_timer(0)
|
||||
self.assertTrue(pm.restore_event.called)
|
||||
pm.restore_event.reset_mock()
|
||||
pm.handle_restore_timer(1)
|
||||
self.assertFalse(pm.restore_event.called)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
86
Dependencies/Python/Lib/idlelib/idle_test/test_pathbrowser.py
vendored
Normal file
86
Dependencies/Python/Lib/idlelib/idle_test/test_pathbrowser.py
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
"Test pathbrowser, coverage 95%."
|
||||
|
||||
from idlelib import pathbrowser
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk
|
||||
|
||||
import os.path
|
||||
import pyclbr # for _modules
|
||||
import sys # for sys.path
|
||||
|
||||
from idlelib.idle_test.mock_idle import Func
|
||||
import idlelib # for __file__
|
||||
from idlelib import browser
|
||||
from idlelib.tree import TreeNode
|
||||
|
||||
|
||||
class PathBrowserTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.pb = pathbrowser.PathBrowser(cls.root, _utest=True)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.pb.close()
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root, cls.pb
|
||||
|
||||
def test_init(self):
|
||||
pb = self.pb
|
||||
eq = self.assertEqual
|
||||
eq(pb.master, self.root)
|
||||
eq(pyclbr._modules, {})
|
||||
self.assertIsInstance(pb.node, TreeNode)
|
||||
self.assertIsNotNone(browser.file_open)
|
||||
|
||||
def test_settitle(self):
|
||||
pb = self.pb
|
||||
self.assertEqual(pb.top.title(), 'Path Browser')
|
||||
self.assertEqual(pb.top.iconname(), 'Path Browser')
|
||||
|
||||
def test_rootnode(self):
|
||||
pb = self.pb
|
||||
rn = pb.rootnode()
|
||||
self.assertIsInstance(rn, pathbrowser.PathBrowserTreeItem)
|
||||
|
||||
def test_close(self):
|
||||
pb = self.pb
|
||||
pb.top.destroy = Func()
|
||||
pb.node.destroy = Func()
|
||||
pb.close()
|
||||
self.assertTrue(pb.top.destroy.called)
|
||||
self.assertTrue(pb.node.destroy.called)
|
||||
del pb.top.destroy, pb.node.destroy
|
||||
|
||||
|
||||
class DirBrowserTreeItemTest(unittest.TestCase):
|
||||
|
||||
def test_DirBrowserTreeItem(self):
|
||||
# Issue16226 - make sure that getting a sublist works
|
||||
d = pathbrowser.DirBrowserTreeItem('')
|
||||
d.GetSubList()
|
||||
self.assertEqual('', d.GetText())
|
||||
|
||||
dir = os.path.split(os.path.abspath(idlelib.__file__))[0]
|
||||
self.assertEqual(d.ispackagedir(dir), True)
|
||||
self.assertEqual(d.ispackagedir(dir + '/Icons'), False)
|
||||
|
||||
|
||||
class PathBrowserTreeItemTest(unittest.TestCase):
|
||||
|
||||
def test_PathBrowserTreeItem(self):
|
||||
p = pathbrowser.PathBrowserTreeItem()
|
||||
self.assertEqual(p.GetText(), 'sys.path')
|
||||
sub = p.GetSubList()
|
||||
self.assertEqual(len(sub), len(sys.path))
|
||||
self.assertEqual(type(sub[0]), pathbrowser.DirBrowserTreeItem)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=False)
|
||||
118
Dependencies/Python/Lib/idlelib/idle_test/test_percolator.py
vendored
Normal file
118
Dependencies/Python/Lib/idlelib/idle_test/test_percolator.py
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
"Test percolator, coverage 100%."
|
||||
|
||||
from idlelib.percolator import Percolator, Delegator
|
||||
import unittest
|
||||
from test.support import requires
|
||||
requires('gui')
|
||||
from tkinter import Text, Tk, END
|
||||
|
||||
|
||||
class MyFilter(Delegator):
|
||||
def __init__(self):
|
||||
Delegator.__init__(self, None)
|
||||
|
||||
def insert(self, *args):
|
||||
self.insert_called_with = args
|
||||
self.delegate.insert(*args)
|
||||
|
||||
def delete(self, *args):
|
||||
self.delete_called_with = args
|
||||
self.delegate.delete(*args)
|
||||
|
||||
def uppercase_insert(self, index, chars, tags=None):
|
||||
chars = chars.upper()
|
||||
self.delegate.insert(index, chars)
|
||||
|
||||
def lowercase_insert(self, index, chars, tags=None):
|
||||
chars = chars.lower()
|
||||
self.delegate.insert(index, chars)
|
||||
|
||||
def dont_insert(self, index, chars, tags=None):
|
||||
pass
|
||||
|
||||
|
||||
class PercolatorTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.root = Tk()
|
||||
cls.text = Text(cls.root)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.percolator = Percolator(self.text)
|
||||
self.filter_one = MyFilter()
|
||||
self.filter_two = MyFilter()
|
||||
self.percolator.insertfilter(self.filter_one)
|
||||
self.percolator.insertfilter(self.filter_two)
|
||||
|
||||
def tearDown(self):
|
||||
self.percolator.close()
|
||||
self.text.delete('1.0', END)
|
||||
|
||||
def test_insertfilter(self):
|
||||
self.assertIsNotNone(self.filter_one.delegate)
|
||||
self.assertEqual(self.percolator.top, self.filter_two)
|
||||
self.assertEqual(self.filter_two.delegate, self.filter_one)
|
||||
self.assertEqual(self.filter_one.delegate, self.percolator.bottom)
|
||||
|
||||
def test_removefilter(self):
|
||||
filter_three = MyFilter()
|
||||
self.percolator.removefilter(self.filter_two)
|
||||
self.assertEqual(self.percolator.top, self.filter_one)
|
||||
self.assertIsNone(self.filter_two.delegate)
|
||||
|
||||
filter_three = MyFilter()
|
||||
self.percolator.insertfilter(self.filter_two)
|
||||
self.percolator.insertfilter(filter_three)
|
||||
self.percolator.removefilter(self.filter_one)
|
||||
self.assertEqual(self.percolator.top, filter_three)
|
||||
self.assertEqual(filter_three.delegate, self.filter_two)
|
||||
self.assertEqual(self.filter_two.delegate, self.percolator.bottom)
|
||||
self.assertIsNone(self.filter_one.delegate)
|
||||
|
||||
def test_insert(self):
|
||||
self.text.insert('insert', 'foo')
|
||||
self.assertEqual(self.text.get('1.0', END), 'foo\n')
|
||||
self.assertTupleEqual(self.filter_one.insert_called_with,
|
||||
('insert', 'foo', None))
|
||||
|
||||
def test_modify_insert(self):
|
||||
self.filter_one.insert = self.filter_one.uppercase_insert
|
||||
self.text.insert('insert', 'bAr')
|
||||
self.assertEqual(self.text.get('1.0', END), 'BAR\n')
|
||||
|
||||
def test_modify_chain_insert(self):
|
||||
filter_three = MyFilter()
|
||||
self.percolator.insertfilter(filter_three)
|
||||
self.filter_two.insert = self.filter_two.uppercase_insert
|
||||
self.filter_one.insert = self.filter_one.lowercase_insert
|
||||
self.text.insert('insert', 'BaR')
|
||||
self.assertEqual(self.text.get('1.0', END), 'bar\n')
|
||||
|
||||
def test_dont_insert(self):
|
||||
self.filter_one.insert = self.filter_one.dont_insert
|
||||
self.text.insert('insert', 'foo bar')
|
||||
self.assertEqual(self.text.get('1.0', END), '\n')
|
||||
self.filter_one.insert = self.filter_one.dont_insert
|
||||
self.text.insert('insert', 'foo bar')
|
||||
self.assertEqual(self.text.get('1.0', END), '\n')
|
||||
|
||||
def test_without_filter(self):
|
||||
self.text.insert('insert', 'hello')
|
||||
self.assertEqual(self.text.get('1.0', 'end'), 'hello\n')
|
||||
|
||||
def test_delete(self):
|
||||
self.text.insert('insert', 'foo')
|
||||
self.text.delete('1.0', '1.2')
|
||||
self.assertEqual(self.text.get('1.0', END), 'o\n')
|
||||
self.assertTupleEqual(self.filter_one.delete_called_with,
|
||||
('1.0', '1.2'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
483
Dependencies/Python/Lib/idlelib/idle_test/test_pyparse.py
vendored
Normal file
483
Dependencies/Python/Lib/idlelib/idle_test/test_pyparse.py
vendored
Normal file
@@ -0,0 +1,483 @@
|
||||
"Test pyparse, coverage 96%."
|
||||
|
||||
from idlelib import pyparse
|
||||
import unittest
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
class ParseMapTest(unittest.TestCase):
|
||||
|
||||
def test_parsemap(self):
|
||||
keepwhite = {ord(c): ord(c) for c in ' \t\n\r'}
|
||||
mapping = pyparse.ParseMap(keepwhite)
|
||||
self.assertEqual(mapping[ord('\t')], ord('\t'))
|
||||
self.assertEqual(mapping[ord('a')], ord('x'))
|
||||
self.assertEqual(mapping[1000], ord('x'))
|
||||
|
||||
def test_trans(self):
|
||||
# trans is the production instance of ParseMap, used in _study1
|
||||
parser = pyparse.Parser(4, 4)
|
||||
self.assertEqual('\t a([{b}])b"c\'d\n'.translate(pyparse.trans),
|
||||
'xxx(((x)))x"x\'x\n')
|
||||
|
||||
|
||||
class PyParseTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.parser = pyparse.Parser(indentwidth=4, tabwidth=4)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.parser
|
||||
|
||||
def test_init(self):
|
||||
self.assertEqual(self.parser.indentwidth, 4)
|
||||
self.assertEqual(self.parser.tabwidth, 4)
|
||||
|
||||
def test_set_code(self):
|
||||
eq = self.assertEqual
|
||||
p = self.parser
|
||||
setcode = p.set_code
|
||||
|
||||
# Not empty and doesn't end with newline.
|
||||
with self.assertRaises(AssertionError):
|
||||
setcode('a')
|
||||
|
||||
tests = ('',
|
||||
'a\n')
|
||||
|
||||
for string in tests:
|
||||
with self.subTest(string=string):
|
||||
setcode(string)
|
||||
eq(p.code, string)
|
||||
eq(p.study_level, 0)
|
||||
|
||||
def test_find_good_parse_start(self):
|
||||
eq = self.assertEqual
|
||||
p = self.parser
|
||||
setcode = p.set_code
|
||||
start = p.find_good_parse_start
|
||||
def char_in_string_false(index): return False
|
||||
|
||||
# First line starts with 'def' and ends with ':', then 0 is the pos.
|
||||
setcode('def spam():\n')
|
||||
eq(start(char_in_string_false), 0)
|
||||
|
||||
# First line begins with a keyword in the list and ends
|
||||
# with an open brace, then 0 is the pos. This is how
|
||||
# hyperparser calls this function as the newline is not added
|
||||
# in the editor, but rather on the call to setcode.
|
||||
setcode('class spam( ' + ' \n')
|
||||
eq(start(char_in_string_false), 0)
|
||||
|
||||
# Split def across lines.
|
||||
setcode('"""This is a module docstring"""\n'
|
||||
'class C:\n'
|
||||
' def __init__(self, a,\n'
|
||||
' b=True):\n'
|
||||
' pass\n'
|
||||
)
|
||||
pos0, pos = 33, 42 # Start of 'class...', ' def' lines.
|
||||
|
||||
# Passing no value or non-callable should fail (issue 32989).
|
||||
with self.assertRaises(TypeError):
|
||||
start()
|
||||
with self.assertRaises(TypeError):
|
||||
start(False)
|
||||
|
||||
# Make text look like a string. This returns pos as the start
|
||||
# position, but it's set to None.
|
||||
self.assertIsNone(start(is_char_in_string=lambda index: True))
|
||||
|
||||
# Make all text look like it's not in a string. This means that it
|
||||
# found a good start position.
|
||||
eq(start(char_in_string_false), pos)
|
||||
|
||||
# If the beginning of the def line is not in a string, then it
|
||||
# returns that as the index.
|
||||
eq(start(is_char_in_string=lambda index: index > pos), pos)
|
||||
# If the beginning of the def line is in a string, then it
|
||||
# looks for a previous index.
|
||||
eq(start(is_char_in_string=lambda index: index >= pos), pos0)
|
||||
# If everything before the 'def' is in a string, then returns None.
|
||||
# The non-continuation def line returns 44 (see below).
|
||||
eq(start(is_char_in_string=lambda index: index < pos), None)
|
||||
|
||||
# Code without extra line break in def line - mostly returns the same
|
||||
# values.
|
||||
setcode('"""This is a module docstring"""\n'
|
||||
'class C:\n'
|
||||
' def __init__(self, a, b=True):\n'
|
||||
' pass\n'
|
||||
) # Does not affect class, def positions.
|
||||
eq(start(char_in_string_false), pos)
|
||||
eq(start(is_char_in_string=lambda index: index > pos), pos)
|
||||
eq(start(is_char_in_string=lambda index: index >= pos), pos0)
|
||||
# When the def line isn't split, this returns which doesn't match the
|
||||
# split line test.
|
||||
eq(start(is_char_in_string=lambda index: index < pos), pos)
|
||||
|
||||
def test_set_lo(self):
|
||||
code = (
|
||||
'"""This is a module docstring"""\n'
|
||||
'class C:\n'
|
||||
' def __init__(self, a,\n'
|
||||
' b=True):\n'
|
||||
' pass\n'
|
||||
)
|
||||
pos = 42
|
||||
p = self.parser
|
||||
p.set_code(code)
|
||||
|
||||
# Previous character is not a newline.
|
||||
with self.assertRaises(AssertionError):
|
||||
p.set_lo(5)
|
||||
|
||||
# A value of 0 doesn't change self.code.
|
||||
p.set_lo(0)
|
||||
self.assertEqual(p.code, code)
|
||||
|
||||
# An index that is preceded by a newline.
|
||||
p.set_lo(pos)
|
||||
self.assertEqual(p.code, code[pos:])
|
||||
|
||||
def test_study1(self):
|
||||
eq = self.assertEqual
|
||||
p = self.parser
|
||||
setcode = p.set_code
|
||||
study = p._study1
|
||||
|
||||
(NONE, BACKSLASH, FIRST, NEXT, BRACKET) = range(5)
|
||||
TestInfo = namedtuple('TestInfo', ['string', 'goodlines',
|
||||
'continuation'])
|
||||
tests = (
|
||||
TestInfo('', [0], NONE),
|
||||
# Docstrings.
|
||||
TestInfo('"""This is a complete docstring."""\n', [0, 1], NONE),
|
||||
TestInfo("'''This is a complete docstring.'''\n", [0, 1], NONE),
|
||||
TestInfo('"""This is a continued docstring.\n', [0, 1], FIRST),
|
||||
TestInfo("'''This is a continued docstring.\n", [0, 1], FIRST),
|
||||
TestInfo('"""Closing quote does not match."\n', [0, 1], FIRST),
|
||||
TestInfo('"""Bracket in docstring [\n', [0, 1], FIRST),
|
||||
TestInfo("'''Incomplete two line docstring.\n\n", [0, 2], NEXT),
|
||||
# Single-quoted strings.
|
||||
TestInfo('"This is a complete string."\n', [0, 1], NONE),
|
||||
TestInfo('"This is an incomplete string.\n', [0, 1], NONE),
|
||||
TestInfo("'This is more incomplete.\n\n", [0, 1, 2], NONE),
|
||||
# Comment (backslash does not continue comments).
|
||||
TestInfo('# Comment\\\n', [0, 1], NONE),
|
||||
# Brackets.
|
||||
TestInfo('("""Complete string in bracket"""\n', [0, 1], BRACKET),
|
||||
TestInfo('("""Open string in bracket\n', [0, 1], FIRST),
|
||||
TestInfo('a = (1 + 2) - 5 *\\\n', [0, 1], BACKSLASH), # No bracket.
|
||||
TestInfo('\n def function1(self, a,\n b):\n',
|
||||
[0, 1, 3], NONE),
|
||||
TestInfo('\n def function1(self, a,\\\n', [0, 1, 2], BRACKET),
|
||||
TestInfo('\n def function1(self, a,\n', [0, 1, 2], BRACKET),
|
||||
TestInfo('())\n', [0, 1], NONE), # Extra closer.
|
||||
TestInfo(')(\n', [0, 1], BRACKET), # Extra closer.
|
||||
# For the mismatched example, it doesn't look like continuation.
|
||||
TestInfo('{)(]\n', [0, 1], NONE), # Mismatched.
|
||||
)
|
||||
|
||||
for test in tests:
|
||||
with self.subTest(string=test.string):
|
||||
setcode(test.string) # resets study_level
|
||||
study()
|
||||
eq(p.study_level, 1)
|
||||
eq(p.goodlines, test.goodlines)
|
||||
eq(p.continuation, test.continuation)
|
||||
|
||||
# Called again, just returns without reprocessing.
|
||||
self.assertIsNone(study())
|
||||
|
||||
def test_get_continuation_type(self):
|
||||
eq = self.assertEqual
|
||||
p = self.parser
|
||||
setcode = p.set_code
|
||||
gettype = p.get_continuation_type
|
||||
|
||||
(NONE, BACKSLASH, FIRST, NEXT, BRACKET) = range(5)
|
||||
TestInfo = namedtuple('TestInfo', ['string', 'continuation'])
|
||||
tests = (
|
||||
TestInfo('', NONE),
|
||||
TestInfo('"""This is a continuation docstring.\n', FIRST),
|
||||
TestInfo("'''This is a multiline-continued docstring.\n\n", NEXT),
|
||||
TestInfo('a = (1 + 2) - 5 *\\\n', BACKSLASH),
|
||||
TestInfo('\n def function1(self, a,\\\n', BRACKET)
|
||||
)
|
||||
|
||||
for test in tests:
|
||||
with self.subTest(string=test.string):
|
||||
setcode(test.string)
|
||||
eq(gettype(), test.continuation)
|
||||
|
||||
def test_study2(self):
|
||||
eq = self.assertEqual
|
||||
p = self.parser
|
||||
setcode = p.set_code
|
||||
study = p._study2
|
||||
|
||||
TestInfo = namedtuple('TestInfo', ['string', 'start', 'end', 'lastch',
|
||||
'openbracket', 'bracketing'])
|
||||
tests = (
|
||||
TestInfo('', 0, 0, '', None, ((0, 0),)),
|
||||
TestInfo("'''This is a multiline continuation docstring.\n\n",
|
||||
0, 48, "'", None, ((0, 0), (0, 1), (48, 0))),
|
||||
TestInfo(' # Comment\\\n',
|
||||
0, 12, '', None, ((0, 0), (1, 1), (12, 0))),
|
||||
# A comment without a space is a special case
|
||||
TestInfo(' #Comment\\\n',
|
||||
0, 0, '', None, ((0, 0),)),
|
||||
# Backslash continuation.
|
||||
TestInfo('a = (1 + 2) - 5 *\\\n',
|
||||
0, 19, '*', None, ((0, 0), (4, 1), (11, 0))),
|
||||
# Bracket continuation with close.
|
||||
TestInfo('\n def function1(self, a,\n b):\n',
|
||||
1, 48, ':', None, ((1, 0), (17, 1), (46, 0))),
|
||||
# Bracket continuation with unneeded backslash.
|
||||
TestInfo('\n def function1(self, a,\\\n',
|
||||
1, 28, ',', 17, ((1, 0), (17, 1))),
|
||||
# Bracket continuation.
|
||||
TestInfo('\n def function1(self, a,\n',
|
||||
1, 27, ',', 17, ((1, 0), (17, 1))),
|
||||
# Bracket continuation with comment at end of line with text.
|
||||
TestInfo('\n def function1(self, a, # End of line comment.\n',
|
||||
1, 51, ',', 17, ((1, 0), (17, 1), (28, 2), (51, 1))),
|
||||
# Multi-line statement with comment line in between code lines.
|
||||
TestInfo(' a = ["first item",\n # Comment line\n "next item",\n',
|
||||
0, 55, ',', 6, ((0, 0), (6, 1), (7, 2), (19, 1),
|
||||
(23, 2), (38, 1), (42, 2), (53, 1))),
|
||||
TestInfo('())\n',
|
||||
0, 4, ')', None, ((0, 0), (0, 1), (2, 0), (3, 0))),
|
||||
TestInfo(')(\n', 0, 3, '(', 1, ((0, 0), (1, 0), (1, 1))),
|
||||
# Wrong closers still decrement stack level.
|
||||
TestInfo('{)(]\n',
|
||||
0, 5, ']', None, ((0, 0), (0, 1), (2, 0), (2, 1), (4, 0))),
|
||||
# Character after backslash.
|
||||
TestInfo(':\\a\n', 0, 4, '\\a', None, ((0, 0),)),
|
||||
TestInfo('\n', 0, 0, '', None, ((0, 0),)),
|
||||
)
|
||||
|
||||
for test in tests:
|
||||
with self.subTest(string=test.string):
|
||||
setcode(test.string)
|
||||
study()
|
||||
eq(p.study_level, 2)
|
||||
eq(p.stmt_start, test.start)
|
||||
eq(p.stmt_end, test.end)
|
||||
eq(p.lastch, test.lastch)
|
||||
eq(p.lastopenbracketpos, test.openbracket)
|
||||
eq(p.stmt_bracketing, test.bracketing)
|
||||
|
||||
# Called again, just returns without reprocessing.
|
||||
self.assertIsNone(study())
|
||||
|
||||
def test_get_num_lines_in_stmt(self):
|
||||
eq = self.assertEqual
|
||||
p = self.parser
|
||||
setcode = p.set_code
|
||||
getlines = p.get_num_lines_in_stmt
|
||||
|
||||
TestInfo = namedtuple('TestInfo', ['string', 'lines'])
|
||||
tests = (
|
||||
TestInfo('[x for x in a]\n', 1), # Closed on one line.
|
||||
TestInfo('[x\nfor x in a\n', 2), # Not closed.
|
||||
TestInfo('[x\\\nfor x in a\\\n', 2), # "", unneeded backslashes.
|
||||
TestInfo('[x\nfor x in a\n]\n', 3), # Closed on multi-line.
|
||||
TestInfo('\n"""Docstring comment L1"""\nL2\nL3\nL4\n', 1),
|
||||
TestInfo('\n"""Docstring comment L1\nL2"""\nL3\nL4\n', 1),
|
||||
TestInfo('\n"""Docstring comment L1\\\nL2\\\nL3\\\nL4\\\n', 4),
|
||||
TestInfo('\n\n"""Docstring comment L1\\\nL2\\\nL3\\\nL4\\\n"""\n', 5)
|
||||
)
|
||||
|
||||
# Blank string doesn't have enough elements in goodlines.
|
||||
setcode('')
|
||||
with self.assertRaises(IndexError):
|
||||
getlines()
|
||||
|
||||
for test in tests:
|
||||
with self.subTest(string=test.string):
|
||||
setcode(test.string)
|
||||
eq(getlines(), test.lines)
|
||||
|
||||
def test_compute_bracket_indent(self):
|
||||
eq = self.assertEqual
|
||||
p = self.parser
|
||||
setcode = p.set_code
|
||||
indent = p.compute_bracket_indent
|
||||
|
||||
TestInfo = namedtuple('TestInfo', ['string', 'spaces'])
|
||||
tests = (
|
||||
TestInfo('def function1(self, a,\n', 14),
|
||||
# Characters after bracket.
|
||||
TestInfo('\n def function1(self, a,\n', 18),
|
||||
TestInfo('\n\tdef function1(self, a,\n', 18),
|
||||
# No characters after bracket.
|
||||
TestInfo('\n def function1(\n', 8),
|
||||
TestInfo('\n\tdef function1(\n', 8),
|
||||
TestInfo('\n def function1( \n', 8), # Ignore extra spaces.
|
||||
TestInfo('[\n"first item",\n # Comment line\n "next item",\n', 0),
|
||||
TestInfo('[\n "first item",\n # Comment line\n "next item",\n', 2),
|
||||
TestInfo('["first item",\n # Comment line\n "next item",\n', 1),
|
||||
TestInfo('(\n', 4),
|
||||
TestInfo('(a\n', 1),
|
||||
)
|
||||
|
||||
# Must be C_BRACKET continuation type.
|
||||
setcode('def function1(self, a, b):\n')
|
||||
with self.assertRaises(AssertionError):
|
||||
indent()
|
||||
|
||||
for test in tests:
|
||||
setcode(test.string)
|
||||
eq(indent(), test.spaces)
|
||||
|
||||
def test_compute_backslash_indent(self):
|
||||
eq = self.assertEqual
|
||||
p = self.parser
|
||||
setcode = p.set_code
|
||||
indent = p.compute_backslash_indent
|
||||
|
||||
# Must be C_BACKSLASH continuation type.
|
||||
errors = (('def function1(self, a, b\\\n'), # Bracket.
|
||||
(' """ (\\\n'), # Docstring.
|
||||
('a = #\\\n'), # Inline comment.
|
||||
)
|
||||
for string in errors:
|
||||
with self.subTest(string=string):
|
||||
setcode(string)
|
||||
with self.assertRaises(AssertionError):
|
||||
indent()
|
||||
|
||||
TestInfo = namedtuple('TestInfo', ('string', 'spaces'))
|
||||
tests = (TestInfo('a = (1 + 2) - 5 *\\\n', 4),
|
||||
TestInfo('a = 1 + 2 - 5 *\\\n', 4),
|
||||
TestInfo(' a = 1 + 2 - 5 *\\\n', 8),
|
||||
TestInfo(' a = "spam"\\\n', 6),
|
||||
TestInfo(' a = \\\n"a"\\\n', 4),
|
||||
TestInfo(' a = #\\\n"a"\\\n', 5),
|
||||
TestInfo('a == \\\n', 2),
|
||||
TestInfo('a != \\\n', 2),
|
||||
# Difference between containing = and those not.
|
||||
TestInfo('\\\n', 2),
|
||||
TestInfo(' \\\n', 6),
|
||||
TestInfo('\t\\\n', 6),
|
||||
TestInfo('a\\\n', 3),
|
||||
TestInfo('{}\\\n', 4),
|
||||
TestInfo('(1 + 2) - 5 *\\\n', 3),
|
||||
)
|
||||
for test in tests:
|
||||
with self.subTest(string=test.string):
|
||||
setcode(test.string)
|
||||
eq(indent(), test.spaces)
|
||||
|
||||
def test_get_base_indent_string(self):
|
||||
eq = self.assertEqual
|
||||
p = self.parser
|
||||
setcode = p.set_code
|
||||
baseindent = p.get_base_indent_string
|
||||
|
||||
TestInfo = namedtuple('TestInfo', ['string', 'indent'])
|
||||
tests = (TestInfo('', ''),
|
||||
TestInfo('def a():\n', ''),
|
||||
TestInfo('\tdef a():\n', '\t'),
|
||||
TestInfo(' def a():\n', ' '),
|
||||
TestInfo(' def a(\n', ' '),
|
||||
TestInfo('\t\n def a(\n', ' '),
|
||||
TestInfo('\t\n # Comment.\n', ' '),
|
||||
)
|
||||
|
||||
for test in tests:
|
||||
with self.subTest(string=test.string):
|
||||
setcode(test.string)
|
||||
eq(baseindent(), test.indent)
|
||||
|
||||
def test_is_block_opener(self):
|
||||
yes = self.assertTrue
|
||||
no = self.assertFalse
|
||||
p = self.parser
|
||||
setcode = p.set_code
|
||||
opener = p.is_block_opener
|
||||
|
||||
TestInfo = namedtuple('TestInfo', ['string', 'assert_'])
|
||||
tests = (
|
||||
TestInfo('def a():\n', yes),
|
||||
TestInfo('\n def function1(self, a,\n b):\n', yes),
|
||||
TestInfo(':\n', yes),
|
||||
TestInfo('a:\n', yes),
|
||||
TestInfo('):\n', yes),
|
||||
TestInfo('(:\n', yes),
|
||||
TestInfo('":\n', no),
|
||||
TestInfo('\n def function1(self, a,\n', no),
|
||||
TestInfo('def function1(self, a):\n pass\n', no),
|
||||
TestInfo('# A comment:\n', no),
|
||||
TestInfo('"""A docstring:\n', no),
|
||||
TestInfo('"""A docstring:\n', no),
|
||||
)
|
||||
|
||||
for test in tests:
|
||||
with self.subTest(string=test.string):
|
||||
setcode(test.string)
|
||||
test.assert_(opener())
|
||||
|
||||
def test_is_block_closer(self):
|
||||
yes = self.assertTrue
|
||||
no = self.assertFalse
|
||||
p = self.parser
|
||||
setcode = p.set_code
|
||||
closer = p.is_block_closer
|
||||
|
||||
TestInfo = namedtuple('TestInfo', ['string', 'assert_'])
|
||||
tests = (
|
||||
TestInfo('return\n', yes),
|
||||
TestInfo('\tbreak\n', yes),
|
||||
TestInfo(' continue\n', yes),
|
||||
TestInfo(' raise\n', yes),
|
||||
TestInfo('pass \n', yes),
|
||||
TestInfo('pass\t\n', yes),
|
||||
TestInfo('return #\n', yes),
|
||||
TestInfo('raised\n', no),
|
||||
TestInfo('returning\n', no),
|
||||
TestInfo('# return\n', no),
|
||||
TestInfo('"""break\n', no),
|
||||
TestInfo('"continue\n', no),
|
||||
TestInfo('def function1(self, a):\n pass\n', yes),
|
||||
)
|
||||
|
||||
for test in tests:
|
||||
with self.subTest(string=test.string):
|
||||
setcode(test.string)
|
||||
test.assert_(closer())
|
||||
|
||||
def test_get_last_stmt_bracketing(self):
|
||||
eq = self.assertEqual
|
||||
p = self.parser
|
||||
setcode = p.set_code
|
||||
bracketing = p.get_last_stmt_bracketing
|
||||
|
||||
TestInfo = namedtuple('TestInfo', ['string', 'bracket'])
|
||||
tests = (
|
||||
TestInfo('', ((0, 0),)),
|
||||
TestInfo('a\n', ((0, 0),)),
|
||||
TestInfo('()()\n', ((0, 0), (0, 1), (2, 0), (2, 1), (4, 0))),
|
||||
TestInfo('(\n)()\n', ((0, 0), (0, 1), (3, 0), (3, 1), (5, 0))),
|
||||
TestInfo('()\n()\n', ((3, 0), (3, 1), (5, 0))),
|
||||
TestInfo('()(\n)\n', ((0, 0), (0, 1), (2, 0), (2, 1), (5, 0))),
|
||||
TestInfo('(())\n', ((0, 0), (0, 1), (1, 2), (3, 1), (4, 0))),
|
||||
TestInfo('(\n())\n', ((0, 0), (0, 1), (2, 2), (4, 1), (5, 0))),
|
||||
# Same as matched test.
|
||||
TestInfo('{)(]\n', ((0, 0), (0, 1), (2, 0), (2, 1), (4, 0))),
|
||||
TestInfo('(((())\n',
|
||||
((0, 0), (0, 1), (1, 2), (2, 3), (3, 4), (5, 3), (6, 2))),
|
||||
)
|
||||
|
||||
for test in tests:
|
||||
with self.subTest(string=test.string):
|
||||
setcode(test.string)
|
||||
eq(bracketing(), test.bracket)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
148
Dependencies/Python/Lib/idlelib/idle_test/test_pyshell.py
vendored
Normal file
148
Dependencies/Python/Lib/idlelib/idle_test/test_pyshell.py
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
"Test pyshell, coverage 12%."
|
||||
# Plus coverage of test_warning. Was 20% with test_openshell.
|
||||
|
||||
from idlelib import pyshell
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk
|
||||
|
||||
|
||||
class FunctionTest(unittest.TestCase):
|
||||
# Test stand-alone module level non-gui functions.
|
||||
|
||||
def test_restart_line_wide(self):
|
||||
eq = self.assertEqual
|
||||
for file, mul, extra in (('', 22, ''), ('finame', 21, '=')):
|
||||
width = 60
|
||||
bar = mul * '='
|
||||
with self.subTest(file=file, bar=bar):
|
||||
file = file or 'Shell'
|
||||
line = pyshell.restart_line(width, file)
|
||||
eq(len(line), width)
|
||||
eq(line, f"{bar+extra} RESTART: {file} {bar}")
|
||||
|
||||
def test_restart_line_narrow(self):
|
||||
expect, taglen = "= RESTART: Shell", 16
|
||||
for width in (taglen-1, taglen, taglen+1):
|
||||
with self.subTest(width=width):
|
||||
self.assertEqual(pyshell.restart_line(width, ''), expect)
|
||||
self.assertEqual(pyshell.restart_line(taglen+2, ''), expect+' =')
|
||||
|
||||
|
||||
class PyShellFileListTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
#cls.root.update_idletasks()
|
||||
## for id in cls.root.tk.call('after', 'info'):
|
||||
## cls.root.after_cancel(id) # Need for EditorWindow.
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_init(self):
|
||||
psfl = pyshell.PyShellFileList(self.root)
|
||||
self.assertEqual(psfl.EditorWindow, pyshell.PyShellEditorWindow)
|
||||
self.assertIsNone(psfl.pyshell)
|
||||
|
||||
# The following sometimes causes 'invalid command name "109734456recolorize"'.
|
||||
# Uncommenting after_cancel above prevents this, but results in
|
||||
# TclError: bad window path name ".!listedtoplevel.!frame.text"
|
||||
# which is normally prevented by after_cancel.
|
||||
## def test_openshell(self):
|
||||
## pyshell.use_subprocess = False
|
||||
## ps = pyshell.PyShellFileList(self.root).open_shell()
|
||||
## self.assertIsInstance(ps, pyshell.PyShell)
|
||||
|
||||
|
||||
class PyShellRemoveLastNewlineAndSurroundingWhitespaceTest(unittest.TestCase):
|
||||
regexp = pyshell.PyShell._last_newline_re
|
||||
|
||||
def all_removed(self, text):
|
||||
self.assertEqual('', self.regexp.sub('', text))
|
||||
|
||||
def none_removed(self, text):
|
||||
self.assertEqual(text, self.regexp.sub('', text))
|
||||
|
||||
def check_result(self, text, expected):
|
||||
self.assertEqual(expected, self.regexp.sub('', text))
|
||||
|
||||
def test_empty(self):
|
||||
self.all_removed('')
|
||||
|
||||
def test_newline(self):
|
||||
self.all_removed('\n')
|
||||
|
||||
def test_whitespace_no_newline(self):
|
||||
self.all_removed(' ')
|
||||
self.all_removed(' ')
|
||||
self.all_removed(' ')
|
||||
self.all_removed(' ' * 20)
|
||||
self.all_removed('\t')
|
||||
self.all_removed('\t\t')
|
||||
self.all_removed('\t\t\t')
|
||||
self.all_removed('\t' * 20)
|
||||
self.all_removed('\t ')
|
||||
self.all_removed(' \t')
|
||||
self.all_removed(' \t \t ')
|
||||
self.all_removed('\t \t \t')
|
||||
|
||||
def test_newline_with_whitespace(self):
|
||||
self.all_removed(' \n')
|
||||
self.all_removed('\t\n')
|
||||
self.all_removed(' \t\n')
|
||||
self.all_removed('\t \n')
|
||||
self.all_removed('\n ')
|
||||
self.all_removed('\n\t')
|
||||
self.all_removed('\n \t')
|
||||
self.all_removed('\n\t ')
|
||||
self.all_removed(' \n ')
|
||||
self.all_removed('\t\n ')
|
||||
self.all_removed(' \n\t')
|
||||
self.all_removed('\t\n\t')
|
||||
self.all_removed('\t \t \t\n')
|
||||
self.all_removed(' \t \t \n')
|
||||
self.all_removed('\n\t \t \t')
|
||||
self.all_removed('\n \t \t ')
|
||||
|
||||
def test_multiple_newlines(self):
|
||||
self.check_result('\n\n', '\n')
|
||||
self.check_result('\n' * 5, '\n' * 4)
|
||||
self.check_result('\n' * 5 + '\t', '\n' * 4)
|
||||
self.check_result('\n' * 20, '\n' * 19)
|
||||
self.check_result('\n' * 20 + ' ', '\n' * 19)
|
||||
self.check_result(' \n \n ', ' \n')
|
||||
self.check_result(' \n\n ', ' \n')
|
||||
self.check_result(' \n\n', ' \n')
|
||||
self.check_result('\t\n\n', '\t\n')
|
||||
self.check_result('\n\n ', '\n')
|
||||
self.check_result('\n\n\t', '\n')
|
||||
self.check_result(' \n \n ', ' \n')
|
||||
self.check_result('\t\n\t\n\t', '\t\n')
|
||||
|
||||
def test_non_whitespace(self):
|
||||
self.none_removed('a')
|
||||
self.check_result('a\n', 'a')
|
||||
self.check_result('a\n ', 'a')
|
||||
self.check_result('a \n ', 'a')
|
||||
self.check_result('a \n\t', 'a')
|
||||
self.none_removed('-')
|
||||
self.check_result('-\n', '-')
|
||||
self.none_removed('.')
|
||||
self.check_result('.\n', '.')
|
||||
|
||||
def test_unsupported_whitespace(self):
|
||||
self.none_removed('\v')
|
||||
self.none_removed('\n\v')
|
||||
self.check_result('\v\n', '\v')
|
||||
self.none_removed(' \n\v')
|
||||
self.check_result('\v\n ', '\v')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
452
Dependencies/Python/Lib/idlelib/idle_test/test_query.py
vendored
Normal file
452
Dependencies/Python/Lib/idlelib/idle_test/test_query.py
vendored
Normal file
@@ -0,0 +1,452 @@
|
||||
"""Test query, coverage 93%.
|
||||
|
||||
Non-gui tests for Query, SectionName, ModuleName, and HelpSource use
|
||||
dummy versions that extract the non-gui methods and add other needed
|
||||
attributes. GUI tests create an instance of each class and simulate
|
||||
entries and button clicks. Subclass tests only target the new code in
|
||||
the subclass definition.
|
||||
|
||||
The appearance of the widgets is checked by the Query and
|
||||
HelpSource htests. These are run by running query.py.
|
||||
"""
|
||||
from idlelib import query
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from test.support.testcase import ExtraAssertions
|
||||
from tkinter import Tk, END
|
||||
|
||||
import sys
|
||||
from unittest import mock
|
||||
from idlelib.idle_test.mock_tk import Var
|
||||
|
||||
|
||||
# NON-GUI TESTS
|
||||
|
||||
class QueryTest(unittest.TestCase):
|
||||
"Test Query base class."
|
||||
|
||||
class Dummy_Query:
|
||||
# Test the following Query methods.
|
||||
entry_ok = query.Query.entry_ok
|
||||
ok = query.Query.ok
|
||||
cancel = query.Query.cancel
|
||||
# Add attributes and initialization needed for tests.
|
||||
def __init__(self, dummy_entry):
|
||||
self.entry = Var(value=dummy_entry)
|
||||
self.entry_error = {'text': ''}
|
||||
self.result = None
|
||||
self.destroyed = False
|
||||
def showerror(self, message):
|
||||
self.entry_error['text'] = message
|
||||
def destroy(self):
|
||||
self.destroyed = True
|
||||
|
||||
def test_entry_ok_blank(self):
|
||||
dialog = self.Dummy_Query(' ')
|
||||
self.assertEqual(dialog.entry_ok(), None)
|
||||
self.assertEqual((dialog.result, dialog.destroyed), (None, False))
|
||||
self.assertIn('blank line', dialog.entry_error['text'])
|
||||
|
||||
def test_entry_ok_good(self):
|
||||
dialog = self.Dummy_Query(' good ')
|
||||
Equal = self.assertEqual
|
||||
Equal(dialog.entry_ok(), 'good')
|
||||
Equal((dialog.result, dialog.destroyed), (None, False))
|
||||
Equal(dialog.entry_error['text'], '')
|
||||
|
||||
def test_ok_blank(self):
|
||||
dialog = self.Dummy_Query('')
|
||||
dialog.entry.focus_set = mock.Mock()
|
||||
self.assertEqual(dialog.ok(), None)
|
||||
self.assertTrue(dialog.entry.focus_set.called)
|
||||
del dialog.entry.focus_set
|
||||
self.assertEqual((dialog.result, dialog.destroyed), (None, False))
|
||||
|
||||
def test_ok_good(self):
|
||||
dialog = self.Dummy_Query('good')
|
||||
self.assertEqual(dialog.ok(), None)
|
||||
self.assertEqual((dialog.result, dialog.destroyed), ('good', True))
|
||||
|
||||
def test_cancel(self):
|
||||
dialog = self.Dummy_Query('does not matter')
|
||||
self.assertEqual(dialog.cancel(), None)
|
||||
self.assertEqual((dialog.result, dialog.destroyed), (None, True))
|
||||
|
||||
|
||||
class SectionNameTest(unittest.TestCase):
|
||||
"Test SectionName subclass of Query."
|
||||
|
||||
class Dummy_SectionName:
|
||||
entry_ok = query.SectionName.entry_ok # Function being tested.
|
||||
used_names = ['used']
|
||||
def __init__(self, dummy_entry):
|
||||
self.entry = Var(value=dummy_entry)
|
||||
self.entry_error = {'text': ''}
|
||||
def showerror(self, message):
|
||||
self.entry_error['text'] = message
|
||||
|
||||
def test_blank_section_name(self):
|
||||
dialog = self.Dummy_SectionName(' ')
|
||||
self.assertEqual(dialog.entry_ok(), None)
|
||||
self.assertIn('no name', dialog.entry_error['text'])
|
||||
|
||||
def test_used_section_name(self):
|
||||
dialog = self.Dummy_SectionName('used')
|
||||
self.assertEqual(dialog.entry_ok(), None)
|
||||
self.assertIn('use', dialog.entry_error['text'])
|
||||
|
||||
def test_long_section_name(self):
|
||||
dialog = self.Dummy_SectionName('good'*8)
|
||||
self.assertEqual(dialog.entry_ok(), None)
|
||||
self.assertIn('longer than 30', dialog.entry_error['text'])
|
||||
|
||||
def test_good_section_name(self):
|
||||
dialog = self.Dummy_SectionName(' good ')
|
||||
self.assertEqual(dialog.entry_ok(), 'good')
|
||||
self.assertEqual(dialog.entry_error['text'], '')
|
||||
|
||||
|
||||
class ModuleNameTest(unittest.TestCase, ExtraAssertions):
|
||||
"Test ModuleName subclass of Query."
|
||||
|
||||
class Dummy_ModuleName:
|
||||
entry_ok = query.ModuleName.entry_ok # Function being tested.
|
||||
text0 = ''
|
||||
def __init__(self, dummy_entry):
|
||||
self.entry = Var(value=dummy_entry)
|
||||
self.entry_error = {'text': ''}
|
||||
def showerror(self, message):
|
||||
self.entry_error['text'] = message
|
||||
|
||||
def test_blank_module_name(self):
|
||||
dialog = self.Dummy_ModuleName(' ')
|
||||
self.assertEqual(dialog.entry_ok(), None)
|
||||
self.assertIn('no name', dialog.entry_error['text'])
|
||||
|
||||
def test_bogus_module_name(self):
|
||||
dialog = self.Dummy_ModuleName('__name_xyz123_should_not_exist__')
|
||||
self.assertEqual(dialog.entry_ok(), None)
|
||||
self.assertIn('not found', dialog.entry_error['text'])
|
||||
|
||||
def test_c_source_name(self):
|
||||
dialog = self.Dummy_ModuleName('itertools')
|
||||
self.assertEqual(dialog.entry_ok(), None)
|
||||
self.assertIn('source-based', dialog.entry_error['text'])
|
||||
|
||||
def test_good_module_name(self):
|
||||
dialog = self.Dummy_ModuleName('idlelib')
|
||||
self.assertEndsWith(dialog.entry_ok(), '__init__.py')
|
||||
self.assertEqual(dialog.entry_error['text'], '')
|
||||
dialog = self.Dummy_ModuleName('idlelib.idle')
|
||||
self.assertEndsWith(dialog.entry_ok(), 'idle.py')
|
||||
self.assertEqual(dialog.entry_error['text'], '')
|
||||
|
||||
|
||||
class GotoTest(unittest.TestCase):
|
||||
"Test Goto subclass of Query."
|
||||
|
||||
class Dummy_ModuleName:
|
||||
entry_ok = query.Goto.entry_ok # Function being tested.
|
||||
def __init__(self, dummy_entry):
|
||||
self.entry = Var(value=dummy_entry)
|
||||
self.entry_error = {'text': ''}
|
||||
def showerror(self, message):
|
||||
self.entry_error['text'] = message
|
||||
|
||||
def test_bogus_goto(self):
|
||||
dialog = self.Dummy_ModuleName('a')
|
||||
self.assertEqual(dialog.entry_ok(), None)
|
||||
self.assertIn('not a base 10 integer', dialog.entry_error['text'])
|
||||
|
||||
def test_bad_goto(self):
|
||||
dialog = self.Dummy_ModuleName('0')
|
||||
self.assertEqual(dialog.entry_ok(), None)
|
||||
self.assertIn('not a positive integer', dialog.entry_error['text'])
|
||||
|
||||
def test_good_goto(self):
|
||||
dialog = self.Dummy_ModuleName('1')
|
||||
self.assertEqual(dialog.entry_ok(), 1)
|
||||
self.assertEqual(dialog.entry_error['text'], '')
|
||||
|
||||
|
||||
# 3 HelpSource test classes each test one method.
|
||||
|
||||
class HelpsourceBrowsefileTest(unittest.TestCase):
|
||||
"Test browse_file method of ModuleName subclass of Query."
|
||||
|
||||
class Dummy_HelpSource:
|
||||
browse_file = query.HelpSource.browse_file
|
||||
pathvar = Var()
|
||||
|
||||
def test_file_replaces_path(self):
|
||||
dialog = self.Dummy_HelpSource()
|
||||
# Path is widget entry, either '' or something.
|
||||
# Func return is file dialog return, either '' or something.
|
||||
# Func return should override widget entry.
|
||||
# We need all 4 combinations to test all (most) code paths.
|
||||
for path, func, result in (
|
||||
('', lambda a,b,c:'', ''),
|
||||
('', lambda a,b,c: __file__, __file__),
|
||||
('htest', lambda a,b,c:'', 'htest'),
|
||||
('htest', lambda a,b,c: __file__, __file__)):
|
||||
with self.subTest():
|
||||
dialog.pathvar.set(path)
|
||||
dialog.askfilename = func
|
||||
dialog.browse_file()
|
||||
self.assertEqual(dialog.pathvar.get(), result)
|
||||
|
||||
|
||||
class HelpsourcePathokTest(unittest.TestCase):
|
||||
"Test path_ok method of HelpSource subclass of Query."
|
||||
|
||||
class Dummy_HelpSource:
|
||||
path_ok = query.HelpSource.path_ok
|
||||
def __init__(self, dummy_path):
|
||||
self.path = Var(value=dummy_path)
|
||||
self.path_error = {'text': ''}
|
||||
def showerror(self, message, widget=None):
|
||||
self.path_error['text'] = message
|
||||
|
||||
orig_platform = query.platform # Set in test_path_ok_file.
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
query.platform = cls.orig_platform
|
||||
|
||||
def test_path_ok_blank(self):
|
||||
dialog = self.Dummy_HelpSource(' ')
|
||||
self.assertEqual(dialog.path_ok(), None)
|
||||
self.assertIn('no help file', dialog.path_error['text'])
|
||||
|
||||
def test_path_ok_bad(self):
|
||||
dialog = self.Dummy_HelpSource(__file__ + 'bad-bad-bad')
|
||||
self.assertEqual(dialog.path_ok(), None)
|
||||
self.assertIn('not exist', dialog.path_error['text'])
|
||||
|
||||
def test_path_ok_web(self):
|
||||
dialog = self.Dummy_HelpSource('')
|
||||
Equal = self.assertEqual
|
||||
for url in 'www.py.org', 'http://py.org':
|
||||
with self.subTest():
|
||||
dialog.path.set(url)
|
||||
self.assertEqual(dialog.path_ok(), url)
|
||||
self.assertEqual(dialog.path_error['text'], '')
|
||||
|
||||
def test_path_ok_file(self):
|
||||
dialog = self.Dummy_HelpSource('')
|
||||
for platform, prefix in ('darwin', 'file://'), ('other', ''):
|
||||
with self.subTest():
|
||||
query.platform = platform
|
||||
dialog.path.set(__file__)
|
||||
self.assertEqual(dialog.path_ok(), prefix + __file__)
|
||||
self.assertEqual(dialog.path_error['text'], '')
|
||||
|
||||
|
||||
class HelpsourceEntryokTest(unittest.TestCase):
|
||||
"Test entry_ok method of HelpSource subclass of Query."
|
||||
|
||||
class Dummy_HelpSource:
|
||||
entry_ok = query.HelpSource.entry_ok
|
||||
entry_error = {}
|
||||
path_error = {}
|
||||
def item_ok(self):
|
||||
return self.name
|
||||
def path_ok(self):
|
||||
return self.path
|
||||
|
||||
def test_entry_ok_helpsource(self):
|
||||
dialog = self.Dummy_HelpSource()
|
||||
for name, path, result in ((None, None, None),
|
||||
(None, 'doc.txt', None),
|
||||
('doc', None, None),
|
||||
('doc', 'doc.txt', ('doc', 'doc.txt'))):
|
||||
with self.subTest():
|
||||
dialog.name, dialog.path = name, path
|
||||
self.assertEqual(dialog.entry_ok(), result)
|
||||
|
||||
|
||||
# 2 CustomRun test classes each test one method.
|
||||
|
||||
class CustomRunCLIargsokTest(unittest.TestCase):
|
||||
"Test cli_ok method of the CustomRun subclass of Query."
|
||||
|
||||
class Dummy_CustomRun:
|
||||
cli_args_ok = query.CustomRun.cli_args_ok
|
||||
def __init__(self, dummy_entry):
|
||||
self.entry = Var(value=dummy_entry)
|
||||
self.entry_error = {'text': ''}
|
||||
def showerror(self, message):
|
||||
self.entry_error['text'] = message
|
||||
|
||||
def test_blank_args(self):
|
||||
dialog = self.Dummy_CustomRun(' ')
|
||||
self.assertEqual(dialog.cli_args_ok(), [])
|
||||
|
||||
def test_invalid_args(self):
|
||||
dialog = self.Dummy_CustomRun("'no-closing-quote")
|
||||
self.assertEqual(dialog.cli_args_ok(), None)
|
||||
self.assertIn('No closing', dialog.entry_error['text'])
|
||||
|
||||
def test_good_args(self):
|
||||
args = ['-n', '10', '--verbose', '-p', '/path', '--name']
|
||||
dialog = self.Dummy_CustomRun(' '.join(args) + ' "my name"')
|
||||
self.assertEqual(dialog.cli_args_ok(), args + ["my name"])
|
||||
self.assertEqual(dialog.entry_error['text'], '')
|
||||
|
||||
|
||||
class CustomRunEntryokTest(unittest.TestCase):
|
||||
"Test entry_ok method of the CustomRun subclass of Query."
|
||||
|
||||
class Dummy_CustomRun:
|
||||
entry_ok = query.CustomRun.entry_ok
|
||||
entry_error = {}
|
||||
restartvar = Var()
|
||||
def cli_args_ok(self):
|
||||
return self.cli_args
|
||||
|
||||
def test_entry_ok_customrun(self):
|
||||
dialog = self.Dummy_CustomRun()
|
||||
for restart in {True, False}:
|
||||
dialog.restartvar.set(restart)
|
||||
for cli_args, result in ((None, None),
|
||||
(['my arg'], (['my arg'], restart))):
|
||||
with self.subTest(restart=restart, cli_args=cli_args):
|
||||
dialog.cli_args = cli_args
|
||||
self.assertEqual(dialog.entry_ok(), result)
|
||||
|
||||
|
||||
# GUI TESTS
|
||||
|
||||
class QueryGuiTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.dialog = query.Query(root, 'TEST', 'test', _utest=True)
|
||||
cls.dialog.destroy = mock.Mock()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.dialog.destroy
|
||||
del cls.dialog
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.dialog.entry.delete(0, 'end')
|
||||
self.dialog.result = None
|
||||
self.dialog.destroy.reset_mock()
|
||||
|
||||
def test_click_ok(self):
|
||||
dialog = self.dialog
|
||||
dialog.entry.insert(0, 'abc')
|
||||
dialog.button_ok.invoke()
|
||||
self.assertEqual(dialog.result, 'abc')
|
||||
self.assertTrue(dialog.destroy.called)
|
||||
|
||||
def test_click_blank(self):
|
||||
dialog = self.dialog
|
||||
dialog.button_ok.invoke()
|
||||
self.assertEqual(dialog.result, None)
|
||||
self.assertFalse(dialog.destroy.called)
|
||||
|
||||
def test_click_cancel(self):
|
||||
dialog = self.dialog
|
||||
dialog.entry.insert(0, 'abc')
|
||||
dialog.button_cancel.invoke()
|
||||
self.assertEqual(dialog.result, None)
|
||||
self.assertTrue(dialog.destroy.called)
|
||||
|
||||
|
||||
class SectionnameGuiTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
|
||||
def test_click_section_name(self):
|
||||
root = Tk()
|
||||
root.withdraw()
|
||||
dialog = query.SectionName(root, 'T', 't', {'abc'}, _utest=True)
|
||||
Equal = self.assertEqual
|
||||
self.assertEqual(dialog.used_names, {'abc'})
|
||||
dialog.entry.insert(0, 'okay')
|
||||
dialog.button_ok.invoke()
|
||||
self.assertEqual(dialog.result, 'okay')
|
||||
root.destroy()
|
||||
|
||||
|
||||
class ModulenameGuiTest(unittest.TestCase, ExtraAssertions):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
|
||||
def test_click_module_name(self):
|
||||
root = Tk()
|
||||
root.withdraw()
|
||||
dialog = query.ModuleName(root, 'T', 't', 'idlelib', _utest=True)
|
||||
self.assertEqual(dialog.text0, 'idlelib')
|
||||
self.assertEqual(dialog.entry.get(), 'idlelib')
|
||||
dialog.button_ok.invoke()
|
||||
self.assertEndsWith(dialog.result, '__init__.py')
|
||||
root.destroy()
|
||||
|
||||
|
||||
class GotoGuiTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
|
||||
def test_click_module_name(self):
|
||||
root = Tk()
|
||||
root.withdraw()
|
||||
dialog = query.Goto(root, 'T', 't', _utest=True)
|
||||
dialog.entry.insert(0, '22')
|
||||
dialog.button_ok.invoke()
|
||||
self.assertEqual(dialog.result, 22)
|
||||
root.destroy()
|
||||
|
||||
|
||||
class HelpsourceGuiTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
|
||||
def test_click_help_source(self):
|
||||
root = Tk()
|
||||
root.withdraw()
|
||||
dialog = query.HelpSource(root, 'T', menuitem='__test__',
|
||||
filepath=__file__, _utest=True)
|
||||
Equal = self.assertEqual
|
||||
Equal(dialog.entry.get(), '__test__')
|
||||
Equal(dialog.path.get(), __file__)
|
||||
dialog.button_ok.invoke()
|
||||
prefix = "file://" if sys.platform == 'darwin' else ''
|
||||
Equal(dialog.result, ('__test__', prefix + __file__))
|
||||
root.destroy()
|
||||
|
||||
|
||||
class CustomRunGuiTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
|
||||
def test_click_args(self):
|
||||
root = Tk()
|
||||
root.withdraw()
|
||||
dialog = query.CustomRun(root, 'Title',
|
||||
cli_args=['a', 'b=1'], _utest=True)
|
||||
self.assertEqual(dialog.entry.get(), 'a b=1')
|
||||
dialog.entry.insert(END, ' c')
|
||||
dialog.button_ok.invoke()
|
||||
self.assertEqual(dialog.result, (['a', 'b=1', 'c'], True))
|
||||
root.destroy()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=False)
|
||||
123
Dependencies/Python/Lib/idlelib/idle_test/test_redirector.py
vendored
Normal file
123
Dependencies/Python/Lib/idlelib/idle_test/test_redirector.py
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
"Test redirector, coverage 100%."
|
||||
|
||||
from idlelib.redirector import WidgetRedirector
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from test.support.testcase import ExtraAssertions
|
||||
from tkinter import Tk, Text, TclError
|
||||
from idlelib.idle_test.mock_idle import Func
|
||||
|
||||
|
||||
class InitCloseTest(unittest.TestCase, ExtraAssertions):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.text = Text(cls.root)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_init(self):
|
||||
redir = WidgetRedirector(self.text)
|
||||
self.assertEqual(redir.widget, self.text)
|
||||
self.assertEqual(redir.tk, self.text.tk)
|
||||
self.assertRaises(TclError, WidgetRedirector, self.text)
|
||||
redir.close() # restore self.tk, self.text
|
||||
|
||||
def test_close(self):
|
||||
redir = WidgetRedirector(self.text)
|
||||
redir.register('insert', Func)
|
||||
redir.close()
|
||||
self.assertEqual(redir._operations, {})
|
||||
self.assertNotHasAttr(self.text, 'widget')
|
||||
|
||||
|
||||
class WidgetRedirectorTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.text = Text(cls.root)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.redir = WidgetRedirector(self.text)
|
||||
self.func = Func()
|
||||
self.orig_insert = self.redir.register('insert', self.func)
|
||||
self.text.insert('insert', 'asdf') # leaves self.text empty
|
||||
|
||||
def tearDown(self):
|
||||
self.text.delete('1.0', 'end')
|
||||
self.redir.close()
|
||||
|
||||
def test_repr(self): # partly for 100% coverage
|
||||
self.assertIn('Redirector', repr(self.redir))
|
||||
self.assertIn('Original', repr(self.orig_insert))
|
||||
|
||||
def test_register(self):
|
||||
self.assertEqual(self.text.get('1.0', 'end'), '\n')
|
||||
self.assertEqual(self.func.args, ('insert', 'asdf'))
|
||||
self.assertIn('insert', self.redir._operations)
|
||||
self.assertIn('insert', self.text.__dict__)
|
||||
self.assertEqual(self.text.insert, self.func)
|
||||
|
||||
def test_original_command(self):
|
||||
self.assertEqual(self.orig_insert.operation, 'insert')
|
||||
self.assertEqual(self.orig_insert.tk_call, self.text.tk.call)
|
||||
self.orig_insert('insert', 'asdf')
|
||||
self.assertEqual(self.text.get('1.0', 'end'), 'asdf\n')
|
||||
|
||||
def test_unregister(self):
|
||||
self.assertIsNone(self.redir.unregister('invalid operation name'))
|
||||
self.assertEqual(self.redir.unregister('insert'), self.func)
|
||||
self.assertNotIn('insert', self.redir._operations)
|
||||
self.assertNotIn('insert', self.text.__dict__)
|
||||
|
||||
def test_unregister_no_attribute(self):
|
||||
del self.text.insert
|
||||
self.assertEqual(self.redir.unregister('insert'), self.func)
|
||||
|
||||
def test_dispatch_intercept(self):
|
||||
self.func.__init__(True)
|
||||
self.assertTrue(self.redir.dispatch('insert', False))
|
||||
self.assertFalse(self.func.args[0])
|
||||
|
||||
def test_dispatch_bypass(self):
|
||||
self.orig_insert('insert', 'asdf')
|
||||
# tk.call returns '' where Python would return None
|
||||
self.assertEqual(self.redir.dispatch('delete', '1.0', 'end'), '')
|
||||
self.assertEqual(self.text.get('1.0', 'end'), '\n')
|
||||
|
||||
def test_dispatch_error(self):
|
||||
self.func.__init__(TclError())
|
||||
self.assertEqual(self.redir.dispatch('insert', False), '')
|
||||
self.assertEqual(self.redir.dispatch('invalid'), '')
|
||||
|
||||
def test_command_dispatch(self):
|
||||
# Test that .__init__ causes redirection of tk calls
|
||||
# through redir.dispatch
|
||||
self.root.call(self.text._w, 'insert', 'hello')
|
||||
self.assertEqual(self.func.args, ('hello',))
|
||||
self.assertEqual(self.text.get('1.0', 'end'), '\n')
|
||||
# Ensure that called through redir .dispatch and not through
|
||||
# self.text.insert by having mock raise TclError.
|
||||
self.func.__init__(TclError())
|
||||
self.assertEqual(self.root.call(self.text._w, 'insert', 'boo'), '')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
294
Dependencies/Python/Lib/idlelib/idle_test/test_replace.py
vendored
Normal file
294
Dependencies/Python/Lib/idlelib/idle_test/test_replace.py
vendored
Normal file
@@ -0,0 +1,294 @@
|
||||
"Test replace, coverage 78%."
|
||||
|
||||
from idlelib.replace import ReplaceDialog
|
||||
import unittest
|
||||
from test.support import requires
|
||||
requires('gui')
|
||||
from tkinter import Tk, Text
|
||||
|
||||
from unittest.mock import Mock
|
||||
from idlelib.idle_test.mock_tk import Mbox
|
||||
import idlelib.searchengine as se
|
||||
|
||||
orig_mbox = se.messagebox
|
||||
showerror = Mbox.showerror
|
||||
|
||||
|
||||
class ReplaceDialogTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
se.messagebox = Mbox
|
||||
cls.engine = se.SearchEngine(cls.root)
|
||||
cls.dialog = ReplaceDialog(cls.root, cls.engine)
|
||||
cls.dialog.bell = lambda: None
|
||||
cls.dialog.ok = Mock()
|
||||
cls.text = Text(cls.root)
|
||||
cls.text.undo_block_start = Mock()
|
||||
cls.text.undo_block_stop = Mock()
|
||||
cls.dialog.text = cls.text
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
se.messagebox = orig_mbox
|
||||
del cls.text, cls.dialog, cls.engine
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.text.insert('insert', 'This is a sample sTring')
|
||||
|
||||
def tearDown(self):
|
||||
self.engine.patvar.set('')
|
||||
self.dialog.replvar.set('')
|
||||
self.engine.wordvar.set(False)
|
||||
self.engine.casevar.set(False)
|
||||
self.engine.revar.set(False)
|
||||
self.engine.wrapvar.set(True)
|
||||
self.engine.backvar.set(False)
|
||||
showerror.title = ''
|
||||
showerror.message = ''
|
||||
self.text.delete('1.0', 'end')
|
||||
|
||||
def test_replace_simple(self):
|
||||
# Test replace function with all options at default setting.
|
||||
# Wrap around - True
|
||||
# Regular Expression - False
|
||||
# Match case - False
|
||||
# Match word - False
|
||||
# Direction - Forwards
|
||||
text = self.text
|
||||
equal = self.assertEqual
|
||||
pv = self.engine.patvar
|
||||
rv = self.dialog.replvar
|
||||
replace = self.dialog.replace_it
|
||||
|
||||
# test accessor method
|
||||
self.engine.setpat('asdf')
|
||||
equal(self.engine.getpat(), pv.get())
|
||||
|
||||
# text found and replaced
|
||||
pv.set('a')
|
||||
rv.set('asdf')
|
||||
replace()
|
||||
equal(text.get('1.8', '1.12'), 'asdf')
|
||||
|
||||
# don't "match word" case
|
||||
text.mark_set('insert', '1.0')
|
||||
pv.set('is')
|
||||
rv.set('hello')
|
||||
replace()
|
||||
equal(text.get('1.2', '1.7'), 'hello')
|
||||
|
||||
# don't "match case" case
|
||||
pv.set('string')
|
||||
rv.set('world')
|
||||
replace()
|
||||
equal(text.get('1.23', '1.28'), 'world')
|
||||
|
||||
# without "regular expression" case
|
||||
text.mark_set('insert', 'end')
|
||||
text.insert('insert', '\nline42:')
|
||||
before_text = text.get('1.0', 'end')
|
||||
pv.set(r'[a-z][\d]+')
|
||||
replace()
|
||||
after_text = text.get('1.0', 'end')
|
||||
equal(before_text, after_text)
|
||||
|
||||
# test with wrap around selected and complete a cycle
|
||||
text.mark_set('insert', '1.9')
|
||||
pv.set('i')
|
||||
rv.set('j')
|
||||
replace()
|
||||
equal(text.get('1.8'), 'i')
|
||||
equal(text.get('2.1'), 'j')
|
||||
replace()
|
||||
equal(text.get('2.1'), 'j')
|
||||
equal(text.get('1.8'), 'j')
|
||||
before_text = text.get('1.0', 'end')
|
||||
replace()
|
||||
after_text = text.get('1.0', 'end')
|
||||
equal(before_text, after_text)
|
||||
|
||||
# text not found
|
||||
before_text = text.get('1.0', 'end')
|
||||
pv.set('foobar')
|
||||
replace()
|
||||
after_text = text.get('1.0', 'end')
|
||||
equal(before_text, after_text)
|
||||
|
||||
# test access method
|
||||
self.dialog.find_it(0)
|
||||
|
||||
def test_replace_wrap_around(self):
|
||||
text = self.text
|
||||
equal = self.assertEqual
|
||||
pv = self.engine.patvar
|
||||
rv = self.dialog.replvar
|
||||
replace = self.dialog.replace_it
|
||||
self.engine.wrapvar.set(False)
|
||||
|
||||
# replace candidate found both after and before 'insert'
|
||||
text.mark_set('insert', '1.4')
|
||||
pv.set('i')
|
||||
rv.set('j')
|
||||
replace()
|
||||
equal(text.get('1.2'), 'i')
|
||||
equal(text.get('1.5'), 'j')
|
||||
replace()
|
||||
equal(text.get('1.2'), 'i')
|
||||
equal(text.get('1.20'), 'j')
|
||||
replace()
|
||||
equal(text.get('1.2'), 'i')
|
||||
|
||||
# replace candidate found only before 'insert'
|
||||
text.mark_set('insert', '1.8')
|
||||
pv.set('is')
|
||||
before_text = text.get('1.0', 'end')
|
||||
replace()
|
||||
after_text = text.get('1.0', 'end')
|
||||
equal(before_text, after_text)
|
||||
|
||||
def test_replace_whole_word(self):
|
||||
text = self.text
|
||||
equal = self.assertEqual
|
||||
pv = self.engine.patvar
|
||||
rv = self.dialog.replvar
|
||||
replace = self.dialog.replace_it
|
||||
self.engine.wordvar.set(True)
|
||||
|
||||
pv.set('is')
|
||||
rv.set('hello')
|
||||
replace()
|
||||
equal(text.get('1.0', '1.4'), 'This')
|
||||
equal(text.get('1.5', '1.10'), 'hello')
|
||||
|
||||
def test_replace_match_case(self):
|
||||
equal = self.assertEqual
|
||||
text = self.text
|
||||
pv = self.engine.patvar
|
||||
rv = self.dialog.replvar
|
||||
replace = self.dialog.replace_it
|
||||
self.engine.casevar.set(True)
|
||||
|
||||
before_text = self.text.get('1.0', 'end')
|
||||
pv.set('this')
|
||||
rv.set('that')
|
||||
replace()
|
||||
after_text = self.text.get('1.0', 'end')
|
||||
equal(before_text, after_text)
|
||||
|
||||
pv.set('This')
|
||||
replace()
|
||||
equal(text.get('1.0', '1.4'), 'that')
|
||||
|
||||
def test_replace_regex(self):
|
||||
equal = self.assertEqual
|
||||
text = self.text
|
||||
pv = self.engine.patvar
|
||||
rv = self.dialog.replvar
|
||||
replace = self.dialog.replace_it
|
||||
self.engine.revar.set(True)
|
||||
|
||||
before_text = text.get('1.0', 'end')
|
||||
pv.set(r'[a-z][\d]+')
|
||||
rv.set('hello')
|
||||
replace()
|
||||
after_text = text.get('1.0', 'end')
|
||||
equal(before_text, after_text)
|
||||
|
||||
text.insert('insert', '\nline42')
|
||||
replace()
|
||||
equal(text.get('2.0', '2.8'), 'linhello')
|
||||
|
||||
pv.set('')
|
||||
replace()
|
||||
self.assertIn('error', showerror.title)
|
||||
self.assertIn('Empty', showerror.message)
|
||||
|
||||
pv.set(r'[\d')
|
||||
replace()
|
||||
self.assertIn('error', showerror.title)
|
||||
self.assertIn('Pattern', showerror.message)
|
||||
|
||||
showerror.title = ''
|
||||
showerror.message = ''
|
||||
pv.set('[a]')
|
||||
rv.set('test\\')
|
||||
replace()
|
||||
self.assertIn('error', showerror.title)
|
||||
self.assertIn('Invalid Replace Expression', showerror.message)
|
||||
|
||||
# test access method
|
||||
self.engine.setcookedpat("?")
|
||||
equal(pv.get(), "\\?")
|
||||
|
||||
def test_replace_backwards(self):
|
||||
equal = self.assertEqual
|
||||
text = self.text
|
||||
pv = self.engine.patvar
|
||||
rv = self.dialog.replvar
|
||||
replace = self.dialog.replace_it
|
||||
self.engine.backvar.set(True)
|
||||
|
||||
text.insert('insert', '\nis as ')
|
||||
|
||||
pv.set('is')
|
||||
rv.set('was')
|
||||
replace()
|
||||
equal(text.get('1.2', '1.4'), 'is')
|
||||
equal(text.get('2.0', '2.3'), 'was')
|
||||
replace()
|
||||
equal(text.get('1.5', '1.8'), 'was')
|
||||
replace()
|
||||
equal(text.get('1.2', '1.5'), 'was')
|
||||
|
||||
def test_replace_all(self):
|
||||
text = self.text
|
||||
pv = self.engine.patvar
|
||||
rv = self.dialog.replvar
|
||||
replace_all = self.dialog.replace_all
|
||||
|
||||
text.insert('insert', '\n')
|
||||
text.insert('insert', text.get('1.0', 'end')*100)
|
||||
pv.set('is')
|
||||
rv.set('was')
|
||||
replace_all()
|
||||
self.assertNotIn('is', text.get('1.0', 'end'))
|
||||
|
||||
self.engine.revar.set(True)
|
||||
pv.set('')
|
||||
replace_all()
|
||||
self.assertIn('error', showerror.title)
|
||||
self.assertIn('Empty', showerror.message)
|
||||
|
||||
pv.set('[s][T]')
|
||||
rv.set('\\')
|
||||
replace_all()
|
||||
|
||||
self.engine.revar.set(False)
|
||||
pv.set('text which is not present')
|
||||
rv.set('foobar')
|
||||
replace_all()
|
||||
|
||||
def test_default_command(self):
|
||||
text = self.text
|
||||
pv = self.engine.patvar
|
||||
rv = self.dialog.replvar
|
||||
replace_find = self.dialog.default_command
|
||||
equal = self.assertEqual
|
||||
|
||||
pv.set('This')
|
||||
rv.set('was')
|
||||
replace_find()
|
||||
equal(text.get('sel.first', 'sel.last'), 'was')
|
||||
|
||||
self.engine.revar.set(True)
|
||||
pv.set('')
|
||||
replace_find()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
29
Dependencies/Python/Lib/idlelib/idle_test/test_rpc.py
vendored
Normal file
29
Dependencies/Python/Lib/idlelib/idle_test/test_rpc.py
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
"Test rpc, coverage 20%."
|
||||
|
||||
from idlelib import rpc
|
||||
import unittest
|
||||
|
||||
|
||||
|
||||
class CodePicklerTest(unittest.TestCase):
|
||||
|
||||
def test_pickle_unpickle(self):
|
||||
def f(): return a + b + c
|
||||
func, (cbytes,) = rpc.pickle_code(f.__code__)
|
||||
self.assertIs(func, rpc.unpickle_code)
|
||||
self.assertIn(b'test_rpc.py', cbytes)
|
||||
code = rpc.unpickle_code(cbytes)
|
||||
self.assertEqual(code.co_names, ('a', 'b', 'c'))
|
||||
|
||||
def test_code_pickler(self):
|
||||
self.assertIn(type((lambda:None).__code__),
|
||||
rpc.CodePickler.dispatch_table)
|
||||
|
||||
def test_dumps(self):
|
||||
def f(): pass
|
||||
# The main test here is that pickling code does not raise.
|
||||
self.assertIn(b'test_rpc.py', rpc.dumps(f.__code__))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
433
Dependencies/Python/Lib/idlelib/idle_test/test_run.py
vendored
Normal file
433
Dependencies/Python/Lib/idlelib/idle_test/test_run.py
vendored
Normal file
@@ -0,0 +1,433 @@
|
||||
"Test run, coverage 54%."
|
||||
|
||||
from idlelib import run
|
||||
import io
|
||||
import sys
|
||||
from test.support import captured_output, captured_stderr
|
||||
import unittest
|
||||
from unittest import mock
|
||||
import idlelib
|
||||
from idlelib.idle_test.mock_idle import Func
|
||||
from test.support import force_not_colorized
|
||||
|
||||
idlelib.testing = True # Use {} for executing test user code.
|
||||
|
||||
|
||||
class ExceptionTest(unittest.TestCase):
|
||||
|
||||
def test_print_exception_unhashable(self):
|
||||
class UnhashableException(Exception):
|
||||
def __eq__(self, other):
|
||||
return True
|
||||
|
||||
ex1 = UnhashableException('ex1')
|
||||
ex2 = UnhashableException('ex2')
|
||||
try:
|
||||
raise ex2 from ex1
|
||||
except UnhashableException:
|
||||
try:
|
||||
raise ex1
|
||||
except UnhashableException:
|
||||
with captured_stderr() as output:
|
||||
with mock.patch.object(run, 'cleanup_traceback') as ct:
|
||||
ct.side_effect = lambda t, e: t
|
||||
run.print_exception()
|
||||
|
||||
tb = output.getvalue().strip().splitlines()
|
||||
self.assertEqual(11, len(tb))
|
||||
self.assertIn('UnhashableException: ex2', tb[3])
|
||||
self.assertIn('UnhashableException: ex1', tb[10])
|
||||
|
||||
data = (('1/0', ZeroDivisionError, "division by zero\n"),
|
||||
('abc', NameError, "name 'abc' is not defined. "
|
||||
"Did you mean: 'abs'? "
|
||||
"Or did you forget to import 'abc'?\n"),
|
||||
('int.reel', AttributeError,
|
||||
"type object 'int' has no attribute 'reel'. "
|
||||
"Did you mean: 'real'?\n"),
|
||||
)
|
||||
|
||||
@force_not_colorized
|
||||
def test_get_message(self):
|
||||
for code, exc, msg in self.data:
|
||||
with self.subTest(code=code):
|
||||
try:
|
||||
eval(compile(code, '', 'eval'))
|
||||
except exc:
|
||||
typ, val, tb = sys.exc_info()
|
||||
actual = run.get_message_lines(typ, val, tb)[0]
|
||||
expect = f'{exc.__name__}: {msg}'
|
||||
self.assertEqual(actual, expect)
|
||||
|
||||
@force_not_colorized
|
||||
@mock.patch.object(run, 'cleanup_traceback',
|
||||
new_callable=lambda: (lambda t, e: None))
|
||||
def test_get_multiple_message(self, mock):
|
||||
d = self.data
|
||||
data2 = ((d[0], d[1]), (d[1], d[2]), (d[2], d[0]))
|
||||
subtests = 0
|
||||
for (code1, exc1, msg1), (code2, exc2, msg2) in data2:
|
||||
with self.subTest(codes=(code1,code2)):
|
||||
try:
|
||||
eval(compile(code1, '', 'eval'))
|
||||
except exc1:
|
||||
try:
|
||||
eval(compile(code2, '', 'eval'))
|
||||
except exc2:
|
||||
with captured_stderr() as output:
|
||||
run.print_exception()
|
||||
actual = output.getvalue()
|
||||
self.assertIn(msg1, actual)
|
||||
self.assertIn(msg2, actual)
|
||||
subtests += 1
|
||||
self.assertEqual(subtests, len(data2)) # All subtests ran?
|
||||
|
||||
# StdioFile tests.
|
||||
|
||||
class S(str):
|
||||
def __str__(self):
|
||||
return '%s:str' % type(self).__name__
|
||||
def __unicode__(self):
|
||||
return '%s:unicode' % type(self).__name__
|
||||
def __len__(self):
|
||||
return 3
|
||||
def __iter__(self):
|
||||
return iter('abc')
|
||||
def __getitem__(self, *args):
|
||||
return '%s:item' % type(self).__name__
|
||||
def __getslice__(self, *args):
|
||||
return '%s:slice' % type(self).__name__
|
||||
|
||||
|
||||
class MockShell:
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
def write(self, *args):
|
||||
self.written.append(args)
|
||||
def readline(self):
|
||||
return self.lines.pop()
|
||||
def close(self):
|
||||
pass
|
||||
def reset(self):
|
||||
self.written = []
|
||||
def push(self, lines):
|
||||
self.lines = list(lines)[::-1]
|
||||
|
||||
|
||||
class StdInputFilesTest(unittest.TestCase):
|
||||
|
||||
def test_misc(self):
|
||||
shell = MockShell()
|
||||
f = run.StdInputFile(shell, 'stdin')
|
||||
self.assertIsInstance(f, io.TextIOBase)
|
||||
self.assertEqual(f.encoding, 'utf-8')
|
||||
self.assertEqual(f.errors, 'strict')
|
||||
self.assertIsNone(f.newlines)
|
||||
self.assertEqual(f.name, '<stdin>')
|
||||
self.assertFalse(f.closed)
|
||||
self.assertTrue(f.isatty())
|
||||
self.assertTrue(f.readable())
|
||||
self.assertFalse(f.writable())
|
||||
self.assertFalse(f.seekable())
|
||||
|
||||
def test_unsupported(self):
|
||||
shell = MockShell()
|
||||
f = run.StdInputFile(shell, 'stdin')
|
||||
self.assertRaises(OSError, f.fileno)
|
||||
self.assertRaises(OSError, f.tell)
|
||||
self.assertRaises(OSError, f.seek, 0)
|
||||
self.assertRaises(OSError, f.write, 'x')
|
||||
self.assertRaises(OSError, f.writelines, ['x'])
|
||||
|
||||
def test_read(self):
|
||||
shell = MockShell()
|
||||
f = run.StdInputFile(shell, 'stdin')
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.read(), 'one\ntwo\n')
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.read(-1), 'one\ntwo\n')
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.read(None), 'one\ntwo\n')
|
||||
shell.push(['one\n', 'two\n', 'three\n', ''])
|
||||
self.assertEqual(f.read(2), 'on')
|
||||
self.assertEqual(f.read(3), 'e\nt')
|
||||
self.assertEqual(f.read(10), 'wo\nthree\n')
|
||||
|
||||
shell.push(['one\n', 'two\n'])
|
||||
self.assertEqual(f.read(0), '')
|
||||
self.assertRaises(TypeError, f.read, 1.5)
|
||||
self.assertRaises(TypeError, f.read, '1')
|
||||
self.assertRaises(TypeError, f.read, 1, 1)
|
||||
|
||||
def test_readline(self):
|
||||
shell = MockShell()
|
||||
f = run.StdInputFile(shell, 'stdin')
|
||||
shell.push(['one\n', 'two\n', 'three\n', 'four\n'])
|
||||
self.assertEqual(f.readline(), 'one\n')
|
||||
self.assertEqual(f.readline(-1), 'two\n')
|
||||
self.assertEqual(f.readline(None), 'three\n')
|
||||
shell.push(['one\ntwo\n'])
|
||||
self.assertEqual(f.readline(), 'one\n')
|
||||
self.assertEqual(f.readline(), 'two\n')
|
||||
shell.push(['one', 'two', 'three'])
|
||||
self.assertEqual(f.readline(), 'one')
|
||||
self.assertEqual(f.readline(), 'two')
|
||||
shell.push(['one\n', 'two\n', 'three\n'])
|
||||
self.assertEqual(f.readline(2), 'on')
|
||||
self.assertEqual(f.readline(1), 'e')
|
||||
self.assertEqual(f.readline(1), '\n')
|
||||
self.assertEqual(f.readline(10), 'two\n')
|
||||
|
||||
shell.push(['one\n', 'two\n'])
|
||||
self.assertEqual(f.readline(0), '')
|
||||
self.assertRaises(TypeError, f.readlines, 1.5)
|
||||
self.assertRaises(TypeError, f.readlines, '1')
|
||||
self.assertRaises(TypeError, f.readlines, 1, 1)
|
||||
|
||||
def test_readlines(self):
|
||||
shell = MockShell()
|
||||
f = run.StdInputFile(shell, 'stdin')
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.readlines(), ['one\n', 'two\n'])
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.readlines(-1), ['one\n', 'two\n'])
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.readlines(None), ['one\n', 'two\n'])
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.readlines(0), ['one\n', 'two\n'])
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.readlines(3), ['one\n'])
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertEqual(f.readlines(4), ['one\n', 'two\n'])
|
||||
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertRaises(TypeError, f.readlines, 1.5)
|
||||
self.assertRaises(TypeError, f.readlines, '1')
|
||||
self.assertRaises(TypeError, f.readlines, 1, 1)
|
||||
|
||||
def test_close(self):
|
||||
shell = MockShell()
|
||||
f = run.StdInputFile(shell, 'stdin')
|
||||
shell.push(['one\n', 'two\n', ''])
|
||||
self.assertFalse(f.closed)
|
||||
self.assertEqual(f.readline(), 'one\n')
|
||||
f.close()
|
||||
self.assertFalse(f.closed)
|
||||
self.assertEqual(f.readline(), 'two\n')
|
||||
self.assertRaises(TypeError, f.close, 1)
|
||||
|
||||
|
||||
class StdOutputFilesTest(unittest.TestCase):
|
||||
|
||||
def test_misc(self):
|
||||
shell = MockShell()
|
||||
f = run.StdOutputFile(shell, 'stdout')
|
||||
self.assertIsInstance(f, io.TextIOBase)
|
||||
self.assertEqual(f.encoding, 'utf-8')
|
||||
self.assertEqual(f.errors, 'strict')
|
||||
self.assertIsNone(f.newlines)
|
||||
self.assertEqual(f.name, '<stdout>')
|
||||
self.assertFalse(f.closed)
|
||||
self.assertTrue(f.isatty())
|
||||
self.assertFalse(f.readable())
|
||||
self.assertTrue(f.writable())
|
||||
self.assertFalse(f.seekable())
|
||||
|
||||
def test_unsupported(self):
|
||||
shell = MockShell()
|
||||
f = run.StdOutputFile(shell, 'stdout')
|
||||
self.assertRaises(OSError, f.fileno)
|
||||
self.assertRaises(OSError, f.tell)
|
||||
self.assertRaises(OSError, f.seek, 0)
|
||||
self.assertRaises(OSError, f.read, 0)
|
||||
self.assertRaises(OSError, f.readline, 0)
|
||||
|
||||
def test_write(self):
|
||||
shell = MockShell()
|
||||
f = run.StdOutputFile(shell, 'stdout')
|
||||
f.write('test')
|
||||
self.assertEqual(shell.written, [('test', 'stdout')])
|
||||
shell.reset()
|
||||
f.write('t\xe8\u015b\U0001d599')
|
||||
self.assertEqual(shell.written, [('t\xe8\u015b\U0001d599', 'stdout')])
|
||||
shell.reset()
|
||||
|
||||
f.write(S('t\xe8\u015b\U0001d599'))
|
||||
self.assertEqual(shell.written, [('t\xe8\u015b\U0001d599', 'stdout')])
|
||||
self.assertEqual(type(shell.written[0][0]), str)
|
||||
shell.reset()
|
||||
|
||||
self.assertRaises(TypeError, f.write)
|
||||
self.assertEqual(shell.written, [])
|
||||
self.assertRaises(TypeError, f.write, b'test')
|
||||
self.assertRaises(TypeError, f.write, 123)
|
||||
self.assertEqual(shell.written, [])
|
||||
self.assertRaises(TypeError, f.write, 'test', 'spam')
|
||||
self.assertEqual(shell.written, [])
|
||||
|
||||
def test_write_stderr_nonencodable(self):
|
||||
shell = MockShell()
|
||||
f = run.StdOutputFile(shell, 'stderr', 'iso-8859-15', 'backslashreplace')
|
||||
f.write('t\xe8\u015b\U0001d599\xa4')
|
||||
self.assertEqual(shell.written, [('t\xe8\\u015b\\U0001d599\\xa4', 'stderr')])
|
||||
shell.reset()
|
||||
|
||||
f.write(S('t\xe8\u015b\U0001d599\xa4'))
|
||||
self.assertEqual(shell.written, [('t\xe8\\u015b\\U0001d599\\xa4', 'stderr')])
|
||||
self.assertEqual(type(shell.written[0][0]), str)
|
||||
shell.reset()
|
||||
|
||||
self.assertRaises(TypeError, f.write)
|
||||
self.assertEqual(shell.written, [])
|
||||
self.assertRaises(TypeError, f.write, b'test')
|
||||
self.assertRaises(TypeError, f.write, 123)
|
||||
self.assertEqual(shell.written, [])
|
||||
self.assertRaises(TypeError, f.write, 'test', 'spam')
|
||||
self.assertEqual(shell.written, [])
|
||||
|
||||
def test_writelines(self):
|
||||
shell = MockShell()
|
||||
f = run.StdOutputFile(shell, 'stdout')
|
||||
f.writelines([])
|
||||
self.assertEqual(shell.written, [])
|
||||
shell.reset()
|
||||
f.writelines(['one\n', 'two'])
|
||||
self.assertEqual(shell.written,
|
||||
[('one\n', 'stdout'), ('two', 'stdout')])
|
||||
shell.reset()
|
||||
f.writelines(['on\xe8\n', 'tw\xf2'])
|
||||
self.assertEqual(shell.written,
|
||||
[('on\xe8\n', 'stdout'), ('tw\xf2', 'stdout')])
|
||||
shell.reset()
|
||||
|
||||
f.writelines([S('t\xe8st')])
|
||||
self.assertEqual(shell.written, [('t\xe8st', 'stdout')])
|
||||
self.assertEqual(type(shell.written[0][0]), str)
|
||||
shell.reset()
|
||||
|
||||
self.assertRaises(TypeError, f.writelines)
|
||||
self.assertEqual(shell.written, [])
|
||||
self.assertRaises(TypeError, f.writelines, 123)
|
||||
self.assertEqual(shell.written, [])
|
||||
self.assertRaises(TypeError, f.writelines, [b'test'])
|
||||
self.assertRaises(TypeError, f.writelines, [123])
|
||||
self.assertEqual(shell.written, [])
|
||||
self.assertRaises(TypeError, f.writelines, [], [])
|
||||
self.assertEqual(shell.written, [])
|
||||
|
||||
def test_close(self):
|
||||
shell = MockShell()
|
||||
f = run.StdOutputFile(shell, 'stdout')
|
||||
self.assertFalse(f.closed)
|
||||
f.write('test')
|
||||
f.close()
|
||||
self.assertTrue(f.closed)
|
||||
self.assertRaises(ValueError, f.write, 'x')
|
||||
self.assertEqual(shell.written, [('test', 'stdout')])
|
||||
f.close()
|
||||
self.assertRaises(TypeError, f.close, 1)
|
||||
|
||||
|
||||
class RecursionLimitTest(unittest.TestCase):
|
||||
# Test (un)install_recursionlimit_wrappers and fixdoc.
|
||||
|
||||
def test_bad_setrecursionlimit_calls(self):
|
||||
run.install_recursionlimit_wrappers()
|
||||
self.addCleanup(run.uninstall_recursionlimit_wrappers)
|
||||
f = sys.setrecursionlimit
|
||||
self.assertRaises(TypeError, f, limit=100)
|
||||
self.assertRaises(TypeError, f, 100, 1000)
|
||||
self.assertRaises(ValueError, f, 0)
|
||||
|
||||
def test_roundtrip(self):
|
||||
run.install_recursionlimit_wrappers()
|
||||
self.addCleanup(run.uninstall_recursionlimit_wrappers)
|
||||
|
||||
# Check that setting the recursion limit works.
|
||||
orig_reclimit = sys.getrecursionlimit()
|
||||
self.addCleanup(sys.setrecursionlimit, orig_reclimit)
|
||||
sys.setrecursionlimit(orig_reclimit + 3)
|
||||
|
||||
# Check that the new limit is returned by sys.getrecursionlimit().
|
||||
new_reclimit = sys.getrecursionlimit()
|
||||
self.assertEqual(new_reclimit, orig_reclimit + 3)
|
||||
|
||||
def test_default_recursion_limit_preserved(self):
|
||||
orig_reclimit = sys.getrecursionlimit()
|
||||
run.install_recursionlimit_wrappers()
|
||||
self.addCleanup(run.uninstall_recursionlimit_wrappers)
|
||||
new_reclimit = sys.getrecursionlimit()
|
||||
self.assertEqual(new_reclimit, orig_reclimit)
|
||||
|
||||
def test_fixdoc(self):
|
||||
# Put here until better place for miscellaneous test.
|
||||
def func(): "docstring"
|
||||
run.fixdoc(func, "more")
|
||||
self.assertEqual(func.__doc__, "docstring\n\nmore")
|
||||
func.__doc__ = None
|
||||
run.fixdoc(func, "more")
|
||||
self.assertEqual(func.__doc__, "more")
|
||||
|
||||
|
||||
class HandleErrorTest(unittest.TestCase):
|
||||
# Method of MyRPCServer
|
||||
def test_fatal_error(self):
|
||||
eq = self.assertEqual
|
||||
with captured_output('__stderr__') as err,\
|
||||
mock.patch('idlelib.run.thread.interrupt_main',
|
||||
new_callable=Func) as func:
|
||||
try:
|
||||
raise EOFError
|
||||
except EOFError:
|
||||
run.MyRPCServer.handle_error(None, 'abc', '123')
|
||||
eq(run.exit_now, True)
|
||||
run.exit_now = False
|
||||
eq(err.getvalue(), '')
|
||||
|
||||
try:
|
||||
raise IndexError
|
||||
except IndexError:
|
||||
run.MyRPCServer.handle_error(None, 'abc', '123')
|
||||
eq(run.quitting, True)
|
||||
run.quitting = False
|
||||
msg = err.getvalue()
|
||||
self.assertIn('abc', msg)
|
||||
self.assertIn('123', msg)
|
||||
self.assertIn('IndexError', msg)
|
||||
eq(func.called, 2)
|
||||
|
||||
|
||||
class ExecRuncodeTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.addClassCleanup(setattr,run,'print_exception',run.print_exception)
|
||||
cls.prt = Func() # Need reference.
|
||||
run.print_exception = cls.prt
|
||||
mockrpc = mock.Mock()
|
||||
mockrpc.console.getvar = Func(result=False)
|
||||
cls.ex = run.Executive(mockrpc)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
assert sys.excepthook == sys.__excepthook__
|
||||
|
||||
def test_exceptions(self):
|
||||
ex = self.ex
|
||||
ex.runcode('1/0')
|
||||
self.assertIs(ex.user_exc_info[0], ZeroDivisionError)
|
||||
|
||||
self.addCleanup(setattr, sys, 'excepthook', sys.__excepthook__)
|
||||
sys.excepthook = lambda t, e, tb: run.print_exception(t)
|
||||
ex.runcode('1/0')
|
||||
self.assertIs(self.prt.args[0], ZeroDivisionError)
|
||||
|
||||
sys.excepthook = lambda: None
|
||||
ex.runcode('1/0')
|
||||
t, e, tb = ex.user_exc_info
|
||||
self.assertIs(t, TypeError)
|
||||
self.assertTrue(isinstance(e.__context__, ZeroDivisionError))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
33
Dependencies/Python/Lib/idlelib/idle_test/test_runscript.py
vendored
Normal file
33
Dependencies/Python/Lib/idlelib/idle_test/test_runscript.py
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
"Test runscript, coverage 16%."
|
||||
|
||||
from idlelib import runscript
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk
|
||||
from idlelib.editor import EditorWindow
|
||||
|
||||
|
||||
class ScriptBindingTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.update_idletasks()
|
||||
for id in cls.root.tk.call('after', 'info'):
|
||||
cls.root.after_cancel(id) # Need for EditorWindow.
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_init(self):
|
||||
ew = EditorWindow(root=self.root)
|
||||
sb = runscript.ScriptBinding(ew)
|
||||
ew._close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
27
Dependencies/Python/Lib/idlelib/idle_test/test_scrolledlist.py
vendored
Normal file
27
Dependencies/Python/Lib/idlelib/idle_test/test_scrolledlist.py
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
"Test scrolledlist, coverage 38%."
|
||||
|
||||
from idlelib.scrolledlist import ScrolledList
|
||||
import unittest
|
||||
from test.support import requires
|
||||
requires('gui')
|
||||
from tkinter import Tk
|
||||
|
||||
|
||||
class ScrolledListTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.root = Tk()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
|
||||
def test_init(self):
|
||||
ScrolledList(self.root)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
80
Dependencies/Python/Lib/idlelib/idle_test/test_search.py
vendored
Normal file
80
Dependencies/Python/Lib/idlelib/idle_test/test_search.py
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
"Test search, coverage 69%."
|
||||
|
||||
from idlelib import search
|
||||
import unittest
|
||||
from test.support import requires
|
||||
requires('gui')
|
||||
from tkinter import Tk, Text, BooleanVar
|
||||
from idlelib import searchengine
|
||||
|
||||
# Does not currently test the event handler wrappers.
|
||||
# A usage test should simulate clicks and check highlighting.
|
||||
# Tests need to be coordinated with SearchDialogBase tests
|
||||
# to avoid duplication.
|
||||
|
||||
|
||||
class SearchDialogTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.root = Tk()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.engine = searchengine.SearchEngine(self.root)
|
||||
self.dialog = search.SearchDialog(self.root, self.engine)
|
||||
self.dialog.bell = lambda: None
|
||||
self.text = Text(self.root)
|
||||
self.text.insert('1.0', 'Hello World!')
|
||||
|
||||
def test_find_again(self):
|
||||
# Search for various expressions
|
||||
text = self.text
|
||||
|
||||
self.engine.setpat('')
|
||||
self.assertFalse(self.dialog.find_again(text))
|
||||
self.dialog.bell = lambda: None
|
||||
|
||||
self.engine.setpat('Hello')
|
||||
self.assertTrue(self.dialog.find_again(text))
|
||||
|
||||
self.engine.setpat('Goodbye')
|
||||
self.assertFalse(self.dialog.find_again(text))
|
||||
|
||||
self.engine.setpat('World!')
|
||||
self.assertTrue(self.dialog.find_again(text))
|
||||
|
||||
self.engine.setpat('Hello World!')
|
||||
self.assertTrue(self.dialog.find_again(text))
|
||||
|
||||
# Regular expression
|
||||
self.engine.revar = BooleanVar(self.root, True)
|
||||
self.engine.setpat('W[aeiouy]r')
|
||||
self.assertTrue(self.dialog.find_again(text))
|
||||
|
||||
def test_find_selection(self):
|
||||
# Select some text and make sure it's found
|
||||
text = self.text
|
||||
# Add additional line to find
|
||||
self.text.insert('2.0', 'Hello World!')
|
||||
|
||||
text.tag_add('sel', '1.0', '1.4') # Select 'Hello'
|
||||
self.assertTrue(self.dialog.find_selection(text))
|
||||
|
||||
text.tag_remove('sel', '1.0', 'end')
|
||||
text.tag_add('sel', '1.6', '1.11') # Select 'World!'
|
||||
self.assertTrue(self.dialog.find_selection(text))
|
||||
|
||||
text.tag_remove('sel', '1.0', 'end')
|
||||
text.tag_add('sel', '1.0', '1.11') # Select 'Hello World!'
|
||||
self.assertTrue(self.dialog.find_selection(text))
|
||||
|
||||
# Remove additional line
|
||||
text.delete('2.0', 'end')
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=2)
|
||||
160
Dependencies/Python/Lib/idlelib/idle_test/test_searchbase.py
vendored
Normal file
160
Dependencies/Python/Lib/idlelib/idle_test/test_searchbase.py
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
"Test searchbase, coverage 98%."
|
||||
# The only thing not covered is inconsequential --
|
||||
# testing skipping of suite when self.needwrapbutton is false.
|
||||
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Text, Tk, Toplevel
|
||||
from tkinter.ttk import Frame
|
||||
from idlelib import searchengine as se
|
||||
from idlelib import searchbase as sdb
|
||||
from idlelib.idle_test.mock_idle import Func
|
||||
## from idlelib.idle_test.mock_tk import Var
|
||||
|
||||
# The ## imports above & following could help make some tests gui-free.
|
||||
# However, they currently make radiobutton tests fail.
|
||||
##def setUpModule():
|
||||
## # Replace tk objects used to initialize se.SearchEngine.
|
||||
## se.BooleanVar = Var
|
||||
## se.StringVar = Var
|
||||
##
|
||||
##def tearDownModule():
|
||||
## se.BooleanVar = BooleanVar
|
||||
## se.StringVar = StringVar
|
||||
|
||||
|
||||
class SearchDialogBaseTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.engine = se.SearchEngine(self.root) # None also seems to work
|
||||
self.dialog = sdb.SearchDialogBase(root=self.root, engine=self.engine)
|
||||
|
||||
def tearDown(self):
|
||||
self.dialog.close()
|
||||
|
||||
def test_open_and_close(self):
|
||||
# open calls create_widgets, which needs default_command
|
||||
self.dialog.default_command = None
|
||||
|
||||
toplevel = Toplevel(self.root)
|
||||
text = Text(toplevel)
|
||||
self.dialog.open(text)
|
||||
self.assertEqual(self.dialog.top.state(), 'normal')
|
||||
self.dialog.close()
|
||||
self.assertEqual(self.dialog.top.state(), 'withdrawn')
|
||||
|
||||
self.dialog.open(text, searchphrase="hello")
|
||||
self.assertEqual(self.dialog.ent.get(), 'hello')
|
||||
toplevel.update_idletasks()
|
||||
toplevel.destroy()
|
||||
|
||||
def test_create_widgets(self):
|
||||
self.dialog.create_entries = Func()
|
||||
self.dialog.create_option_buttons = Func()
|
||||
self.dialog.create_other_buttons = Func()
|
||||
self.dialog.create_command_buttons = Func()
|
||||
|
||||
self.dialog.default_command = None
|
||||
self.dialog.create_widgets()
|
||||
|
||||
self.assertTrue(self.dialog.create_entries.called)
|
||||
self.assertTrue(self.dialog.create_option_buttons.called)
|
||||
self.assertTrue(self.dialog.create_other_buttons.called)
|
||||
self.assertTrue(self.dialog.create_command_buttons.called)
|
||||
|
||||
def test_make_entry(self):
|
||||
equal = self.assertEqual
|
||||
self.dialog.row = 0
|
||||
self.dialog.frame = Frame(self.root)
|
||||
entry, label = self.dialog.make_entry("Test:", 'hello')
|
||||
equal(label['text'], 'Test:')
|
||||
|
||||
self.assertIn(entry.get(), 'hello')
|
||||
egi = entry.grid_info()
|
||||
equal(int(egi['row']), 0)
|
||||
equal(int(egi['column']), 1)
|
||||
equal(int(egi['rowspan']), 1)
|
||||
equal(int(egi['columnspan']), 1)
|
||||
equal(self.dialog.row, 1)
|
||||
|
||||
def test_create_entries(self):
|
||||
self.dialog.frame = Frame(self.root)
|
||||
self.dialog.row = 0
|
||||
self.engine.setpat('hello')
|
||||
self.dialog.create_entries()
|
||||
self.assertIn(self.dialog.ent.get(), 'hello')
|
||||
|
||||
def test_make_frame(self):
|
||||
self.dialog.row = 0
|
||||
self.dialog.frame = Frame(self.root)
|
||||
frame, label = self.dialog.make_frame()
|
||||
self.assertEqual(label, '')
|
||||
self.assertEqual(str(type(frame)), "<class 'tkinter.ttk.Frame'>")
|
||||
# self.assertIsInstance(frame, Frame) fails when test is run by
|
||||
# test_idle not run from IDLE editor. See issue 33987 PR.
|
||||
|
||||
frame, label = self.dialog.make_frame('testlabel')
|
||||
self.assertEqual(label['text'], 'testlabel')
|
||||
|
||||
def btn_test_setup(self, meth):
|
||||
self.dialog.frame = Frame(self.root)
|
||||
self.dialog.row = 0
|
||||
return meth()
|
||||
|
||||
def test_create_option_buttons(self):
|
||||
e = self.engine
|
||||
for state in (0, 1):
|
||||
for var in (e.revar, e.casevar, e.wordvar, e.wrapvar):
|
||||
var.set(state)
|
||||
frame, options = self.btn_test_setup(
|
||||
self.dialog.create_option_buttons)
|
||||
for spec, button in zip (options, frame.pack_slaves()):
|
||||
var, label = spec
|
||||
self.assertEqual(button['text'], label)
|
||||
self.assertEqual(var.get(), state)
|
||||
|
||||
def test_create_other_buttons(self):
|
||||
for state in (False, True):
|
||||
var = self.engine.backvar
|
||||
var.set(state)
|
||||
frame, others = self.btn_test_setup(
|
||||
self.dialog.create_other_buttons)
|
||||
buttons = frame.pack_slaves()
|
||||
for spec, button in zip(others, buttons):
|
||||
val, label = spec
|
||||
self.assertEqual(button['text'], label)
|
||||
if val == state:
|
||||
# hit other button, then this one
|
||||
# indexes depend on button order
|
||||
self.assertEqual(var.get(), state)
|
||||
|
||||
def test_make_button(self):
|
||||
self.dialog.frame = Frame(self.root)
|
||||
self.dialog.buttonframe = Frame(self.dialog.frame)
|
||||
btn = self.dialog.make_button('Test', self.dialog.close)
|
||||
self.assertEqual(btn['text'], 'Test')
|
||||
|
||||
def test_create_command_buttons(self):
|
||||
self.dialog.frame = Frame(self.root)
|
||||
self.dialog.create_command_buttons()
|
||||
# Look for close button command in buttonframe
|
||||
closebuttoncommand = ''
|
||||
for child in self.dialog.buttonframe.winfo_children():
|
||||
if child['text'] == 'Close':
|
||||
closebuttoncommand = child['command']
|
||||
self.assertIn('close', closebuttoncommand)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=2)
|
||||
332
Dependencies/Python/Lib/idlelib/idle_test/test_searchengine.py
vendored
Normal file
332
Dependencies/Python/Lib/idlelib/idle_test/test_searchengine.py
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
"Test searchengine, coverage 99%."
|
||||
|
||||
from idlelib import searchengine as se
|
||||
import unittest
|
||||
# from test.support import requires
|
||||
from tkinter import BooleanVar, StringVar, TclError # ,Tk, Text
|
||||
from tkinter import messagebox
|
||||
from idlelib.idle_test.mock_tk import Var, Mbox
|
||||
from idlelib.idle_test.mock_tk import Text as mockText
|
||||
import re
|
||||
|
||||
# With mock replacements, the module does not use any gui widgets.
|
||||
# The use of tk.Text is avoided (for now, until mock Text is improved)
|
||||
# by patching instances with an index function returning what is needed.
|
||||
# This works because mock Text.get does not use .index.
|
||||
# The tkinter imports are used to restore searchengine.
|
||||
|
||||
def setUpModule():
|
||||
# Replace s-e module tkinter imports other than non-gui TclError.
|
||||
se.BooleanVar = Var
|
||||
se.StringVar = Var
|
||||
se.messagebox = Mbox
|
||||
|
||||
def tearDownModule():
|
||||
# Restore 'just in case', though other tests should also replace.
|
||||
se.BooleanVar = BooleanVar
|
||||
se.StringVar = StringVar
|
||||
se.messagebox = messagebox
|
||||
|
||||
|
||||
class Mock:
|
||||
def __init__(self, *args, **kwargs): pass
|
||||
|
||||
class GetTest(unittest.TestCase):
|
||||
# SearchEngine.get returns singleton created & saved on first call.
|
||||
def test_get(self):
|
||||
saved_Engine = se.SearchEngine
|
||||
se.SearchEngine = Mock # monkey-patch class
|
||||
try:
|
||||
root = Mock()
|
||||
engine = se.get(root)
|
||||
self.assertIsInstance(engine, se.SearchEngine)
|
||||
self.assertIs(root._searchengine, engine)
|
||||
self.assertIs(se.get(root), engine)
|
||||
finally:
|
||||
se.SearchEngine = saved_Engine # restore class to module
|
||||
|
||||
class GetLineColTest(unittest.TestCase):
|
||||
# Test simple text-independent helper function
|
||||
def test_get_line_col(self):
|
||||
self.assertEqual(se.get_line_col('1.0'), (1, 0))
|
||||
self.assertEqual(se.get_line_col('1.11'), (1, 11))
|
||||
|
||||
self.assertRaises(ValueError, se.get_line_col, ('1.0 lineend'))
|
||||
self.assertRaises(ValueError, se.get_line_col, ('end'))
|
||||
|
||||
class GetSelectionTest(unittest.TestCase):
|
||||
# Test text-dependent helper function.
|
||||
## # Need gui for text.index('sel.first/sel.last/insert').
|
||||
## @classmethod
|
||||
## def setUpClass(cls):
|
||||
## requires('gui')
|
||||
## cls.root = Tk()
|
||||
##
|
||||
## @classmethod
|
||||
## def tearDownClass(cls):
|
||||
## cls.root.destroy()
|
||||
## del cls.root
|
||||
|
||||
def test_get_selection(self):
|
||||
# text = Text(master=self.root)
|
||||
text = mockText()
|
||||
text.insert('1.0', 'Hello World!')
|
||||
|
||||
# fix text.index result when called in get_selection
|
||||
def sel(s):
|
||||
# select entire text, cursor irrelevant
|
||||
if s == 'sel.first': return '1.0'
|
||||
if s == 'sel.last': return '1.12'
|
||||
raise TclError
|
||||
text.index = sel # replaces .tag_add('sel', '1.0, '1.12')
|
||||
self.assertEqual(se.get_selection(text), ('1.0', '1.12'))
|
||||
|
||||
def mark(s):
|
||||
# no selection, cursor after 'Hello'
|
||||
if s == 'insert': return '1.5'
|
||||
raise TclError
|
||||
text.index = mark # replaces .mark_set('insert', '1.5')
|
||||
self.assertEqual(se.get_selection(text), ('1.5', '1.5'))
|
||||
|
||||
|
||||
class ReverseSearchTest(unittest.TestCase):
|
||||
# Test helper function that searches backwards within a line.
|
||||
def test_search_reverse(self):
|
||||
Equal = self.assertEqual
|
||||
line = "Here is an 'is' test text."
|
||||
prog = re.compile('is')
|
||||
Equal(se.search_reverse(prog, line, len(line)).span(), (12, 14))
|
||||
Equal(se.search_reverse(prog, line, 14).span(), (12, 14))
|
||||
Equal(se.search_reverse(prog, line, 13).span(), (5, 7))
|
||||
Equal(se.search_reverse(prog, line, 7).span(), (5, 7))
|
||||
Equal(se.search_reverse(prog, line, 6), None)
|
||||
|
||||
|
||||
class SearchEngineTest(unittest.TestCase):
|
||||
# Test class methods that do not use Text widget.
|
||||
|
||||
def setUp(self):
|
||||
self.engine = se.SearchEngine(root=None)
|
||||
# Engine.root is only used to create error message boxes.
|
||||
# The mock replacement ignores the root argument.
|
||||
|
||||
def test_is_get(self):
|
||||
engine = self.engine
|
||||
Equal = self.assertEqual
|
||||
|
||||
Equal(engine.getpat(), '')
|
||||
engine.setpat('hello')
|
||||
Equal(engine.getpat(), 'hello')
|
||||
|
||||
Equal(engine.isre(), False)
|
||||
engine.revar.set(1)
|
||||
Equal(engine.isre(), True)
|
||||
|
||||
Equal(engine.iscase(), False)
|
||||
engine.casevar.set(1)
|
||||
Equal(engine.iscase(), True)
|
||||
|
||||
Equal(engine.isword(), False)
|
||||
engine.wordvar.set(1)
|
||||
Equal(engine.isword(), True)
|
||||
|
||||
Equal(engine.iswrap(), True)
|
||||
engine.wrapvar.set(0)
|
||||
Equal(engine.iswrap(), False)
|
||||
|
||||
Equal(engine.isback(), False)
|
||||
engine.backvar.set(1)
|
||||
Equal(engine.isback(), True)
|
||||
|
||||
def test_setcookedpat(self):
|
||||
engine = self.engine
|
||||
engine.setcookedpat(r'\s')
|
||||
self.assertEqual(engine.getpat(), r'\s')
|
||||
engine.revar.set(1)
|
||||
engine.setcookedpat(r'\s')
|
||||
self.assertEqual(engine.getpat(), r'\\s')
|
||||
|
||||
def test_getcookedpat(self):
|
||||
engine = self.engine
|
||||
Equal = self.assertEqual
|
||||
|
||||
Equal(engine.getcookedpat(), '')
|
||||
engine.setpat('hello')
|
||||
Equal(engine.getcookedpat(), 'hello')
|
||||
engine.wordvar.set(True)
|
||||
Equal(engine.getcookedpat(), r'\bhello\b')
|
||||
engine.wordvar.set(False)
|
||||
|
||||
engine.setpat(r'\s')
|
||||
Equal(engine.getcookedpat(), r'\\s')
|
||||
engine.revar.set(True)
|
||||
Equal(engine.getcookedpat(), r'\s')
|
||||
|
||||
def test_getprog(self):
|
||||
engine = self.engine
|
||||
Equal = self.assertEqual
|
||||
|
||||
engine.setpat('Hello')
|
||||
temppat = engine.getprog()
|
||||
Equal(temppat.pattern, re.compile('Hello', re.IGNORECASE).pattern)
|
||||
engine.casevar.set(1)
|
||||
temppat = engine.getprog()
|
||||
Equal(temppat.pattern, re.compile('Hello').pattern, 0)
|
||||
|
||||
engine.setpat('')
|
||||
Equal(engine.getprog(), None)
|
||||
Equal(Mbox.showerror.message,
|
||||
'Error: Empty regular expression')
|
||||
engine.setpat('+')
|
||||
engine.revar.set(1)
|
||||
Equal(engine.getprog(), None)
|
||||
Equal(Mbox.showerror.message,
|
||||
'Error: nothing to repeat\nPattern: +\nOffset: 0')
|
||||
|
||||
def test_report_error(self):
|
||||
showerror = Mbox.showerror
|
||||
Equal = self.assertEqual
|
||||
pat = '[a-z'
|
||||
msg = 'unexpected end of regular expression'
|
||||
|
||||
Equal(self.engine.report_error(pat, msg), None)
|
||||
Equal(showerror.title, 'Regular expression error')
|
||||
expected_message = ("Error: " + msg + "\nPattern: [a-z")
|
||||
Equal(showerror.message, expected_message)
|
||||
|
||||
Equal(self.engine.report_error(pat, msg, 5), None)
|
||||
Equal(showerror.title, 'Regular expression error')
|
||||
expected_message += "\nOffset: 5"
|
||||
Equal(showerror.message, expected_message)
|
||||
|
||||
|
||||
class SearchTest(unittest.TestCase):
|
||||
# Test that search_text makes right call to right method.
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
## requires('gui')
|
||||
## cls.root = Tk()
|
||||
## cls.text = Text(master=cls.root)
|
||||
cls.text = mockText()
|
||||
test_text = (
|
||||
'First line\n'
|
||||
'Line with target\n'
|
||||
'Last line\n')
|
||||
cls.text.insert('1.0', test_text)
|
||||
cls.pat = re.compile('target')
|
||||
|
||||
cls.engine = se.SearchEngine(None)
|
||||
cls.engine.search_forward = lambda *args: ('f', args)
|
||||
cls.engine.search_backward = lambda *args: ('b', args)
|
||||
|
||||
## @classmethod
|
||||
## def tearDownClass(cls):
|
||||
## cls.root.destroy()
|
||||
## del cls.root
|
||||
|
||||
def test_search(self):
|
||||
Equal = self.assertEqual
|
||||
engine = self.engine
|
||||
search = engine.search_text
|
||||
text = self.text
|
||||
pat = self.pat
|
||||
|
||||
engine.patvar.set(None)
|
||||
#engine.revar.set(pat)
|
||||
Equal(search(text), None)
|
||||
|
||||
def mark(s):
|
||||
# no selection, cursor after 'Hello'
|
||||
if s == 'insert': return '1.5'
|
||||
raise TclError
|
||||
text.index = mark
|
||||
Equal(search(text, pat), ('f', (text, pat, 1, 5, True, False)))
|
||||
engine.wrapvar.set(False)
|
||||
Equal(search(text, pat), ('f', (text, pat, 1, 5, False, False)))
|
||||
engine.wrapvar.set(True)
|
||||
engine.backvar.set(True)
|
||||
Equal(search(text, pat), ('b', (text, pat, 1, 5, True, False)))
|
||||
engine.backvar.set(False)
|
||||
|
||||
def sel(s):
|
||||
if s == 'sel.first': return '2.10'
|
||||
if s == 'sel.last': return '2.16'
|
||||
raise TclError
|
||||
text.index = sel
|
||||
Equal(search(text, pat), ('f', (text, pat, 2, 16, True, False)))
|
||||
Equal(search(text, pat, True), ('f', (text, pat, 2, 10, True, True)))
|
||||
engine.backvar.set(True)
|
||||
Equal(search(text, pat), ('b', (text, pat, 2, 10, True, False)))
|
||||
Equal(search(text, pat, True), ('b', (text, pat, 2, 16, True, True)))
|
||||
|
||||
|
||||
class ForwardBackwardTest(unittest.TestCase):
|
||||
# Test that search_forward method finds the target.
|
||||
## @classmethod
|
||||
## def tearDownClass(cls):
|
||||
## cls.root.destroy()
|
||||
## del cls.root
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.engine = se.SearchEngine(None)
|
||||
## requires('gui')
|
||||
## cls.root = Tk()
|
||||
## cls.text = Text(master=cls.root)
|
||||
cls.text = mockText()
|
||||
# search_backward calls index('end-1c')
|
||||
cls.text.index = lambda index: '4.0'
|
||||
test_text = (
|
||||
'First line\n'
|
||||
'Line with target\n'
|
||||
'Last line\n')
|
||||
cls.text.insert('1.0', test_text)
|
||||
cls.pat = re.compile('target')
|
||||
cls.res = (2, (10, 16)) # line, slice indexes of 'target'
|
||||
cls.failpat = re.compile('xyz') # not in text
|
||||
cls.emptypat = re.compile(r'\w*') # empty match possible
|
||||
|
||||
def make_search(self, func):
|
||||
def search(pat, line, col, wrap, ok=0):
|
||||
res = func(self.text, pat, line, col, wrap, ok)
|
||||
# res is (line, matchobject) or None
|
||||
return (res[0], res[1].span()) if res else res
|
||||
return search
|
||||
|
||||
def test_search_forward(self):
|
||||
# search for non-empty match
|
||||
Equal = self.assertEqual
|
||||
forward = self.make_search(self.engine.search_forward)
|
||||
pat = self.pat
|
||||
Equal(forward(pat, 1, 0, True), self.res)
|
||||
Equal(forward(pat, 3, 0, True), self.res) # wrap
|
||||
Equal(forward(pat, 3, 0, False), None) # no wrap
|
||||
Equal(forward(pat, 2, 10, False), self.res)
|
||||
|
||||
Equal(forward(self.failpat, 1, 0, True), None)
|
||||
Equal(forward(self.emptypat, 2, 9, True, ok=True), (2, (9, 9)))
|
||||
#Equal(forward(self.emptypat, 2, 9, True), self.res)
|
||||
# While the initial empty match is correctly ignored, skipping
|
||||
# the rest of the line and returning (3, (0,4)) seems buggy - tjr.
|
||||
Equal(forward(self.emptypat, 2, 10, True), self.res)
|
||||
|
||||
def test_search_backward(self):
|
||||
# search for non-empty match
|
||||
Equal = self.assertEqual
|
||||
backward = self.make_search(self.engine.search_backward)
|
||||
pat = self.pat
|
||||
Equal(backward(pat, 3, 5, True), self.res)
|
||||
Equal(backward(pat, 2, 0, True), self.res) # wrap
|
||||
Equal(backward(pat, 2, 0, False), None) # no wrap
|
||||
Equal(backward(pat, 2, 16, False), self.res)
|
||||
|
||||
Equal(backward(self.failpat, 3, 9, True), None)
|
||||
Equal(backward(self.emptypat, 2, 10, True, ok=True), (2, (9,9)))
|
||||
# Accepted because 9 < 10, not because ok=True.
|
||||
# It is not clear that ok=True is useful going back - tjr
|
||||
Equal(backward(self.emptypat, 2, 9, True), (2, (5, 9)))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
775
Dependencies/Python/Lib/idlelib/idle_test/test_sidebar.py
vendored
Normal file
775
Dependencies/Python/Lib/idlelib/idle_test/test_sidebar.py
vendored
Normal file
@@ -0,0 +1,775 @@
|
||||
"""Test sidebar, coverage 85%"""
|
||||
from textwrap import dedent
|
||||
import sys
|
||||
|
||||
from itertools import chain
|
||||
import unittest
|
||||
import unittest.mock
|
||||
from test.support import adjust_int_max_str_digits, requires, swap_attr
|
||||
from test.support.testcase import ExtraAssertions
|
||||
import tkinter as tk
|
||||
from idlelib.idle_test.tkinter_testing_utils import run_in_tk_mainloop
|
||||
|
||||
from idlelib.delegator import Delegator
|
||||
from idlelib.editor import fixwordbreaks
|
||||
from idlelib.percolator import Percolator
|
||||
import idlelib.pyshell
|
||||
from idlelib.pyshell import fix_x11_paste, PyShell, PyShellFileList
|
||||
from idlelib.run import fix_scaling
|
||||
import idlelib.sidebar
|
||||
from idlelib.sidebar import get_end_linenumber, get_lineno
|
||||
|
||||
|
||||
class Dummy_editwin:
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
self.text_frame = self.text.master
|
||||
self.per = Percolator(text)
|
||||
self.undo = Delegator()
|
||||
self.per.insertfilter(self.undo)
|
||||
|
||||
def setvar(self, name, value):
|
||||
pass
|
||||
|
||||
def getlineno(self, index):
|
||||
return int(float(self.text.index(index)))
|
||||
|
||||
|
||||
class LineNumbersTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = tk.Tk()
|
||||
cls.root.withdraw()
|
||||
|
||||
cls.text_frame = tk.Frame(cls.root)
|
||||
cls.text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
cls.text_frame.rowconfigure(1, weight=1)
|
||||
cls.text_frame.columnconfigure(1, weight=1)
|
||||
|
||||
cls.text = tk.Text(cls.text_frame, width=80, height=24, wrap=tk.NONE)
|
||||
cls.text.grid(row=1, column=1, sticky=tk.NSEW)
|
||||
|
||||
cls.editwin = Dummy_editwin(cls.text)
|
||||
cls.editwin.vbar = tk.Scrollbar(cls.text_frame)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.editwin.per.close()
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.text, cls.text_frame, cls.editwin, cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.linenumber = idlelib.sidebar.LineNumbers(self.editwin)
|
||||
|
||||
self.highlight_cfg = {"background": '#abcdef',
|
||||
"foreground": '#123456'}
|
||||
orig_idleConf_GetHighlight = idlelib.sidebar.idleConf.GetHighlight
|
||||
def mock_idleconf_GetHighlight(theme, element):
|
||||
if element == 'linenumber':
|
||||
return self.highlight_cfg
|
||||
return orig_idleConf_GetHighlight(theme, element)
|
||||
GetHighlight_patcher = unittest.mock.patch.object(
|
||||
idlelib.sidebar.idleConf, 'GetHighlight', mock_idleconf_GetHighlight)
|
||||
GetHighlight_patcher.start()
|
||||
self.addCleanup(GetHighlight_patcher.stop)
|
||||
|
||||
self.font_override = 'TkFixedFont'
|
||||
def mock_idleconf_GetFont(root, configType, section):
|
||||
return self.font_override
|
||||
GetFont_patcher = unittest.mock.patch.object(
|
||||
idlelib.sidebar.idleConf, 'GetFont', mock_idleconf_GetFont)
|
||||
GetFont_patcher.start()
|
||||
self.addCleanup(GetFont_patcher.stop)
|
||||
|
||||
def tearDown(self):
|
||||
self.text.delete('1.0', 'end')
|
||||
|
||||
def get_selection(self):
|
||||
return tuple(map(str, self.text.tag_ranges('sel')))
|
||||
|
||||
def get_line_screen_position(self, line):
|
||||
bbox = self.linenumber.sidebar_text.bbox(f'{line}.end -1c')
|
||||
x = bbox[0] + 2
|
||||
y = bbox[1] + 2
|
||||
return x, y
|
||||
|
||||
def assert_state_disabled(self):
|
||||
state = self.linenumber.sidebar_text.config()['state']
|
||||
self.assertEqual(state[-1], tk.DISABLED)
|
||||
|
||||
def get_sidebar_text_contents(self):
|
||||
return self.linenumber.sidebar_text.get('1.0', tk.END)
|
||||
|
||||
def assert_sidebar_n_lines(self, n_lines):
|
||||
expected = '\n'.join(chain(map(str, range(1, n_lines + 1)), ['']))
|
||||
self.assertEqual(self.get_sidebar_text_contents(), expected)
|
||||
|
||||
def assert_text_equals(self, expected):
|
||||
return self.assertEqual(self.text.get('1.0', 'end'), expected)
|
||||
|
||||
def test_init_empty(self):
|
||||
self.assert_sidebar_n_lines(1)
|
||||
|
||||
def test_init_not_empty(self):
|
||||
self.text.insert('insert', 'foo bar\n'*3)
|
||||
self.assert_text_equals('foo bar\n'*3 + '\n')
|
||||
self.assert_sidebar_n_lines(4)
|
||||
|
||||
def test_toggle_linenumbering(self):
|
||||
self.assertEqual(self.linenumber.is_shown, False)
|
||||
self.linenumber.show_sidebar()
|
||||
self.assertEqual(self.linenumber.is_shown, True)
|
||||
self.linenumber.hide_sidebar()
|
||||
self.assertEqual(self.linenumber.is_shown, False)
|
||||
self.linenumber.hide_sidebar()
|
||||
self.assertEqual(self.linenumber.is_shown, False)
|
||||
self.linenumber.show_sidebar()
|
||||
self.assertEqual(self.linenumber.is_shown, True)
|
||||
self.linenumber.show_sidebar()
|
||||
self.assertEqual(self.linenumber.is_shown, True)
|
||||
|
||||
def test_insert(self):
|
||||
self.text.insert('insert', 'foobar')
|
||||
self.assert_text_equals('foobar\n')
|
||||
self.assert_sidebar_n_lines(1)
|
||||
self.assert_state_disabled()
|
||||
|
||||
self.text.insert('insert', '\nfoo')
|
||||
self.assert_text_equals('foobar\nfoo\n')
|
||||
self.assert_sidebar_n_lines(2)
|
||||
self.assert_state_disabled()
|
||||
|
||||
self.text.insert('insert', 'hello\n'*2)
|
||||
self.assert_text_equals('foobar\nfoohello\nhello\n\n')
|
||||
self.assert_sidebar_n_lines(4)
|
||||
self.assert_state_disabled()
|
||||
|
||||
self.text.insert('insert', '\nworld')
|
||||
self.assert_text_equals('foobar\nfoohello\nhello\n\nworld\n')
|
||||
self.assert_sidebar_n_lines(5)
|
||||
self.assert_state_disabled()
|
||||
|
||||
def test_delete(self):
|
||||
self.text.insert('insert', 'foobar')
|
||||
self.assert_text_equals('foobar\n')
|
||||
self.text.delete('1.1', '1.3')
|
||||
self.assert_text_equals('fbar\n')
|
||||
self.assert_sidebar_n_lines(1)
|
||||
self.assert_state_disabled()
|
||||
|
||||
self.text.insert('insert', 'foo\n'*2)
|
||||
self.assert_text_equals('fbarfoo\nfoo\n\n')
|
||||
self.assert_sidebar_n_lines(3)
|
||||
self.assert_state_disabled()
|
||||
|
||||
# Deleting up to "2.end" doesn't delete the final newline.
|
||||
self.text.delete('2.0', '2.end')
|
||||
self.assert_text_equals('fbarfoo\n\n\n')
|
||||
self.assert_sidebar_n_lines(3)
|
||||
self.assert_state_disabled()
|
||||
|
||||
self.text.delete('1.3', 'end')
|
||||
self.assert_text_equals('fba\n')
|
||||
self.assert_sidebar_n_lines(1)
|
||||
self.assert_state_disabled()
|
||||
|
||||
# Text widgets always keep a single '\n' character at the end.
|
||||
self.text.delete('1.0', 'end')
|
||||
self.assert_text_equals('\n')
|
||||
self.assert_sidebar_n_lines(1)
|
||||
self.assert_state_disabled()
|
||||
|
||||
def test_sidebar_text_width(self):
|
||||
"""
|
||||
Test that linenumber text widget is always at the minimum
|
||||
width
|
||||
"""
|
||||
def get_width():
|
||||
return self.linenumber.sidebar_text.config()['width'][-1]
|
||||
|
||||
self.assert_sidebar_n_lines(1)
|
||||
self.assertEqual(get_width(), 1)
|
||||
|
||||
self.text.insert('insert', 'foo')
|
||||
self.assert_sidebar_n_lines(1)
|
||||
self.assertEqual(get_width(), 1)
|
||||
|
||||
self.text.insert('insert', 'foo\n'*8)
|
||||
self.assert_sidebar_n_lines(9)
|
||||
self.assertEqual(get_width(), 1)
|
||||
|
||||
self.text.insert('insert', 'foo\n')
|
||||
self.assert_sidebar_n_lines(10)
|
||||
self.assertEqual(get_width(), 2)
|
||||
|
||||
self.text.insert('insert', 'foo\n')
|
||||
self.assert_sidebar_n_lines(11)
|
||||
self.assertEqual(get_width(), 2)
|
||||
|
||||
self.text.delete('insert -1l linestart', 'insert linestart')
|
||||
self.assert_sidebar_n_lines(10)
|
||||
self.assertEqual(get_width(), 2)
|
||||
|
||||
self.text.delete('insert -1l linestart', 'insert linestart')
|
||||
self.assert_sidebar_n_lines(9)
|
||||
self.assertEqual(get_width(), 1)
|
||||
|
||||
self.text.insert('insert', 'foo\n'*90)
|
||||
self.assert_sidebar_n_lines(99)
|
||||
self.assertEqual(get_width(), 2)
|
||||
|
||||
self.text.insert('insert', 'foo\n')
|
||||
self.assert_sidebar_n_lines(100)
|
||||
self.assertEqual(get_width(), 3)
|
||||
|
||||
self.text.insert('insert', 'foo\n')
|
||||
self.assert_sidebar_n_lines(101)
|
||||
self.assertEqual(get_width(), 3)
|
||||
|
||||
self.text.delete('insert -1l linestart', 'insert linestart')
|
||||
self.assert_sidebar_n_lines(100)
|
||||
self.assertEqual(get_width(), 3)
|
||||
|
||||
self.text.delete('insert -1l linestart', 'insert linestart')
|
||||
self.assert_sidebar_n_lines(99)
|
||||
self.assertEqual(get_width(), 2)
|
||||
|
||||
self.text.delete('50.0 -1c', 'end -1c')
|
||||
self.assert_sidebar_n_lines(49)
|
||||
self.assertEqual(get_width(), 2)
|
||||
|
||||
self.text.delete('5.0 -1c', 'end -1c')
|
||||
self.assert_sidebar_n_lines(4)
|
||||
self.assertEqual(get_width(), 1)
|
||||
|
||||
# Text widgets always keep a single '\n' character at the end.
|
||||
self.text.delete('1.0', 'end -1c')
|
||||
self.assert_sidebar_n_lines(1)
|
||||
self.assertEqual(get_width(), 1)
|
||||
|
||||
# The following tests are temporarily disabled due to relying on
|
||||
# simulated user input and inspecting which text is selected, which
|
||||
# are fragile and can fail when several GUI tests are run in parallel
|
||||
# or when the windows created by the test lose focus.
|
||||
#
|
||||
# TODO: Re-work these tests or remove them from the test suite.
|
||||
|
||||
@unittest.skip('test disabled')
|
||||
def test_click_selection(self):
|
||||
self.linenumber.show_sidebar()
|
||||
self.text.insert('1.0', 'one\ntwo\nthree\nfour\n')
|
||||
self.root.update()
|
||||
|
||||
# Click on the second line.
|
||||
x, y = self.get_line_screen_position(2)
|
||||
self.linenumber.sidebar_text.event_generate('<Button-1>', x=x, y=y)
|
||||
self.linenumber.sidebar_text.update()
|
||||
self.root.update()
|
||||
|
||||
self.assertEqual(self.get_selection(), ('2.0', '3.0'))
|
||||
|
||||
def simulate_drag(self, start_line, end_line):
|
||||
start_x, start_y = self.get_line_screen_position(start_line)
|
||||
end_x, end_y = self.get_line_screen_position(end_line)
|
||||
|
||||
self.linenumber.sidebar_text.event_generate('<Button-1>',
|
||||
x=start_x, y=start_y)
|
||||
self.root.update()
|
||||
|
||||
def lerp(a, b, steps):
|
||||
"""linearly interpolate from a to b (inclusive) in equal steps"""
|
||||
last_step = steps - 1
|
||||
for i in range(steps):
|
||||
yield ((last_step - i) / last_step) * a + (i / last_step) * b
|
||||
|
||||
for x, y in zip(
|
||||
map(int, lerp(start_x, end_x, steps=11)),
|
||||
map(int, lerp(start_y, end_y, steps=11)),
|
||||
):
|
||||
self.linenumber.sidebar_text.event_generate('<B1-Motion>', x=x, y=y)
|
||||
self.root.update()
|
||||
|
||||
self.linenumber.sidebar_text.event_generate('<ButtonRelease-1>',
|
||||
x=end_x, y=end_y)
|
||||
self.root.update()
|
||||
|
||||
@unittest.skip('test disabled')
|
||||
def test_drag_selection_down(self):
|
||||
self.linenumber.show_sidebar()
|
||||
self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n')
|
||||
self.root.update()
|
||||
|
||||
# Drag from the second line to the fourth line.
|
||||
self.simulate_drag(2, 4)
|
||||
self.assertEqual(self.get_selection(), ('2.0', '5.0'))
|
||||
|
||||
@unittest.skip('test disabled')
|
||||
def test_drag_selection_up(self):
|
||||
self.linenumber.show_sidebar()
|
||||
self.text.insert('1.0', 'one\ntwo\nthree\nfour\nfive\n')
|
||||
self.root.update()
|
||||
|
||||
# Drag from the fourth line to the second line.
|
||||
self.simulate_drag(4, 2)
|
||||
self.assertEqual(self.get_selection(), ('2.0', '5.0'))
|
||||
|
||||
def test_scroll(self):
|
||||
self.linenumber.show_sidebar()
|
||||
self.text.insert('1.0', 'line\n' * 100)
|
||||
self.root.update()
|
||||
|
||||
# Scroll down 10 lines.
|
||||
self.text.yview_scroll(10, 'unit')
|
||||
self.root.update()
|
||||
self.assertEqual(self.text.index('@0,0'), '11.0')
|
||||
self.assertEqual(self.linenumber.sidebar_text.index('@0,0'), '11.0')
|
||||
|
||||
# Generate a mouse-wheel event and make sure it scrolled up or down.
|
||||
# The meaning of the "delta" is OS-dependent, so this just checks for
|
||||
# any change.
|
||||
self.linenumber.sidebar_text.event_generate('<MouseWheel>',
|
||||
x=0, y=0,
|
||||
delta=10)
|
||||
self.root.update()
|
||||
self.assertNotEqual(self.text.index('@0,0'), '11.0')
|
||||
self.assertNotEqual(self.linenumber.sidebar_text.index('@0,0'), '11.0')
|
||||
|
||||
def test_font(self):
|
||||
ln = self.linenumber
|
||||
|
||||
orig_font = ln.sidebar_text['font']
|
||||
test_font = 'TkTextFont'
|
||||
self.assertNotEqual(orig_font, test_font)
|
||||
|
||||
# Ensure line numbers aren't shown.
|
||||
ln.hide_sidebar()
|
||||
|
||||
self.font_override = test_font
|
||||
# Nothing breaks when line numbers aren't shown.
|
||||
ln.update_font()
|
||||
|
||||
# Activate line numbers, previous font change is immediately effective.
|
||||
ln.show_sidebar()
|
||||
self.assertEqual(ln.sidebar_text['font'], test_font)
|
||||
|
||||
# Call the font update with line numbers shown, change is picked up.
|
||||
self.font_override = orig_font
|
||||
ln.update_font()
|
||||
self.assertEqual(ln.sidebar_text['font'], orig_font)
|
||||
|
||||
def test_highlight_colors(self):
|
||||
ln = self.linenumber
|
||||
|
||||
orig_colors = dict(self.highlight_cfg)
|
||||
test_colors = {'background': '#222222', 'foreground': '#ffff00'}
|
||||
|
||||
def assert_colors_are_equal(colors):
|
||||
self.assertEqual(ln.sidebar_text['background'], colors['background'])
|
||||
self.assertEqual(ln.sidebar_text['foreground'], colors['foreground'])
|
||||
|
||||
# Ensure line numbers aren't shown.
|
||||
ln.hide_sidebar()
|
||||
|
||||
self.highlight_cfg = test_colors
|
||||
# Nothing breaks with inactive line numbers.
|
||||
ln.update_colors()
|
||||
|
||||
# Show line numbers, previous colors change is immediately effective.
|
||||
ln.show_sidebar()
|
||||
assert_colors_are_equal(test_colors)
|
||||
|
||||
# Call colors update with no change to the configured colors.
|
||||
ln.update_colors()
|
||||
assert_colors_are_equal(test_colors)
|
||||
|
||||
# Call the colors update with line numbers shown, change is picked up.
|
||||
self.highlight_cfg = orig_colors
|
||||
ln.update_colors()
|
||||
assert_colors_are_equal(orig_colors)
|
||||
|
||||
|
||||
class ShellSidebarTest(unittest.TestCase, ExtraAssertions):
|
||||
root: tk.Tk = None
|
||||
shell: PyShell = None
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
|
||||
cls.root = root = tk.Tk()
|
||||
root.withdraw()
|
||||
|
||||
fix_scaling(root)
|
||||
fixwordbreaks(root)
|
||||
fix_x11_paste(root)
|
||||
|
||||
cls.flist = flist = PyShellFileList(root)
|
||||
# See #43981 about macosx.setupApp(root, flist) causing failure.
|
||||
root.update_idletasks()
|
||||
|
||||
cls.init_shell()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
if cls.shell is not None:
|
||||
cls.shell.executing = False
|
||||
cls.shell.close()
|
||||
cls.shell = None
|
||||
cls.flist = None
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
cls.root = None
|
||||
|
||||
@classmethod
|
||||
def init_shell(cls):
|
||||
cls.shell = cls.flist.open_shell()
|
||||
cls.shell.pollinterval = 10
|
||||
cls.root.update()
|
||||
cls.n_preface_lines = get_lineno(cls.shell.text, 'end-1c') - 1
|
||||
|
||||
@classmethod
|
||||
def reset_shell(cls):
|
||||
cls.shell.per.bottom.delete(f'{cls.n_preface_lines+1}.0', 'end-1c')
|
||||
cls.shell.shell_sidebar.update_sidebar()
|
||||
cls.root.update()
|
||||
|
||||
def setUp(self):
|
||||
# In some test environments, e.g. Azure Pipelines (as of
|
||||
# Apr. 2021), sys.stdout is changed between tests. However,
|
||||
# PyShell relies on overriding sys.stdout when run without a
|
||||
# sub-process (as done here; see setUpClass).
|
||||
self._saved_stdout = None
|
||||
if sys.stdout != self.shell.stdout:
|
||||
self._saved_stdout = sys.stdout
|
||||
sys.stdout = self.shell.stdout
|
||||
|
||||
self.reset_shell()
|
||||
|
||||
def tearDown(self):
|
||||
if self._saved_stdout is not None:
|
||||
sys.stdout = self._saved_stdout
|
||||
|
||||
def get_sidebar_lines(self):
|
||||
canvas = self.shell.shell_sidebar.canvas
|
||||
texts = list(canvas.find(tk.ALL))
|
||||
texts_by_y_coords = {
|
||||
canvas.bbox(text)[1]: canvas.itemcget(text, 'text')
|
||||
for text in texts
|
||||
}
|
||||
line_y_coords = self.get_shell_line_y_coords()
|
||||
return [texts_by_y_coords.get(y, None) for y in line_y_coords]
|
||||
|
||||
def assert_sidebar_lines_end_with(self, expected_lines):
|
||||
self.shell.shell_sidebar.update_sidebar()
|
||||
self.assertEqual(
|
||||
self.get_sidebar_lines()[-len(expected_lines):],
|
||||
expected_lines,
|
||||
)
|
||||
|
||||
def get_shell_line_y_coords(self):
|
||||
text = self.shell.text
|
||||
y_coords = []
|
||||
index = text.index("@0,0")
|
||||
if index.split('.', 1)[1] != '0':
|
||||
index = text.index(f"{index} +1line linestart")
|
||||
while (lineinfo := text.dlineinfo(index)) is not None:
|
||||
y_coords.append(lineinfo[1])
|
||||
index = text.index(f"{index} +1line")
|
||||
return y_coords
|
||||
|
||||
def get_sidebar_line_y_coords(self):
|
||||
canvas = self.shell.shell_sidebar.canvas
|
||||
texts = list(canvas.find(tk.ALL))
|
||||
texts.sort(key=lambda text: canvas.bbox(text)[1])
|
||||
return [canvas.bbox(text)[1] for text in texts]
|
||||
|
||||
def assert_sidebar_lines_synced(self):
|
||||
self.assertLessEqual(
|
||||
set(self.get_sidebar_line_y_coords()),
|
||||
set(self.get_shell_line_y_coords()),
|
||||
)
|
||||
|
||||
def do_input(self, input):
|
||||
shell = self.shell
|
||||
text = shell.text
|
||||
for line_index, line in enumerate(input.split('\n')):
|
||||
if line_index > 0:
|
||||
text.event_generate('<<newline-and-indent>>')
|
||||
text.insert('insert', line, 'stdin')
|
||||
|
||||
def test_initial_state(self):
|
||||
sidebar_lines = self.get_sidebar_lines()
|
||||
self.assertEqual(
|
||||
sidebar_lines,
|
||||
[None] * (len(sidebar_lines) - 1) + ['>>>'],
|
||||
)
|
||||
self.assert_sidebar_lines_synced()
|
||||
|
||||
@run_in_tk_mainloop()
|
||||
def test_single_empty_input(self):
|
||||
self.do_input('\n')
|
||||
yield
|
||||
self.assert_sidebar_lines_end_with(['>>>', '>>>'])
|
||||
|
||||
@run_in_tk_mainloop()
|
||||
def test_single_line_statement(self):
|
||||
self.do_input('1\n')
|
||||
yield
|
||||
self.assert_sidebar_lines_end_with(['>>>', None, '>>>'])
|
||||
|
||||
@run_in_tk_mainloop()
|
||||
def test_multi_line_statement(self):
|
||||
# Block statements are not indented because IDLE auto-indents.
|
||||
self.do_input(dedent('''\
|
||||
if True:
|
||||
print(1)
|
||||
|
||||
'''))
|
||||
yield
|
||||
self.assert_sidebar_lines_end_with([
|
||||
'>>>',
|
||||
'...',
|
||||
'...',
|
||||
'...',
|
||||
None,
|
||||
'>>>',
|
||||
])
|
||||
|
||||
@run_in_tk_mainloop()
|
||||
def test_single_long_line_wraps(self):
|
||||
self.do_input('1' * 200 + '\n')
|
||||
yield
|
||||
self.assert_sidebar_lines_end_with(['>>>', None, '>>>'])
|
||||
self.assert_sidebar_lines_synced()
|
||||
|
||||
@run_in_tk_mainloop()
|
||||
def test_squeeze_multi_line_output(self):
|
||||
shell = self.shell
|
||||
text = shell.text
|
||||
|
||||
self.do_input('print("a\\nb\\nc")\n')
|
||||
yield
|
||||
self.assert_sidebar_lines_end_with(['>>>', None, None, None, '>>>'])
|
||||
|
||||
text.mark_set('insert', f'insert -1line linestart')
|
||||
text.event_generate('<<squeeze-current-text>>')
|
||||
yield
|
||||
self.assert_sidebar_lines_end_with(['>>>', None, '>>>'])
|
||||
self.assert_sidebar_lines_synced()
|
||||
|
||||
shell.squeezer.expandingbuttons[0].expand()
|
||||
yield
|
||||
self.assert_sidebar_lines_end_with(['>>>', None, None, None, '>>>'])
|
||||
self.assert_sidebar_lines_synced()
|
||||
|
||||
@run_in_tk_mainloop()
|
||||
def test_interrupt_recall_undo_redo(self):
|
||||
text = self.shell.text
|
||||
# Block statements are not indented because IDLE auto-indents.
|
||||
initial_sidebar_lines = self.get_sidebar_lines()
|
||||
|
||||
self.do_input(dedent('''\
|
||||
if True:
|
||||
print(1)
|
||||
'''))
|
||||
yield
|
||||
self.assert_sidebar_lines_end_with(['>>>', '...', '...'])
|
||||
with_block_sidebar_lines = self.get_sidebar_lines()
|
||||
self.assertNotEqual(with_block_sidebar_lines, initial_sidebar_lines)
|
||||
|
||||
# Control-C
|
||||
text.event_generate('<<interrupt-execution>>')
|
||||
yield
|
||||
self.assert_sidebar_lines_end_with(['>>>', '...', '...', None, '>>>'])
|
||||
|
||||
# Recall previous via history
|
||||
text.event_generate('<<history-previous>>')
|
||||
text.event_generate('<<interrupt-execution>>')
|
||||
yield
|
||||
self.assert_sidebar_lines_end_with(['>>>', '...', None, '>>>'])
|
||||
|
||||
# Recall previous via recall
|
||||
text.mark_set('insert', text.index('insert -2l'))
|
||||
text.event_generate('<<newline-and-indent>>')
|
||||
yield
|
||||
|
||||
text.event_generate('<<undo>>')
|
||||
yield
|
||||
self.assert_sidebar_lines_end_with(['>>>'])
|
||||
|
||||
text.event_generate('<<redo>>')
|
||||
yield
|
||||
self.assert_sidebar_lines_end_with(['>>>', '...'])
|
||||
|
||||
text.event_generate('<<newline-and-indent>>')
|
||||
text.event_generate('<<newline-and-indent>>')
|
||||
yield
|
||||
self.assert_sidebar_lines_end_with(
|
||||
['>>>', '...', '...', '...', None, '>>>']
|
||||
)
|
||||
|
||||
@run_in_tk_mainloop()
|
||||
def test_very_long_wrapped_line(self):
|
||||
with adjust_int_max_str_digits(11_111), \
|
||||
swap_attr(self.shell, 'squeezer', None):
|
||||
self.do_input('x = ' + '1'*10_000 + '\n')
|
||||
yield
|
||||
self.assertEqual(self.get_sidebar_lines(), ['>>>'])
|
||||
|
||||
def test_font(self):
|
||||
sidebar = self.shell.shell_sidebar
|
||||
|
||||
test_font = 'TkTextFont'
|
||||
|
||||
def mock_idleconf_GetFont(root, configType, section):
|
||||
return test_font
|
||||
GetFont_patcher = unittest.mock.patch.object(
|
||||
idlelib.sidebar.idleConf, 'GetFont', mock_idleconf_GetFont)
|
||||
GetFont_patcher.start()
|
||||
def cleanup():
|
||||
GetFont_patcher.stop()
|
||||
sidebar.update_font()
|
||||
self.addCleanup(cleanup)
|
||||
|
||||
def get_sidebar_font():
|
||||
canvas = sidebar.canvas
|
||||
texts = list(canvas.find(tk.ALL))
|
||||
fonts = {canvas.itemcget(text, 'font') for text in texts}
|
||||
self.assertEqual(len(fonts), 1)
|
||||
return next(iter(fonts))
|
||||
|
||||
self.assertNotEqual(get_sidebar_font(), test_font)
|
||||
sidebar.update_font()
|
||||
self.assertEqual(get_sidebar_font(), test_font)
|
||||
|
||||
def test_highlight_colors(self):
|
||||
sidebar = self.shell.shell_sidebar
|
||||
|
||||
test_colors = {"background": '#abcdef', "foreground": '#123456'}
|
||||
|
||||
orig_idleConf_GetHighlight = idlelib.sidebar.idleConf.GetHighlight
|
||||
def mock_idleconf_GetHighlight(theme, element):
|
||||
if element in ['linenumber', 'console']:
|
||||
return test_colors
|
||||
return orig_idleConf_GetHighlight(theme, element)
|
||||
GetHighlight_patcher = unittest.mock.patch.object(
|
||||
idlelib.sidebar.idleConf, 'GetHighlight',
|
||||
mock_idleconf_GetHighlight)
|
||||
GetHighlight_patcher.start()
|
||||
def cleanup():
|
||||
GetHighlight_patcher.stop()
|
||||
sidebar.update_colors()
|
||||
self.addCleanup(cleanup)
|
||||
|
||||
def get_sidebar_colors():
|
||||
canvas = sidebar.canvas
|
||||
texts = list(canvas.find(tk.ALL))
|
||||
fgs = {canvas.itemcget(text, 'fill') for text in texts}
|
||||
self.assertEqual(len(fgs), 1)
|
||||
fg = next(iter(fgs))
|
||||
bg = canvas.cget('background')
|
||||
return {"background": bg, "foreground": fg}
|
||||
|
||||
self.assertNotEqual(get_sidebar_colors(), test_colors)
|
||||
sidebar.update_colors()
|
||||
self.assertEqual(get_sidebar_colors(), test_colors)
|
||||
|
||||
@run_in_tk_mainloop()
|
||||
def test_mousewheel(self):
|
||||
sidebar = self.shell.shell_sidebar
|
||||
text = self.shell.text
|
||||
|
||||
# Enter a 100-line string to scroll the shell screen down.
|
||||
self.do_input('x = """' + '\n'*100 + '"""\n')
|
||||
yield
|
||||
self.assertGreater(get_lineno(text, '@0,0'), 1)
|
||||
|
||||
last_lineno = get_end_linenumber(text)
|
||||
self.assertIsNotNone(text.dlineinfo(text.index(f'{last_lineno}.0')))
|
||||
|
||||
# Delta for <MouseWheel>, whose meaning is platform-dependent.
|
||||
delta = 1 if sidebar.canvas._windowingsystem == 'aqua' else 120
|
||||
|
||||
# Scroll up.
|
||||
if sidebar.canvas._windowingsystem == 'x11':
|
||||
sidebar.canvas.event_generate('<Button-4>', x=0, y=0)
|
||||
else:
|
||||
sidebar.canvas.event_generate('<MouseWheel>', x=0, y=0, delta=delta)
|
||||
yield
|
||||
self.assertIsNone(text.dlineinfo(text.index(f'{last_lineno}.0')))
|
||||
|
||||
# Scroll back down.
|
||||
if sidebar.canvas._windowingsystem == 'x11':
|
||||
sidebar.canvas.event_generate('<Button-5>', x=0, y=0)
|
||||
else:
|
||||
sidebar.canvas.event_generate('<MouseWheel>', x=0, y=0, delta=-delta)
|
||||
yield
|
||||
self.assertIsNotNone(text.dlineinfo(text.index(f'{last_lineno}.0')))
|
||||
|
||||
@run_in_tk_mainloop()
|
||||
def test_copy(self):
|
||||
sidebar = self.shell.shell_sidebar
|
||||
text = self.shell.text
|
||||
|
||||
first_line = get_end_linenumber(text)
|
||||
|
||||
self.do_input(dedent('''\
|
||||
if True:
|
||||
print(1)
|
||||
|
||||
'''))
|
||||
yield
|
||||
|
||||
text.tag_add('sel', f'{first_line}.0', 'end-1c')
|
||||
selected_text = text.get('sel.first', 'sel.last')
|
||||
self.assertStartsWith(selected_text, 'if True:\n')
|
||||
self.assertIn('\n1\n', selected_text)
|
||||
|
||||
text.event_generate('<<copy>>')
|
||||
self.addCleanup(text.clipboard_clear)
|
||||
|
||||
copied_text = text.clipboard_get()
|
||||
self.assertEqual(copied_text, selected_text)
|
||||
|
||||
@run_in_tk_mainloop()
|
||||
def test_copy_with_prompts(self):
|
||||
sidebar = self.shell.shell_sidebar
|
||||
text = self.shell.text
|
||||
|
||||
first_line = get_end_linenumber(text)
|
||||
self.do_input(dedent('''\
|
||||
if True:
|
||||
print(1)
|
||||
|
||||
'''))
|
||||
yield
|
||||
|
||||
text.tag_add('sel', f'{first_line}.3', 'end-1c')
|
||||
selected_text = text.get('sel.first', 'sel.last')
|
||||
self.assertStartsWith(selected_text, 'True:\n')
|
||||
|
||||
selected_lines_text = text.get('sel.first linestart', 'sel.last')
|
||||
selected_lines = selected_lines_text.split('\n')
|
||||
selected_lines.pop() # Final '' is a split artifact, not a line.
|
||||
# Expect a block of input and a single output line.
|
||||
expected_prompts = \
|
||||
['>>>'] + ['...'] * (len(selected_lines) - 2) + [None]
|
||||
selected_text_with_prompts = '\n'.join(
|
||||
line if prompt is None else prompt + ' ' + line
|
||||
for prompt, line in zip(expected_prompts,
|
||||
selected_lines,
|
||||
strict=True)
|
||||
) + '\n'
|
||||
|
||||
text.event_generate('<<copy-with-prompts>>')
|
||||
self.addCleanup(text.clipboard_clear)
|
||||
|
||||
copied_text = text.clipboard_get()
|
||||
self.assertEqual(copied_text, selected_text_with_prompts)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
466
Dependencies/Python/Lib/idlelib/idle_test/test_squeezer.py
vendored
Normal file
466
Dependencies/Python/Lib/idlelib/idle_test/test_squeezer.py
vendored
Normal file
@@ -0,0 +1,466 @@
|
||||
"Test squeezer, coverage 95%"
|
||||
|
||||
from textwrap import dedent
|
||||
from tkinter import Text, Tk
|
||||
import unittest
|
||||
from unittest.mock import Mock, NonCallableMagicMock, patch, sentinel, ANY
|
||||
from test.support import requires
|
||||
|
||||
from idlelib.config import idleConf
|
||||
from idlelib.percolator import Percolator
|
||||
from idlelib.squeezer import count_lines_with_wrapping, ExpandingButton, \
|
||||
Squeezer
|
||||
from idlelib import macosx
|
||||
from idlelib.textview import view_text
|
||||
from idlelib.tooltip import Hovertip
|
||||
|
||||
SENTINEL_VALUE = sentinel.SENTINEL_VALUE
|
||||
|
||||
|
||||
def get_test_tk_root(test_instance):
|
||||
"""Helper for tests: Create a root Tk object."""
|
||||
requires('gui')
|
||||
root = Tk()
|
||||
root.withdraw()
|
||||
|
||||
def cleanup_root():
|
||||
root.update_idletasks()
|
||||
root.destroy()
|
||||
test_instance.addCleanup(cleanup_root)
|
||||
|
||||
return root
|
||||
|
||||
|
||||
class CountLinesTest(unittest.TestCase):
|
||||
"""Tests for the count_lines_with_wrapping function."""
|
||||
def check(self, expected, text, linewidth):
|
||||
return self.assertEqual(
|
||||
expected,
|
||||
count_lines_with_wrapping(text, linewidth),
|
||||
)
|
||||
|
||||
def test_count_empty(self):
|
||||
"""Test with an empty string."""
|
||||
self.assertEqual(count_lines_with_wrapping(""), 0)
|
||||
|
||||
def test_count_begins_with_empty_line(self):
|
||||
"""Test with a string which begins with a newline."""
|
||||
self.assertEqual(count_lines_with_wrapping("\ntext"), 2)
|
||||
|
||||
def test_count_ends_with_empty_line(self):
|
||||
"""Test with a string which ends with a newline."""
|
||||
self.assertEqual(count_lines_with_wrapping("text\n"), 1)
|
||||
|
||||
def test_count_several_lines(self):
|
||||
"""Test with several lines of text."""
|
||||
self.assertEqual(count_lines_with_wrapping("1\n2\n3\n"), 3)
|
||||
|
||||
def test_empty_lines(self):
|
||||
self.check(expected=1, text='\n', linewidth=80)
|
||||
self.check(expected=2, text='\n\n', linewidth=80)
|
||||
self.check(expected=10, text='\n' * 10, linewidth=80)
|
||||
|
||||
def test_long_line(self):
|
||||
self.check(expected=3, text='a' * 200, linewidth=80)
|
||||
self.check(expected=3, text='a' * 200 + '\n', linewidth=80)
|
||||
|
||||
def test_several_lines_different_lengths(self):
|
||||
text = dedent("""\
|
||||
13 characters
|
||||
43 is the number of characters on this line
|
||||
|
||||
7 chars
|
||||
13 characters""")
|
||||
self.check(expected=5, text=text, linewidth=80)
|
||||
self.check(expected=5, text=text + '\n', linewidth=80)
|
||||
self.check(expected=6, text=text, linewidth=40)
|
||||
self.check(expected=7, text=text, linewidth=20)
|
||||
self.check(expected=11, text=text, linewidth=10)
|
||||
|
||||
|
||||
class SqueezerTest(unittest.TestCase):
|
||||
"""Tests for the Squeezer class."""
|
||||
def make_mock_editor_window(self, with_text_widget=False):
|
||||
"""Create a mock EditorWindow instance."""
|
||||
editwin = NonCallableMagicMock()
|
||||
editwin.width = 80
|
||||
|
||||
if with_text_widget:
|
||||
editwin.root = get_test_tk_root(self)
|
||||
text_widget = self.make_text_widget(root=editwin.root)
|
||||
editwin.text = editwin.per.bottom = text_widget
|
||||
|
||||
return editwin
|
||||
|
||||
def make_squeezer_instance(self, editor_window=None):
|
||||
"""Create an actual Squeezer instance with a mock EditorWindow."""
|
||||
if editor_window is None:
|
||||
editor_window = self.make_mock_editor_window()
|
||||
squeezer = Squeezer(editor_window)
|
||||
return squeezer
|
||||
|
||||
def make_text_widget(self, root=None):
|
||||
if root is None:
|
||||
root = get_test_tk_root(self)
|
||||
text_widget = Text(root)
|
||||
text_widget["font"] = ('Courier', 10)
|
||||
text_widget.mark_set("iomark", "1.0")
|
||||
return text_widget
|
||||
|
||||
def set_idleconf_option_with_cleanup(self, configType, section, option, value):
|
||||
prev_val = idleConf.GetOption(configType, section, option)
|
||||
idleConf.SetOption(configType, section, option, value)
|
||||
self.addCleanup(idleConf.SetOption,
|
||||
configType, section, option, prev_val)
|
||||
|
||||
def test_count_lines(self):
|
||||
"""Test Squeezer.count_lines() with various inputs."""
|
||||
editwin = self.make_mock_editor_window()
|
||||
squeezer = self.make_squeezer_instance(editwin)
|
||||
|
||||
for text_code, line_width, expected in [
|
||||
(r"'\n'", 80, 1),
|
||||
(r"'\n' * 3", 80, 3),
|
||||
(r"'a' * 40 + '\n'", 80, 1),
|
||||
(r"'a' * 80 + '\n'", 80, 1),
|
||||
(r"'a' * 200 + '\n'", 80, 3),
|
||||
(r"'aa\t' * 20", 80, 2),
|
||||
(r"'aa\t' * 21", 80, 3),
|
||||
(r"'aa\t' * 20", 40, 4),
|
||||
]:
|
||||
with self.subTest(text_code=text_code,
|
||||
line_width=line_width,
|
||||
expected=expected):
|
||||
text = eval(text_code)
|
||||
with patch.object(editwin, 'width', line_width):
|
||||
self.assertEqual(squeezer.count_lines(text), expected)
|
||||
|
||||
def test_init(self):
|
||||
"""Test the creation of Squeezer instances."""
|
||||
editwin = self.make_mock_editor_window()
|
||||
squeezer = self.make_squeezer_instance(editwin)
|
||||
self.assertIs(squeezer.editwin, editwin)
|
||||
self.assertEqual(squeezer.expandingbuttons, [])
|
||||
|
||||
def test_write_no_tags(self):
|
||||
"""Test Squeezer's overriding of the EditorWindow's write() method."""
|
||||
editwin = self.make_mock_editor_window()
|
||||
for text in ['', 'TEXT', 'LONG TEXT' * 1000, 'MANY_LINES\n' * 100]:
|
||||
editwin.write = orig_write = Mock(return_value=SENTINEL_VALUE)
|
||||
squeezer = self.make_squeezer_instance(editwin)
|
||||
|
||||
self.assertEqual(squeezer.editwin.write(text, ()), SENTINEL_VALUE)
|
||||
self.assertEqual(orig_write.call_count, 1)
|
||||
orig_write.assert_called_with(text, ())
|
||||
self.assertEqual(len(squeezer.expandingbuttons), 0)
|
||||
|
||||
def test_write_not_stdout(self):
|
||||
"""Test Squeezer's overriding of the EditorWindow's write() method."""
|
||||
for text in ['', 'TEXT', 'LONG TEXT' * 1000, 'MANY_LINES\n' * 100]:
|
||||
editwin = self.make_mock_editor_window()
|
||||
editwin.write.return_value = SENTINEL_VALUE
|
||||
orig_write = editwin.write
|
||||
squeezer = self.make_squeezer_instance(editwin)
|
||||
|
||||
self.assertEqual(squeezer.editwin.write(text, "stderr"),
|
||||
SENTINEL_VALUE)
|
||||
self.assertEqual(orig_write.call_count, 1)
|
||||
orig_write.assert_called_with(text, "stderr")
|
||||
self.assertEqual(len(squeezer.expandingbuttons), 0)
|
||||
|
||||
def test_write_stdout(self):
|
||||
"""Test Squeezer's overriding of the EditorWindow's write() method."""
|
||||
editwin = self.make_mock_editor_window()
|
||||
|
||||
for text in ['', 'TEXT']:
|
||||
editwin.write = orig_write = Mock(return_value=SENTINEL_VALUE)
|
||||
squeezer = self.make_squeezer_instance(editwin)
|
||||
squeezer.auto_squeeze_min_lines = 50
|
||||
|
||||
self.assertEqual(squeezer.editwin.write(text, "stdout"),
|
||||
SENTINEL_VALUE)
|
||||
self.assertEqual(orig_write.call_count, 1)
|
||||
orig_write.assert_called_with(text, "stdout")
|
||||
self.assertEqual(len(squeezer.expandingbuttons), 0)
|
||||
|
||||
for text in ['LONG TEXT' * 1000, 'MANY_LINES\n' * 100]:
|
||||
editwin.write = orig_write = Mock(return_value=SENTINEL_VALUE)
|
||||
squeezer = self.make_squeezer_instance(editwin)
|
||||
squeezer.auto_squeeze_min_lines = 50
|
||||
|
||||
self.assertEqual(squeezer.editwin.write(text, "stdout"), None)
|
||||
self.assertEqual(orig_write.call_count, 0)
|
||||
self.assertEqual(len(squeezer.expandingbuttons), 1)
|
||||
|
||||
def test_auto_squeeze(self):
|
||||
"""Test that the auto-squeezing creates an ExpandingButton properly."""
|
||||
editwin = self.make_mock_editor_window(with_text_widget=True)
|
||||
text_widget = editwin.text
|
||||
squeezer = self.make_squeezer_instance(editwin)
|
||||
squeezer.auto_squeeze_min_lines = 5
|
||||
squeezer.count_lines = Mock(return_value=6)
|
||||
|
||||
editwin.write('TEXT\n'*6, "stdout")
|
||||
self.assertEqual(text_widget.get('1.0', 'end'), '\n')
|
||||
self.assertEqual(len(squeezer.expandingbuttons), 1)
|
||||
|
||||
def test_squeeze_current_text(self):
|
||||
"""Test the squeeze_current_text method."""
|
||||
# Squeezing text should work for both stdout and stderr.
|
||||
for tag_name in ["stdout", "stderr"]:
|
||||
editwin = self.make_mock_editor_window(with_text_widget=True)
|
||||
text_widget = editwin.text
|
||||
squeezer = self.make_squeezer_instance(editwin)
|
||||
squeezer.count_lines = Mock(return_value=6)
|
||||
|
||||
# Prepare some text in the Text widget.
|
||||
text_widget.insert("1.0", "SOME\nTEXT\n", tag_name)
|
||||
text_widget.mark_set("insert", "1.0")
|
||||
self.assertEqual(text_widget.get('1.0', 'end'), 'SOME\nTEXT\n\n')
|
||||
|
||||
self.assertEqual(len(squeezer.expandingbuttons), 0)
|
||||
|
||||
# Test squeezing the current text.
|
||||
retval = squeezer.squeeze_current_text()
|
||||
self.assertEqual(retval, "break")
|
||||
self.assertEqual(text_widget.get('1.0', 'end'), '\n\n')
|
||||
self.assertEqual(len(squeezer.expandingbuttons), 1)
|
||||
self.assertEqual(squeezer.expandingbuttons[0].s, 'SOME\nTEXT')
|
||||
|
||||
# Test that expanding the squeezed text works and afterwards
|
||||
# the Text widget contains the original text.
|
||||
squeezer.expandingbuttons[0].expand()
|
||||
self.assertEqual(text_widget.get('1.0', 'end'), 'SOME\nTEXT\n\n')
|
||||
self.assertEqual(len(squeezer.expandingbuttons), 0)
|
||||
|
||||
def test_squeeze_current_text_no_allowed_tags(self):
|
||||
"""Test that the event doesn't squeeze text without a relevant tag."""
|
||||
editwin = self.make_mock_editor_window(with_text_widget=True)
|
||||
text_widget = editwin.text
|
||||
squeezer = self.make_squeezer_instance(editwin)
|
||||
squeezer.count_lines = Mock(return_value=6)
|
||||
|
||||
# Prepare some text in the Text widget.
|
||||
text_widget.insert("1.0", "SOME\nTEXT\n", "TAG")
|
||||
text_widget.mark_set("insert", "1.0")
|
||||
self.assertEqual(text_widget.get('1.0', 'end'), 'SOME\nTEXT\n\n')
|
||||
|
||||
self.assertEqual(len(squeezer.expandingbuttons), 0)
|
||||
|
||||
# Test squeezing the current text.
|
||||
retval = squeezer.squeeze_current_text()
|
||||
self.assertEqual(retval, "break")
|
||||
self.assertEqual(text_widget.get('1.0', 'end'), 'SOME\nTEXT\n\n')
|
||||
self.assertEqual(len(squeezer.expandingbuttons), 0)
|
||||
|
||||
def test_squeeze_text_before_existing_squeezed_text(self):
|
||||
"""Test squeezing text before existing squeezed text."""
|
||||
editwin = self.make_mock_editor_window(with_text_widget=True)
|
||||
text_widget = editwin.text
|
||||
squeezer = self.make_squeezer_instance(editwin)
|
||||
squeezer.count_lines = Mock(return_value=6)
|
||||
|
||||
# Prepare some text in the Text widget and squeeze it.
|
||||
text_widget.insert("1.0", "SOME\nTEXT\n", "stdout")
|
||||
text_widget.mark_set("insert", "1.0")
|
||||
squeezer.squeeze_current_text()
|
||||
self.assertEqual(len(squeezer.expandingbuttons), 1)
|
||||
|
||||
# Test squeezing the current text.
|
||||
text_widget.insert("1.0", "MORE\nSTUFF\n", "stdout")
|
||||
text_widget.mark_set("insert", "1.0")
|
||||
retval = squeezer.squeeze_current_text()
|
||||
self.assertEqual(retval, "break")
|
||||
self.assertEqual(text_widget.get('1.0', 'end'), '\n\n\n')
|
||||
self.assertEqual(len(squeezer.expandingbuttons), 2)
|
||||
self.assertTrue(text_widget.compare(
|
||||
squeezer.expandingbuttons[0],
|
||||
'<',
|
||||
squeezer.expandingbuttons[1],
|
||||
))
|
||||
|
||||
def test_reload(self):
|
||||
"""Test the reload() class-method."""
|
||||
editwin = self.make_mock_editor_window(with_text_widget=True)
|
||||
squeezer = self.make_squeezer_instance(editwin)
|
||||
|
||||
orig_auto_squeeze_min_lines = squeezer.auto_squeeze_min_lines
|
||||
|
||||
# Increase auto-squeeze-min-lines.
|
||||
new_auto_squeeze_min_lines = orig_auto_squeeze_min_lines + 10
|
||||
self.set_idleconf_option_with_cleanup(
|
||||
'main', 'PyShell', 'auto-squeeze-min-lines',
|
||||
str(new_auto_squeeze_min_lines))
|
||||
|
||||
Squeezer.reload()
|
||||
self.assertEqual(squeezer.auto_squeeze_min_lines,
|
||||
new_auto_squeeze_min_lines)
|
||||
|
||||
def test_reload_no_squeezer_instances(self):
|
||||
"""Test that Squeezer.reload() runs without any instances existing."""
|
||||
Squeezer.reload()
|
||||
|
||||
|
||||
class ExpandingButtonTest(unittest.TestCase):
|
||||
"""Tests for the ExpandingButton class."""
|
||||
# In these tests the squeezer instance is a mock, but actual tkinter
|
||||
# Text and Button instances are created.
|
||||
def make_mock_squeezer(self):
|
||||
"""Helper for tests: Create a mock Squeezer object."""
|
||||
root = get_test_tk_root(self)
|
||||
squeezer = Mock()
|
||||
squeezer.editwin.text = Text(root)
|
||||
squeezer.editwin.per = Percolator(squeezer.editwin.text)
|
||||
self.addCleanup(squeezer.editwin.per.close)
|
||||
|
||||
# Set default values for the configuration settings.
|
||||
squeezer.auto_squeeze_min_lines = 50
|
||||
return squeezer
|
||||
|
||||
@patch('idlelib.squeezer.Hovertip', autospec=Hovertip)
|
||||
def test_init(self, MockHovertip):
|
||||
"""Test the simplest creation of an ExpandingButton."""
|
||||
squeezer = self.make_mock_squeezer()
|
||||
text_widget = squeezer.editwin.text
|
||||
|
||||
expandingbutton = ExpandingButton('TEXT', 'TAGS', 50, squeezer)
|
||||
self.assertEqual(expandingbutton.s, 'TEXT')
|
||||
|
||||
# Check that the underlying tkinter.Button is properly configured.
|
||||
self.assertEqual(expandingbutton.master, text_widget)
|
||||
self.assertTrue('50 lines' in expandingbutton.cget('text'))
|
||||
|
||||
# Check that the text widget still contains no text.
|
||||
self.assertEqual(text_widget.get('1.0', 'end'), '\n')
|
||||
|
||||
# Check that the mouse events are bound.
|
||||
self.assertIn('<Double-Button-1>', expandingbutton.bind())
|
||||
right_button_code = '<Button-%s>' % ('2' if macosx.isAquaTk() else '3')
|
||||
self.assertIn(right_button_code, expandingbutton.bind())
|
||||
|
||||
# Check that ToolTip was called once, with appropriate values.
|
||||
self.assertEqual(MockHovertip.call_count, 1)
|
||||
MockHovertip.assert_called_with(expandingbutton, ANY, hover_delay=ANY)
|
||||
|
||||
# Check that 'right-click' appears in the tooltip text.
|
||||
tooltip_text = MockHovertip.call_args[0][1]
|
||||
self.assertIn('right-click', tooltip_text.lower())
|
||||
|
||||
def test_expand(self):
|
||||
"""Test the expand event."""
|
||||
squeezer = self.make_mock_squeezer()
|
||||
expandingbutton = ExpandingButton('TEXT', 'TAGS', 50, squeezer)
|
||||
|
||||
# Insert the button into the text widget
|
||||
# (this is normally done by the Squeezer class).
|
||||
text_widget = squeezer.editwin.text
|
||||
text_widget.window_create("1.0", window=expandingbutton)
|
||||
|
||||
# trigger the expand event
|
||||
retval = expandingbutton.expand(event=Mock())
|
||||
self.assertEqual(retval, None)
|
||||
|
||||
# Check that the text was inserted into the text widget.
|
||||
self.assertEqual(text_widget.get('1.0', 'end'), 'TEXT\n')
|
||||
|
||||
# Check that the 'TAGS' tag was set on the inserted text.
|
||||
text_end_index = text_widget.index('end-1c')
|
||||
self.assertEqual(text_widget.get('1.0', text_end_index), 'TEXT')
|
||||
self.assertEqual(text_widget.tag_nextrange('TAGS', '1.0'),
|
||||
('1.0', text_end_index))
|
||||
|
||||
# Check that the button removed itself from squeezer.expandingbuttons.
|
||||
self.assertEqual(squeezer.expandingbuttons.remove.call_count, 1)
|
||||
squeezer.expandingbuttons.remove.assert_called_with(expandingbutton)
|
||||
|
||||
def test_expand_dangerous_oupput(self):
|
||||
"""Test that expanding very long output asks user for confirmation."""
|
||||
squeezer = self.make_mock_squeezer()
|
||||
text = 'a' * 10**5
|
||||
expandingbutton = ExpandingButton(text, 'TAGS', 50, squeezer)
|
||||
expandingbutton.set_is_dangerous()
|
||||
self.assertTrue(expandingbutton.is_dangerous)
|
||||
|
||||
# Insert the button into the text widget
|
||||
# (this is normally done by the Squeezer class).
|
||||
text_widget = expandingbutton.text
|
||||
text_widget.window_create("1.0", window=expandingbutton)
|
||||
|
||||
# Patch the message box module to always return False.
|
||||
with patch('idlelib.squeezer.messagebox') as mock_msgbox:
|
||||
mock_msgbox.askokcancel.return_value = False
|
||||
mock_msgbox.askyesno.return_value = False
|
||||
# Trigger the expand event.
|
||||
retval = expandingbutton.expand(event=Mock())
|
||||
|
||||
# Check that the event chain was broken and no text was inserted.
|
||||
self.assertEqual(retval, 'break')
|
||||
self.assertEqual(expandingbutton.text.get('1.0', 'end-1c'), '')
|
||||
|
||||
# Patch the message box module to always return True.
|
||||
with patch('idlelib.squeezer.messagebox') as mock_msgbox:
|
||||
mock_msgbox.askokcancel.return_value = True
|
||||
mock_msgbox.askyesno.return_value = True
|
||||
# Trigger the expand event.
|
||||
retval = expandingbutton.expand(event=Mock())
|
||||
|
||||
# Check that the event chain wasn't broken and the text was inserted.
|
||||
self.assertEqual(retval, None)
|
||||
self.assertEqual(expandingbutton.text.get('1.0', 'end-1c'), text)
|
||||
|
||||
def test_copy(self):
|
||||
"""Test the copy event."""
|
||||
# Testing with the actual clipboard proved problematic, so this
|
||||
# test replaces the clipboard manipulation functions with mocks
|
||||
# and checks that they are called appropriately.
|
||||
squeezer = self.make_mock_squeezer()
|
||||
expandingbutton = ExpandingButton('TEXT', 'TAGS', 50, squeezer)
|
||||
expandingbutton.clipboard_clear = Mock()
|
||||
expandingbutton.clipboard_append = Mock()
|
||||
|
||||
# Trigger the copy event.
|
||||
retval = expandingbutton.copy(event=Mock())
|
||||
self.assertEqual(retval, None)
|
||||
|
||||
# Vheck that the expanding button called clipboard_clear() and
|
||||
# clipboard_append('TEXT') once each.
|
||||
self.assertEqual(expandingbutton.clipboard_clear.call_count, 1)
|
||||
self.assertEqual(expandingbutton.clipboard_append.call_count, 1)
|
||||
expandingbutton.clipboard_append.assert_called_with('TEXT')
|
||||
|
||||
def test_view(self):
|
||||
"""Test the view event."""
|
||||
squeezer = self.make_mock_squeezer()
|
||||
expandingbutton = ExpandingButton('TEXT', 'TAGS', 50, squeezer)
|
||||
expandingbutton.selection_own = Mock()
|
||||
|
||||
with patch('idlelib.squeezer.view_text', autospec=view_text)\
|
||||
as mock_view_text:
|
||||
# Trigger the view event.
|
||||
expandingbutton.view(event=Mock())
|
||||
|
||||
# Check that the expanding button called view_text.
|
||||
self.assertEqual(mock_view_text.call_count, 1)
|
||||
|
||||
# Check that the proper text was passed.
|
||||
self.assertEqual(mock_view_text.call_args[0][2], 'TEXT')
|
||||
|
||||
def test_rmenu(self):
|
||||
"""Test the context menu."""
|
||||
squeezer = self.make_mock_squeezer()
|
||||
expandingbutton = ExpandingButton('TEXT', 'TAGS', 50, squeezer)
|
||||
with patch('tkinter.Menu') as mock_Menu:
|
||||
mock_menu = Mock()
|
||||
mock_Menu.return_value = mock_menu
|
||||
mock_event = Mock()
|
||||
mock_event.x = 10
|
||||
mock_event.y = 10
|
||||
expandingbutton.context_menu_event(event=mock_event)
|
||||
self.assertEqual(mock_menu.add_command.call_count,
|
||||
len(expandingbutton.rmenu_specs))
|
||||
for label, *data in expandingbutton.rmenu_specs:
|
||||
mock_menu.add_command.assert_any_call(label=label, command=ANY)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
41
Dependencies/Python/Lib/idlelib/idle_test/test_stackviewer.py
vendored
Normal file
41
Dependencies/Python/Lib/idlelib/idle_test/test_stackviewer.py
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
"Test stackviewer, coverage 63%."
|
||||
|
||||
from idlelib import stackviewer
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk
|
||||
|
||||
from idlelib.tree import TreeNode, ScrolledCanvas
|
||||
|
||||
|
||||
class StackBrowserTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
|
||||
cls.root.update_idletasks()
|
||||
## for id in cls.root.tk.call('after', 'info'):
|
||||
## cls.root.after_cancel(id) # Need for EditorWindow.
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_init(self):
|
||||
try:
|
||||
abc
|
||||
except NameError as exc:
|
||||
sb = stackviewer.StackBrowser(self.root, exc)
|
||||
isi = self.assertIsInstance
|
||||
isi(stackviewer.sc, ScrolledCanvas)
|
||||
isi(stackviewer.item, stackviewer.StackTreeItem)
|
||||
isi(stackviewer.node, TreeNode)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
41
Dependencies/Python/Lib/idlelib/idle_test/test_statusbar.py
vendored
Normal file
41
Dependencies/Python/Lib/idlelib/idle_test/test_statusbar.py
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
"Test statusbar, coverage 100%."
|
||||
|
||||
from idlelib import statusbar
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk
|
||||
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_init(self):
|
||||
bar = statusbar.MultiStatusBar(self.root)
|
||||
self.assertEqual(bar.labels, {})
|
||||
|
||||
def test_set_label(self):
|
||||
bar = statusbar.MultiStatusBar(self.root)
|
||||
bar.set_label('left', text='sometext', width=10)
|
||||
self.assertIn('left', bar.labels)
|
||||
left = bar.labels['left']
|
||||
self.assertEqual(left['text'], 'sometext')
|
||||
self.assertEqual(left['width'], 10)
|
||||
bar.set_label('left', text='revised text')
|
||||
self.assertEqual(left['text'], 'revised text')
|
||||
bar.set_label('right', text='correct text')
|
||||
self.assertEqual(bar.labels['right']['text'], 'correct text')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
236
Dependencies/Python/Lib/idlelib/idle_test/test_text.py
vendored
Normal file
236
Dependencies/Python/Lib/idlelib/idle_test/test_text.py
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
''' Test mock_tk.Text class against tkinter.Text class
|
||||
|
||||
Run same tests with both by creating a mixin class.
|
||||
'''
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from _tkinter import TclError
|
||||
|
||||
class TextTest:
|
||||
"Define items common to both sets of tests."
|
||||
|
||||
hw = 'hello\nworld' # Several tests insert this after initialization.
|
||||
hwn = hw+'\n' # \n present at initialization, before insert
|
||||
|
||||
# setUpClass defines cls.Text and maybe cls.root.
|
||||
# setUp defines self.text from Text and maybe root.
|
||||
|
||||
def test_init(self):
|
||||
self.assertEqual(self.text.get('1.0'), '\n')
|
||||
self.assertEqual(self.text.get('end'), '')
|
||||
|
||||
def test_index_empty(self):
|
||||
index = self.text.index
|
||||
|
||||
for dex in (-1.0, 0.3, '1.-1', '1.0', '1.0 lineend', '1.end', '1.33',
|
||||
'insert'):
|
||||
self.assertEqual(index(dex), '1.0')
|
||||
|
||||
for dex in 'end', 2.0, '2.1', '33.44':
|
||||
self.assertEqual(index(dex), '2.0')
|
||||
|
||||
def test_index_data(self):
|
||||
index = self.text.index
|
||||
self.text.insert('1.0', self.hw)
|
||||
|
||||
for dex in -1.0, 0.3, '1.-1', '1.0':
|
||||
self.assertEqual(index(dex), '1.0')
|
||||
|
||||
for dex in '1.0 lineend', '1.end', '1.33':
|
||||
self.assertEqual(index(dex), '1.5')
|
||||
|
||||
for dex in 'end', '33.44':
|
||||
self.assertEqual(index(dex), '3.0')
|
||||
|
||||
def test_get(self):
|
||||
get = self.text.get
|
||||
Equal = self.assertEqual
|
||||
self.text.insert('1.0', self.hw)
|
||||
|
||||
Equal(get('end'), '')
|
||||
Equal(get('end', 'end'), '')
|
||||
Equal(get('1.0'), 'h')
|
||||
Equal(get('1.0', '1.1'), 'h')
|
||||
Equal(get('1.0', '1.3'), 'hel')
|
||||
Equal(get('1.1', '1.3'), 'el')
|
||||
Equal(get('1.0', '1.0 lineend'), 'hello')
|
||||
Equal(get('1.0', '1.10'), 'hello')
|
||||
Equal(get('1.0 lineend'), '\n')
|
||||
Equal(get('1.1', '2.3'), 'ello\nwor')
|
||||
Equal(get('1.0', '2.5'), self.hw)
|
||||
Equal(get('1.0', 'end'), self.hwn)
|
||||
Equal(get('0.0', '5.0'), self.hwn)
|
||||
|
||||
def test_insert(self):
|
||||
insert = self.text.insert
|
||||
get = self.text.get
|
||||
Equal = self.assertEqual
|
||||
|
||||
insert('1.0', self.hw)
|
||||
Equal(get('1.0', 'end'), self.hwn)
|
||||
|
||||
insert('1.0', '') # nothing
|
||||
Equal(get('1.0', 'end'), self.hwn)
|
||||
|
||||
insert('1.0', '*')
|
||||
Equal(get('1.0', 'end'), '*hello\nworld\n')
|
||||
|
||||
insert('1.0 lineend', '*')
|
||||
Equal(get('1.0', 'end'), '*hello*\nworld\n')
|
||||
|
||||
insert('2.3', '*')
|
||||
Equal(get('1.0', 'end'), '*hello*\nwor*ld\n')
|
||||
|
||||
insert('end', 'x')
|
||||
Equal(get('1.0', 'end'), '*hello*\nwor*ldx\n')
|
||||
|
||||
insert('1.4', 'x\n')
|
||||
Equal(get('1.0', 'end'), '*helx\nlo*\nwor*ldx\n')
|
||||
|
||||
def test_no_delete(self):
|
||||
# if index1 == 'insert' or 'end' or >= end, there is no deletion
|
||||
delete = self.text.delete
|
||||
get = self.text.get
|
||||
Equal = self.assertEqual
|
||||
self.text.insert('1.0', self.hw)
|
||||
|
||||
delete('insert')
|
||||
Equal(get('1.0', 'end'), self.hwn)
|
||||
|
||||
delete('end')
|
||||
Equal(get('1.0', 'end'), self.hwn)
|
||||
|
||||
delete('insert', 'end')
|
||||
Equal(get('1.0', 'end'), self.hwn)
|
||||
|
||||
delete('insert', '5.5')
|
||||
Equal(get('1.0', 'end'), self.hwn)
|
||||
|
||||
delete('1.4', '1.0')
|
||||
Equal(get('1.0', 'end'), self.hwn)
|
||||
|
||||
delete('1.4', '1.4')
|
||||
Equal(get('1.0', 'end'), self.hwn)
|
||||
|
||||
def test_delete_char(self):
|
||||
delete = self.text.delete
|
||||
get = self.text.get
|
||||
Equal = self.assertEqual
|
||||
self.text.insert('1.0', self.hw)
|
||||
|
||||
delete('1.0')
|
||||
Equal(get('1.0', '1.end'), 'ello')
|
||||
|
||||
delete('1.0', '1.1')
|
||||
Equal(get('1.0', '1.end'), 'llo')
|
||||
|
||||
# delete \n and combine 2 lines into 1
|
||||
delete('1.end')
|
||||
Equal(get('1.0', '1.end'), 'lloworld')
|
||||
|
||||
self.text.insert('1.3', '\n')
|
||||
delete('1.10')
|
||||
Equal(get('1.0', '1.end'), 'lloworld')
|
||||
|
||||
self.text.insert('1.3', '\n')
|
||||
delete('1.3', '2.0')
|
||||
Equal(get('1.0', '1.end'), 'lloworld')
|
||||
|
||||
def test_delete_slice(self):
|
||||
delete = self.text.delete
|
||||
get = self.text.get
|
||||
Equal = self.assertEqual
|
||||
self.text.insert('1.0', self.hw)
|
||||
|
||||
delete('1.0', '1.0 lineend')
|
||||
Equal(get('1.0', 'end'), '\nworld\n')
|
||||
|
||||
delete('1.0', 'end')
|
||||
Equal(get('1.0', 'end'), '\n')
|
||||
|
||||
self.text.insert('1.0', self.hw)
|
||||
delete('1.0', '2.0')
|
||||
Equal(get('1.0', 'end'), 'world\n')
|
||||
|
||||
delete('1.0', 'end')
|
||||
Equal(get('1.0', 'end'), '\n')
|
||||
|
||||
self.text.insert('1.0', self.hw)
|
||||
delete('1.2', '2.3')
|
||||
Equal(get('1.0', 'end'), 'held\n')
|
||||
|
||||
def test_multiple_lines(self): # insert and delete
|
||||
self.text.insert('1.0', 'hello')
|
||||
|
||||
self.text.insert('1.3', '1\n2\n3\n4\n5')
|
||||
self.assertEqual(self.text.get('1.0', 'end'), 'hel1\n2\n3\n4\n5lo\n')
|
||||
|
||||
self.text.delete('1.3', '5.1')
|
||||
self.assertEqual(self.text.get('1.0', 'end'), 'hello\n')
|
||||
|
||||
def test_compare(self):
|
||||
compare = self.text.compare
|
||||
Equal = self.assertEqual
|
||||
# need data so indexes not squished to 1,0
|
||||
self.text.insert('1.0', 'First\nSecond\nThird\n')
|
||||
|
||||
self.assertRaises(TclError, compare, '2.2', 'op', '2.2')
|
||||
|
||||
for op, less1, less0, equal, greater0, greater1 in (
|
||||
('<', True, True, False, False, False),
|
||||
('<=', True, True, True, False, False),
|
||||
('>', False, False, False, True, True),
|
||||
('>=', False, False, True, True, True),
|
||||
('==', False, False, True, False, False),
|
||||
('!=', True, True, False, True, True),
|
||||
):
|
||||
Equal(compare('1.1', op, '2.2'), less1, op)
|
||||
Equal(compare('2.1', op, '2.2'), less0, op)
|
||||
Equal(compare('2.2', op, '2.2'), equal, op)
|
||||
Equal(compare('2.3', op, '2.2'), greater0, op)
|
||||
Equal(compare('3.3', op, '2.2'), greater1, op)
|
||||
|
||||
|
||||
class MockTextTest(TextTest, unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
from idlelib.idle_test.mock_tk import Text
|
||||
cls.Text = Text
|
||||
|
||||
def setUp(self):
|
||||
self.text = self.Text()
|
||||
|
||||
|
||||
def test_decode(self):
|
||||
# test endflags (-1, 0) not tested by test_index (which uses +1)
|
||||
decode = self.text._decode
|
||||
Equal = self.assertEqual
|
||||
self.text.insert('1.0', self.hw)
|
||||
|
||||
Equal(decode('end', -1), (2, 5))
|
||||
Equal(decode('3.1', -1), (2, 5))
|
||||
Equal(decode('end', 0), (2, 6))
|
||||
Equal(decode('3.1', 0), (2, 6))
|
||||
|
||||
|
||||
class TkTextTest(TextTest, unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
from tkinter import Tk, Text
|
||||
cls.Text = Text
|
||||
cls.root = Tk()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.text = self.Text(self.root)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=False)
|
||||
233
Dependencies/Python/Lib/idlelib/idle_test/test_textview.py
vendored
Normal file
233
Dependencies/Python/Lib/idlelib/idle_test/test_textview.py
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
"""Test textview, coverage 100%.
|
||||
|
||||
Since all methods and functions create (or destroy) a ViewWindow, which
|
||||
is a widget containing a widget, etcetera, all tests must be gui tests.
|
||||
Using mock Text would not change this. Other mocks are used to retrieve
|
||||
information about calls.
|
||||
"""
|
||||
from idlelib import textview as tv
|
||||
from test.support import requires
|
||||
requires('gui')
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from tkinter import Tk, TclError, CHAR, NONE, WORD
|
||||
from tkinter.ttk import Button
|
||||
from idlelib.idle_test.mock_idle import Func
|
||||
from idlelib.idle_test.mock_tk import Mbox_func
|
||||
|
||||
def setUpModule():
|
||||
global root
|
||||
root = Tk()
|
||||
root.withdraw()
|
||||
|
||||
def tearDownModule():
|
||||
global root
|
||||
root.update_idletasks()
|
||||
root.destroy()
|
||||
del root
|
||||
|
||||
# If we call ViewWindow or wrapper functions with defaults
|
||||
# modal=True, _utest=False, test hangs on call to wait_window.
|
||||
# Have also gotten tk error 'can't invoke "event" command'.
|
||||
|
||||
|
||||
class VW(tv.ViewWindow): # Used in ViewWindowTest.
|
||||
transient = Func()
|
||||
grab_set = Func()
|
||||
wait_window = Func()
|
||||
|
||||
|
||||
# Call wrapper class VW with mock wait_window.
|
||||
class ViewWindowTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
VW.transient.__init__()
|
||||
VW.grab_set.__init__()
|
||||
VW.wait_window.__init__()
|
||||
|
||||
def test_init_modal(self):
|
||||
view = VW(root, 'Title', 'test text')
|
||||
self.assertTrue(VW.transient.called)
|
||||
self.assertTrue(VW.grab_set.called)
|
||||
self.assertTrue(VW.wait_window.called)
|
||||
view.ok()
|
||||
|
||||
def test_init_nonmodal(self):
|
||||
view = VW(root, 'Title', 'test text', modal=False)
|
||||
self.assertFalse(VW.transient.called)
|
||||
self.assertFalse(VW.grab_set.called)
|
||||
self.assertFalse(VW.wait_window.called)
|
||||
view.ok()
|
||||
|
||||
def test_ok(self):
|
||||
view = VW(root, 'Title', 'test text', modal=False)
|
||||
view.destroy = Func()
|
||||
view.ok()
|
||||
self.assertTrue(view.destroy.called)
|
||||
del view.destroy # Unmask real function.
|
||||
view.destroy()
|
||||
|
||||
|
||||
class AutoHideScrollbarTest(unittest.TestCase):
|
||||
# Method set is tested in ScrollableTextFrameTest
|
||||
def test_forbidden_geometry(self):
|
||||
scroll = tv.AutoHideScrollbar(root)
|
||||
self.assertRaises(TclError, scroll.pack)
|
||||
self.assertRaises(TclError, scroll.place)
|
||||
|
||||
|
||||
class ScrollableTextFrameTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.root = root = Tk()
|
||||
root.withdraw()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def make_frame(self, wrap=NONE, **kwargs):
|
||||
frame = tv.ScrollableTextFrame(self.root, wrap=wrap, **kwargs)
|
||||
def cleanup_frame():
|
||||
frame.update_idletasks()
|
||||
frame.destroy()
|
||||
self.addCleanup(cleanup_frame)
|
||||
return frame
|
||||
|
||||
def test_line1(self):
|
||||
frame = self.make_frame()
|
||||
frame.text.insert('1.0', 'test text')
|
||||
self.assertEqual(frame.text.get('1.0', '1.end'), 'test text')
|
||||
|
||||
def test_horiz_scrollbar(self):
|
||||
# The horizontal scrollbar should be shown/hidden according to
|
||||
# the 'wrap' setting: It should only be shown when 'wrap' is
|
||||
# set to NONE.
|
||||
|
||||
# wrap = NONE -> with horizontal scrolling
|
||||
frame = self.make_frame(wrap=NONE)
|
||||
self.assertEqual(frame.text.cget('wrap'), NONE)
|
||||
self.assertIsNotNone(frame.xscroll)
|
||||
|
||||
# wrap != NONE -> no horizontal scrolling
|
||||
for wrap in [CHAR, WORD]:
|
||||
with self.subTest(wrap=wrap):
|
||||
frame = self.make_frame(wrap=wrap)
|
||||
self.assertEqual(frame.text.cget('wrap'), wrap)
|
||||
self.assertIsNone(frame.xscroll)
|
||||
|
||||
|
||||
class ViewFrameTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.root = root = Tk()
|
||||
root.withdraw()
|
||||
cls.frame = tv.ViewFrame(root, 'test text')
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.frame
|
||||
cls.root.update_idletasks()
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_line1(self):
|
||||
get = self.frame.text.get
|
||||
self.assertEqual(get('1.0', '1.end'), 'test text')
|
||||
|
||||
|
||||
# Call ViewWindow with modal=False.
|
||||
class ViewFunctionTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.orig_error = tv.showerror
|
||||
tv.showerror = Mbox_func()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
tv.showerror = cls.orig_error
|
||||
del cls.orig_error
|
||||
|
||||
def test_view_text(self):
|
||||
view = tv.view_text(root, 'Title', 'test text', modal=False)
|
||||
self.assertIsInstance(view, tv.ViewWindow)
|
||||
self.assertIsInstance(view.viewframe, tv.ViewFrame)
|
||||
view.viewframe.ok()
|
||||
|
||||
def test_view_file(self):
|
||||
view = tv.view_file(root, 'Title', __file__, 'ascii', modal=False)
|
||||
self.assertIsInstance(view, tv.ViewWindow)
|
||||
self.assertIsInstance(view.viewframe, tv.ViewFrame)
|
||||
get = view.viewframe.textframe.text.get
|
||||
self.assertIn('Test', get('1.0', '1.end'))
|
||||
view.ok()
|
||||
|
||||
def test_bad_file(self):
|
||||
# Mock showerror will be used; view_file will return None.
|
||||
view = tv.view_file(root, 'Title', 'abc.xyz', 'ascii', modal=False)
|
||||
self.assertIsNone(view)
|
||||
self.assertEqual(tv.showerror.title, 'File Load Error')
|
||||
|
||||
def test_bad_encoding(self):
|
||||
p = os.path
|
||||
fn = p.abspath(p.join(p.dirname(__file__), '..', 'CREDITS.txt'))
|
||||
view = tv.view_file(root, 'Title', fn, 'ascii', modal=False)
|
||||
self.assertIsNone(view)
|
||||
self.assertEqual(tv.showerror.title, 'Unicode Decode Error')
|
||||
|
||||
def test_nowrap(self):
|
||||
view = tv.view_text(root, 'Title', 'test', modal=False, wrap='none')
|
||||
text_widget = view.viewframe.textframe.text
|
||||
self.assertEqual(text_widget.cget('wrap'), 'none')
|
||||
|
||||
|
||||
# Call ViewWindow with _utest=True.
|
||||
class ButtonClickTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.view = None
|
||||
self.called = False
|
||||
|
||||
def tearDown(self):
|
||||
if self.view:
|
||||
self.view.destroy()
|
||||
|
||||
def test_view_text_bind_with_button(self):
|
||||
def _command():
|
||||
self.called = True
|
||||
self.view = tv.view_text(root, 'TITLE_TEXT', 'COMMAND', _utest=True)
|
||||
button = Button(root, text='BUTTON', command=_command)
|
||||
button.invoke()
|
||||
self.addCleanup(button.destroy)
|
||||
|
||||
self.assertEqual(self.called, True)
|
||||
self.assertEqual(self.view.title(), 'TITLE_TEXT')
|
||||
self.assertEqual(self.view.viewframe.textframe.text.get('1.0', '1.end'),
|
||||
'COMMAND')
|
||||
|
||||
def test_view_file_bind_with_button(self):
|
||||
def _command():
|
||||
self.called = True
|
||||
self.view = tv.view_file(root, 'TITLE_FILE', __file__,
|
||||
encoding='ascii', _utest=True)
|
||||
button = Button(root, text='BUTTON', command=_command)
|
||||
button.invoke()
|
||||
self.addCleanup(button.destroy)
|
||||
|
||||
self.assertEqual(self.called, True)
|
||||
self.assertEqual(self.view.title(), 'TITLE_FILE')
|
||||
get = self.view.viewframe.textframe.text.get
|
||||
with open(__file__) as f:
|
||||
self.assertEqual(get('1.0', '1.end'), f.readline().strip())
|
||||
f.readline()
|
||||
self.assertEqual(get('3.0', '3.end'), f.readline().strip())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
161
Dependencies/Python/Lib/idlelib/idle_test/test_tooltip.py
vendored
Normal file
161
Dependencies/Python/Lib/idlelib/idle_test/test_tooltip.py
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
"""Test tooltip, coverage 100%.
|
||||
|
||||
Coverage is 100% after excluding 6 lines with "# pragma: no cover".
|
||||
They involve TclErrors that either should or should not happen in a
|
||||
particular situation, and which are 'pass'ed if they do.
|
||||
"""
|
||||
|
||||
from idlelib.tooltip import TooltipBase, Hovertip
|
||||
from test.support import requires
|
||||
requires('gui')
|
||||
|
||||
from functools import wraps
|
||||
import time
|
||||
from tkinter import Button, Tk, Toplevel
|
||||
import unittest
|
||||
|
||||
|
||||
def setUpModule():
|
||||
global root
|
||||
root = Tk()
|
||||
|
||||
def tearDownModule():
|
||||
global root
|
||||
root.update_idletasks()
|
||||
root.destroy()
|
||||
del root
|
||||
|
||||
|
||||
def add_call_counting(func):
|
||||
@wraps(func)
|
||||
def wrapped_func(*args, **kwargs):
|
||||
wrapped_func.call_args_list.append((args, kwargs))
|
||||
return func(*args, **kwargs)
|
||||
wrapped_func.call_args_list = []
|
||||
return wrapped_func
|
||||
|
||||
|
||||
def _make_top_and_button(testobj):
|
||||
global root
|
||||
top = Toplevel(root)
|
||||
testobj.addCleanup(top.destroy)
|
||||
top.title("Test tooltip")
|
||||
button = Button(top, text='ToolTip test button')
|
||||
button.pack()
|
||||
testobj.addCleanup(button.destroy)
|
||||
top.lift()
|
||||
return top, button
|
||||
|
||||
|
||||
class ToolTipBaseTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.top, self.button = _make_top_and_button(self)
|
||||
|
||||
def test_base_class_is_unusable(self):
|
||||
global root
|
||||
top = Toplevel(root)
|
||||
self.addCleanup(top.destroy)
|
||||
|
||||
button = Button(top, text='ToolTip test button')
|
||||
button.pack()
|
||||
self.addCleanup(button.destroy)
|
||||
|
||||
with self.assertRaises(NotImplementedError):
|
||||
tooltip = TooltipBase(button)
|
||||
tooltip.showtip()
|
||||
|
||||
|
||||
class HovertipTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.top, self.button = _make_top_and_button(self)
|
||||
|
||||
def is_tipwindow_shown(self, tooltip):
|
||||
return tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()
|
||||
|
||||
def test_showtip(self):
|
||||
tooltip = Hovertip(self.button, 'ToolTip text')
|
||||
self.addCleanup(tooltip.hidetip)
|
||||
self.assertFalse(self.is_tipwindow_shown(tooltip))
|
||||
tooltip.showtip()
|
||||
self.assertTrue(self.is_tipwindow_shown(tooltip))
|
||||
|
||||
def test_showtip_twice(self):
|
||||
tooltip = Hovertip(self.button, 'ToolTip text')
|
||||
self.addCleanup(tooltip.hidetip)
|
||||
self.assertFalse(self.is_tipwindow_shown(tooltip))
|
||||
tooltip.showtip()
|
||||
self.assertTrue(self.is_tipwindow_shown(tooltip))
|
||||
orig_tipwindow = tooltip.tipwindow
|
||||
tooltip.showtip()
|
||||
self.assertTrue(self.is_tipwindow_shown(tooltip))
|
||||
self.assertIs(tooltip.tipwindow, orig_tipwindow)
|
||||
|
||||
def test_hidetip(self):
|
||||
tooltip = Hovertip(self.button, 'ToolTip text')
|
||||
self.addCleanup(tooltip.hidetip)
|
||||
tooltip.showtip()
|
||||
tooltip.hidetip()
|
||||
self.assertFalse(self.is_tipwindow_shown(tooltip))
|
||||
|
||||
def test_showtip_on_mouse_enter_no_delay(self):
|
||||
tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None)
|
||||
self.addCleanup(tooltip.hidetip)
|
||||
tooltip.showtip = add_call_counting(tooltip.showtip)
|
||||
root.update()
|
||||
self.assertFalse(self.is_tipwindow_shown(tooltip))
|
||||
self.button.event_generate('<Enter>', x=0, y=0)
|
||||
root.update()
|
||||
self.assertTrue(self.is_tipwindow_shown(tooltip))
|
||||
self.assertGreater(len(tooltip.showtip.call_args_list), 0)
|
||||
|
||||
def test_hover_with_delay(self):
|
||||
# Run multiple tests requiring an actual delay simultaneously.
|
||||
|
||||
# Test #1: A hover tip with a non-zero delay appears after the delay.
|
||||
tooltip1 = Hovertip(self.button, 'ToolTip text', hover_delay=100)
|
||||
self.addCleanup(tooltip1.hidetip)
|
||||
tooltip1.showtip = add_call_counting(tooltip1.showtip)
|
||||
root.update()
|
||||
self.assertFalse(self.is_tipwindow_shown(tooltip1))
|
||||
self.button.event_generate('<Enter>', x=0, y=0)
|
||||
root.update()
|
||||
self.assertFalse(self.is_tipwindow_shown(tooltip1))
|
||||
|
||||
# Test #2: A hover tip with a non-zero delay doesn't appear when
|
||||
# the mouse stops hovering over the base widget before the delay
|
||||
# expires.
|
||||
tooltip2 = Hovertip(self.button, 'ToolTip text', hover_delay=100)
|
||||
self.addCleanup(tooltip2.hidetip)
|
||||
tooltip2.showtip = add_call_counting(tooltip2.showtip)
|
||||
root.update()
|
||||
self.button.event_generate('<Enter>', x=0, y=0)
|
||||
root.update()
|
||||
self.button.event_generate('<Leave>', x=0, y=0)
|
||||
root.update()
|
||||
|
||||
time.sleep(0.15)
|
||||
root.update()
|
||||
|
||||
# Test #1 assertions.
|
||||
self.assertTrue(self.is_tipwindow_shown(tooltip1))
|
||||
self.assertGreater(len(tooltip1.showtip.call_args_list), 0)
|
||||
|
||||
# Test #2 assertions.
|
||||
self.assertFalse(self.is_tipwindow_shown(tooltip2))
|
||||
self.assertEqual(tooltip2.showtip.call_args_list, [])
|
||||
|
||||
def test_hidetip_on_mouse_leave(self):
|
||||
tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None)
|
||||
self.addCleanup(tooltip.hidetip)
|
||||
tooltip.showtip = add_call_counting(tooltip.showtip)
|
||||
root.update()
|
||||
self.button.event_generate('<Enter>', x=0, y=0)
|
||||
root.update()
|
||||
self.button.event_generate('<Leave>', x=0, y=0)
|
||||
root.update()
|
||||
self.assertFalse(self.is_tipwindow_shown(tooltip))
|
||||
self.assertGreater(len(tooltip.showtip.call_args_list), 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
60
Dependencies/Python/Lib/idlelib/idle_test/test_tree.py
vendored
Normal file
60
Dependencies/Python/Lib/idlelib/idle_test/test_tree.py
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
"Test tree. coverage 56%."
|
||||
|
||||
from idlelib import tree
|
||||
import unittest
|
||||
from test.support import requires
|
||||
requires('gui')
|
||||
from tkinter import Tk, EventType, SCROLL
|
||||
|
||||
|
||||
class TreeTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_init(self):
|
||||
# Start with code slightly adapted from htest.
|
||||
sc = tree.ScrolledCanvas(
|
||||
self.root, bg="white", highlightthickness=0, takefocus=1)
|
||||
sc.frame.pack(expand=1, fill="both", side='left')
|
||||
item = tree.FileTreeItem(tree.ICONDIR)
|
||||
node = tree.TreeNode(sc.canvas, None, item)
|
||||
node.expand()
|
||||
|
||||
|
||||
class TestScrollEvent(unittest.TestCase):
|
||||
|
||||
def test_wheel_event(self):
|
||||
# Fake widget class containing `yview` only.
|
||||
class _Widget:
|
||||
def __init__(widget, *expected):
|
||||
widget.expected = expected
|
||||
def yview(widget, *args):
|
||||
self.assertTupleEqual(widget.expected, args)
|
||||
# Fake event class
|
||||
class _Event:
|
||||
pass
|
||||
# (type, delta, num, amount)
|
||||
tests = ((EventType.MouseWheel, 120, -1, -5),
|
||||
(EventType.MouseWheel, -120, -1, 5),
|
||||
(EventType.ButtonPress, -1, 4, -5),
|
||||
(EventType.ButtonPress, -1, 5, 5))
|
||||
|
||||
event = _Event()
|
||||
for ty, delta, num, amount in tests:
|
||||
event.type = ty
|
||||
event.delta = delta
|
||||
event.num = num
|
||||
res = tree.wheel_event(event, _Widget(SCROLL, amount, "units"))
|
||||
self.assertEqual(res, "break")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
135
Dependencies/Python/Lib/idlelib/idle_test/test_undo.py
vendored
Normal file
135
Dependencies/Python/Lib/idlelib/idle_test/test_undo.py
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
"Test undo, coverage 77%."
|
||||
# Only test UndoDelegator so far.
|
||||
|
||||
from idlelib.undo import UndoDelegator
|
||||
import unittest
|
||||
from test.support import requires
|
||||
requires('gui')
|
||||
|
||||
from unittest.mock import Mock
|
||||
from tkinter import Text, Tk
|
||||
from idlelib.percolator import Percolator
|
||||
|
||||
|
||||
class UndoDelegatorTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.root = Tk()
|
||||
cls.text = Text(cls.root)
|
||||
cls.percolator = Percolator(cls.text)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.percolator.redir.close()
|
||||
del cls.percolator, cls.text
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
self.delegator = UndoDelegator()
|
||||
self.delegator.bell = Mock()
|
||||
self.percolator.insertfilter(self.delegator)
|
||||
|
||||
def tearDown(self):
|
||||
self.percolator.removefilter(self.delegator)
|
||||
self.text.delete('1.0', 'end')
|
||||
self.delegator.resetcache()
|
||||
|
||||
def test_undo_event(self):
|
||||
text = self.text
|
||||
|
||||
text.insert('insert', 'foobar')
|
||||
text.insert('insert', 'h')
|
||||
text.event_generate('<<undo>>')
|
||||
self.assertEqual(text.get('1.0', 'end'), '\n')
|
||||
|
||||
text.insert('insert', 'foo')
|
||||
text.insert('insert', 'bar')
|
||||
text.delete('1.2', '1.4')
|
||||
text.insert('insert', 'hello')
|
||||
text.event_generate('<<undo>>')
|
||||
self.assertEqual(text.get('1.0', '1.4'), 'foar')
|
||||
text.event_generate('<<undo>>')
|
||||
self.assertEqual(text.get('1.0', '1.6'), 'foobar')
|
||||
text.event_generate('<<undo>>')
|
||||
self.assertEqual(text.get('1.0', '1.3'), 'foo')
|
||||
text.event_generate('<<undo>>')
|
||||
self.delegator.undo_event('event')
|
||||
self.assertTrue(self.delegator.bell.called)
|
||||
|
||||
def test_redo_event(self):
|
||||
text = self.text
|
||||
|
||||
text.insert('insert', 'foo')
|
||||
text.insert('insert', 'bar')
|
||||
text.delete('1.0', '1.3')
|
||||
text.event_generate('<<undo>>')
|
||||
text.event_generate('<<redo>>')
|
||||
self.assertEqual(text.get('1.0', '1.3'), 'bar')
|
||||
text.event_generate('<<redo>>')
|
||||
self.assertTrue(self.delegator.bell.called)
|
||||
|
||||
def test_dump_event(self):
|
||||
"""
|
||||
Dump_event cannot be tested directly without changing
|
||||
environment variables. So, test statements in dump_event
|
||||
indirectly
|
||||
"""
|
||||
text = self.text
|
||||
d = self.delegator
|
||||
|
||||
text.insert('insert', 'foo')
|
||||
text.insert('insert', 'bar')
|
||||
text.delete('1.2', '1.4')
|
||||
self.assertTupleEqual((d.pointer, d.can_merge), (3, True))
|
||||
text.event_generate('<<undo>>')
|
||||
self.assertTupleEqual((d.pointer, d.can_merge), (2, False))
|
||||
|
||||
def test_get_set_saved(self):
|
||||
# test the getter method get_saved
|
||||
# test the setter method set_saved
|
||||
# indirectly test check_saved
|
||||
d = self.delegator
|
||||
|
||||
self.assertTrue(d.get_saved())
|
||||
self.text.insert('insert', 'a')
|
||||
self.assertFalse(d.get_saved())
|
||||
d.saved_change_hook = Mock()
|
||||
|
||||
d.set_saved(True)
|
||||
self.assertEqual(d.pointer, d.saved)
|
||||
self.assertTrue(d.saved_change_hook.called)
|
||||
|
||||
d.set_saved(False)
|
||||
self.assertEqual(d.saved, -1)
|
||||
self.assertTrue(d.saved_change_hook.called)
|
||||
|
||||
def test_undo_start_stop(self):
|
||||
# test the undo_block_start and undo_block_stop methods
|
||||
text = self.text
|
||||
|
||||
text.insert('insert', 'foo')
|
||||
self.delegator.undo_block_start()
|
||||
text.insert('insert', 'bar')
|
||||
text.insert('insert', 'bar')
|
||||
self.delegator.undo_block_stop()
|
||||
self.assertEqual(text.get('1.0', '1.3'), 'foo')
|
||||
|
||||
# test another code path
|
||||
self.delegator.undo_block_start()
|
||||
text.insert('insert', 'bar')
|
||||
self.delegator.undo_block_stop()
|
||||
self.assertEqual(text.get('1.0', '1.3'), 'foo')
|
||||
|
||||
def test_addcmd(self):
|
||||
text = self.text
|
||||
# when number of undo operations exceeds max_undo
|
||||
self.delegator.max_undo = max_undo = 10
|
||||
for i in range(max_undo + 10):
|
||||
text.insert('insert', 'foo')
|
||||
self.assertLessEqual(len(self.delegator.undolist), max_undo)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=False)
|
||||
14
Dependencies/Python/Lib/idlelib/idle_test/test_util.py
vendored
Normal file
14
Dependencies/Python/Lib/idlelib/idle_test/test_util.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
"""Test util, coverage 100%"""
|
||||
|
||||
import unittest
|
||||
from idlelib import util
|
||||
|
||||
|
||||
class UtilTest(unittest.TestCase):
|
||||
def test_extensions(self):
|
||||
for extension in {'.pyi', '.py', '.pyw'}:
|
||||
self.assertIn(extension, util.py_extensions)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
73
Dependencies/Python/Lib/idlelib/idle_test/test_warning.py
vendored
Normal file
73
Dependencies/Python/Lib/idlelib/idle_test/test_warning.py
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
'''Test warnings replacement in pyshell.py and run.py.
|
||||
|
||||
This file could be expanded to include traceback overrides
|
||||
(in same two modules). If so, change name.
|
||||
Revise if output destination changes (http://bugs.python.org/issue18318).
|
||||
Make sure warnings module is left unaltered (http://bugs.python.org/issue18081).
|
||||
'''
|
||||
from idlelib import run
|
||||
from idlelib import pyshell as shell
|
||||
import unittest
|
||||
from test.support import captured_stderr
|
||||
import warnings
|
||||
|
||||
# Try to capture default showwarning before Idle modules are imported.
|
||||
showwarning = warnings.showwarning
|
||||
# But if we run this file within idle, we are in the middle of the run.main loop
|
||||
# and default showwarnings has already been replaced.
|
||||
running_in_idle = 'idle' in showwarning.__name__
|
||||
|
||||
# The following was generated from pyshell.idle_formatwarning
|
||||
# and checked as matching expectation.
|
||||
idlemsg = '''
|
||||
Warning (from warnings module):
|
||||
File "test_warning.py", line 99
|
||||
Line of code
|
||||
UserWarning: Test
|
||||
'''
|
||||
shellmsg = idlemsg + ">>> "
|
||||
|
||||
|
||||
class RunWarnTest(unittest.TestCase):
|
||||
|
||||
@unittest.skipIf(running_in_idle, "Does not work when run within Idle.")
|
||||
def test_showwarnings(self):
|
||||
self.assertIs(warnings.showwarning, showwarning)
|
||||
run.capture_warnings(True)
|
||||
self.assertIs(warnings.showwarning, run.idle_showwarning_subproc)
|
||||
run.capture_warnings(False)
|
||||
self.assertIs(warnings.showwarning, showwarning)
|
||||
|
||||
def test_run_show(self):
|
||||
with captured_stderr() as f:
|
||||
run.idle_showwarning_subproc(
|
||||
'Test', UserWarning, 'test_warning.py', 99, f, 'Line of code')
|
||||
# The following uses .splitlines to erase line-ending differences
|
||||
self.assertEqual(idlemsg.splitlines(), f.getvalue().splitlines())
|
||||
|
||||
|
||||
class ShellWarnTest(unittest.TestCase):
|
||||
|
||||
@unittest.skipIf(running_in_idle, "Does not work when run within Idle.")
|
||||
def test_showwarnings(self):
|
||||
self.assertIs(warnings.showwarning, showwarning)
|
||||
shell.capture_warnings(True)
|
||||
self.assertIs(warnings.showwarning, shell.idle_showwarning)
|
||||
shell.capture_warnings(False)
|
||||
self.assertIs(warnings.showwarning, showwarning)
|
||||
|
||||
def test_idle_formatter(self):
|
||||
# Will fail if format changed without regenerating idlemsg
|
||||
s = shell.idle_formatwarning(
|
||||
'Test', UserWarning, 'test_warning.py', 99, 'Line of code')
|
||||
self.assertEqual(idlemsg, s)
|
||||
|
||||
def test_shell_show(self):
|
||||
with captured_stderr() as f:
|
||||
shell.idle_showwarning(
|
||||
'Test', UserWarning, 'test_warning.py', 99, f, 'Line of code')
|
||||
self.assertEqual(shellmsg.splitlines(), f.getvalue().splitlines())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
45
Dependencies/Python/Lib/idlelib/idle_test/test_window.py
vendored
Normal file
45
Dependencies/Python/Lib/idlelib/idle_test/test_window.py
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
"Test window, coverage 47%."
|
||||
|
||||
from idlelib import window
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk
|
||||
|
||||
|
||||
class WindowListTest(unittest.TestCase):
|
||||
|
||||
def test_init(self):
|
||||
wl = window.WindowList()
|
||||
self.assertEqual(wl.dict, {})
|
||||
self.assertEqual(wl.callbacks, [])
|
||||
|
||||
# Further tests need mock Window.
|
||||
|
||||
|
||||
class ListedToplevelTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
window.registry = set()
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
window.registry = window.WindowList()
|
||||
cls.root.update_idletasks()
|
||||
## for id in cls.root.tk.call('after', 'info'):
|
||||
## cls.root.after_cancel(id) # Need for EditorWindow.
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_init(self):
|
||||
|
||||
win = window.ListedToplevel(self.root)
|
||||
self.assertIn(win, window.registry)
|
||||
self.assertEqual(win.focused_widget, win)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
39
Dependencies/Python/Lib/idlelib/idle_test/test_zoomheight.py
vendored
Normal file
39
Dependencies/Python/Lib/idlelib/idle_test/test_zoomheight.py
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
"Test zoomheight, coverage 66%."
|
||||
# Some code is system dependent.
|
||||
|
||||
from idlelib import zoomheight
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk
|
||||
from idlelib.editor import EditorWindow
|
||||
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.root.withdraw()
|
||||
cls.editwin = EditorWindow(root=cls.root)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.editwin._close()
|
||||
cls.root.update_idletasks()
|
||||
for id in cls.root.tk.call('after', 'info'):
|
||||
cls.root.after_cancel(id) # Need for EditorWindow.
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_init(self):
|
||||
zoom = zoomheight.ZoomHeight(self.editwin)
|
||||
self.assertIs(zoom.editwin, self.editwin)
|
||||
|
||||
def test_zoom_height_event(self):
|
||||
zoom = zoomheight.ZoomHeight(self.editwin)
|
||||
zoom.zoom_height_event()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
152
Dependencies/Python/Lib/idlelib/idle_test/test_zzdummy.py
vendored
Normal file
152
Dependencies/Python/Lib/idlelib/idle_test/test_zzdummy.py
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
"Test zzdummy, coverage 100%."
|
||||
|
||||
from idlelib import zzdummy
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk, Text
|
||||
from unittest import mock
|
||||
from idlelib import config
|
||||
from idlelib import editor
|
||||
from idlelib import format
|
||||
|
||||
|
||||
usercfg = zzdummy.idleConf.userCfg
|
||||
testcfg = {
|
||||
'main': config.IdleUserConfParser(''),
|
||||
'highlight': config.IdleUserConfParser(''),
|
||||
'keys': config.IdleUserConfParser(''),
|
||||
'extensions': config.IdleUserConfParser(''),
|
||||
}
|
||||
code_sample = """\
|
||||
|
||||
class C1:
|
||||
# Class comment.
|
||||
def __init__(self, a, b):
|
||||
self.a = a
|
||||
self.b = b
|
||||
"""
|
||||
|
||||
|
||||
class DummyEditwin:
|
||||
get_selection_indices = editor.EditorWindow.get_selection_indices
|
||||
def __init__(self, root, text):
|
||||
self.root = root
|
||||
self.top = root
|
||||
self.text = text
|
||||
self.fregion = format.FormatRegion(self)
|
||||
self.text.undo_block_start = mock.Mock()
|
||||
self.text.undo_block_stop = mock.Mock()
|
||||
|
||||
|
||||
class ZZDummyTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
root = cls.root = Tk()
|
||||
root.withdraw()
|
||||
text = cls.text = Text(cls.root)
|
||||
cls.editor = DummyEditwin(root, text)
|
||||
zzdummy.idleConf.userCfg = testcfg
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
zzdummy.idleConf.userCfg = usercfg
|
||||
del cls.editor, cls.text
|
||||
cls.root.update_idletasks()
|
||||
for id in cls.root.tk.call('after', 'info'):
|
||||
cls.root.after_cancel(id) # Need for EditorWindow.
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def setUp(self):
|
||||
text = self.text
|
||||
text.insert('1.0', code_sample)
|
||||
text.undo_block_start.reset_mock()
|
||||
text.undo_block_stop.reset_mock()
|
||||
zz = self.zz = zzdummy.ZzDummy(self.editor)
|
||||
zzdummy.ZzDummy.ztext = '# ignore #'
|
||||
|
||||
def tearDown(self):
|
||||
self.text.delete('1.0', 'end')
|
||||
del self.zz
|
||||
|
||||
def checklines(self, text, value):
|
||||
# Verify that there are lines being checked.
|
||||
end_line = int(float(text.index('end')))
|
||||
|
||||
# Check each line for the starting text.
|
||||
actual = []
|
||||
for line in range(1, end_line):
|
||||
txt = text.get(f'{line}.0', f'{line}.end')
|
||||
actual.append(txt.startswith(value))
|
||||
return actual
|
||||
|
||||
def test_init(self):
|
||||
zz = self.zz
|
||||
self.assertEqual(zz.editwin, self.editor)
|
||||
self.assertEqual(zz.text, self.editor.text)
|
||||
|
||||
def test_reload(self):
|
||||
self.assertEqual(self.zz.ztext, '# ignore #')
|
||||
testcfg['extensions'].SetOption('ZzDummy', 'z-text', 'spam')
|
||||
zzdummy.ZzDummy.reload()
|
||||
self.assertEqual(self.zz.ztext, 'spam')
|
||||
|
||||
def test_z_in_event(self):
|
||||
eq = self.assertEqual
|
||||
zz = self.zz
|
||||
text = zz.text
|
||||
eq(self.zz.ztext, '# ignore #')
|
||||
|
||||
# No lines have the leading text.
|
||||
expected = [False, False, False, False, False, False, False]
|
||||
actual = self.checklines(text, zz.ztext)
|
||||
eq(expected, actual)
|
||||
|
||||
text.tag_add('sel', '2.0', '4.end')
|
||||
eq(zz.z_in_event(), 'break')
|
||||
expected = [False, True, True, True, False, False, False]
|
||||
actual = self.checklines(text, zz.ztext)
|
||||
eq(expected, actual)
|
||||
|
||||
text.undo_block_start.assert_called_once()
|
||||
text.undo_block_stop.assert_called_once()
|
||||
|
||||
def test_z_out_event(self):
|
||||
eq = self.assertEqual
|
||||
zz = self.zz
|
||||
text = zz.text
|
||||
eq(self.zz.ztext, '# ignore #')
|
||||
|
||||
# Prepend text.
|
||||
text.tag_add('sel', '2.0', '5.end')
|
||||
zz.z_in_event()
|
||||
text.undo_block_start.reset_mock()
|
||||
text.undo_block_stop.reset_mock()
|
||||
|
||||
# Select a few lines to remove text.
|
||||
text.tag_remove('sel', '1.0', 'end')
|
||||
text.tag_add('sel', '3.0', '4.end')
|
||||
eq(zz.z_out_event(), 'break')
|
||||
expected = [False, True, False, False, True, False, False]
|
||||
actual = self.checklines(text, zz.ztext)
|
||||
eq(expected, actual)
|
||||
|
||||
text.undo_block_start.assert_called_once()
|
||||
text.undo_block_stop.assert_called_once()
|
||||
|
||||
def test_roundtrip(self):
|
||||
# Insert and remove to all code should give back original text.
|
||||
zz = self.zz
|
||||
text = zz.text
|
||||
|
||||
text.tag_add('sel', '1.0', 'end-1c')
|
||||
zz.z_in_event()
|
||||
zz.z_out_event()
|
||||
|
||||
self.assertEqual(text.get('1.0', 'end-1c'), code_sample)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
62
Dependencies/Python/Lib/idlelib/idle_test/tkinter_testing_utils.py
vendored
Normal file
62
Dependencies/Python/Lib/idlelib/idle_test/tkinter_testing_utils.py
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
"""Utilities for testing with Tkinter"""
|
||||
import functools
|
||||
|
||||
|
||||
def run_in_tk_mainloop(delay=1):
|
||||
"""Decorator for running a test method with a real Tk mainloop.
|
||||
|
||||
This starts a Tk mainloop before running the test, and stops it
|
||||
at the end. This is faster and more robust than the common
|
||||
alternative method of calling .update() and/or .update_idletasks().
|
||||
|
||||
Test methods using this must be written as generator functions,
|
||||
using "yield" to allow the mainloop to process events and "after"
|
||||
callbacks, and then continue the test from that point.
|
||||
|
||||
The delay argument is passed into root.after(...) calls as the number
|
||||
of ms to wait before passing execution back to the generator function.
|
||||
|
||||
This also assumes that the test class has a .root attribute,
|
||||
which is a tkinter.Tk object.
|
||||
|
||||
For example (from test_sidebar.py):
|
||||
|
||||
@run_test_with_tk_mainloop()
|
||||
def test_single_empty_input(self):
|
||||
self.do_input('\n')
|
||||
yield
|
||||
self.assert_sidebar_lines_end_with(['>>>', '>>>'])
|
||||
"""
|
||||
def decorator(test_method):
|
||||
@functools.wraps(test_method)
|
||||
def new_test_method(self):
|
||||
test_generator = test_method(self)
|
||||
root = self.root
|
||||
# Exceptions raised by self.assert...() need to be raised
|
||||
# outside of the after() callback in order for the test
|
||||
# harness to capture them.
|
||||
exception = None
|
||||
def after_callback():
|
||||
nonlocal exception
|
||||
try:
|
||||
next(test_generator)
|
||||
except StopIteration:
|
||||
root.quit()
|
||||
except Exception as exc:
|
||||
exception = exc
|
||||
root.quit()
|
||||
else:
|
||||
# Schedule the Tk mainloop to call this function again,
|
||||
# using a robust method of ensuring that it gets a
|
||||
# chance to process queued events before doing so.
|
||||
# See: https://stackoverflow.com/q/18499082#comment65004099_38817470
|
||||
root.after(delay, root.after_idle, after_callback)
|
||||
root.after(0, root.after_idle, after_callback)
|
||||
root.mainloop()
|
||||
|
||||
if exception:
|
||||
raise exception
|
||||
|
||||
return new_test_method
|
||||
|
||||
return decorator
|
||||
Reference in New Issue
Block a user