Merge master into features
Conflicts: src/_pytest/_code/code.py src/_pytest/main.py
This commit is contained in:
commit
09bdbffbde
|
@ -45,6 +45,8 @@ jobs:
|
|||
"macos-py38",
|
||||
|
||||
"linting",
|
||||
"docs",
|
||||
"doctesting",
|
||||
]
|
||||
|
||||
include:
|
||||
|
@ -114,7 +116,17 @@ jobs:
|
|||
- name: "linting"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "linting,docs,doctesting"
|
||||
tox_env: "linting"
|
||||
skip_coverage: true
|
||||
- name: "docs"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "docs"
|
||||
skip_coverage: true
|
||||
- name: "doctesting"
|
||||
python: "3.7"
|
||||
os: ubuntu-latest
|
||||
tox_env: "doctesting"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
@ -144,21 +156,11 @@ jobs:
|
|||
run: |
|
||||
python scripts/append_codecov_token.py
|
||||
|
||||
- name: Combine coverage
|
||||
- name: Report coverage
|
||||
if: (!matrix.skip_coverage)
|
||||
run: |
|
||||
python -m coverage combine
|
||||
python -m coverage xml
|
||||
|
||||
- name: Codecov upload
|
||||
if: (!matrix.skip_coverage)
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
token: ${{ secrets.codecov }}
|
||||
file: ./coverage.xml
|
||||
flags: ${{ runner.os }}
|
||||
fail_ci_if_error: false
|
||||
name: ${{ matrix.name }}
|
||||
env:
|
||||
CODECOV_NAME: ${{ matrix.name }}
|
||||
run: bash scripts/report-coverage.sh -F GHA,${{ runner.os }}
|
||||
|
||||
deploy:
|
||||
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && github.repository == 'pytest-dev/pytest'
|
||||
|
|
|
@ -64,7 +64,7 @@ repos:
|
|||
_code\.|
|
||||
builtin\.|
|
||||
code\.|
|
||||
io\.(BytesIO|saferepr)|
|
||||
io\.(BytesIO|saferepr|TerminalWriter)|
|
||||
path\.local\.sysfind|
|
||||
process\.|
|
||||
std\.
|
||||
|
|
|
@ -47,11 +47,6 @@ jobs:
|
|||
python: '3.5.1'
|
||||
dist: trusty
|
||||
|
||||
- env: TOXENV=linting,docs,doctesting PYTEST_COVERAGE=1
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/pre-commit
|
||||
|
||||
before_script:
|
||||
- |
|
||||
# Do not (re-)upload coverage with cron runs.
|
||||
|
@ -71,7 +66,7 @@ script: tox
|
|||
after_success:
|
||||
- |
|
||||
if [[ "$PYTEST_COVERAGE" = 1 ]]; then
|
||||
env CODECOV_NAME="$TOXENV-$TRAVIS_OS_NAME" scripts/report-coverage.sh
|
||||
env CODECOV_NAME="$TOXENV-$TRAVIS_OS_NAME" scripts/report-coverage.sh -F Travis
|
||||
fi
|
||||
|
||||
notifications:
|
||||
|
|
|
@ -166,7 +166,7 @@ Short version
|
|||
|
||||
#. Fork the repository.
|
||||
#. Enable and install `pre-commit <https://pre-commit.com>`_ to ensure style-guides and code checks are followed.
|
||||
#. Target ``master`` for bugfixes and doc changes.
|
||||
#. Target ``master`` for bug fixes and doc changes.
|
||||
#. Target ``features`` for new features or functionality changes.
|
||||
#. Follow **PEP-8** for naming and `black <https://github.com/psf/black>`_ for formatting.
|
||||
#. Tests are run using ``tox``::
|
||||
|
@ -212,7 +212,7 @@ Here is a simple overview, with pytest-specific bits:
|
|||
|
||||
$ git checkout -b your-feature-branch-name features
|
||||
|
||||
Given we have "major.minor.micro" version numbers, bugfixes will usually
|
||||
Given we have "major.minor.micro" version numbers, bug fixes will usually
|
||||
be released in micro releases whereas features will be released in
|
||||
minor releases and incompatible changes in major releases.
|
||||
|
||||
|
@ -294,7 +294,7 @@ Here is a simple overview, with pytest-specific bits:
|
|||
compare: your-branch-name
|
||||
|
||||
base-fork: pytest-dev/pytest
|
||||
base: master # if it's a bugfix
|
||||
base: master # if it's a bug fix
|
||||
base: features # if it's a feature
|
||||
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
Release Procedure
|
||||
-----------------
|
||||
|
||||
Our current policy for releasing is to aim for a bugfix every few weeks and a minor release every 2-3 months. The idea
|
||||
Our current policy for releasing is to aim for a bug-fix release every few weeks and a minor release every 2-3 months. The idea
|
||||
is to get fixes and new features out instead of trying to cram a ton of features into a release and by consequence
|
||||
taking a lot of time to make a new one.
|
||||
|
||||
.. important::
|
||||
|
||||
pytest releases must be prepared on **Linux** because the docs and examples expect
|
||||
to be executed in that platform.
|
||||
to be executed on that platform.
|
||||
|
||||
#. Create a branch ``release-X.Y.Z`` with the version for the release.
|
||||
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
trigger:
|
||||
- master
|
||||
- features
|
||||
|
||||
variables:
|
||||
PYTEST_ADDOPTS: "--junitxml=build/test-results/$(tox.env).xml -vv"
|
||||
PYTEST_COVERAGE: '0'
|
||||
|
||||
jobs:
|
||||
|
||||
- job: 'Test'
|
||||
pool:
|
||||
vmImage: "vs2017-win2016"
|
||||
strategy:
|
||||
matrix:
|
||||
# -- pypy3 disabled for now: #5279 --
|
||||
# pypy3:
|
||||
# python.version: 'pypy3'
|
||||
# tox.env: 'pypy3'
|
||||
py35-xdist:
|
||||
python.version: '3.5'
|
||||
tox.env: 'py35-xdist'
|
||||
# Coverage for:
|
||||
# - test_supports_breakpoint_module_global
|
||||
PYTEST_COVERAGE: '1'
|
||||
py36-xdist:
|
||||
python.version: '3.6'
|
||||
tox.env: 'py36-xdist'
|
||||
py37:
|
||||
python.version: '3.7'
|
||||
tox.env: 'py37-twisted-numpy'
|
||||
# Coverage for:
|
||||
# - _py36_windowsconsoleio_workaround (with py36+)
|
||||
# - test_request_garbage (no xdist)
|
||||
PYTEST_COVERAGE: '1'
|
||||
py37-linting/docs/doctesting:
|
||||
python.version: '3.7'
|
||||
tox.env: 'linting,docs,doctesting'
|
||||
py37-pluggymaster-xdist:
|
||||
python.version: '3.7'
|
||||
tox.env: 'py37-pluggymaster-xdist'
|
||||
py38-xdist:
|
||||
python.version: '3.8'
|
||||
tox.env: 'py38-xdist'
|
||||
maxParallel: 10
|
||||
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: '$(python.version)'
|
||||
architecture: 'x64'
|
||||
|
||||
- script: python -m pip install --upgrade pip && python -m pip install tox
|
||||
displayName: 'Install tox'
|
||||
|
||||
- bash: |
|
||||
if [[ "$PYTEST_COVERAGE" == "1" ]]; then
|
||||
export _PYTEST_TOX_COVERAGE_RUN="coverage run -m"
|
||||
export _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess
|
||||
export COVERAGE_FILE="$PWD/.coverage"
|
||||
export COVERAGE_PROCESS_START="$PWD/.coveragerc"
|
||||
fi
|
||||
python -m tox -e $(tox.env)
|
||||
displayName: 'Run tests'
|
||||
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
testResultsFiles: 'build/test-results/$(tox.env).xml'
|
||||
testRunTitle: '$(tox.env)'
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- bash: |
|
||||
if [[ "$PYTEST_COVERAGE" == 1 ]]; then
|
||||
scripts/report-coverage.sh
|
||||
fi
|
||||
env:
|
||||
CODECOV_NAME: $(tox.env)
|
||||
CODECOV_TOKEN: $(CODECOV_TOKEN)
|
||||
displayName: Report and upload coverage
|
||||
condition: eq(variables['PYTEST_COVERAGE'], '1')
|
|
@ -15,7 +15,7 @@ Each file should be named like ``<ISSUE>.<TYPE>.rst``, where
|
|||
|
||||
* ``feature``: new user facing features, like new command-line options and new behavior.
|
||||
* ``improvement``: improvement of existing functionality, usually without requiring user intervention (for example, new fields being written in ``--junitxml``, improved colors in terminal, etc).
|
||||
* ``bugfix``: fixes a reported bug.
|
||||
* ``bugfix``: fixes a bug.
|
||||
* ``doc``: documentation improvement, like rewording an entire session or adding missing docs.
|
||||
* ``deprecation``: feature deprecation.
|
||||
* ``breaking``: a change which may break existing suites, such as feature removal or behavior change.
|
||||
|
|
|
@ -2357,7 +2357,7 @@ Deprecations and Removals
|
|||
- `#4036 <https://github.com/pytest-dev/pytest/issues/4036>`_: The ``item`` parameter of ``pytest_warning_captured`` hook is now documented as deprecated. We realized only after
|
||||
the ``3.8`` release that this parameter is incompatible with ``pytest-xdist``.
|
||||
|
||||
Our policy is to not deprecate features during bugfix releases, but in this case we believe it makes sense as we are
|
||||
Our policy is to not deprecate features during bug-fix releases, but in this case we believe it makes sense as we are
|
||||
only documenting it as deprecated, without issuing warnings which might potentially break test suites. This will get
|
||||
the word out that hook implementers should not use this parameter at all.
|
||||
|
||||
|
@ -5380,7 +5380,7 @@ time or change existing behaviors in order to make them less surprising/more use
|
|||
Thanks Daniel Grunwald for the report and Bruno Oliveira for the PR.
|
||||
|
||||
- (experimental) adapt more SEMVER style versioning and change meaning of
|
||||
master branch in git repo: "master" branch now keeps the bugfixes, changes
|
||||
master branch in git repo: "master" branch now keeps the bug fixes, changes
|
||||
aimed for micro releases. "features" branch will only be released
|
||||
with minor or major pytest releases.
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ Branches
|
|||
|
||||
We have two long term branches:
|
||||
|
||||
* ``master``: contains the code for the next bugfix release.
|
||||
* ``master``: contains the code for the next bug-fix release.
|
||||
* ``features``: contains the code with new features for the next minor release.
|
||||
|
||||
The official repository usually does not contain topic branches, developers and contributors should create topic
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def setup(request):
|
||||
setup = CostlySetup()
|
||||
yield setup
|
||||
setup.finalize()
|
||||
|
||||
|
||||
class CostlySetup:
|
||||
def __init__(self):
|
||||
import time
|
||||
|
||||
print("performing costly setup")
|
||||
time.sleep(5)
|
||||
self.timecostly = 1
|
||||
|
||||
def finalize(self):
|
||||
del self.timecostly
|
|
@ -1 +0,0 @@
|
|||
#
|
|
@ -1,2 +0,0 @@
|
|||
def test_quick(setup):
|
||||
pass
|
|
@ -1 +0,0 @@
|
|||
#
|
|
@ -1,6 +0,0 @@
|
|||
def test_something(setup):
|
||||
assert setup.timecostly == 1
|
||||
|
||||
|
||||
def test_something_more(setup):
|
||||
assert setup.timecostly == 1
|
|
@ -1042,11 +1042,13 @@ file:
|
|||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
@pytest.fixture
|
||||
def cleandir():
|
||||
old_cwd = os.getcwd()
|
||||
newpath = tempfile.mkdtemp()
|
||||
os.chdir(newpath)
|
||||
yield
|
||||
os.chdir(old_cwd)
|
||||
shutil.rmtree(newpath)
|
||||
|
||||
and declare its use in a test module via a ``usefixtures`` marker:
|
||||
|
|
|
@ -6,7 +6,7 @@ The pytest team is proud to announce the {version} release!
|
|||
pytest is a mature Python testing tool with more than a 2000 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
|
||||
This release contains a number of bugs fixes and improvements, so users are encouraged
|
||||
This release contains a number of bug fixes and improvements, so users are encouraged
|
||||
to take a look at the CHANGELOG:
|
||||
|
||||
https://docs.pytest.org/en/latest/changelog.html
|
||||
|
@ -15,7 +15,7 @@ For complete documentation, please visit:
|
|||
|
||||
https://docs.pytest.org/en/latest/
|
||||
|
||||
As usual, you can upgrade from pypi via:
|
||||
As usual, you can upgrade from PyPI via:
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
|
@ -24,4 +24,4 @@ Thanks to all who contributed to this release, among them:
|
|||
{contributors}
|
||||
|
||||
Happy testing,
|
||||
The Pytest Development Team
|
||||
The pytest Development Team
|
||||
|
|
|
@ -15,4 +15,4 @@ python -m coverage xml
|
|||
python -m coverage report -m
|
||||
# Set --connect-timeout to work around https://github.com/curl/curl/issues/4461
|
||||
curl -S -L --connect-timeout 5 --retry 6 -s https://codecov.io/bash -o codecov-upload.sh
|
||||
bash codecov-upload.sh -Z -X fix -f coverage.xml
|
||||
bash codecov-upload.sh -Z -X fix -f coverage.xml "$@"
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
@echo off
|
||||
rem Source: https://github.com/appveyor/ci/blob/master/scripts/appveyor-retry.cmd
|
||||
rem initiate the retry number
|
||||
set retryNumber=0
|
||||
set maxRetries=3
|
||||
|
||||
:RUN
|
||||
%*
|
||||
set LastErrorLevel=%ERRORLEVEL%
|
||||
IF %LastErrorLevel% == 0 GOTO :EOF
|
||||
set /a retryNumber=%retryNumber%+1
|
||||
IF %reTryNumber% == %maxRetries% (GOTO :FAILED)
|
||||
|
||||
:RETRY
|
||||
set /a retryNumberDisp=%retryNumber%+1
|
||||
@echo Command "%*" failed with exit code %LastErrorLevel%. Retrying %retryNumberDisp% of %maxRetries%
|
||||
GOTO :RUN
|
||||
|
||||
: FAILED
|
||||
@echo Sorry, we tried running command for %maxRetries% times and all attempts were unsuccessful!
|
||||
EXIT /B %LastErrorLevel%
|
|
@ -29,6 +29,7 @@ import pluggy
|
|||
import py
|
||||
|
||||
import _pytest
|
||||
from _pytest._io import TerminalWriter
|
||||
from _pytest._io.saferepr import safeformat
|
||||
from _pytest._io.saferepr import saferepr
|
||||
from _pytest.compat import overload
|
||||
|
@ -913,14 +914,14 @@ class TerminalRepr:
|
|||
# FYI this is called from pytest-xdist's serialization of exception
|
||||
# information.
|
||||
io = StringIO()
|
||||
tw = py.io.TerminalWriter(file=io)
|
||||
tw = TerminalWriter(file=io)
|
||||
self.toterminal(tw)
|
||||
return io.getvalue().strip()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<{} instance at {:0x}>".format(self.__class__, id(self))
|
||||
|
||||
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
|
@ -931,7 +932,7 @@ class ExceptionRepr(TerminalRepr):
|
|||
def addsection(self, name: str, content: str, sep: str = "-") -> None:
|
||||
self.sections.append((name, content, sep))
|
||||
|
||||
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
for name, content, sep in self.sections:
|
||||
tw.sep(sep, name)
|
||||
tw.line(content)
|
||||
|
@ -951,7 +952,7 @@ class ExceptionChainRepr(ExceptionRepr):
|
|||
self.reprtraceback = chain[-1][0]
|
||||
self.reprcrash = chain[-1][1]
|
||||
|
||||
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
for element in self.chain:
|
||||
element[0].toterminal(tw)
|
||||
if element[2] is not None:
|
||||
|
@ -968,7 +969,7 @@ class ReprExceptionInfo(ExceptionRepr):
|
|||
self.reprtraceback = reprtraceback
|
||||
self.reprcrash = reprcrash
|
||||
|
||||
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
self.reprtraceback.toterminal(tw)
|
||||
super().toterminal(tw)
|
||||
|
||||
|
@ -986,7 +987,7 @@ class ReprTraceback(TerminalRepr):
|
|||
self.extraline = extraline
|
||||
self.style = style
|
||||
|
||||
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
# the entries might have different styles
|
||||
for i, entry in enumerate(self.reprentries):
|
||||
if entry.style == "long":
|
||||
|
@ -1018,7 +1019,7 @@ class ReprEntryNative(TerminalRepr):
|
|||
def __init__(self, tblines: Sequence[str]) -> None:
|
||||
self.lines = tblines
|
||||
|
||||
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
tw.write("".join(self.lines))
|
||||
|
||||
|
||||
|
@ -1037,7 +1038,7 @@ class ReprEntry(TerminalRepr):
|
|||
self.reprfileloc = filelocrepr
|
||||
self.style = style
|
||||
|
||||
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
if self.style == "short":
|
||||
assert self.reprfileloc is not None
|
||||
self.reprfileloc.toterminal(tw)
|
||||
|
@ -1072,7 +1073,7 @@ class ReprFileLocation(TerminalRepr):
|
|||
self.lineno = lineno
|
||||
self.message = message
|
||||
|
||||
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
# filename and lineno output for each entry,
|
||||
# using an output format that most editors understand
|
||||
msg = self.message
|
||||
|
@ -1087,7 +1088,7 @@ class ReprLocals(TerminalRepr):
|
|||
def __init__(self, lines: Sequence[str]) -> None:
|
||||
self.lines = lines
|
||||
|
||||
def toterminal(self, tw: py.io.TerminalWriter, indent="") -> None:
|
||||
def toterminal(self, tw: TerminalWriter, indent="") -> None:
|
||||
for line in self.lines:
|
||||
tw.line(indent + line)
|
||||
|
||||
|
@ -1096,7 +1097,7 @@ class ReprFuncArgs(TerminalRepr):
|
|||
def __init__(self, args: Sequence[Tuple[str, object]]) -> None:
|
||||
self.args = args
|
||||
|
||||
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
if self.args:
|
||||
linesofar = ""
|
||||
for name, value in self.args:
|
||||
|
|
|
@ -277,7 +277,7 @@ def compile_( # noqa: F811
|
|||
return s.compile(filename, mode, flags, _genframe=_genframe)
|
||||
|
||||
|
||||
def getfslineno(obj) -> Tuple[Union[str, py.path.local], int]:
|
||||
def getfslineno(obj) -> Tuple[Optional[Union["Literal['']", py.path.local]], int]:
|
||||
""" Return source location (path, lineno) for the given object.
|
||||
If the source cannot be determined return ("", -1).
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# Reexport TerminalWriter from here instead of py, to make it easier to
|
||||
# extend or swap our own implementation in the future.
|
||||
from py.io import TerminalWriter as TerminalWriter # noqa: F401
|
|
@ -17,6 +17,7 @@ from .pathlib import Path
|
|||
from .pathlib import resolve_from_str
|
||||
from .pathlib import rm_rf
|
||||
from _pytest import nodes
|
||||
from _pytest._io import TerminalWriter
|
||||
from _pytest.config import Config
|
||||
from _pytest.main import Session
|
||||
|
||||
|
@ -418,7 +419,7 @@ def pytest_report_header(config):
|
|||
def cacheshow(config, session):
|
||||
from pprint import pformat
|
||||
|
||||
tw = py.io.TerminalWriter()
|
||||
tw = TerminalWriter()
|
||||
tw.line("cachedir: " + str(config.cache._cachedir))
|
||||
if not config.cache._cachedir.is_dir():
|
||||
tw.line("cache is empty")
|
||||
|
|
|
@ -308,7 +308,7 @@ def get_real_method(obj, holder):
|
|||
return obj
|
||||
|
||||
|
||||
def getfslineno(obj):
|
||||
def getfslineno(obj) -> Tuple[Union[str, py.path.local], int]:
|
||||
# xxx let decorators etc specify a sane ordering
|
||||
obj = get_real_func(obj)
|
||||
if hasattr(obj, "place_as"):
|
||||
|
|
|
@ -36,6 +36,7 @@ from .findpaths import determine_setup
|
|||
from .findpaths import exists
|
||||
from _pytest._code import ExceptionInfo
|
||||
from _pytest._code import filter_traceback
|
||||
from _pytest._io import TerminalWriter
|
||||
from _pytest.compat import importlib_metadata
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
from _pytest.outcomes import fail
|
||||
|
@ -75,7 +76,7 @@ def main(args=None, plugins=None) -> "Union[int, _pytest.main.ExitCode]":
|
|||
config = _prepareconfig(args, plugins)
|
||||
except ConftestImportFailure as e:
|
||||
exc_info = ExceptionInfo(e.excinfo)
|
||||
tw = py.io.TerminalWriter(sys.stderr)
|
||||
tw = TerminalWriter(sys.stderr)
|
||||
tw.line(
|
||||
"ImportError while loading conftest '{e.path}'.".format(e=e), red=True
|
||||
)
|
||||
|
@ -101,7 +102,7 @@ def main(args=None, plugins=None) -> "Union[int, _pytest.main.ExitCode]":
|
|||
finally:
|
||||
config._ensure_unconfigure()
|
||||
except UsageError as e:
|
||||
tw = py.io.TerminalWriter(sys.stderr)
|
||||
tw = TerminalWriter(sys.stderr)
|
||||
for msg in e.args:
|
||||
tw.line("ERROR: {}\n".format(msg), red=True)
|
||||
return ExitCode.USAGE_ERROR
|
||||
|
@ -1177,12 +1178,12 @@ def setns(obj, dic):
|
|||
setattr(pytest, name, value)
|
||||
|
||||
|
||||
def create_terminal_writer(config, *args, **kwargs):
|
||||
def create_terminal_writer(config: Config, *args, **kwargs) -> TerminalWriter:
|
||||
"""Create a TerminalWriter instance configured according to the options
|
||||
in the config object. Every code which requires a TerminalWriter object
|
||||
and has access to a config object should use this function.
|
||||
"""
|
||||
tw = py.io.TerminalWriter(*args, **kwargs)
|
||||
tw = TerminalWriter(*args, **kwargs)
|
||||
if config.option.color == "yes":
|
||||
tw.hasmarkup = True
|
||||
if config.option.color == "no":
|
||||
|
|
|
@ -102,8 +102,8 @@ class Parser:
|
|||
|
||||
self.optparser = self._getparser()
|
||||
try_argcomplete(self.optparser)
|
||||
args = [str(x) if isinstance(x, py.path.local) else x for x in args]
|
||||
return self.optparser.parse_args(args, namespace=namespace)
|
||||
strargs = [str(x) if isinstance(x, py.path.local) else x for x in args]
|
||||
return self.optparser.parse_args(strargs, namespace=namespace)
|
||||
|
||||
def _getparser(self) -> "MyOptionParser":
|
||||
from _pytest._argcomplete import filescompleter
|
||||
|
@ -154,8 +154,8 @@ class Parser:
|
|||
the remaining arguments unknown at this point.
|
||||
"""
|
||||
optparser = self._getparser()
|
||||
args = [str(x) if isinstance(x, py.path.local) else x for x in args]
|
||||
return optparser.parse_known_args(args, namespace=namespace)
|
||||
strargs = [str(x) if isinstance(x, py.path.local) else x for x in args]
|
||||
return optparser.parse_known_args(strargs, namespace=namespace)
|
||||
|
||||
def addini(
|
||||
self,
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import os
|
||||
from typing import Any
|
||||
from typing import Iterable
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
|
||||
import py
|
||||
|
||||
|
@ -60,7 +63,7 @@ def getcfg(args, config=None):
|
|||
return None, None, None
|
||||
|
||||
|
||||
def get_common_ancestor(paths):
|
||||
def get_common_ancestor(paths: Iterable[py.path.local]) -> py.path.local:
|
||||
common_ancestor = None
|
||||
for path in paths:
|
||||
if not path.exists():
|
||||
|
@ -113,7 +116,7 @@ def determine_setup(
|
|||
args: List[str],
|
||||
rootdir_cmd_arg: Optional[str] = None,
|
||||
config: Optional["Config"] = None,
|
||||
):
|
||||
) -> Tuple[py.path.local, Optional[str], Any]:
|
||||
dirs = get_dirs_from_args(args)
|
||||
if inifile:
|
||||
iniconfig = py.iniconfig.IniConfig(inifile)
|
||||
|
|
|
@ -13,13 +13,14 @@ from typing import Sequence
|
|||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
import py
|
||||
import py.path
|
||||
|
||||
import pytest
|
||||
from _pytest import outcomes
|
||||
from _pytest._code.code import ExceptionInfo
|
||||
from _pytest._code.code import ReprFileLocation
|
||||
from _pytest._code.code import TerminalRepr
|
||||
from _pytest._io import TerminalWriter
|
||||
from _pytest.compat import safe_getattr
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
from _pytest.fixtures import FixtureRequest
|
||||
|
@ -139,7 +140,7 @@ class ReprFailDoctest(TerminalRepr):
|
|||
):
|
||||
self.reprlocation_lines = reprlocation_lines
|
||||
|
||||
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
for reprlocation, lines in self.reprlocation_lines:
|
||||
for line in lines:
|
||||
tw.line(line)
|
||||
|
@ -312,7 +313,7 @@ class DoctestItem(pytest.Item):
|
|||
else:
|
||||
return super().repr_failure(excinfo)
|
||||
|
||||
def reportinfo(self) -> Tuple[str, int, str]:
|
||||
def reportinfo(self) -> Tuple[py.path.local, int, str]:
|
||||
return self.fspath, self.dtest.lineno, "[doctest] %s" % self.name
|
||||
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import py
|
|||
import _pytest
|
||||
from _pytest._code.code import FormattedExcinfo
|
||||
from _pytest._code.code import TerminalRepr
|
||||
from _pytest._io import TerminalWriter
|
||||
from _pytest.compat import _format_args
|
||||
from _pytest.compat import _PytestWrapper
|
||||
from _pytest.compat import get_real_func
|
||||
|
@ -352,7 +353,7 @@ class FixtureRequest:
|
|||
self.fixturename = None
|
||||
#: Scope string, one of "function", "class", "module", "session"
|
||||
self.scope = "function"
|
||||
self._fixture_defs = {} # argname -> FixtureDef
|
||||
self._fixture_defs = {} # type: Dict[str, FixtureDef]
|
||||
fixtureinfo = pyfuncitem._fixtureinfo
|
||||
self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy()
|
||||
self._arg2index = {}
|
||||
|
@ -427,7 +428,8 @@ class FixtureRequest:
|
|||
@scopeproperty()
|
||||
def fspath(self) -> py.path.local:
|
||||
""" the file system path of the test module which collected this test. """
|
||||
return self._pyfuncitem.fspath
|
||||
# TODO: Remove ignore once _pyfuncitem is properly typed.
|
||||
return self._pyfuncitem.fspath # type: ignore
|
||||
|
||||
@property
|
||||
def keywords(self):
|
||||
|
@ -547,7 +549,9 @@ class FixtureRequest:
|
|||
source_path = py.path.local(frameinfo.filename)
|
||||
source_lineno = frameinfo.lineno
|
||||
if source_path.relto(funcitem.config.rootdir):
|
||||
source_path = source_path.relto(funcitem.config.rootdir)
|
||||
source_path_str = source_path.relto(funcitem.config.rootdir)
|
||||
else:
|
||||
source_path_str = str(source_path)
|
||||
msg = (
|
||||
"The requested fixture has no parameter defined for test:\n"
|
||||
" {}\n\n"
|
||||
|
@ -556,7 +560,7 @@ class FixtureRequest:
|
|||
funcitem.nodeid,
|
||||
fixturedef.argname,
|
||||
getlocation(fixturedef.func, funcitem.config.rootdir),
|
||||
source_path,
|
||||
source_path_str,
|
||||
source_lineno,
|
||||
)
|
||||
)
|
||||
|
@ -749,7 +753,7 @@ class FixtureLookupErrorRepr(TerminalRepr):
|
|||
self.firstlineno = firstlineno
|
||||
self.argname = argname
|
||||
|
||||
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
# tw.line("FixtureLookupError: %s" %(self.argname), red=True)
|
||||
for tbline in self.tblines:
|
||||
tw.line(tbline.rstrip())
|
||||
|
|
|
@ -15,6 +15,7 @@ import py
|
|||
import _pytest._code
|
||||
from _pytest import nodes
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
from _pytest.config import Config
|
||||
from _pytest.config import directory_arg
|
||||
from _pytest.config import hookimpl
|
||||
from _pytest.config import UsageError
|
||||
|
@ -375,9 +376,9 @@ class Failed(Exception):
|
|||
|
||||
@attr.s
|
||||
class _bestrelpath_cache(dict):
|
||||
path = attr.ib()
|
||||
path = attr.ib(type=py.path.local)
|
||||
|
||||
def __missing__(self, path: str) -> str:
|
||||
def __missing__(self, path: py.path.local) -> str:
|
||||
r = self.path.bestrelpath(path) # type: str
|
||||
self[path] = r
|
||||
return r
|
||||
|
@ -391,7 +392,7 @@ class Session(nodes.FSCollector):
|
|||
# Set on the session by fixtures.pytest_sessionstart.
|
||||
_fixturemanager = None # type: FixtureManager
|
||||
|
||||
def __init__(self, config) -> None:
|
||||
def __init__(self, config: Config) -> None:
|
||||
nodes.FSCollector.__init__(
|
||||
self, config.rootdir, parent=None, config=config, session=self, nodeid=""
|
||||
)
|
||||
|
@ -411,7 +412,7 @@ class Session(nodes.FSCollector):
|
|||
|
||||
self._bestrelpathcache = _bestrelpath_cache(
|
||||
config.rootdir
|
||||
) # type: Dict[str, str]
|
||||
) # type: Dict[py.path.local, str]
|
||||
|
||||
self.config.pluginmanager.register(self, name="session")
|
||||
|
||||
|
@ -428,7 +429,7 @@ class Session(nodes.FSCollector):
|
|||
self.testscollected,
|
||||
)
|
||||
|
||||
def _node_location_to_relpath(self, node_path: str) -> str:
|
||||
def _node_location_to_relpath(self, node_path: py.path.local) -> str:
|
||||
# bestrelpath is a quite slow function
|
||||
return self._bestrelpathcache[node_path]
|
||||
|
||||
|
|
|
@ -482,6 +482,10 @@ class Item(Node):
|
|||
@cached_property
|
||||
def location(self) -> Tuple[str, Optional[int], str]:
|
||||
location = self.reportinfo()
|
||||
fspath = self.session._node_location_to_relpath(location[0])
|
||||
if isinstance(location[0], py.path.local):
|
||||
fspath = location[0]
|
||||
else:
|
||||
fspath = py.path.local(location[0])
|
||||
relfspath = self.session._node_location_to_relpath(fspath)
|
||||
assert type(location[2]) is str
|
||||
return (fspath, location[1], location[2])
|
||||
return (relfspath, location[1], location[2])
|
||||
|
|
|
@ -13,6 +13,7 @@ from textwrap import dedent
|
|||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
import py
|
||||
|
||||
|
@ -282,15 +283,16 @@ class PyobjMixin(PyobjContext):
|
|||
parts.reverse()
|
||||
return ".".join(parts)
|
||||
|
||||
def reportinfo(self) -> Tuple[str, int, str]:
|
||||
def reportinfo(self) -> Tuple[Union[py.path.local, str], int, str]:
|
||||
# XXX caching?
|
||||
obj = self.obj
|
||||
compat_co_firstlineno = getattr(obj, "compat_co_firstlineno", None)
|
||||
if isinstance(compat_co_firstlineno, int):
|
||||
# nose compatibility
|
||||
fspath = sys.modules[obj.__module__].__file__
|
||||
if fspath.endswith(".pyc"):
|
||||
fspath = fspath[:-1]
|
||||
file_path = sys.modules[obj.__module__].__file__
|
||||
if file_path.endswith(".pyc"):
|
||||
file_path = file_path[:-1]
|
||||
fspath = file_path # type: Union[py.path.local, str]
|
||||
lineno = compat_co_firstlineno
|
||||
else:
|
||||
fspath, lineno = getfslineno(obj)
|
||||
|
@ -369,7 +371,12 @@ class PyCollector(PyobjMixin, nodes.Collector):
|
|||
if not isinstance(res, list):
|
||||
res = [res]
|
||||
values.extend(res)
|
||||
values.sort(key=lambda item: item.reportinfo()[:2])
|
||||
|
||||
def sort_key(item):
|
||||
fspath, lineno, _ = item.reportinfo()
|
||||
return (str(fspath), lineno)
|
||||
|
||||
values.sort(key=sort_key)
|
||||
return values
|
||||
|
||||
def _makeitem(self, name, obj):
|
||||
|
|
|
@ -18,6 +18,7 @@ from _pytest._code.code import ReprFuncArgs
|
|||
from _pytest._code.code import ReprLocals
|
||||
from _pytest._code.code import ReprTraceback
|
||||
from _pytest._code.code import TerminalRepr
|
||||
from _pytest._io import TerminalWriter
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
from _pytest.nodes import Node
|
||||
from _pytest.outcomes import skip
|
||||
|
@ -80,7 +81,7 @@ class BaseReport:
|
|||
|
||||
.. versionadded:: 3.0
|
||||
"""
|
||||
tw = py.io.TerminalWriter(stringio=True)
|
||||
tw = TerminalWriter(stringio=True)
|
||||
tw.hasmarkup = False
|
||||
self.toterminal(tw)
|
||||
exc = tw.stringio.getvalue()
|
||||
|
|
|
@ -12,7 +12,8 @@ import pytest
|
|||
from _pytest._code.code import ExceptionChainRepr
|
||||
from _pytest._code.code import ExceptionInfo
|
||||
from _pytest._code.code import FormattedExcinfo
|
||||
|
||||
from _pytest._io import TerminalWriter
|
||||
from _pytest.pytester import LineMatcher
|
||||
|
||||
try:
|
||||
import importlib
|
||||
|
@ -775,14 +776,43 @@ raise ValueError()
|
|||
)
|
||||
excinfo = pytest.raises(ValueError, mod.entry)
|
||||
|
||||
p = FormattedExcinfo()
|
||||
p = FormattedExcinfo(abspath=False)
|
||||
|
||||
raised = 0
|
||||
|
||||
orig_getcwd = os.getcwd
|
||||
|
||||
def raiseos():
|
||||
raise OSError(2)
|
||||
nonlocal raised
|
||||
if sys._getframe().f_back.f_code.co_name == "checked_call":
|
||||
# Only raise with expected calls, but not via e.g. inspect for
|
||||
# py38-windows.
|
||||
raised += 1
|
||||
raise OSError(2, "custom_oserror")
|
||||
return orig_getcwd()
|
||||
|
||||
monkeypatch.setattr(os, "getcwd", raiseos)
|
||||
assert p._makepath(__file__) == __file__
|
||||
p.repr_traceback(excinfo)
|
||||
assert raised == 1
|
||||
repr_tb = p.repr_traceback(excinfo)
|
||||
|
||||
matcher = LineMatcher(str(repr_tb).splitlines())
|
||||
matcher.fnmatch_lines(
|
||||
[
|
||||
"def entry():",
|
||||
"> f(0)",
|
||||
"",
|
||||
"{}:5: ".format(mod.__file__),
|
||||
"_ _ *",
|
||||
"",
|
||||
" def f(x):",
|
||||
"> raise ValueError(x)",
|
||||
"E ValueError: 0",
|
||||
"",
|
||||
"{}:3: ValueError".format(mod.__file__),
|
||||
]
|
||||
)
|
||||
assert raised == 3
|
||||
|
||||
def test_repr_excinfo_addouterr(self, importasmod, tw_mock):
|
||||
mod = importasmod(
|
||||
|
@ -855,7 +885,7 @@ raise ValueError()
|
|||
from _pytest._code.code import TerminalRepr
|
||||
|
||||
class MyRepr(TerminalRepr):
|
||||
def toterminal(self, tw: py.io.TerminalWriter) -> None:
|
||||
def toterminal(self, tw: TerminalWriter) -> None:
|
||||
tw.line("я")
|
||||
|
||||
x = str(MyRepr())
|
||||
|
@ -1005,7 +1035,7 @@ raise ValueError()
|
|||
"""
|
||||
)
|
||||
excinfo = pytest.raises(ValueError, mod.f)
|
||||
tw = py.io.TerminalWriter(stringio=True)
|
||||
tw = TerminalWriter(stringio=True)
|
||||
repr = excinfo.getrepr(**reproptions)
|
||||
repr.toterminal(tw)
|
||||
assert tw.stringio.getvalue()
|
||||
|
@ -1200,8 +1230,6 @@ raise ValueError()
|
|||
real traceback, such as those raised in a subprocess submitted by the multiprocessing
|
||||
module (#1984).
|
||||
"""
|
||||
from _pytest.pytester import LineMatcher
|
||||
|
||||
exc_handling_code = " from e" if reason == "cause" else ""
|
||||
mod = importasmod(
|
||||
"""
|
||||
|
@ -1225,7 +1253,7 @@ raise ValueError()
|
|||
getattr(excinfo.value, attr).__traceback__ = None
|
||||
|
||||
r = excinfo.getrepr()
|
||||
tw = py.io.TerminalWriter(stringio=True)
|
||||
tw = TerminalWriter(stringio=True)
|
||||
tw.hasmarkup = False
|
||||
r.toterminal(tw)
|
||||
|
||||
|
@ -1320,7 +1348,6 @@ def test_exception_repr_extraction_error_on_recursion():
|
|||
Ensure we can properly detect a recursion error even
|
||||
if some locals raise error on comparison (#2459).
|
||||
"""
|
||||
from _pytest.pytester import LineMatcher
|
||||
|
||||
class numpy_like:
|
||||
def __eq__(self, other):
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import logging
|
||||
|
||||
import py.io
|
||||
|
||||
from _pytest._io import TerminalWriter
|
||||
from _pytest.logging import ColoredLevelFormatter
|
||||
|
||||
|
||||
|
@ -22,7 +21,7 @@ def test_coloredlogformatter():
|
|||
class option:
|
||||
pass
|
||||
|
||||
tw = py.io.TerminalWriter()
|
||||
tw = TerminalWriter()
|
||||
tw.hasmarkup = True
|
||||
formatter = ColoredLevelFormatter(tw, logfmt)
|
||||
output = formatter.format(record)
|
||||
|
@ -142,7 +141,7 @@ def test_colored_short_level():
|
|||
class option:
|
||||
pass
|
||||
|
||||
tw = py.io.TerminalWriter()
|
||||
tw = TerminalWriter()
|
||||
tw.hasmarkup = True
|
||||
formatter = ColoredLevelFormatter(tw, logfmt)
|
||||
output = formatter.format(record)
|
||||
|
|
|
@ -68,7 +68,7 @@ class TestModule:
|
|||
def test_invalid_test_module_name(self, testdir):
|
||||
a = testdir.mkdir("a")
|
||||
a.ensure("test_one.part1.py")
|
||||
result = testdir.runpytest("-rw")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"ImportError while importing test module*test_one.part1*",
|
||||
|
@ -137,7 +137,7 @@ class TestClass:
|
|||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("-rw")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*cannot collect test class 'TestClass1' because it has "
|
||||
|
@ -153,7 +153,7 @@ class TestClass:
|
|||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("-rw")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*cannot collect test class 'TestClass1' because it has "
|
||||
|
@ -230,7 +230,7 @@ class TestClass:
|
|||
TestCase = collections.namedtuple('TestCase', ['a'])
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("-rw")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(
|
||||
"*cannot collect test class 'TestCase' "
|
||||
"because it has a __new__ constructor*"
|
||||
|
@ -1162,7 +1162,7 @@ def test_dont_collect_non_function_callable(testdir):
|
|||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("-rw")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*collected 1 item*",
|
||||
|
|
|
@ -4207,3 +4207,38 @@ def test_fixture_parametrization_nparray(testdir):
|
|||
)
|
||||
result = testdir.runpytest()
|
||||
result.assert_outcomes(passed=10)
|
||||
|
||||
|
||||
def test_fixture_arg_ordering(testdir):
|
||||
"""
|
||||
This test describes how fixtures in the same scope but without explicit dependencies
|
||||
between them are created. While users should make dependencies explicit, often
|
||||
they rely on this order, so this test exists to catch regressions in this regard.
|
||||
See #6540 and #6492.
|
||||
"""
|
||||
p1 = testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
suffixes = []
|
||||
|
||||
@pytest.fixture
|
||||
def fix_1(): suffixes.append("fix_1")
|
||||
@pytest.fixture
|
||||
def fix_2(): suffixes.append("fix_2")
|
||||
@pytest.fixture
|
||||
def fix_3(): suffixes.append("fix_3")
|
||||
@pytest.fixture
|
||||
def fix_4(): suffixes.append("fix_4")
|
||||
@pytest.fixture
|
||||
def fix_5(): suffixes.append("fix_5")
|
||||
|
||||
@pytest.fixture
|
||||
def fix_combined(fix_1, fix_2, fix_3, fix_4, fix_5): pass
|
||||
|
||||
def test_suffix(fix_combined):
|
||||
assert suffixes == ["fix_1", "fix_2", "fix_3", "fix_4", "fix_5"]
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("-vv", str(p1))
|
||||
assert result.ret == 0
|
||||
|
|
|
@ -1349,7 +1349,7 @@ def test_assert_indirect_tuple_no_warning(testdir):
|
|||
assert tpl
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("-rw")
|
||||
result = testdir.runpytest()
|
||||
output = "\n".join(result.stdout.lines)
|
||||
assert "WR1" not in output
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ class TestNewAPI:
|
|||
testdir.tmpdir.ensure_dir(".pytest_cache").chmod(0)
|
||||
try:
|
||||
testdir.makepyfile("def test_error(): raise Exception")
|
||||
result = testdir.runpytest("-rw")
|
||||
result = testdir.runpytest()
|
||||
assert result.ret == 1
|
||||
# warnings from nodeids, lastfailed, and stepwise
|
||||
result.stdout.fnmatch_lines(
|
||||
|
|
|
@ -1134,13 +1134,14 @@ def test_record_property(testdir, run_and_parse):
|
|||
record_property("foo", "<1");
|
||||
"""
|
||||
)
|
||||
result, dom = run_and_parse("-rwv")
|
||||
result, dom = run_and_parse()
|
||||
node = dom.find_first_by_tag("testsuite")
|
||||
tnode = node.find_first_by_tag("testcase")
|
||||
psnode = tnode.find_first_by_tag("properties")
|
||||
pnodes = psnode.find_by_tag("property")
|
||||
pnodes[0].assert_attr(name="bar", value="1")
|
||||
pnodes[1].assert_attr(name="foo", value="<1")
|
||||
result.stdout.fnmatch_lines(["*= 1 passed in *"])
|
||||
|
||||
|
||||
def test_record_property_same_name(testdir, run_and_parse):
|
||||
|
@ -1151,7 +1152,7 @@ def test_record_property_same_name(testdir, run_and_parse):
|
|||
record_property("foo", "baz")
|
||||
"""
|
||||
)
|
||||
result, dom = run_and_parse("-rw")
|
||||
result, dom = run_and_parse()
|
||||
node = dom.find_first_by_tag("testsuite")
|
||||
tnode = node.find_first_by_tag("testcase")
|
||||
psnode = tnode.find_first_by_tag("properties")
|
||||
|
@ -1193,7 +1194,7 @@ def test_record_attribute(testdir, run_and_parse):
|
|||
record_xml_attribute("foo", "<1");
|
||||
"""
|
||||
)
|
||||
result, dom = run_and_parse("-rw")
|
||||
result, dom = run_and_parse()
|
||||
node = dom.find_first_by_tag("testsuite")
|
||||
tnode = node.find_first_by_tag("testcase")
|
||||
tnode.assert_attr(bar="1")
|
||||
|
@ -1228,7 +1229,7 @@ def test_record_fixtures_xunit2(testdir, fixture_name, run_and_parse):
|
|||
)
|
||||
)
|
||||
|
||||
result, dom = run_and_parse("-rw", family=None)
|
||||
result, dom = run_and_parse(family=None)
|
||||
expected_lines = []
|
||||
if fixture_name == "record_xml_attribute":
|
||||
expected_lines.append(
|
||||
|
|
|
@ -377,15 +377,48 @@ def test_skip_test_with_unicode(testdir):
|
|||
result.stdout.fnmatch_lines(["* 1 skipped *"])
|
||||
|
||||
|
||||
def test_issue_6517(testdir):
|
||||
def test_raises(testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
from nose.tools import raises
|
||||
|
||||
@raises(RuntimeError)
|
||||
def test_fail_without_tcp():
|
||||
def test_raises_runtimeerror():
|
||||
raise RuntimeError
|
||||
|
||||
@raises(Exception)
|
||||
def test_raises_baseexception_not_caught():
|
||||
raise BaseException
|
||||
|
||||
@raises(BaseException)
|
||||
def test_raises_baseexception_caught():
|
||||
raise BaseException
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(["* 1 passed *"])
|
||||
result = testdir.runpytest("-vv")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"test_raises.py::test_raises_runtimeerror PASSED*",
|
||||
"test_raises.py::test_raises_baseexception_not_caught FAILED*",
|
||||
"test_raises.py::test_raises_baseexception_caught PASSED*",
|
||||
"*= FAILURES =*",
|
||||
"*_ test_raises_baseexception_not_caught _*",
|
||||
"",
|
||||
"arg = (), kw = {}",
|
||||
"",
|
||||
" def newfunc(*arg, **kw):",
|
||||
" try:",
|
||||
"> func(*arg, **kw)",
|
||||
"",
|
||||
"*/nose/*: ",
|
||||
"_ _ *",
|
||||
"",
|
||||
" @raises(Exception)",
|
||||
" def test_raises_baseexception_not_caught():",
|
||||
"> raise BaseException",
|
||||
"E BaseException",
|
||||
"",
|
||||
"test_raises.py:9: BaseException",
|
||||
"* 1 failed, 2 passed *",
|
||||
]
|
||||
)
|
||||
|
|
|
@ -256,7 +256,7 @@ class TestPytestPluginManager:
|
|||
)
|
||||
p.copy(p.dirpath("skipping2.py"))
|
||||
monkeypatch.setenv("PYTEST_PLUGINS", "skipping2")
|
||||
result = testdir.runpytest("-rw", "-p", "skipping1", syspathinsert=True)
|
||||
result = testdir.runpytest("-p", "skipping1", syspathinsert=True)
|
||||
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
||||
result.stdout.fnmatch_lines(
|
||||
["*skipped plugin*skipping1*hello*", "*skipped plugin*skipping2*hello*"]
|
||||
|
|
Loading…
Reference in New Issue