Compare commits

...

35 Commits
6.2.0 ... 6.2.4

Author SHA1 Message Date
pytest bot
017dd1ccd6 Prepare release version 6.2.4 2021-05-04 08:33:43 -07:00
Anthony Sottile
18569f44c1 Merge pull request #8629 from asottile/backport_8540
[6.2.x] Merge pull request #8540 from hauntsaninja/assert310
2021-05-04 08:13:02 -07:00
Bruno Oliveira
d8d6812bdf Merge pull request #8540 from hauntsaninja/assert310
(cherry picked from commit af31c60db1)
2021-05-04 07:52:27 -07:00
Bruno Oliveira
a5061484d4 Merge pull request #8607 from cmaurer/patch-1
Update fixture.rst
2021-04-28 09:54:22 -03:00
Christian Maurer
69ea076d55 Update fixture.rst
Availability was misspelled.  It was `availabiility`
2021-04-28 07:01:45 -05:00
Florian Bruhin
40cb2f5b54 Backport training update (#8557) 2021-04-15 20:02:50 +02:00
Ran Benita
724e22cb00 Merge pull request #8519 from pytest-dev/release-6.2.3
Prepare release 6.2.3
2021-04-04 00:43:05 +03:00
pytest bot
3a2fd96305 Prepare release version 6.2.3 2021-04-03 21:41:18 +00:00
Ran Benita
138b19a930 Merge pull request #8517 from bluetech/backport-mktmp
[6.2.x] Fix minor temporary directory security issue
2021-04-04 00:34:15 +03:00
Ran Benita
822686e880 tmpdir: prevent using a non-private root temp directory
pytest uses a root temp directory named `/tmp/pytest-of-<username>`. The
name is predictable, and the directory might already exists from a
previous run, so that's allowed.

This makes it possible for my_user to pre-create
`/tmp/pytest-of-another_user`, thus giving my_user control of
another_user's tempdir.

Prevent this scenario by adding a couple of safety checks. I believe
they are sufficient.

Testing the first check requires changing the owner, which requires
root permissions, so can't be unit-tested easily, but I checked it
manually.
2021-04-04 00:04:50 +03:00
Ran Benita
9dc54f79b0 tmpdir: fix temporary directories created with world-readable permissions
(Written for a Unix system, but might be applicable to Windows as well).

pytest creates a root temporary directory under /tmp, named
`pytest-of-<username>`, and creates tmp_path's and other under it.
/tmp is shared between all users of the system.

This root temporary directory was created with 0o777&~umask permissions,
which usually becomes 0o755, meaning any user in the system could list
and read the files, which is undesirable.

Use 0o700 permissions instead. Also for subdirectories, because the root
dir is adjustable.
2021-04-04 00:00:42 +03:00
Ran Benita
93dbae24e1 pathlib: inline ensure_reset_dir()
This is only used in TempPathFactory.getbasetemp(). We'll be wanting
further control/care there, so move it into there.
2021-04-03 23:39:37 +03:00
Ran Benita
02fdbe2e76 pathlib: remove useless temporary variable 2021-04-03 23:39:32 +03:00
Bruno Oliveira
12e7db85af Merge pull request #8285 from nicoddemus/backport-8280
[6.2.x] Doc: Move the module declaration to index.rst
2021-01-27 09:13:27 -03:00
Bruno Oliveira
56e4392444 Merge pull request #8280 from xuhdev/module
Doc: Move the module declaration to index.rst
2021-01-27 09:07:33 -03:00
Bruno Oliveira
8220eca963 Merge pull request #8275 from pytest-dev/release-6.2.2
Prepare release 6.2.2
2021-01-25 11:52:23 -03:00
pytest bot
b9c98762f5 Prepare release version 6.2.2 2021-01-25 12:30:53 +00:00
Bruno Oliveira
8003fd23b9 Merge pull request #8259 from nicoddemus/backport-8250
[6.2.x] Fix faulthandler for Twisted Logger when used with "--capture=no"
2021-01-20 10:14:34 -03:00
Bruno Oliveira
8d605b9b26 Merge pull request #8250 from daq-tools/fix-twisted-capture 2021-01-20 09:47:10 -03:00
Bruno Oliveira
14e0c3e105 Merge pull request #8225 from The-Compiler/training-update (#8226)
doc: Add note about training early bird discount
2021-01-05 20:49:50 +01:00
Bruno Oliveira
45facc16c8 Merge pull request #8224 from nicoddemus/backport-8220
[6.2.x] DOC: Mark pytest module
2021-01-05 13:31:19 -03:00
Bruno Oliveira
99fe887d7c Merge pull request #8220 from xuhdev/module-doc
DOC: Mark pytest module
2021-01-05 13:26:19 -03:00
Bruno Oliveira
8dbf9dc1aa Merge pull request #8167 from nicoddemus/backport-8166
[6.2.x] Add Changelog to setup.cfg (#8166)
2020-12-17 13:59:14 -03:00
Adam Johnson
baaee2148d Add Changelog to setup.cfg (#8166)
Co-authored-by: Thomas Grainger <tagrain@gmail.com>
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2020-12-17 13:56:18 -03:00
Bruno Oliveira
f7d1ab870f Merge pull request #8163 from bluetech/backport-8152
[6.2.x] terminal: fix "(<Skipped instance>)" skip reason in test status line
2020-12-17 08:31:53 -03:00
Ran Benita
b8201c280e Merge pull request #8152 from bluetech/empty-skip
terminal: fix "(<Skipped instance>)" skip reason in test status line
(cherry picked from commit 02e69e5cdc)
2020-12-17 12:59:01 +02:00
Bruno Oliveira
1f0c50b475 Merge pull request #8160 from nicoddemus/backport-7381
[6.2.x] Clarify fixture execution order and provide visual aids (#7381)
2020-12-16 14:02:02 -03:00
Chris NeJame
da82e1853c Clarify fixture execution order and provide visual aids (#7381)
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
Co-authored-by: Ran Benita <ran@unusedvar.com>
2020-12-16 13:54:52 -03:00
Bruno Oliveira
a566eb9c70 Merge pull request #8149 from pytest-dev/release-6.2.1
Prepare release 6.2.1
2020-12-15 12:39:06 -03:00
pytest bot
d3971c30f4 Prepare release version 6.2.1 2020-12-15 13:06:34 +00:00
Bruno Oliveira
780044b64a Merge pull request #8147 from nicoddemus/backport-8137
[6.2.x] python_api: handle array-like args in approx() #8137
2020-12-15 09:08:58 -03:00
Jakob van Santen
8354995abc python_api: handle array-like args in approx() (#8137) 2020-12-15 08:50:11 -03:00
Bruno Oliveira
8b8b1214f4 Merge pull request #8135 from nicoddemus/backport-8123
[6.2] Merge pull request #8123 from nicoddemus/import-mismatch-unc
2020-12-13 10:50:49 -03:00
Bruno Oliveira
f854cf66f4 Merge pull request #8123 from nicoddemus/import-mismatch-unc
Compare also paths on Windows when considering ImportPathMismatchError
2020-12-13 10:35:59 -03:00
Ran Benita
c475106f12 Merge pull request #8130 from pytest-dev/release-6.2.0
Prepare release 6.2.0
2020-12-12 23:21:28 +02:00
42 changed files with 2797 additions and 468 deletions

View File

@@ -21,6 +21,7 @@ Anders Hovmöller
Andras Mitzki
Andras Tim
Andrea Cimatoribus
Andreas Motl
Andreas Zeidler
Andrey Paramonov
Andrzej Klajnert
@@ -56,6 +57,7 @@ Charles Cloud
Charles Machalow
Charnjit SiNGH (CCSJ)
Chris Lamb
Chris NeJame
Christian Boelsen
Christian Fetzer
Christian Neumüller
@@ -271,6 +273,7 @@ Sankt Petersbug
Segev Finer
Serhii Mozghovyi
Seth Junot
Shantanu Jain
Shubham Adep
Simon Gomizelj
Simon Kerr

View File

@@ -6,6 +6,10 @@ Release announcements
:maxdepth: 2
release-6.2.4
release-6.2.3
release-6.2.2
release-6.2.1
release-6.2.0
release-6.1.2
release-6.1.1

View File

@@ -0,0 +1,20 @@
pytest-6.2.1
=======================================
pytest 6.2.1 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
Thanks to all of the contributors to this release:
* Bruno Oliveira
* Jakob van Santen
* Ran Benita
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,21 @@
pytest-6.2.2
=======================================
pytest 6.2.2 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
Thanks to all of the contributors to this release:
* Adam Johnson
* Bruno Oliveira
* Chris NeJame
* Ran Benita
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,19 @@
pytest-6.2.3
=======================================
pytest 6.2.3 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
Thanks to all of the contributors to this release:
* Bruno Oliveira
* Ran Benita
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,22 @@
pytest-6.2.4
=======================================
pytest 6.2.4 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
Thanks to all of the contributors to this release:
* Anthony Sottile
* Bruno Oliveira
* Christian Maurer
* Florian Bruhin
* Ran Benita
Happy testing,
The pytest Development Team

View File

@@ -28,6 +28,67 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start
pytest 6.2.4 (2021-05-04)
=========================
Bug Fixes
---------
- `#8539 <https://github.com/pytest-dev/pytest/issues/8539>`_: Fixed assertion rewriting on Python 3.10.
pytest 6.2.3 (2021-04-03)
=========================
Bug Fixes
---------
- `#8414 <https://github.com/pytest-dev/pytest/issues/8414>`_: pytest used to create directories under ``/tmp`` with world-readable
permissions. This means that any user in the system was able to read
information written by tests in temporary directories (such as those created by
the ``tmp_path``/``tmpdir`` fixture). Now the directories are created with
private permissions.
pytest used silenty use a pre-existing ``/tmp/pytest-of-<username>`` directory,
even if owned by another user. This means another user could pre-create such a
directory and gain control of another user's temporary directory. Now such a
condition results in an error.
pytest 6.2.2 (2021-01-25)
=========================
Bug Fixes
---------
- `#8152 <https://github.com/pytest-dev/pytest/issues/8152>`_: Fixed "(<Skipped instance>)" being shown as a skip reason in the verbose test summary line when the reason is empty.
- `#8249 <https://github.com/pytest-dev/pytest/issues/8249>`_: Fix the ``faulthandler`` plugin for occasions when running with ``twisted.logger`` and using ``pytest --capture=no``.
pytest 6.2.1 (2020-12-15)
=========================
Bug Fixes
---------
- `#7678 <https://github.com/pytest-dev/pytest/issues/7678>`_: Fixed bug where ``ImportPathMismatchError`` would be raised for files compiled in
the host and loaded later from an UNC mounted path (Windows).
- `#8132 <https://github.com/pytest-dev/pytest/issues/8132>`_: Fixed regression in ``approx``: in 6.2.0 ``approx`` no longer raises
``TypeError`` when dealing with non-numeric types, falling back to normal comparison.
Before 6.2.0, array types like tf.DeviceArray fell through to the scalar case,
and happened to compare correctly to a scalar if they had only one element.
After 6.2.0, these types began failing, because they inherited neither from
standard Python number hierarchy nor from ``numpy.ndarray``.
``approx`` now converts arguments to ``numpy.ndarray`` if they expose the array
protocol and are not scalars. This treats array-like objects like numpy arrays,
regardless of size.
pytest 6.2.0 (2020-12-12)
=========================

View File

@@ -0,0 +1,132 @@
<svg xmlns="http://www.w3.org/2000/svg" width="572" height="542">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class, circle.module, circle.package {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class, text.module, text.package {
fill: #0e84b5;
}
line, path {
stroke: black;
stroke-width: 2;
fill: none;
}
</style>
<!-- main scope -->
<circle class="package" r="270" cx="286" cy="271" />
<!-- scope name -->
<defs>
<path d="M 26,271 A 260 260 0 0 1 546 271" id="testp"/>
</defs>
<text class="package">
<textPath href="#testp" startOffset="50%">tests</textPath>
</text>
<!-- subpackage -->
<circle class="package" r="140" cx="186" cy="271" />
<!-- scope name -->
<defs>
<path d="M 56,271 A 130 130 0 0 1 316 271" id="subpackage"/>
</defs>
<text class="package">
<textPath href="#subpackage" startOffset="50%">subpackage</textPath>
</text>
<!-- test_subpackage.py -->
<circle class="module" r="90" cx="186" cy="311" />
<!-- scope name -->
<defs>
<path d="M 106,311 A 80 80 0 0 1 266 311" id="testSubpackage"/>
</defs>
<text class="module">
<textPath href="#testSubpackage" startOffset="50%">test_subpackage.py</textPath>
</text>
<!-- innermost -->
<line x1="186" x2="186" y1="271" y2="351"/>
<!-- mid -->
<path d="M 186 351 L 136 351 L 106 331 L 106 196" />
<!-- order -->
<path d="M 186 351 L 256 351 L 316 291 L 316 136" />
<!-- top -->
<path d="M 186 351 L 186 391 L 231 436 L 331 436" />
<ellipse class="fixture" rx="50" ry="25" cx="186" cy="271" />
<text x="186" y="271">innermost</text>
<rect class="test" width="110" height="50" x="131" y="326" />
<text x="186" y="351">test_order</text>
<ellipse class="fixture" rx="50" ry="25" cx="126" cy="196" />
<text x="126" y="196">mid</text>
<!-- scope order number -->
<mask id="testSubpackageOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="90" cx="186" cy="311" />
</mask>
<circle class="module" r="15" cx="96" cy="311" mask="url(#testSubpackageOrderMask)"/>
<text class="module" x="96" y="311">1</text>
<!-- scope order number -->
<mask id="subpackageOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="140" cx="186" cy="271" />
</mask>
<circle class="module" r="15" cx="46" cy="271" mask="url(#subpackageOrderMask)"/>
<text class="module" x="46" y="271">2</text>
<!-- scope order number -->
<mask id="testsOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="270" cx="286" cy="271" />
</mask>
<circle class="module" r="15" cx="16" cy="271" mask="url(#testsOrderMask)"/>
<text class="module" x="16" y="271">3</text>
<!-- test_top.py -->
<circle class="module" r="85" cx="441" cy="271" />
<!-- scope name -->
<defs>
<path d="M 366,271 A 75 75 0 0 1 516 271" id="testTop"/>
</defs>
<text class="module">
<textPath href="#testTop" startOffset="50%">test_top.py</textPath>
</text>
<!-- innermost -->
<line x1="441" x2="441" y1="306" y2="236"/>
<!-- order -->
<path d="M 441 306 L 376 306 L 346 276 L 346 136" />
<!-- top -->
<path d="M 441 306 L 441 411 L 411 436 L 331 436" />
<ellipse class="fixture" rx="50" ry="25" cx="441" cy="236" />
<text x="441" y="236">innermost</text>
<rect class="test" width="110" height="50" x="386" y="281" />
<text x="441" y="306">test_order</text>
<!-- scope order number -->
<mask id="testTopOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="85" cx="441" cy="271" />
</mask>
<circle class="module" r="15" cx="526" cy="271" mask="url(#testTopOrderMask)"/>
<text class="module" x="526" y="271">1</text>
<!-- scope order number -->
<circle class="module" r="15" cx="556" cy="271" mask="url(#testsOrderMask)"/>
<text class="module" x="556" y="271">2</text>
<ellipse class="fixture" rx="50" ry="25" cx="331" cy="436" />
<text x="331" y="436">top</text>
<ellipse class="fixture" rx="50" ry="25" cx="331" cy="136" />
<text x="331" y="136">order</text>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -0,0 +1,142 @@
<svg xmlns="http://www.w3.org/2000/svg" width="587" height="382">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
alignment-baseline: center;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class, circle.module, circle.package, circle.plugin {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class, text.module, text.package, text.plugin {
fill: #0e84b5;
}
line, path {
stroke: black;
stroke-width: 2;
fill: none;
}
</style>
<!-- plugin_a.py scope -->
<circle class="plugin" r="85" cx="486" cy="86" />
<!-- plugin name -->
<defs>
<path d="M 411,86 A 75 75 0 0 1 561 86" id="pluginA"/>
</defs>
<text class="plugin">
<textPath href="#pluginA" startOffset="50%">plugin_a</textPath>
</text>
<!-- scope order number -->
<mask id="pluginAOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="85" cx="486" cy="86" />
</mask>
<circle class="module" r="15" cx="571" cy="86" mask="url(#pluginAOrderMask)"/>
<text class="module" x="571" y="86">4</text>
<!-- plugin_b.py scope -->
<circle class="plugin" r="85" cx="486" cy="296" />
<!-- plugin name -->
<defs>
<path d="M 411,296 A 75 75 0 0 1 561 296" id="pluginB"/>
</defs>
<text class="plugin">
<textPath href="#pluginB" startOffset="50%">plugin_b</textPath>
</text>
<!-- scope order number -->
<mask id="pluginBOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="85" cx="486" cy="296" />
</mask>
<circle class="module" r="15" cx="571" cy="296" mask="url(#pluginBOrderMask)"/>
<text class="module" x="571" y="296">4</text>
<!-- main scope -->
<circle class="package" r="190" cx="191" cy="191" />
<!-- scope name -->
<defs>
<path d="M 11,191 A 180 180 0 0 1 371 191" id="testp"/>
</defs>
<text class="package">
<textPath href="#testp" startOffset="50%">tests</textPath>
</text>
<!-- scope order number -->
<mask id="mainOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="190" cx="191" cy="191" />
</mask>
<circle class="module" r="15" cx="381" cy="191" mask="url(#mainOrderMask)"/>
<text class="module" x="381" y="191">3</text>
<!-- subpackage -->
<circle class="package" r="140" cx="191" cy="231" />
<!-- scope name -->
<defs>
<path d="M 61,231 A 130 130 0 0 1 321 231" id="subpackage"/>
</defs>
<text class="package">
<textPath href="#subpackage" startOffset="50%">subpackage</textPath>
</text>
<!-- scope order number -->
<mask id="subpackageOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="140" cx="191" cy="231" />
</mask>
<circle class="module" r="15" cx="331" cy="231" mask="url(#subpackageOrderMask)"/>
<text class="module" x="331" y="231">2</text>
<!-- test_subpackage.py -->
<circle class="module" r="90" cx="191" cy="271" />
<!-- scope name -->
<defs>
<path d="M 111,271 A 80 80 0 0 1 271 271" id="testSubpackage"/>
</defs>
<text class="module">
<textPath href="#testSubpackage" startOffset="50%">test_subpackage.py</textPath>
</text>
<!-- scope order number -->
<mask id="testSubpackageOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="90" cx="191" cy="271" />
</mask>
<circle class="module" r="15" cx="281" cy="271" mask="url(#testSubpackageOrderMask)"/>
<text class="module" x="281" y="271">1</text>
<!-- innermost -->
<line x1="191" x2="191" y1="231" y2="311"/>
<!-- mid -->
<path d="M 191 306 L 101 306 L 91 296 L 91 156 L 101 146 L 191 146" />
<!-- order -->
<path d="M 191 316 L 91 316 L 81 306 L 81 61 L 91 51 L 191 51" />
<!-- a_fix -->
<path d="M 191 306 L 291 306 L 301 296 L 301 96 L 311 86 L 486 86" />
<!-- b_fix -->
<path d="M 191 316 L 316 316 L 336 296 L 486 296" />
<ellipse class="fixture" rx="50" ry="25" cx="191" cy="231" />
<text x="191" y="231">inner</text>
<rect class="test" width="110" height="50" x="136" y="286" />
<text x="191" y="311">test_order</text>
<ellipse class="fixture" rx="50" ry="25" cx="191" cy="146" />
<text x="191" y="146">mid</text>
<ellipse class="fixture" rx="50" ry="25" cx="191" cy="51" />
<text x="191" y="51">order</text>
<ellipse class="fixture" rx="50" ry="25" cx="486" cy="86" />
<text x="486" y="86">a_fix</text>
<ellipse class="fixture" rx="50" ry="25" cx="486" cy="296" />
<text x="486" y="296">b_fix</text>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -1,38 +0,0 @@
import pytest
# fixtures documentation order example
order = []
@pytest.fixture(scope="session")
def s1():
order.append("s1")
@pytest.fixture(scope="module")
def m1():
order.append("m1")
@pytest.fixture
def f1(f3):
order.append("f1")
@pytest.fixture
def f3():
order.append("f3")
@pytest.fixture(autouse=True)
def a1():
order.append("a1")
@pytest.fixture
def f2():
order.append("f2")
def test_order(f1, m1, f2, s1):
assert order == ["s1", "m1", "a1", "f3", "f1", "f2"]

View File

@@ -0,0 +1,45 @@
import pytest
@pytest.fixture
def order():
return []
@pytest.fixture
def a(order):
order.append("a")
@pytest.fixture
def b(a, order):
order.append("b")
@pytest.fixture(autouse=True)
def c(b, order):
order.append("c")
@pytest.fixture
def d(b, order):
order.append("d")
@pytest.fixture
def e(d, order):
order.append("e")
@pytest.fixture
def f(e, order):
order.append("f")
@pytest.fixture
def g(f, c, order):
order.append("g")
def test_order_and_g(g, order):
assert order == ["a", "b", "c", "d", "e", "f", "g"]

View File

@@ -0,0 +1,64 @@
<svg xmlns="http://www.w3.org/2000/svg" width="252" height="682">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class {
fill: #0e84b5;
}
path, line {
stroke: black;
stroke-width: 2;
fill: none;
}
rect.autouse {
fill: #ca7f3d;
}
</style>
<path d="M126,586 L26,506 L26,236" />
<path d="M226,446 L226,236 L126,166" />
<line x1="126" x2="126" y1="656" y2="516" />
<line x1="126" x2="226" y1="516" y2="446" />
<line x1="226" x2="126" y1="446" y2="376" />
<line x1="126" x2="126" y1="376" y2="166" />
<line x1="26" x2="126" y1="236" y2="166" />
<line x1="126" x2="126" y1="166" y2="26" />
<line x1="126" x2="126" y1="96" y2="26" />
<rect class="autouse" width="251" height="40" x="0" y="286" />
<text x="126" y="306">autouse</text>
<ellipse class="fixture" rx="50" ry="25" cx="126" cy="26" />
<text x="126" y="26">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="96" />
<text x="126" y="96">a</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="166" />
<text x="126" y="166">b</text>
<ellipse class="fixture" rx="25" ry="25" cx="26" cy="236" />
<text x="26" y="236">c</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="376" />
<text x="126" y="376">d</text>
<ellipse class="fixture" rx="25" ry="25" cx="226" cy="446" />
<text x="226" y="446">e</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="516" />
<text x="126" y="516">f</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="586" />
<text x="126" y="586">g</text>
<rect class="test" width="110" height="50" x="71" y="631" />
<text x="126" y="656">test_order</text>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,31 @@
import pytest
@pytest.fixture(scope="class")
def order():
return []
@pytest.fixture(scope="class", autouse=True)
def c1(order):
order.append("c1")
@pytest.fixture(scope="class")
def c2(order):
order.append("c2")
@pytest.fixture(scope="class")
def c3(order, c1):
order.append("c3")
class TestClassWithC1Request:
def test_order(self, order, c1, c3):
assert order == ["c1", "c3"]
class TestClassWithoutC1Request:
def test_order(self, order, c2):
assert order == ["c1", "c2"]

View File

@@ -0,0 +1,76 @@
<svg xmlns="http://www.w3.org/2000/svg" width="862" height="402">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class {
fill: #0e84b5;
}
line {
stroke: black;
stroke-width: 2;
}
rect.autouse {
fill: #ca7f3d;
}
</style>
<!-- TestWithC1Request -->
<circle class="class" r="200" cx="221" cy="201" />
<line x1="221" x2="221" y1="61" y2="316"/>
<ellipse class="fixture" rx="50" ry="25" cx="221" cy="61" />
<text x="221" y="61">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="221" cy="131" />
<text x="221" y="131">c1</text>
<ellipse class="fixture" rx="25" ry="25" cx="221" cy="271" />
<text x="221" y="271">c3</text>
<rect class="test" width="110" height="50" x="166" y="316" />
<text x="221" y="341">test_order</text>
<!-- scope name -->
<defs>
<path d="M31,201 A 190 190 0 0 1 411 201" id="testClassWith"/>
</defs>
<text class="class">
<textPath href="#testClassWith" startOffset="50%">TestWithC1Request</textPath>
</text>
<!-- TestWithoutC1Request -->
<circle class="class" r="200" cx="641" cy="201" />
<line x1="641" x2="641" y1="61" y2="316"/>
<ellipse class="fixture" rx="50" ry="25" cx="641" cy="61" />
<text x="641" y="61">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="641" cy="131" />
<text x="641" y="131">c1</text>
<ellipse class="fixture" rx="25" ry="25" cx="641" cy="271" />
<text x="641" y="271">c2</text>
<rect class="test" width="110" height="50" x="586" y="316" />
<text x="641" y="341">test_order</text>
<!-- scope name -->
<defs>
<path d="M451,201 A 190 190 0 0 1 831 201" id="testClassWithout"/>
</defs>
<text class="class">
<textPath href="#testClassWithout" startOffset="50%">TestWithoutC1Request</textPath>
</text>
<rect class="autouse" width="862" height="40" x="1" y="181" />
<rect width="10" height="100" class="autouse" x="426" y="151" />
<text x="431" y="201">autouse</text>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,36 @@
import pytest
@pytest.fixture
def order():
return []
@pytest.fixture
def c1(order):
order.append("c1")
@pytest.fixture
def c2(order):
order.append("c2")
class TestClassWithAutouse:
@pytest.fixture(autouse=True)
def c3(self, order, c2):
order.append("c3")
def test_req(self, order, c1):
assert order == ["c2", "c3", "c1"]
def test_no_req(self, order):
assert order == ["c2", "c3"]
class TestClassWithoutAutouse:
def test_req(self, order, c1):
assert order == ["c1"]
def test_no_req(self, order):
assert order == []

View File

@@ -0,0 +1,100 @@
<svg xmlns="http://www.w3.org/2000/svg" width="862" height="502">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class {
fill: #0e84b5;
}
line {
stroke: black;
stroke-width: 2;
}
rect.autouse {
fill: #ca7f3d;
}
</style>
<!-- TestWithAutouse -->
<circle class="class" r="250" cx="251" cy="251" />
<!-- scope name -->
<defs>
<path d="M11,251 A 240 240 0 0 1 491 251" id="testClassWith"/>
</defs>
<text class="class">
<textPath href="#testClassWith" startOffset="50%">TestWithAutouse</textPath>
</text>
<mask id="autouseScope">
<circle fill="white" r="249" cx="251" cy="251" />
</mask>
<!-- TestWithAutouse.test_req -->
<line x1="176" x2="176" y1="76" y2="426"/>
<ellipse class="fixture" rx="50" ry="25" cx="176" cy="76" />
<text x="176" y="76">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="176" cy="146" />
<text x="176" y="146">c2</text>
<ellipse class="fixture" rx="25" ry="25" cx="176" cy="216" />
<text x="176" y="216">c3</text>
<ellipse class="fixture" rx="25" ry="25" cx="176" cy="356" />
<text x="176" y="356">c1</text>
<rect class="test" width="100" height="50" x="126" y="401" />
<text x="176" y="426">test_req</text>
<!-- TestWithAutouse.test_no_req -->
<line x1="326" x2="326" y1="76" y2="346"/>
<ellipse class="fixture" rx="50" ry="25" cx="326" cy="76" />
<text x="326" y="76">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="326" cy="146" />
<text x="326" y="146">c2</text>
<ellipse class="fixture" rx="25" ry="25" cx="326" cy="216" />
<text x="326" y="216">c3</text>
<rect class="test" width="120" height="50" x="266" y="331" />
<text x="326" y="356">test_no_req</text>
<rect class="autouse" width="500" height="40" x="1" y="266" mask="url(#autouseScope)"/>
<text x="261" y="286">autouse</text>
<!-- TestWithoutAutouse -->
<circle class="class" r="170" cx="691" cy="251" />
<!-- scope name -->
<defs>
<path d="M 531,251 A 160 160 0 0 1 851 251" id="testClassWithout"/>
</defs>
<text class="class">
<textPath href="#testClassWithout" startOffset="50%">TestWithoutAutouse</textPath>
</text>
<!-- TestWithoutAutouse.test_req -->
<line x1="616" x2="616" y1="181" y2="321"/>
<ellipse class="fixture" rx="50" ry="25" cx="616" cy="181" />
<text x="616" y="181">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="616" cy="251" />
<text x="616" y="251">c1</text>
<rect class="test" width="100" height="50" x="566" y="296" />
<text x="616" y="321">test_req</text>
<!-- TestWithoutAutouse.test_no_req -->
<line x1="766" x2="766" y1="181" y2="251"/>
<ellipse class="fixture" rx="50" ry="25" cx="766" cy="181" />
<text x="766" y="181">order</text>
<rect class="test" width="120" height="50" x="706" y="226" />
<text x="766" y="251">test_no_req</text>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,45 @@
import pytest
@pytest.fixture
def order():
return []
@pytest.fixture
def a(order):
order.append("a")
@pytest.fixture
def b(a, order):
order.append("b")
@pytest.fixture
def c(a, b, order):
order.append("c")
@pytest.fixture
def d(c, b, order):
order.append("d")
@pytest.fixture
def e(d, b, order):
order.append("e")
@pytest.fixture
def f(e, order):
order.append("f")
@pytest.fixture
def g(f, c, order):
order.append("g")
def test_order(g, order):
assert order == ["a", "b", "c", "d", "e", "f", "g"]

View File

@@ -0,0 +1,60 @@
<svg xmlns="http://www.w3.org/2000/svg" width="252" height="612">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class {
fill: #0e84b5;
}
path, line {
stroke: black;
stroke-width: 2;
fill: none;
}
</style>
<path d="M126,516 L26,436 L26,236" />
<path d="M226,376 L226,236 L126,166" />
<line x1="126" x2="126" y1="586" y2="446" />
<line x1="126" x2="226" y1="446" y2="376" />
<line x1="226" x2="126" y1="376" y2="306" />
<line x1="126" x2="26" y1="306" y2="236" />
<line x1="126" x2="126" y1="306" y2="166" />
<line x1="26" x2="126" y1="236" y2="166" />
<line x1="126" x2="126" y1="166" y2="26" />
<line x1="126" x2="126" y1="96" y2="26" />
<ellipse class="fixture" rx="50" ry="25" cx="126" cy="26" />
<text x="126" y="26">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="96" />
<text x="126" y="96">a</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="166" />
<text x="126" y="166">b</text>
<ellipse class="fixture" rx="25" ry="25" cx="26" cy="236" />
<text x="26" y="236">c</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="306" />
<text x="126" y="306">d</text>
<ellipse class="fixture" rx="25" ry="25" cx="226" cy="376" />
<text x="226" y="376">e</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="446" />
<text x="126" y="446">f</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="516" />
<text x="126" y="516">g</text>
<rect class="test" width="110" height="50" x="71" y="561" />
<text x="126" y="586">test_order</text>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,51 @@
<svg xmlns="http://www.w3.org/2000/svg" width="112" height="612">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class {
fill: #0e84b5;
}
path, line {
stroke: black;
stroke-width: 2;
fill: none;
}
</style>
<line x1="56" x2="56" y1="611" y2="26" />
<ellipse class="fixture" rx="50" ry="25" cx="56" cy="26" />
<text x="56" y="26">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="96" />
<text x="56" y="96">a</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="166" />
<text x="56" y="166">b</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="236" />
<text x="56" y="236">c</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="306" />
<text x="56" y="306">d</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="376" />
<text x="56" y="376">e</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="446" />
<text x="56" y="446">f</text>
<ellipse class="fixture" rx="25" ry="25" cx="56" cy="516" />
<text x="56" y="516">g</text>
<rect class="test" width="110" height="50" x="1" y="561" />
<text x="56" y="586">test_order</text>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,60 @@
<svg xmlns="http://www.w3.org/2000/svg" width="252" height="542">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class {
fill: #0e84b5;
}
path, line {
stroke: black;
stroke-width: 2;
fill: none;
}
</style>
<path d="M126,446 L26,376 L26,236" />
<path d="M226,306 L126,236 L126,166" />
<line x1="126" x2="126" y1="516" y2="446" />
<line x1="226" x2="226" y1="376" y2="306" />
<line x1="226" x2="226" y1="306" y2="236" />
<line x1="226" x2="126" y1="236" y2="166" />
<line x1="126" x2="226" y1="446" y2="376" />
<line x1="26" x2="126" y1="236" y2="166" />
<line x1="126" x2="126" y1="166" y2="96" />
<line x1="126" x2="126" y1="96" y2="26" />
<ellipse class="fixture" rx="50" ry="25" cx="126" cy="26" />
<text x="126" y="26">order</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="96" />
<text x="126" y="96">a</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="166" />
<text x="126" y="166">b</text>
<ellipse class="fixture" rx="25" ry="25" cx="26" cy="236" />
<text x="26" y="236">c</text>
<ellipse class="fixture" rx="25" ry="25" cx="226" cy="236" />
<text x="226" y="236">d</text>
<ellipse class="fixture" rx="25" ry="25" cx="226" cy="306" />
<text x="226" y="306">e</text>
<ellipse class="fixture" rx="25" ry="25" cx="226" cy="376" />
<text x="226" y="376">f</text>
<ellipse class="fixture" rx="25" ry="25" cx="126" cy="446" />
<text x="126" y="446">g</text>
<rect class="test" width="110" height="50" x="71" y="491" />
<text x="126" y="516">test_order</text>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,36 @@
import pytest
@pytest.fixture(scope="session")
def order():
return []
@pytest.fixture
def func(order):
order.append("function")
@pytest.fixture(scope="class")
def cls(order):
order.append("class")
@pytest.fixture(scope="module")
def mod(order):
order.append("module")
@pytest.fixture(scope="package")
def pack(order):
order.append("package")
@pytest.fixture(scope="session")
def sess(order):
order.append("session")
class TestClass:
def test_order(self, func, cls, mod, pack, sess, order):
assert order == ["session", "package", "module", "class", "function"]

View File

@@ -0,0 +1,55 @@
<svg xmlns="http://www.w3.org/2000/svg" width="262" height="537">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class {
fill: #0e84b5;
}
line {
stroke: black;
stroke-width: 2;
}
</style>
<!-- TestClass -->
<circle class="class" r="130" cx="131" cy="406" />
<line x1="131" x2="131" y1="21" y2="446"/>
<ellipse class="fixture" rx="50" ry="25" cx="131" cy="26" />
<text x="131" y="26">order</text>
<ellipse class="fixture" rx="50" ry="25" cx="131" cy="96" />
<text x="131" y="96">sess</text>
<ellipse class="fixture" rx="50" ry="25" cx="131" cy="166" />
<text x="131" y="166">pack</text>
<ellipse class="fixture" rx="50" ry="25" cx="131" cy="236" />
<text x="131" y="236">mod</text>
<ellipse class="fixture" rx="50" ry="25" cx="131" cy="306" />
<text x="131" y="306">cls</text>
<ellipse class="fixture" rx="50" ry="25" cx="131" cy="376" />
<text x="131" y="376">func</text>
<rect class="test" width="110" height="50" x="76" y="421" />
<text x="131" y="446">test_order</text>
<!-- scope name -->
<defs>
<path d="M131,526 A 120 120 0 0 1 136 286" id="testClass"/>
</defs>
<text class="class">
<textPath href="#testClass" startOffset="50%">TestClass</textPath>
</text>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,29 @@
import pytest
@pytest.fixture
def order():
return []
@pytest.fixture
def outer(order, inner):
order.append("outer")
class TestOne:
@pytest.fixture
def inner(self, order):
order.append("one")
def test_order(self, order, outer):
assert order == ["one", "outer"]
class TestTwo:
@pytest.fixture
def inner(self, order):
order.append("two")
def test_order(self, order, outer):
assert order == ["two", "outer"]

View File

@@ -0,0 +1,115 @@
<svg xmlns="http://www.w3.org/2000/svg" width="562" height="532">
<style>
text {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
dominant-baseline: middle;
text-anchor: middle;
fill: #062886;
font-size: medium;
}
ellipse.fixture, rect.test {
fill: #eeffcc;
stroke: #007020;
stroke-width: 2;
}
text.fixture {
color: #06287e;
}
circle.class {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
circle.module {
fill: #c3e0ec;
stroke: #0e84b5;
stroke-width: 2;
}
text.class, text.module {
fill: #0e84b5;
}
line, path {
stroke: black;
stroke-width: 2;
fill: none;
}
</style>
<!-- main scope -->
<circle class="module" r="265" cx="281" cy="266" />
<!-- scope name -->
<defs>
<path d="M 26,266 A 255 255 0 0 1 536 266" id="testModule"/>
</defs>
<text class="module">
<textPath href="#testModule" startOffset="50%">test_fixtures_request_different_scope.py</textPath>
</text>
<!-- TestOne -->
<circle class="class" r="100" cx="141" cy="266" />
<!-- inner -->
<line x1="141" x2="141" y1="231" y2="301"/>
<!-- order -->
<path d="M 141 296 L 201 296 L 211 286 L 211 146 L 221 136 L 281 136" />
<!-- outer -->
<path d="M 141 306 L 201 306 L 211 316 L 211 386 L 221 396 L 281 396" />
<ellipse class="fixture" rx="50" ry="25" cx="141" cy="231" />
<text x="141" y="231">inner</text>
<rect class="test" width="110" height="50" x="86" y="276" />
<text x="141" y="301">test_order</text>
<!-- scope name -->
<defs>
<path d="M 51,266 A 90 90 0 0 1 231 266" id="testOne"/>
</defs>
<text class="class">
<textPath href="#testOne" startOffset="50%">TestOne</textPath>
</text>
<!-- scope order number -->
<mask id="testOneOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="100" cx="141" cy="266" />
</mask>
<circle class="module" r="15" cx="41" cy="266" mask="url(#testOneOrderMask)"/>
<text class="module" x="41" y="266">1</text>
<!-- scope order number -->
<mask id="testMainOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="265" cx="281" cy="266" />
</mask>
<circle class="module" r="15" cx="16" cy="266" mask="url(#testMainOrderMask)"/>
<text class="module" x="16" y="266">2</text>
<!-- TestTwo -->
<circle class="class" r="100" cx="421" cy="266" />
<!-- inner -->
<line x1="421" x2="421" y1="231" y2="301"/>
<!-- order -->
<path d="M 421 296 L 361 296 L 351 286 L 351 146 L 341 136 L 281 136" />
<!-- outer -->
<path d="M 421 306 L 361 306 L 351 316 L 351 386 L 341 396 L 281 396" />
<ellipse class="fixture" rx="50" ry="25" cx="421" cy="231" />
<text x="421" y="231">inner</text>
<rect class="test" width="110" height="50" x="366" y="276" />
<text x="421" y="301">test_order</text>
<!-- scope name -->
<defs>
<path d="M 331,266 A 90 90 0 0 1 511 266" id="testTwo"/>
</defs>
<text class="class">
<textPath href="#testTwo" startOffset="50%">TestTwo</textPath>
</text>
<!-- scope order number -->
<mask id="testTwoOrderMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<circle fill="black" stroke="white" stroke-width="2" r="100" cx="421" cy="266" />
</mask>
<circle class="module" r="15" cx="521" cy="266" mask="url(#testTwoOrderMask)"/>
<text class="module" x="521" y="266">1</text>
<!-- scope order number -->
<circle class="module" r="15" cx="546" cy="266" mask="url(#testMainOrderMask)"/>
<text class="module" x="546" y="266">2</text>
<ellipse class="fixture" rx="50" ry="25" cx="281" cy="396" />
<text x="281" y="396">outer</text>
<ellipse class="fixture" rx="50" ry="25" cx="281" cy="136" />
<text x="281" y="136">order</text>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -508,11 +508,12 @@ Running it results in some skips if we don't have all the python interpreters in
.. code-block:: pytest
. $ pytest -rs -q multipython.py
ssssssssssss...ssssssssssss [100%]
sssssssssssssssssssssssssss [100%]
========================= short test summary info ==========================
SKIPPED [12] multipython.py:29: 'python3.5' not found
SKIPPED [12] multipython.py:29: 'python3.7' not found
3 passed, 24 skipped in 0.12s
SKIPPED [9] multipython.py:29: 'python3.5' not found
SKIPPED [9] multipython.py:29: 'python3.6' not found
SKIPPED [9] multipython.py:29: 'python3.7' not found
27 skipped in 0.12s
Indirect parametrization of optional implementations/imports
--------------------------------------------------------------------
@@ -637,13 +638,13 @@ Then run ``pytest`` with verbose mode and with only the ``basic`` marker:
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collecting ... collected 14 items / 11 deselected / 3 selected
collecting ... collected 24 items / 21 deselected / 3 selected
test_pytest_param_example.py::test_eval[1+7-8] PASSED [ 33%]
test_pytest_param_example.py::test_eval[basic_2+4] PASSED [ 66%]
test_pytest_param_example.py::test_eval[basic_6*9] XFAIL [100%]
=============== 2 passed, 11 deselected, 1 xfailed in 0.12s ================
=============== 2 passed, 21 deselected, 1 xfailed in 0.12s ================
As the result:

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,7 @@ Install ``pytest``
.. code-block:: bash
$ pytest --version
pytest 6.2.0
pytest 6.2.4
.. _`simpletest`:

View File

@@ -2,7 +2,7 @@
.. sidebar:: Next Open Trainings
- `Professional testing with Python <https://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_, via Python Academy, February 1-3 2021, Leipzig (Germany) and remote.
- `Professionelles Testen für Python mit pytest <https://www.enterpy.de/lecture_compact1.php?id=12713>`_ (German), part of the enterPy conference, April 22nd (sold out) and May 20th, remote.
Also see `previous talks and blogposts <talks.html>`_.
@@ -11,6 +11,7 @@
pytest: helps you write better programs
=======================================
.. module:: pytest
The ``pytest`` framework makes it easy to write small tests, yet
scales to support complex functional testing for applications and libraries.

View File

@@ -26,6 +26,8 @@ classifiers =
Topic :: Utilities
keywords = test, unittest
project_urls =
Changelog=https://docs.pytest.org/en/stable/changelog.html
Twitter=https://twitter.com/pytestdotorg
Source=https://github.com/pytest-dev/pytest
Tracker=https://github.com/pytest-dev/pytest/issues

View File

@@ -673,12 +673,9 @@ class AssertionRewriter(ast.NodeVisitor):
if not mod.body:
# Nothing to do.
return
# Insert some special imports at the top of the module but after any
# docstrings and __future__ imports.
aliases = [
ast.alias("builtins", "@py_builtins"),
ast.alias("_pytest.assertion.rewrite", "@pytest_ar"),
]
# We'll insert some special imports at the top of the module, but after any
# docstrings and __future__ imports, so first figure out where that is.
doc = getattr(mod, "docstring", None)
expect_docstring = doc is None
if doc is not None and self.is_rewrite_disabled(doc):
@@ -710,10 +707,27 @@ class AssertionRewriter(ast.NodeVisitor):
lineno = item.decorator_list[0].lineno
else:
lineno = item.lineno
# Now actually insert the special imports.
if sys.version_info >= (3, 10):
aliases = [
ast.alias("builtins", "@py_builtins", lineno=lineno, col_offset=0),
ast.alias(
"_pytest.assertion.rewrite",
"@pytest_ar",
lineno=lineno,
col_offset=0,
),
]
else:
aliases = [
ast.alias("builtins", "@py_builtins"),
ast.alias("_pytest.assertion.rewrite", "@pytest_ar"),
]
imports = [
ast.Import([alias], lineno=lineno, col_offset=0) for alias in aliases
]
mod.body[pos:pos] = imports
# Collect asserts.
nodes: List[ast.AST] = [mod]
while nodes:

View File

@@ -69,7 +69,12 @@ class FaultHandlerHooks:
@staticmethod
def _get_stderr_fileno():
try:
return sys.stderr.fileno()
fileno = sys.stderr.fileno()
# The Twisted Logger will return an invalid file descriptor since it is not backed
# by an FD. So, let's also forward this to the same code path as with pytest-xdist.
if fileno == -1:
raise AttributeError()
return fileno
except (AttributeError, io.UnsupportedOperation):
# pytest-xdist monkeypatches sys.stderr with an object that is not an actual file.
# https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors

View File

@@ -38,7 +38,7 @@ class OutcomeException(BaseException):
self.pytrace = pytrace
def __repr__(self) -> str:
if self.msg:
if self.msg is not None:
return self.msg
return f"<{self.__class__.__name__} instance>"

View File

@@ -64,13 +64,6 @@ def get_lock_path(path: _AnyPurePath) -> _AnyPurePath:
return path.joinpath(".lock")
def ensure_reset_dir(path: Path) -> None:
"""Ensure the given path is an empty directory."""
if path.exists():
rm_rf(path)
path.mkdir()
def on_rm_rf_error(func, path: str, exc, *, start_path: Path) -> bool:
"""Handle known read-only errors during rmtree.
@@ -214,7 +207,7 @@ def _force_symlink(
pass
def make_numbered_dir(root: Path, prefix: str) -> Path:
def make_numbered_dir(root: Path, prefix: str, mode: int = 0o700) -> Path:
"""Create a directory with an increased number as suffix for the given prefix."""
for i in range(10):
# try up to 10 times to create the folder
@@ -222,7 +215,7 @@ def make_numbered_dir(root: Path, prefix: str) -> Path:
new_number = max_existing + 1
new_path = root.joinpath(f"{prefix}{new_number}")
try:
new_path.mkdir()
new_path.mkdir(mode=mode)
except Exception:
pass
else:
@@ -354,13 +347,13 @@ def cleanup_numbered_dir(
def make_numbered_dir_with_cleanup(
root: Path, prefix: str, keep: int, lock_timeout: float
root: Path, prefix: str, keep: int, lock_timeout: float, mode: int,
) -> Path:
"""Create a numbered dir with a cleanup lock and remove old ones."""
e = None
for i in range(10):
try:
p = make_numbered_dir(root, prefix)
p = make_numbered_dir(root, prefix, mode)
lock_path = create_cleanup_lock(p)
register_cleanup_lock_removal(lock_path)
except Exception as exc:
@@ -543,7 +536,7 @@ def import_path(
module_file = module_file[: -(len(os.path.sep + "__init__.py"))]
try:
is_same = os.path.samefile(str(path), module_file)
is_same = _is_same(str(path), module_file)
except FileNotFoundError:
is_same = False
@@ -553,6 +546,20 @@ def import_path(
return mod
# Implement a special _is_same function on Windows which returns True if the two filenames
# compare equal, to circumvent os.path.samefile returning False for mounts in UNC (#7678).
if sys.platform.startswith("win"):
def _is_same(f1: str, f2: str) -> bool:
return Path(f1) == Path(f2) or os.path.samefile(f1, f2)
else:
def _is_same(f1: str, f2: str) -> bool:
return os.path.samefile(f1, f2)
def resolve_package_path(path: Path) -> Optional[Path]:
"""Return the Python package path by looking for the last
directory upwards which still contains an __init__.py.

View File

@@ -1426,7 +1426,7 @@ class Pytester:
:rtype: RunResult
"""
__tracebackhide__ = True
p = make_numbered_dir(root=self.path, prefix="runpytest-")
p = make_numbered_dir(root=self.path, prefix="runpytest-", mode=0o700)
args = ("--basetemp=%s" % p,) + args
plugins = [x for x in self.plugins if isinstance(x, str)]
if plugins:
@@ -1445,7 +1445,7 @@ class Pytester:
The pexpect child is returned.
"""
basetemp = self.path / "temp-pexpect"
basetemp.mkdir()
basetemp.mkdir(mode=0o700)
invoke = " ".join(map(str, self._getpytestargs()))
cmd = f"{invoke} --basetemp={basetemp} {string}"
return self.spawn(cmd, expect_timeout=expect_timeout)

View File

@@ -15,9 +15,14 @@ from typing import overload
from typing import Pattern
from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
if TYPE_CHECKING:
from numpy import ndarray
import _pytest._code
from _pytest.compat import final
from _pytest.compat import STRING_TYPES
@@ -232,10 +237,11 @@ class ApproxScalar(ApproxBase):
def __eq__(self, actual) -> bool:
"""Return whether the given value is equal to the expected value
within the pre-specified tolerance."""
if _is_numpy_array(actual):
asarray = _as_numpy_array(actual)
if asarray is not None:
# Call ``__eq__()`` manually to prevent infinite-recursion with
# numpy<1.13. See #3748.
return all(self.__eq__(a) for a in actual.flat)
return all(self.__eq__(a) for a in asarray.flat)
# Short-circuit exact equality.
if actual == self.expected:
@@ -521,6 +527,7 @@ def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase:
elif isinstance(expected, Mapping):
cls = ApproxMapping
elif _is_numpy_array(expected):
expected = _as_numpy_array(expected)
cls = ApproxNumpy
elif (
isinstance(expected, Iterable)
@@ -536,16 +543,30 @@ def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase:
def _is_numpy_array(obj: object) -> bool:
"""Return true if the given object is a numpy array.
"""
Return true if the given object is implicitly convertible to ndarray,
and numpy is already imported.
"""
return _as_numpy_array(obj) is not None
A special effort is made to avoid importing numpy unless it's really necessary.
def _as_numpy_array(obj: object) -> Optional["ndarray"]:
"""
Return an ndarray if the given object is implicitly convertible to ndarray,
and numpy is already imported, otherwise None.
"""
import sys
np: Any = sys.modules.get("numpy")
if np is not None:
return isinstance(obj, np.ndarray)
return False
# avoid infinite recursion on numpy scalars, which have __array__
if np.isscalar(obj):
return None
elif isinstance(obj, np.ndarray):
return obj
elif hasattr(obj, "__array__") or hasattr("obj", "__array_interface__"):
return np.asarray(obj)
return None
# builtin pytest.raises helper

View File

@@ -1400,4 +1400,6 @@ def _get_raw_skip_reason(report: TestReport) -> str:
_, _, reason = report.longrepr
if reason.startswith("Skipped: "):
reason = reason[len("Skipped: ") :]
elif reason == "Skipped":
reason = ""
return reason

View File

@@ -8,10 +8,10 @@ from typing import Optional
import attr
import py
from .pathlib import ensure_reset_dir
from .pathlib import LOCK_TIMEOUT
from .pathlib import make_numbered_dir
from .pathlib import make_numbered_dir_with_cleanup
from .pathlib import rm_rf
from _pytest.compat import final
from _pytest.config import Config
from _pytest.deprecated import check_ispytest
@@ -90,20 +90,22 @@ class TempPathFactory:
basename = self._ensure_relative_to_basetemp(basename)
if not numbered:
p = self.getbasetemp().joinpath(basename)
p.mkdir()
p.mkdir(mode=0o700)
else:
p = make_numbered_dir(root=self.getbasetemp(), prefix=basename)
p = make_numbered_dir(root=self.getbasetemp(), prefix=basename, mode=0o700)
self._trace("mktemp", p)
return p
def getbasetemp(self) -> Path:
"""Return base temporary directory."""
"""Return the base temporary directory, creating it if needed."""
if self._basetemp is not None:
return self._basetemp
if self._given_basetemp is not None:
basetemp = self._given_basetemp
ensure_reset_dir(basetemp)
if basetemp.exists():
rm_rf(basetemp)
basetemp.mkdir(mode=0o700)
basetemp = basetemp.resolve()
else:
from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT")
@@ -112,14 +114,37 @@ class TempPathFactory:
# use a sub-directory in the temproot to speed-up
# make_numbered_dir() call
rootdir = temproot.joinpath(f"pytest-of-{user}")
rootdir.mkdir(exist_ok=True)
rootdir.mkdir(mode=0o700, exist_ok=True)
# Because we use exist_ok=True with a predictable name, make sure
# we are the owners, to prevent any funny business (on unix, where
# temproot is usually shared).
# Also, to keep things private, fixup any world-readable temp
# rootdir's permissions. Historically 0o755 was used, so we can't
# just error out on this, at least for a while.
if hasattr(os, "getuid"):
rootdir_stat = rootdir.stat()
uid = os.getuid()
# getuid shouldn't fail, but cpython defines such a case.
# Let's hope for the best.
if uid != -1:
if rootdir_stat.st_uid != uid:
raise OSError(
f"The temporary directory {rootdir} is not owned by the current user. "
"Fix this and try again."
)
if (rootdir_stat.st_mode & 0o077) != 0:
os.chmod(rootdir, rootdir_stat.st_mode & ~0o077)
basetemp = make_numbered_dir_with_cleanup(
prefix="pytest-", root=rootdir, keep=3, lock_timeout=LOCK_TIMEOUT
prefix="pytest-",
root=rootdir,
keep=3,
lock_timeout=LOCK_TIMEOUT,
mode=0o700,
)
assert basetemp is not None, basetemp
self._basetemp = t = basetemp
self._trace("new basetemp", t)
return t
self._basetemp = basetemp
self._trace("new basetemp", basetemp)
return basetemp
@final

View File

@@ -447,6 +447,36 @@ class TestApprox:
assert a12 != approx(a21)
assert a21 != approx(a12)
def test_numpy_array_protocol(self):
"""
array-like objects such as tensorflow's DeviceArray are handled like ndarray.
See issue #8132
"""
np = pytest.importorskip("numpy")
class DeviceArray:
def __init__(self, value, size):
self.value = value
self.size = size
def __array__(self):
return self.value * np.ones(self.size)
class DeviceScalar:
def __init__(self, value):
self.value = value
def __array__(self):
return np.array(self.value)
expected = 1
actual = 1 + 1e-6
assert approx(expected) == DeviceArray(actual, size=1)
assert approx(expected) == DeviceArray(actual, size=2)
assert approx(expected) == DeviceScalar(actual)
assert approx(DeviceScalar(expected)) == actual
assert approx(DeviceScalar(expected)) == DeviceScalar(actual)
def test_doctests(self, mocked_doctest_runner) -> None:
import doctest

View File

@@ -1,3 +1,4 @@
import io
import sys
import pytest
@@ -135,3 +136,27 @@ def test_already_initialized(faulthandler_timeout: int, pytester: Pytester) -> N
result.stdout.no_fnmatch_line(warning_line)
result.stdout.fnmatch_lines("*1 passed*")
assert result.ret == 0
def test_get_stderr_fileno_invalid_fd() -> None:
"""Test for faulthandler being able to handle invalid file descriptors for stderr (#8249)."""
from _pytest.faulthandler import FaultHandlerHooks
class StdErrWrapper(io.StringIO):
"""
Mimic ``twisted.logger.LoggingFile`` to simulate returning an invalid file descriptor.
https://github.com/twisted/twisted/blob/twisted-20.3.0/src/twisted/logger/_io.py#L132-L139
"""
def fileno(self):
return -1
wrapper = StdErrWrapper()
with pytest.MonkeyPatch.context() as mp:
mp.setattr("sys.stderr", wrapper)
# Even when the stderr wrapper signals an invalid file descriptor,
# ``_get_stderr_fileno()`` should return the real one.
assert FaultHandlerHooks._get_stderr_fileno() == 2

View File

@@ -7,6 +7,7 @@ from textwrap import dedent
import py
import pytest
from _pytest.monkeypatch import MonkeyPatch
from _pytest.pathlib import bestrelpath
from _pytest.pathlib import commonpath
from _pytest.pathlib import ensure_deletable
@@ -414,3 +415,23 @@ def test_visit_ignores_errors(tmpdir) -> None:
"bar",
"foo",
]
@pytest.mark.skipif(not sys.platform.startswith("win"), reason="Windows only")
def test_samefile_false_negatives(tmp_path: Path, monkeypatch: MonkeyPatch) -> None:
"""
import_file() should not raise ImportPathMismatchError if the paths are exactly
equal on Windows. It seems directories mounted as UNC paths make os.path.samefile
return False, even when they are clearly equal.
"""
module_path = tmp_path.joinpath("my_module.py")
module_path.write_text("def foo(): return 42")
monkeypatch.syspath_prepend(tmp_path)
with monkeypatch.context() as mp:
# Forcibly make os.path.samefile() return False here to ensure we are comparing
# the paths too. Using a context to narrow the patch as much as possible given
# this is an important system function.
mp.setattr(os.path, "samefile", lambda x, y: False)
module = import_path(module_path)
assert getattr(module, "foo")() == 42

View File

@@ -366,6 +366,26 @@ class TestTerminal:
@pytest.mark.xfail(reason="")
def test_4():
assert False
@pytest.mark.skip
def test_5():
pass
@pytest.mark.xfail
def test_6():
pass
def test_7():
pytest.skip()
def test_8():
pytest.skip("888 is great")
def test_9():
pytest.xfail()
def test_10():
pytest.xfail("It's 🕙 o'clock")
"""
)
result = pytester.runpytest("-v")
@@ -375,6 +395,12 @@ class TestTerminal:
"test_verbose_skip_reason.py::test_2 XPASS (456) *",
"test_verbose_skip_reason.py::test_3 XFAIL (789) *",
"test_verbose_skip_reason.py::test_4 XFAIL *",
"test_verbose_skip_reason.py::test_5 SKIPPED (unconditional skip) *",
"test_verbose_skip_reason.py::test_6 XPASS *",
"test_verbose_skip_reason.py::test_7 SKIPPED *",
"test_verbose_skip_reason.py::test_8 SKIPPED (888 is great) *",
"test_verbose_skip_reason.py::test_9 XFAIL *",
"test_verbose_skip_reason.py::test_10 XFAIL (It's 🕙 o'clock) *",
]
)

View File

@@ -445,3 +445,44 @@ def test_basetemp_with_read_only_files(pytester: Pytester) -> None:
# running a second time and ensure we don't crash
result = pytester.runpytest("--basetemp=tmp")
assert result.ret == 0
@pytest.mark.skipif(not hasattr(os, "getuid"), reason="checks unix permissions")
def test_tmp_path_factory_create_directory_with_safe_permissions(
tmp_path: Path, monkeypatch,
) -> None:
"""Verify that pytest creates directories under /tmp with private permissions."""
# Use the test's tmp_path as the system temproot (/tmp).
monkeypatch.setenv("PYTEST_DEBUG_TEMPROOT", str(tmp_path))
tmp_factory = TempPathFactory(None, lambda *args: None, _ispytest=True)
basetemp = tmp_factory.getbasetemp()
# No world-readable permissions.
assert (basetemp.stat().st_mode & 0o077) == 0
# Parent too (pytest-of-foo).
assert (basetemp.parent.stat().st_mode & 0o077) == 0
@pytest.mark.skipif(not hasattr(os, "getuid"), reason="checks unix permissions")
def test_tmp_path_factory_fixes_up_world_readable_permissions(
tmp_path: Path, monkeypatch,
) -> None:
"""Verify that if a /tmp/pytest-of-foo directory already exists with
world-readable permissions, it is fixed.
pytest used to mkdir with such permissions, that's why we fix it up.
"""
# Use the test's tmp_path as the system temproot (/tmp).
monkeypatch.setenv("PYTEST_DEBUG_TEMPROOT", str(tmp_path))
tmp_factory = TempPathFactory(None, lambda *args: None, _ispytest=True)
basetemp = tmp_factory.getbasetemp()
# Before - simulate bad perms.
os.chmod(basetemp.parent, 0o777)
assert (basetemp.parent.stat().st_mode & 0o077) != 0
tmp_factory = TempPathFactory(None, lambda *args: None, _ispytest=True)
basetemp = tmp_factory.getbasetemp()
# After - fixed.
assert (basetemp.parent.stat().st_mode & 0o077) == 0