Merge remote-tracking branch 'TheCompiler/master' into relax-editor-templating
This commit is contained in:
commit
54ff2aa46c
13
.pydocstylerc
Normal file
13
.pydocstylerc
Normal file
@ -0,0 +1,13 @@
|
||||
[pydocstyle]
|
||||
# Disabled checks:
|
||||
# D102: Missing docstring in public method (will be handled by others)
|
||||
# D103: Missing docstring in public function (will be handled by others)
|
||||
# D104: Missing docstring in public package (will be handled by others)
|
||||
# D105: Missing docstring in magic method (will be handled by others)
|
||||
# D209: Blank line before closing """ (removed from PEP257)
|
||||
# D211: No blank lines allowed before class docstring
|
||||
# (PEP257 got changed, but let's stick to the old standard)
|
||||
# D402: First line should not be function's signature (false-positives)
|
||||
ignore = D102,D103,D104,D105,D209,D211,D402
|
||||
match = (?!resources|test_*).*\.py
|
||||
inherit = false
|
@ -16,7 +16,7 @@ env:
|
||||
- TESTENV=unittests-nodisp
|
||||
- TESTENV=misc
|
||||
- TESTENV=vulture
|
||||
- TESTENV=pep257
|
||||
- TESTENV=pydocstyle
|
||||
- TESTENV=flake8
|
||||
- TESTENV=pyroma
|
||||
- TESTENV=check-manifest
|
||||
@ -53,7 +53,7 @@ matrix:
|
||||
- os: osx
|
||||
env: TESTENV=vulture
|
||||
- os: osx
|
||||
env: TESTENV=pep257
|
||||
env: TESTENV=pydocstyle
|
||||
- os: osx
|
||||
env: TESTENV=flake8
|
||||
- os: osx
|
||||
|
@ -23,6 +23,9 @@ Added
|
||||
- New `--quiet` argument for the `:debug-pyeval` command to not open a tab with
|
||||
the results. Note `:debug-pyeval` is still only intended for debugging.
|
||||
- The completion now matches each entered word separately.
|
||||
- A new command `:paste-primary` got added to paste the primary selection, and
|
||||
`<Shift-Insert>` got added as a binding so it pastes primary rather than
|
||||
clipboard.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
@ -36,6 +39,10 @@ Fixed
|
||||
- Fixed starting with -c "".
|
||||
- Fixed crash when a tab is closed twice via javascript (e.g. Dropbox
|
||||
authentication dialogs)
|
||||
- Fixed crash when a notification/geolocation prompt is answered after closing
|
||||
the tab it belongs to.
|
||||
- Fixed crash when downloading a file without any path information (e.g a
|
||||
magnet link).
|
||||
|
||||
v0.5.1
|
||||
------
|
||||
|
@ -143,5 +143,5 @@ My issue is not listed.::
|
||||
https://github.com/The-Compiler/qutebrowser/issues[the issue tracker] or
|
||||
using the `:report` command.
|
||||
If you are reporting a segfault, make sure you read the
|
||||
https://github.com/The-Compiler/qutebrowser/blob/master/doc/stacktrace.asciidoc[guide]
|
||||
on how to report them with all needed information.
|
||||
link:doc/stacktrace.asciidoc[guide] on how to report them with all needed
|
||||
information.
|
||||
|
@ -32,6 +32,7 @@ exclude .eslintignore
|
||||
exclude doc/help
|
||||
exclude .appveyor.yml
|
||||
exclude .travis.yml
|
||||
exclude .pydocstylerc
|
||||
exclude misc/appveyor_install.py
|
||||
|
||||
global-exclude __pycache__ *.pyc *.pyo
|
||||
|
@ -165,6 +165,7 @@ Contributors, sorted by the number of commits in descending order:
|
||||
* Jonas Schürmann
|
||||
* Panagiotis Ktistakis
|
||||
* Jimmy
|
||||
* Jakub Klinkovský
|
||||
* skinnay
|
||||
* error800
|
||||
* Zach-Button
|
||||
|
@ -833,6 +833,7 @@ How many steps to zoom out.
|
||||
|<<move-to-start-of-next-block,move-to-start-of-next-block>>|Move the cursor or selection to the start of next block.
|
||||
|<<move-to-start-of-prev-block,move-to-start-of-prev-block>>|Move the cursor or selection to the start of previous block.
|
||||
|<<open-editor,open-editor>>|Open an external editor with the currently selected form field.
|
||||
|<<paste-primary,paste-primary>>|Paste the primary selection at cursor position.
|
||||
|<<prompt-accept,prompt-accept>>|Accept the current prompt.
|
||||
|<<prompt-no,prompt-no>>|Answer no to a yes/no prompt.
|
||||
|<<prompt-yes,prompt-yes>>|Answer yes to a yes/no prompt.
|
||||
@ -1046,6 +1047,10 @@ Open an external editor with the currently selected form field.
|
||||
|
||||
The editor which should be launched can be configured via the `general -> editor` config option.
|
||||
|
||||
[[paste-primary]]
|
||||
=== paste-primary
|
||||
Paste the primary selection at cursor position.
|
||||
|
||||
[[prompt-accept]]
|
||||
=== prompt-accept
|
||||
Accept the current prompt.
|
||||
|
@ -1307,6 +1307,31 @@ class CommandDispatcher:
|
||||
except webelem.IsNullError:
|
||||
raise cmdexc.CommandError("Element vanished while editing!")
|
||||
|
||||
@cmdutils.register(instance='command-dispatcher',
|
||||
modes=[KeyMode.insert], hide=True, scope='window',
|
||||
needs_js=True)
|
||||
def paste_primary(self):
|
||||
"""Paste the primary selection at cursor position."""
|
||||
frame = self._current_widget().page().currentFrame()
|
||||
try:
|
||||
elem = webelem.focus_elem(frame)
|
||||
except webelem.IsNullError:
|
||||
raise cmdexc.CommandError("No element focused!")
|
||||
if not elem.is_editable(strict=True):
|
||||
raise cmdexc.CommandError("Focused element is not editable!")
|
||||
|
||||
clipboard = QApplication.clipboard()
|
||||
if clipboard.supportsSelection():
|
||||
sel = clipboard.text(QClipboard.Selection)
|
||||
log.misc.debug("Pasting primary selection into element {}".format(
|
||||
elem.debug_text()))
|
||||
elem.evaluateJavaScript("""
|
||||
var sel = '{}';
|
||||
var event = document.createEvent('TextEvent');
|
||||
event.initTextEvent('textInput', true, true, null, sel);
|
||||
this.dispatchEvent(event);
|
||||
""".format(webelem.javascript_escape(sel)))
|
||||
|
||||
def _clear_search(self, view, text):
|
||||
"""Clear search string/highlights for the given view.
|
||||
|
||||
|
@ -774,7 +774,10 @@ class DownloadManager(QAbstractListModel):
|
||||
# https://bugreports.qt.io/browse/QTBUG-42757
|
||||
request.setAttribute(QNetworkRequest.CacheLoadControlAttribute,
|
||||
QNetworkRequest.AlwaysNetwork)
|
||||
|
||||
suggested_fn = urlutils.filename_from_url(request.url())
|
||||
if suggested_fn is None:
|
||||
suggested_fn = 'qutebrowser-download'
|
||||
|
||||
# We won't need a question if a filename or fileobj is already given
|
||||
if fileobj is None and filename is None:
|
||||
|
@ -372,6 +372,7 @@ class BrowserPage(QWebPage):
|
||||
q.answered_no.connect(no_action)
|
||||
q.cancelled.connect(no_action)
|
||||
|
||||
self.shutting_down.connect(q.abort)
|
||||
q.completed.connect(q.deleteLater)
|
||||
|
||||
self.featurePermissionRequestCanceled.connect(functools.partial(
|
||||
|
@ -1324,7 +1324,8 @@ KEY_SECTION_DESC = {
|
||||
"Since normal keypresses are passed through, only special keys are "
|
||||
"supported in this mode.\n"
|
||||
"Useful hidden commands to map in this section:\n\n"
|
||||
" * `open-editor`: Open a texteditor with the focused field."),
|
||||
" * `open-editor`: Open a texteditor with the focused field.\n"
|
||||
" * `paste-primary`: Paste primary selection at cursor position."),
|
||||
'hint': (
|
||||
"Keybindings for hint mode.\n"
|
||||
"Since normal keypresses are passed through, only special keys are "
|
||||
@ -1495,6 +1496,7 @@ KEY_DATA = collections.OrderedDict([
|
||||
|
||||
('insert', collections.OrderedDict([
|
||||
('open-editor', ['<Ctrl-E>']),
|
||||
('paste-primary', ['<Shift-Ins>']),
|
||||
])),
|
||||
|
||||
('hint', collections.OrderedDict([
|
||||
|
10
tests/integration/data/downloads/issue1243.html
Normal file
10
tests/integration/data/downloads/issue1243.html
Normal file
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Crash when trying to get filename from custom URL</title>
|
||||
</head>
|
||||
<body>
|
||||
<a href="qute://">download</a>
|
||||
</body>
|
||||
</html>
|
11
tests/integration/data/paste_primary.html
Normal file
11
tests/integration/data/paste_primary.html
Normal file
@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Paste primary selection</title>
|
||||
</head>
|
||||
<body>
|
||||
<textarea id="qute-textarea"></textarea>
|
||||
<textarea id="qute-textarea-noedit" readonly></textarea>
|
||||
</body>
|
||||
</html>
|
@ -24,6 +24,16 @@ Feature: Downloading things from a website.
|
||||
And I wait for the error "Download error: * - server replied: NOT FOUND"
|
||||
Then no crash should happen
|
||||
|
||||
Scenario: Downloading a link without path information (issue 1243)
|
||||
When I set completion -> download-path-suggestion to filename
|
||||
And I set storage -> prompt-download-directory to true
|
||||
And I open data/downloads/issue1243.html
|
||||
And I run :hint links download
|
||||
And I run :follow-hint a
|
||||
And I wait for "Asking question <qutebrowser.utils.usertypes.Question default='qutebrowser-download' mode=<PromptMode.text: 2> text='Save file to:'>, *" in the log
|
||||
And I run :leave-mode
|
||||
Then no crash should happen
|
||||
|
||||
Scenario: Retrying a failed download
|
||||
When I run :download http://localhost:(port)/does-not-exist
|
||||
And I wait for the error "Download error: * - server replied: NOT FOUND"
|
||||
|
@ -178,6 +178,15 @@ Feature: Prompts
|
||||
And I run :leave-mode
|
||||
Then the javascript message "notification permission aborted" should be logged
|
||||
|
||||
Scenario: answering notification after closing tab
|
||||
When I set content -> notifications to ask
|
||||
And I open data/prompt/notifications.html in a new tab
|
||||
And I click the button
|
||||
And I wait for a prompt
|
||||
And I run :tab-close
|
||||
And I wait for "Leaving mode KeyMode.yesno (reason: aborted)" in the log
|
||||
Then no crash should happen
|
||||
|
||||
# Page authentication
|
||||
|
||||
Scenario: Successful webpage authentification
|
||||
|
@ -38,3 +38,16 @@ def skip_with_broken_clipboard(qtbot, qapp):
|
||||
|
||||
if clipboard.text() != "Does this work?":
|
||||
pytest.skip("Clipboard seems to be broken on this platform.")
|
||||
|
||||
|
||||
@bdd.when(bdd.parsers.parse('I set the text field to "{value}"'))
|
||||
def set_text_field(quteproc, value):
|
||||
quteproc.send_cmd(":jseval document.getElementById('qute-textarea').value "
|
||||
"= '{}';".format(value))
|
||||
|
||||
|
||||
@bdd.then(bdd.parsers.parse('the text field should contain "{value}"'))
|
||||
def check_text_field(quteproc, value):
|
||||
quteproc.send_cmd(":jseval console.log('text: ' + "
|
||||
"document.getElementById('qute-textarea').value);")
|
||||
quteproc.wait_for_js('text: ' + value)
|
||||
|
@ -170,3 +170,67 @@ Feature: Yanking and pasting.
|
||||
history:
|
||||
- active: true
|
||||
url: http://localhost:*/data/hello3.txt
|
||||
|
||||
#### :paste-primary
|
||||
|
||||
Scenario: Pasting the primary selection into an empty text field
|
||||
When selection is supported
|
||||
And I open data/paste_primary.html
|
||||
And I put "Hello world" into the primary selection
|
||||
# Click the text field
|
||||
And I run :hint all
|
||||
And I run :follow-hint a
|
||||
And I run :paste-primary
|
||||
# Compare
|
||||
Then the text field should contain "Hello world"
|
||||
|
||||
Scenario: Pasting the primary selection into a text field at specific position
|
||||
When selection is supported
|
||||
And I open data/paste_primary.html
|
||||
And I set the text field to "one two three four"
|
||||
And I put " Hello world" into the primary selection
|
||||
# Click the text field
|
||||
And I run :hint all
|
||||
And I run :follow-hint a
|
||||
# Move to the beginning and two words to the right
|
||||
And I press the keys "<Home>"
|
||||
And I press the key "<Ctrl+Right>"
|
||||
And I press the key "<Ctrl+Right>"
|
||||
And I run :paste-primary
|
||||
# Compare
|
||||
Then the text field should contain "one two Hello world three four"
|
||||
|
||||
Scenario: Pasting the primary selection into a text field with undo
|
||||
When selection is supported
|
||||
And I open data/paste_primary.html
|
||||
# Click the text field
|
||||
And I run :hint all
|
||||
And I run :follow-hint a
|
||||
# Paste and undo
|
||||
And I put "This text should be undone" into the primary selection
|
||||
And I run :paste-primary
|
||||
And I press the key "<Ctrl+z>"
|
||||
# Paste final text
|
||||
And I put "This text should stay" into the primary selection
|
||||
And I run :paste-primary
|
||||
# Compare
|
||||
Then the text field should contain "This text should stay"
|
||||
|
||||
Scenario: Pasting the primary selection without a focused field
|
||||
When selection is supported
|
||||
And I open data/paste_primary.html
|
||||
And I put "test" into the primary selection
|
||||
And I run :enter-mode insert
|
||||
And I run :paste-primary
|
||||
Then the error "No element focused!" should be shown
|
||||
|
||||
Scenario: Pasting the primary selection with a read-only field
|
||||
When selection is supported
|
||||
And I open data/paste_primary.html
|
||||
# Click the text field
|
||||
And I run :hint all
|
||||
And I run :follow-hint s
|
||||
And I put "test" into the primary selection
|
||||
And I run :enter-mode insert
|
||||
And I run :paste-primary
|
||||
Then the error "Focused element is not editable!" should be shown
|
||||
|
@ -599,7 +599,7 @@ class TestJavascriptEscape:
|
||||
# http://qutebrowser.org:8010/builders/debian-jessie/builds/765/steps/unittests/
|
||||
# Should that be ignored?
|
||||
|
||||
@pytest.mark.parametrize('before, after', TESTS.items(), ids=repr)
|
||||
@pytest.mark.parametrize('before, after', sorted(TESTS.items()), ids=repr)
|
||||
def test_fake_escape(self, before, after):
|
||||
"""Test javascript escaping with some expected outcomes."""
|
||||
assert webelem.javascript_escape(before) == after
|
||||
@ -645,7 +645,7 @@ class TestJavascriptEscape:
|
||||
result = webframe.evaluateJavaScript('"{}";'.format(escaped))
|
||||
assert result == text
|
||||
|
||||
@pytest.mark.parametrize('text', TESTS, ids=repr)
|
||||
@pytest.mark.parametrize('text', sorted(TESTS), ids=repr)
|
||||
def test_real_escape(self, webframe, qtbot, text):
|
||||
"""Test javascript escaping with a real QWebPage."""
|
||||
self._test_escape(text, qtbot, webframe)
|
||||
|
@ -189,7 +189,7 @@ class TestConfigParser:
|
||||
assert new_sect in configdata.DATA
|
||||
|
||||
@pytest.mark.parametrize('old_tuple, new_option',
|
||||
config.ConfigManager.RENAMED_OPTIONS.items())
|
||||
sorted(config.ConfigManager.RENAMED_OPTIONS.items()))
|
||||
def test_renamed_options(self, old_tuple, new_option):
|
||||
"""Make sure renamed options exist under the new name."""
|
||||
section, old_option = old_tuple
|
||||
|
@ -254,7 +254,7 @@ class TestMappingType:
|
||||
def klass(self):
|
||||
return MappingSubclass
|
||||
|
||||
@pytest.mark.parametrize('val', TESTS.keys())
|
||||
@pytest.mark.parametrize('val', sorted(TESTS.keys()))
|
||||
def test_validate_valid(self, klass, val):
|
||||
klass(none_ok=True).validate(val)
|
||||
|
||||
@ -263,7 +263,7 @@ class TestMappingType:
|
||||
with pytest.raises(configexc.ValidationError):
|
||||
klass().validate(val)
|
||||
|
||||
@pytest.mark.parametrize('val, expected', TESTS.items())
|
||||
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
|
||||
def test_transform(self, klass, val, expected):
|
||||
assert klass().transform(val) == expected
|
||||
|
||||
@ -488,11 +488,11 @@ class TestBool:
|
||||
def klass(self):
|
||||
return configtypes.Bool
|
||||
|
||||
@pytest.mark.parametrize('val, expected', TESTS.items())
|
||||
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
|
||||
def test_transform(self, klass, val, expected):
|
||||
assert klass().transform(val) == expected
|
||||
|
||||
@pytest.mark.parametrize('val', TESTS)
|
||||
@pytest.mark.parametrize('val', sorted(TESTS))
|
||||
def test_validate_valid(self, klass, val):
|
||||
klass(none_ok=True).validate(val)
|
||||
|
||||
@ -518,11 +518,11 @@ class TestBoolAsk:
|
||||
def klass(self):
|
||||
return configtypes.BoolAsk
|
||||
|
||||
@pytest.mark.parametrize('val, expected', TESTS.items())
|
||||
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
|
||||
def test_transform(self, klass, val, expected):
|
||||
assert klass().transform(val) == expected
|
||||
|
||||
@pytest.mark.parametrize('val', TESTS)
|
||||
@pytest.mark.parametrize('val', sorted(TESTS))
|
||||
def test_validate_valid(self, klass, val):
|
||||
klass(none_ok=True).validate(val)
|
||||
|
||||
@ -871,7 +871,7 @@ class TestColorSystem:
|
||||
def klass(self):
|
||||
return configtypes.ColorSystem
|
||||
|
||||
@pytest.mark.parametrize('val', TESTS)
|
||||
@pytest.mark.parametrize('val', sorted(TESTS))
|
||||
def test_validate_valid(self, klass, val):
|
||||
klass(none_ok=True).validate(val)
|
||||
|
||||
@ -880,7 +880,7 @@ class TestColorSystem:
|
||||
with pytest.raises(configexc.ValidationError):
|
||||
klass().validate(val)
|
||||
|
||||
@pytest.mark.parametrize('val, expected', TESTS.items())
|
||||
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
|
||||
def test_transform(self, klass, val, expected):
|
||||
assert klass().transform(val) == expected
|
||||
|
||||
@ -1048,7 +1048,7 @@ class TestFont:
|
||||
def qtfont_class(self):
|
||||
return configtypes.QtFont
|
||||
|
||||
@pytest.mark.parametrize('val', list(TESTS) + [''])
|
||||
@pytest.mark.parametrize('val', sorted(list(TESTS)) + [''])
|
||||
def test_validate_valid(self, klass, val):
|
||||
klass(none_ok=True).validate(val)
|
||||
|
||||
@ -1077,11 +1077,11 @@ class TestFont:
|
||||
with pytest.raises(configexc.ValidationError):
|
||||
klass().validate('')
|
||||
|
||||
@pytest.mark.parametrize('string', TESTS)
|
||||
@pytest.mark.parametrize('string', sorted(TESTS))
|
||||
def test_transform_font(self, font_class, string):
|
||||
assert font_class().transform(string) == string
|
||||
|
||||
@pytest.mark.parametrize('string, desc', TESTS.items())
|
||||
@pytest.mark.parametrize('string, desc', sorted(TESTS.items()))
|
||||
def test_transform_qtfont(self, qtfont_class, string, desc):
|
||||
assert Font(qtfont_class().transform(string)) == Font.fromdesc(desc)
|
||||
|
||||
@ -1828,7 +1828,7 @@ class TestAutoSearch:
|
||||
def klass(self):
|
||||
return configtypes.AutoSearch
|
||||
|
||||
@pytest.mark.parametrize('val', TESTS)
|
||||
@pytest.mark.parametrize('val', sorted(TESTS))
|
||||
def test_validate_valid(self, klass, val):
|
||||
klass(none_ok=True).validate(val)
|
||||
|
||||
@ -1837,7 +1837,7 @@ class TestAutoSearch:
|
||||
with pytest.raises(configexc.ValidationError):
|
||||
klass().validate(val)
|
||||
|
||||
@pytest.mark.parametrize('val, expected', TESTS.items())
|
||||
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
|
||||
def test_transform(self, klass, val, expected):
|
||||
assert klass().transform(val) == expected
|
||||
|
||||
@ -1858,7 +1858,7 @@ class TestIgnoreCase:
|
||||
def klass(self):
|
||||
return configtypes.IgnoreCase
|
||||
|
||||
@pytest.mark.parametrize('val', TESTS)
|
||||
@pytest.mark.parametrize('val', sorted(TESTS))
|
||||
def test_validate_valid(self, klass, val):
|
||||
klass(none_ok=True).validate(val)
|
||||
|
||||
@ -1867,7 +1867,7 @@ class TestIgnoreCase:
|
||||
with pytest.raises(configexc.ValidationError):
|
||||
klass().validate(val)
|
||||
|
||||
@pytest.mark.parametrize('val, expected', TESTS.items())
|
||||
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
|
||||
def test_transform(self, klass, val, expected):
|
||||
assert klass().transform(val) == expected
|
||||
|
||||
@ -1909,7 +1909,7 @@ class TestUrlList:
|
||||
def klass(self):
|
||||
return configtypes.UrlList
|
||||
|
||||
@pytest.mark.parametrize('val', TESTS)
|
||||
@pytest.mark.parametrize('val', sorted(TESTS))
|
||||
def test_validate_valid(self, klass, val):
|
||||
klass(none_ok=True).validate(val)
|
||||
|
||||
@ -1927,7 +1927,7 @@ class TestUrlList:
|
||||
with pytest.raises(configexc.ValidationError):
|
||||
klass().validate('foo,,bar')
|
||||
|
||||
@pytest.mark.parametrize('val, expected', TESTS.items())
|
||||
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
|
||||
def test_transform_single(self, klass, val, expected):
|
||||
assert klass().transform(val) == expected
|
||||
|
||||
@ -1967,7 +1967,7 @@ class TestConfirmQuit:
|
||||
def klass(self):
|
||||
return configtypes.ConfirmQuit
|
||||
|
||||
@pytest.mark.parametrize('val', TESTS.keys())
|
||||
@pytest.mark.parametrize('val', sorted(TESTS.keys()))
|
||||
def test_validate_valid(self, klass, val):
|
||||
klass(none_ok=True).validate(val)
|
||||
|
||||
@ -1984,7 +1984,7 @@ class TestConfirmQuit:
|
||||
with pytest.raises(configexc.ValidationError):
|
||||
klass().validate(val)
|
||||
|
||||
@pytest.mark.parametrize('val, expected', TESTS.items())
|
||||
@pytest.mark.parametrize('val, expected', sorted(TESTS.items()))
|
||||
def test_transform(self, klass, val, expected):
|
||||
assert klass().transform(val) == expected
|
||||
|
||||
|
@ -94,7 +94,7 @@ class TestParseFatalStacktrace:
|
||||
"QT_IM_MODULE = fcitx"
|
||||
),
|
||||
({'LANGUAGE': 'foo', 'LANG': 'en_US.UTF-8'}, "LANG = en_US.UTF-8"),
|
||||
], ids=repr)
|
||||
], ids=lambda e: e[1])
|
||||
def test_get_environment_vars(monkeypatch, env, expected):
|
||||
"""Test for crashdialog._get_environment_vars."""
|
||||
for key in os.environ.copy():
|
||||
|
@ -126,7 +126,8 @@ class TestSplit:
|
||||
|
||||
"""Test split."""
|
||||
|
||||
@pytest.fixture(params=_parse_split_test_data_str(), ids=lambda e: e.input)
|
||||
@pytest.fixture(params=list(_parse_split_test_data_str()),
|
||||
ids=lambda e: e.input)
|
||||
def split_test_case(self, request):
|
||||
"""Fixture to automatically parametrize all depending tests.
|
||||
|
||||
@ -163,7 +164,7 @@ class TestSimpleSplit:
|
||||
'foo\nbar': ['foo', '\nbar'],
|
||||
}
|
||||
|
||||
@pytest.mark.parametrize('test', TESTS, ids=repr)
|
||||
@pytest.mark.parametrize('test', sorted(TESTS), ids=repr)
|
||||
def test_str_split(self, test):
|
||||
"""Test if the behavior matches str.split."""
|
||||
assert split.simple_split(test) == test.rstrip().split()
|
||||
@ -177,7 +178,7 @@ class TestSimpleSplit:
|
||||
expected = s.rstrip().split(maxsplit=maxsplit)
|
||||
assert actual == expected
|
||||
|
||||
@pytest.mark.parametrize('test, expected', TESTS.items(), ids=repr)
|
||||
@pytest.mark.parametrize('test, expected', sorted(TESTS.items()), ids=repr)
|
||||
def test_split_keep(self, test, expected):
|
||||
"""Test splitting with keep=True."""
|
||||
assert split.simple_split(test, keep=True) == expected
|
||||
|
@ -49,12 +49,20 @@ BAD_VALUES = {
|
||||
}
|
||||
|
||||
|
||||
def good_values():
|
||||
return list(iter_good_values())
|
||||
|
||||
|
||||
def bad_values():
|
||||
return list(iter_bad_values())
|
||||
|
||||
|
||||
def iter_good_values():
|
||||
"""Yield "good" (C data type, value) tuples.
|
||||
|
||||
Those should pass overflow checking.
|
||||
"""
|
||||
for ctype, values in GOOD_VALUES.items():
|
||||
for ctype, values in sorted(GOOD_VALUES.items()):
|
||||
for value in values:
|
||||
yield ctype, value
|
||||
|
||||
@ -65,6 +73,6 @@ def iter_bad_values():
|
||||
These should not pass overflow checking. The third value is the value they
|
||||
should be replaced with if overflow checking should not be fatal.
|
||||
"""
|
||||
for ctype, values in BAD_VALUES.items():
|
||||
for ctype, values in sorted(BAD_VALUES.items()):
|
||||
for value, repl in values:
|
||||
yield ctype, value, repl
|
||||
|
@ -69,21 +69,21 @@ class TestCheckOverflow:
|
||||
"""Test check_overflow."""
|
||||
|
||||
@pytest.mark.parametrize('ctype, val',
|
||||
overflow_test_cases.iter_good_values())
|
||||
overflow_test_cases.good_values())
|
||||
def test_good_values(self, ctype, val):
|
||||
"""Test values which are inside bounds."""
|
||||
qtutils.check_overflow(val, ctype)
|
||||
|
||||
@pytest.mark.parametrize('ctype, val',
|
||||
[(ctype, val) for (ctype, val, _) in
|
||||
overflow_test_cases.iter_bad_values()])
|
||||
overflow_test_cases.bad_values()])
|
||||
def test_bad_values_fatal(self, ctype, val):
|
||||
"""Test values which are outside bounds with fatal=True."""
|
||||
with pytest.raises(OverflowError):
|
||||
qtutils.check_overflow(val, ctype)
|
||||
|
||||
@pytest.mark.parametrize('ctype, val, repl',
|
||||
overflow_test_cases.iter_bad_values())
|
||||
overflow_test_cases.bad_values())
|
||||
def test_bad_values_nonfatal(self, ctype, val, repl):
|
||||
"""Test values which are outside bounds with fatal=False."""
|
||||
newval = qtutils.check_overflow(val, ctype, fatal=False)
|
||||
|
@ -805,7 +805,8 @@ QUALNAME_OBJ = QualnameObj()
|
||||
(qutebrowser, 'qutebrowser'), # module
|
||||
(qutebrowser.utils, 'qutebrowser.utils'), # submodule
|
||||
(utils, 'qutebrowser.utils.utils'), # submodule (from-import)
|
||||
])
|
||||
], ids=['instance', 'class', 'unbound-method', 'bound-method', 'function',
|
||||
'partial', 'module', 'submodule', 'from-import'])
|
||||
def test_qualname(obj, expected):
|
||||
assert utils.qualname(obj) == expected
|
||||
|
||||
|
33
tox.ini
33
tox.ini
@ -4,7 +4,7 @@
|
||||
# and then run "tox" from this directory.
|
||||
|
||||
[tox]
|
||||
envlist = py34,py35-cov,misc,vulture,pep257,flake8,pylint,pyroma,check-manifest
|
||||
envlist = py34,py35-cov,misc,vulture,pydocstyle,flake8,pylint,pyroma,check-manifest
|
||||
|
||||
[testenv]
|
||||
# https://bitbucket.org/hpk42/tox/issue/246/ - only needed for Windows though
|
||||
@ -19,7 +19,7 @@ deps =
|
||||
decorator==4.0.6
|
||||
Flask==0.10.1
|
||||
glob2==0.4.1
|
||||
httpbin==0.4.0
|
||||
httpbin==0.4.1
|
||||
hypothesis==2.0.0
|
||||
itsdangerous==0.24
|
||||
Mako==1.0.3
|
||||
@ -29,10 +29,10 @@ deps =
|
||||
pytest==2.8.7
|
||||
pytest-bdd==2.16.0
|
||||
pytest-catchlog==1.2.2
|
||||
pytest-cov==2.2.0
|
||||
pytest-cov==2.2.1
|
||||
pytest-faulthandler==1.3.0
|
||||
pytest-html==1.7
|
||||
pytest-mock==0.9.0
|
||||
pytest-mock==0.10.1
|
||||
pytest-qt==1.11.0
|
||||
pytest-instafail==0.3.0
|
||||
pytest-travis-fold==1.2.0
|
||||
@ -42,7 +42,7 @@ deps =
|
||||
vulture==0.8.1
|
||||
Werkzeug==0.11.3
|
||||
wheel==0.26.0
|
||||
xvfbwrapper==0.2.7
|
||||
xvfbwrapper==0.2.8
|
||||
cherrypy==4.0.0
|
||||
commands =
|
||||
{envpython} scripts/link_pyqt.py --tox {envdir}
|
||||
@ -150,34 +150,25 @@ commands =
|
||||
{envpython} -m pylint scripts qutebrowser --output-format=colorized --reports=no
|
||||
{envpython} scripts/dev/run_pylint_on_tests.py --output-format=colorized --reports=no
|
||||
|
||||
[testenv:pep257]
|
||||
[testenv:pydocstyle]
|
||||
basepython = python3
|
||||
skip_install = true
|
||||
passenv = PYTHON LANG
|
||||
deps = pep257==0.7.0
|
||||
# Disabled checks:
|
||||
# D102: Missing docstring in public method (will be handled by others)
|
||||
# D103: Missing docstring in public function (will be handled by others)
|
||||
# D104: Missing docstring in public package (will be handled by others)
|
||||
# D105: Missing docstring in magic method (will be handled by others)
|
||||
# D209: Blank line before closing """ (removed from PEP257)
|
||||
# D211: Now b lank lines allowed before class docstring
|
||||
# (PEP257 got changed, but let's stick to the old standard)
|
||||
# D402: First line should not be function's signature (false-positives)
|
||||
commands = {envpython} -m pep257 scripts tests qutebrowser --ignore=D102,D103,D104,D105,D209,D211,D402 '--match=(?!resources|test_*).*\.py'
|
||||
deps = pydocstyle==1.0.0
|
||||
commands = {envpython} -m pydocstyle scripts tests qutebrowser
|
||||
|
||||
[testenv:flake8]
|
||||
basepython = python3
|
||||
passenv =
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
flake8==2.5.1
|
||||
flake8==2.5.2
|
||||
flake8-debugger==1.4.0
|
||||
pep8-naming==0.3.3
|
||||
flake8-putty==0.2.0
|
||||
ebb-lint==0.4.3
|
||||
ebb-lint==0.4.4
|
||||
flake8-copyright==0.1
|
||||
mccabe==0.3.1
|
||||
mccabe==0.4.0
|
||||
pep8==1.7.0
|
||||
pyflakes==1.0.0
|
||||
flake8-string-format==0.2.1
|
||||
@ -202,7 +193,7 @@ basepython = python3
|
||||
skip_install = true
|
||||
passenv =
|
||||
deps =
|
||||
check-manifest==0.30
|
||||
check-manifest==0.31
|
||||
commands =
|
||||
{envdir}/bin/check-manifest --ignore 'qutebrowser/git-commit-id,qutebrowser/html/doc,qutebrowser/html/doc/*,*/__pycache__'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user