Repository: tox-dev/tox

Issue:
Have pre and post test run in config
New summary:

Plugins can already run pre and post test commands via https://tox.readthedocs.io/en/latest/plugins.html#tox.hookspecs.tox_runtest_post and https://tox.readthedocs.io/en/latest/plugins.html#tox.hookspecs.tox_runtest_pre. We should allow users to add this inside their config file too, so they don't have to create tox plugins for one time behaviours (e.g. setup/cleanup operations of dependencies).  




Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=h1) Report
> Merging [#1000](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=desc) into [master](https://codecov.io/gh/tox-dev/tox/commit/70bfb8300e604d8b7297a9458608e326934d49de?src=pr&el=desc) will **decrease** coverage by `5.53%`.
> The diff coverage is `89.47%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/1000/graphs/tree.svg?width=650&token=DYodAwDCZ5&height=150&src=pr)](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1000      +/-   ##
==========================================
- Coverage   69.78%   64.25%   -5.54%     
==========================================
  Files          14       14              
  Lines        3406     2960     -446     
  Branches      453      359      -94     
==========================================
- Hits         2377     1902     -475     
- Misses        965      975      +10     
- Partials       64       83      +19
```


| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/tox/config.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25maWcucHk=) | `57.95% <ø> (-7.62%)` | :arrow_down: |
| [src/tox/package.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9wYWNrYWdlLnB5) | `51.28% <ø> (-7.64%)` | :arrow_down: |
| [src/tox/session.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9zZXNzaW9uLnB5) | `74.86% <50%> (-2.87%)` | :arrow_down: |
| [src/tox/venv.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC92ZW52LnB5) | `73.69% <94.11%> (-2.19%)` | :arrow_down: |
| [src/tox/interpreters.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9pbnRlcnByZXRlcnMucHk=) | `67.42% <0%> (-15.31%)` | :arrow_down: |
| [src/tox/\_quickstart.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9fcXVpY2tzdGFydC5weQ==) | `31.28% <0%> (-11.58%)` | :arrow_down: |
| [src/tox/exception.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9leGNlcHRpb24ucHk=) | `69.04% <0%> (-9.68%)` | :arrow_down: |
| [src/tox/constants.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25zdGFudHMucHk=) | `53.48% <0%> (-6.09%)` | :arrow_down: |
| ... and [5 more](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=footer). Last update [70bfb83...d1d646f](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=h1) Report
> Merging [#1000](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=desc) into [master](https://codecov.io/gh/tox-dev/tox/commit/70bfb8300e604d8b7297a9458608e326934d49de?src=pr&el=desc) will **increase** coverage by `21.9%`.
> The diff coverage is `84.61%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/1000/graphs/tree.svg?width=650&token=DYodAwDCZ5&height=150&src=pr)](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1000      +/-   ##
==========================================
+ Coverage   69.78%   91.68%   +21.9%     
==========================================
  Files          14       14              
  Lines        3406     2563     -843     
  Branches      453      431      -22     
==========================================
- Hits         2377     2350      -27     
+ Misses        965      141     -824     
- Partials       64       72       +8
```


| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/tox/package.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9wYWNrYWdlLnB5) | `89.34% <0%> (+30.42%)` | :arrow_up: |
| [src/tox/config.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25maWcucHk=) | `96.07% <100%> (+30.5%)` | :arrow_up: |
| [src/tox/session.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9zZXNzaW9uLnB5) | `91.36% <50%> (+13.63%)` | :arrow_up: |
| [src/tox/venv.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC92ZW52LnB5) | `92.63% <95%> (+16.74%)` | :arrow_up: |
| [src/tox/interpreters.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9pbnRlcnByZXRlcnMucHk=) | `69.76% <0%> (-12.97%)` | :arrow_down: |
| [src/tox/\_pytestplugin.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9fcHl0ZXN0cGx1Z2luLnB5) | `91.93% <0%> (+11.44%)` | :arrow_up: |
| [src/tox/result.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9yZXN1bHQucHk=) | `96.07% <0%> (+15.75%)` | :arrow_up: |
| [src/tox/exception.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9leGNlcHRpb24ucHk=) | `100% <0%> (+21.27%)` | :arrow_up: |
| ... and [5 more](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=footer). Last update [70bfb83...d1d646f](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=h1) Report
> Merging [#1000](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=desc) into [master](https://codecov.io/gh/tox-dev/tox/commit/70bfb8300e604d8b7297a9458608e326934d49de?src=pr&el=desc) will **decrease** coverage by `1.17%`.
> The diff coverage is `81.48%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/1000/graphs/tree.svg?width=650&token=DYodAwDCZ5&height=150&src=pr)](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1000      +/-   ##
==========================================
- Coverage   69.78%   68.61%   -1.18%     
==========================================
  Files          14       14              
  Lines        3406     3435      +29     
  Branches      453      458       +5     
==========================================
- Hits         2377     2357      -20     
- Misses        965     1012      +47     
- Partials       64       66       +2
```


| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/tox/package.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9wYWNrYWdlLnB5) | `57.67% <0%> (-1.25%)` | :arrow_down: |
| [src/tox/config.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25maWcucHk=) | `64.95% <100%> (-0.62%)` | :arrow_down: |
| [src/tox/session.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9zZXNzaW9uLnB5) | `77.41% <50%> (-0.32%)` | :arrow_down: |
| [src/tox/venv.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC92ZW52LnB5) | `75.34% <90.47%> (-0.55%)` | :arrow_down: |
| [src/tox/interpreters.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9pbnRlcnByZXRlcnMucHk=) | `64.74% <0%> (-17.99%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=footer). Last update [70bfb83...d1d646f](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=h1) Report
> Merging [#1000](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=desc) into [master](https://codecov.io/gh/tox-dev/tox/commit/70bfb8300e604d8b7297a9458608e326934d49de?src=pr&el=desc) will **decrease** coverage by `1.17%`.
> The diff coverage is `81.48%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/1000/graphs/tree.svg?width=650&token=DYodAwDCZ5&height=150&src=pr)](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1000      +/-   ##
==========================================
- Coverage   69.78%   68.61%   -1.18%     
==========================================
  Files          14       14              
  Lines        3406     3435      +29     
  Branches      453      458       +5     
==========================================
- Hits         2377     2357      -20     
- Misses        965     1012      +47     
- Partials       64       66       +2
```


| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/tox/package.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9wYWNrYWdlLnB5) | `57.67% <0%> (-1.25%)` | :arrow_down: |
| [src/tox/config.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25maWcucHk=) | `64.95% <100%> (-0.62%)` | :arrow_down: |
| [src/tox/session.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9zZXNzaW9uLnB5) | `77.41% <50%> (-0.32%)` | :arrow_down: |
| [src/tox/venv.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC92ZW52LnB5) | `75.34% <90.47%> (-0.55%)` | :arrow_down: |
| [src/tox/interpreters.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9pbnRlcnByZXRlcnMucHk=) | `64.74% <0%> (-17.99%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=footer). Last update [70bfb83...d1d646f](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=h1) Report
> Merging [#1000](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=desc) into [master](https://codecov.io/gh/tox-dev/tox/commit/70bfb8300e604d8b7297a9458608e326934d49de?src=pr&el=desc) will **decrease** coverage by `1.17%`.
> The diff coverage is `81.48%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/1000/graphs/tree.svg?width=650&token=DYodAwDCZ5&height=150&src=pr)](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1000      +/-   ##
==========================================
- Coverage   69.78%   68.61%   -1.18%     
==========================================
  Files          14       14              
  Lines        3406     3435      +29     
  Branches      453      458       +5     
==========================================
- Hits         2377     2357      -20     
- Misses        965     1012      +47     
- Partials       64       66       +2
```


| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/tox/package.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9wYWNrYWdlLnB5) | `57.67% <0%> (-1.25%)` | :arrow_down: |
| [src/tox/config.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25maWcucHk=) | `64.95% <100%> (-0.62%)` | :arrow_down: |
| [src/tox/session.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9zZXNzaW9uLnB5) | `77.41% <50%> (-0.32%)` | :arrow_down: |
| [src/tox/venv.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC92ZW52LnB5) | `75.34% <90.47%> (-0.55%)` | :arrow_down: |
| [src/tox/interpreters.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9pbnRlcnByZXRlcnMucHk=) | `64.74% <0%> (-17.99%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=footer). Last update [70bfb83...d1d646f](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=h1) Report
> Merging [#1000](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=desc) into [master](https://codecov.io/gh/tox-dev/tox/commit/70bfb8300e604d8b7297a9458608e326934d49de?src=pr&el=desc) will **decrease** coverage by `0.21%`.
> The diff coverage is `81.48%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/1000/graphs/tree.svg?width=650&token=DYodAwDCZ5&height=150&src=pr)](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1000      +/-   ##
==========================================
- Coverage   69.78%   69.57%   -0.22%     
==========================================
  Files          14       14              
  Lines        3406     3435      +29     
  Branches      453      458       +5     
==========================================
+ Hits         2377     2390      +13     
- Misses        965      980      +15     
- Partials       64       65       +1
```


| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/tox/package.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9wYWNrYWdlLnB5) | `57.67% <0%> (-1.25%)` | :arrow_down: |
| [src/tox/config.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25maWcucHk=) | `65.1% <100%> (-0.47%)` | :arrow_down: |
| [src/tox/session.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9zZXNzaW9uLnB5) | `77.72% <50%> (ø)` | :arrow_up: |
| [src/tox/venv.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC92ZW52LnB5) | `76.26% <90.47%> (+0.38%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=footer). Last update [70bfb83...d1d646f](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=h1) Report
> Merging [#1000](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=desc) into [master](https://codecov.io/gh/tox-dev/tox/commit/70bfb8300e604d8b7297a9458608e326934d49de?src=pr&el=desc) will **decrease** coverage by `0.21%`.
> The diff coverage is `81.48%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/1000/graphs/tree.svg?width=650&token=DYodAwDCZ5&height=150&src=pr)](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1000      +/-   ##
==========================================
- Coverage   69.78%   69.57%   -0.22%     
==========================================
  Files          14       14              
  Lines        3406     3435      +29     
  Branches      453      458       +5     
==========================================
+ Hits         2377     2390      +13     
- Misses        965      980      +15     
- Partials       64       65       +1
```


| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/tox/package.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9wYWNrYWdlLnB5) | `57.67% <0%> (-1.25%)` | :arrow_down: |
| [src/tox/config.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25maWcucHk=) | `65.1% <100%> (-0.47%)` | :arrow_down: |
| [src/tox/session.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9zZXNzaW9uLnB5) | `77.72% <50%> (ø)` | :arrow_up: |
| [src/tox/venv.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC92ZW52LnB5) | `76.26% <90.47%> (+0.38%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=footer). Last update [70bfb83...d1d646f](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=h1) Report
> Merging [#1000](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=desc) into [master](https://codecov.io/gh/tox-dev/tox/commit/70bfb8300e604d8b7297a9458608e326934d49de?src=pr&el=desc) will **decrease** coverage by `0.21%`.
> The diff coverage is `81.48%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/1000/graphs/tree.svg?width=650&token=DYodAwDCZ5&height=150&src=pr)](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1000      +/-   ##
==========================================
- Coverage   69.78%   69.57%   -0.22%     
==========================================
  Files          14       14              
  Lines        3406     3435      +29     
  Branches      453      458       +5     
==========================================
+ Hits         2377     2390      +13     
- Misses        965      980      +15     
- Partials       64       65       +1
```


| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/tox/package.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9wYWNrYWdlLnB5) | `57.67% <0%> (-1.25%)` | :arrow_down: |
| [src/tox/config.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25maWcucHk=) | `65.1% <100%> (-0.47%)` | :arrow_down: |
| [src/tox/session.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9zZXNzaW9uLnB5) | `77.72% <50%> (ø)` | :arrow_up: |
| [src/tox/venv.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC92ZW52LnB5) | `76.26% <90.47%> (+0.38%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=footer). Last update [70bfb83...d1d646f](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=h1) Report
> Merging [#1000](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=desc) into [master](https://codecov.io/gh/tox-dev/tox/commit/70bfb8300e604d8b7297a9458608e326934d49de?src=pr&el=desc) will **decrease** coverage by `0.21%`.
> The diff coverage is `81.48%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/1000/graphs/tree.svg?width=650&token=DYodAwDCZ5&height=150&src=pr)](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1000      +/-   ##
==========================================
- Coverage   69.78%   69.57%   -0.22%     
==========================================
  Files          14       14              
  Lines        3406     3435      +29     
  Branches      453      458       +5     
==========================================
+ Hits         2377     2390      +13     
- Misses        965      980      +15     
- Partials       64       65       +1
```


| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/tox/package.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9wYWNrYWdlLnB5) | `57.67% <0%> (-1.25%)` | :arrow_down: |
| [src/tox/config.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25maWcucHk=) | `65.1% <100%> (-0.47%)` | :arrow_down: |
| [src/tox/session.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9zZXNzaW9uLnB5) | `77.72% <50%> (ø)` | :arrow_up: |
| [src/tox/venv.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC92ZW52LnB5) | `76.26% <90.47%> (+0.38%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=footer). Last update [70bfb83...d1d646f](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=h1) Report
> Merging [#1000](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=desc) into [master](https://codecov.io/gh/tox-dev/tox/commit/70bfb8300e604d8b7297a9458608e326934d49de?src=pr&el=desc) will **decrease** coverage by `0.21%`.
> The diff coverage is `81.48%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/1000/graphs/tree.svg?width=650&token=DYodAwDCZ5&height=150&src=pr)](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1000      +/-   ##
==========================================
- Coverage   69.78%   69.57%   -0.22%     
==========================================
  Files          14       14              
  Lines        3406     3435      +29     
  Branches      453      458       +5     
==========================================
+ Hits         2377     2390      +13     
- Misses        965      980      +15     
- Partials       64       65       +1
```


| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/tox/package.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9wYWNrYWdlLnB5) | `57.67% <0%> (-1.25%)` | :arrow_down: |
| [src/tox/config.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25maWcucHk=) | `65.1% <100%> (-0.47%)` | :arrow_down: |
| [src/tox/session.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9zZXNzaW9uLnB5) | `77.72% <50%> (ø)` | :arrow_up: |
| [src/tox/venv.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC92ZW52LnB5) | `76.26% <90.47%> (+0.38%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=footer). Last update [70bfb83...d1d646f](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=h1) Report
> Merging [#1000](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=desc) into [master](https://codecov.io/gh/tox-dev/tox/commit/70bfb8300e604d8b7297a9458608e326934d49de?src=pr&el=desc) will **decrease** coverage by `0.21%`.
> The diff coverage is `81.48%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/1000/graphs/tree.svg?width=650&token=DYodAwDCZ5&height=150&src=pr)](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1000      +/-   ##
==========================================
- Coverage   69.78%   69.57%   -0.22%     
==========================================
  Files          14       14              
  Lines        3406     3435      +29     
  Branches      453      458       +5     
==========================================
+ Hits         2377     2390      +13     
- Misses        965      980      +15     
- Partials       64       65       +1
```


| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/tox/package.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9wYWNrYWdlLnB5) | `57.67% <0%> (-1.25%)` | :arrow_down: |
| [src/tox/config.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25maWcucHk=) | `65.1% <100%> (-0.47%)` | :arrow_down: |
| [src/tox/session.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9zZXNzaW9uLnB5) | `77.72% <50%> (ø)` | :arrow_up: |
| [src/tox/venv.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC92ZW52LnB5) | `76.26% <90.47%> (+0.38%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=footer). Last update [70bfb83...d1d646f](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=h1) Report
> Merging [#1000](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=desc) into [master](https://codecov.io/gh/tox-dev/tox/commit/70bfb8300e604d8b7297a9458608e326934d49de?src=pr&el=desc) will **decrease** coverage by `0.21%`.
> The diff coverage is `81.48%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/1000/graphs/tree.svg?width=650&token=DYodAwDCZ5&height=150&src=pr)](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1000      +/-   ##
==========================================
- Coverage   69.78%   69.57%   -0.22%     
==========================================
  Files          14       14              
  Lines        3406     3435      +29     
  Branches      453      458       +5     
==========================================
+ Hits         2377     2390      +13     
- Misses        965      980      +15     
- Partials       64       65       +1
```


| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/tox/package.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9wYWNrYWdlLnB5) | `57.67% <0%> (-1.25%)` | :arrow_down: |
| [src/tox/config.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25maWcucHk=) | `65.1% <100%> (-0.47%)` | :arrow_down: |
| [src/tox/session.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9zZXNzaW9uLnB5) | `77.72% <50%> (ø)` | :arrow_up: |
| [src/tox/venv.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC92ZW52LnB5) | `76.26% <90.47%> (+0.38%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=footer). Last update [70bfb83...d1d646f](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=h1) Report
> Merging [#1000](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=desc) into [master](https://codecov.io/gh/tox-dev/tox/commit/70bfb8300e604d8b7297a9458608e326934d49de?src=pr&el=desc) will **decrease** coverage by `0.21%`.
> The diff coverage is `81.48%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/1000/graphs/tree.svg?width=650&token=DYodAwDCZ5&height=150&src=pr)](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1000      +/-   ##
==========================================
- Coverage   69.78%   69.57%   -0.22%     
==========================================
  Files          14       14              
  Lines        3406     3435      +29     
  Branches      453      458       +5     
==========================================
+ Hits         2377     2390      +13     
- Misses        965      980      +15     
- Partials       64       65       +1
```


| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/tox/package.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9wYWNrYWdlLnB5) | `57.67% <0%> (-1.25%)` | :arrow_down: |
| [src/tox/config.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25maWcucHk=) | `65.1% <100%> (-0.47%)` | :arrow_down: |
| [src/tox/session.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9zZXNzaW9uLnB5) | `77.72% <50%> (ø)` | :arrow_up: |
| [src/tox/venv.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC92ZW52LnB5) | `76.26% <90.47%> (+0.38%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=footer). Last update [70bfb83...d1d646f](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=h1) Report
> Merging [#1000](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=desc) into [master](https://codecov.io/gh/tox-dev/tox/commit/70bfb8300e604d8b7297a9458608e326934d49de?src=pr&el=desc) will **decrease** coverage by `0.21%`.
> The diff coverage is `81.48%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/1000/graphs/tree.svg?width=650&token=DYodAwDCZ5&height=150&src=pr)](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1000      +/-   ##
==========================================
- Coverage   69.78%   69.57%   -0.22%     
==========================================
  Files          14       14              
  Lines        3406     3435      +29     
  Branches      453      458       +5     
==========================================
+ Hits         2377     2390      +13     
- Misses        965      980      +15     
- Partials       64       65       +1
```


| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/tox/package.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9wYWNrYWdlLnB5) | `57.67% <0%> (-1.25%)` | :arrow_down: |
| [src/tox/config.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25maWcucHk=) | `65.1% <100%> (-0.47%)` | :arrow_down: |
| [src/tox/session.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9zZXNzaW9uLnB5) | `77.72% <50%> (ø)` | :arrow_up: |
| [src/tox/venv.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC92ZW52LnB5) | `76.26% <90.47%> (+0.38%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=footer). Last update [70bfb83...d1d646f](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=h1) Report
> Merging [#1000](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=desc) into [master](https://codecov.io/gh/tox-dev/tox/commit/70bfb8300e604d8b7297a9458608e326934d49de?src=pr&el=desc) will **decrease** coverage by `0.21%`.
> The diff coverage is `81.48%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/1000/graphs/tree.svg?width=650&token=DYodAwDCZ5&height=150&src=pr)](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1000      +/-   ##
==========================================
- Coverage   69.78%   69.57%   -0.22%     
==========================================
  Files          14       14              
  Lines        3406     3435      +29     
  Branches      453      458       +5     
==========================================
+ Hits         2377     2390      +13     
- Misses        965      980      +15     
- Partials       64       65       +1
```


| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/tox/package.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9wYWNrYWdlLnB5) | `57.67% <0%> (-1.25%)` | :arrow_down: |
| [src/tox/config.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9jb25maWcucHk=) | `65.1% <100%> (-0.47%)` | :arrow_down: |
| [src/tox/session.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC9zZXNzaW9uLnB5) | `77.72% <50%> (ø)` | :arrow_up: |
| [src/tox/venv.py](https://codecov.io/gh/tox-dev/tox/pull/1000/diff?src=pr&el=tree#diff-c3JjL3RveC92ZW52LnB5) | `76.26% <90.47%> (+0.38%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=footer). Last update [70bfb83...d1d646f](https://codecov.io/gh/tox-dev/tox/pull/1000?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

asottile: jfc codecov SETTLE DOWN :laughing: 
gaborbernat: @asottile While trying to write tests for this and making them pass discovered two feature request that helped me understand what's happening
- reasoning for venv recreation,
- only update-ing ``.tox.config1`` when it changes, not always;

And two user affecting bugs:
- PEP-517 packaging
- and command quoting being incorrect.

And two internal bugs (thing happened to work by chance, but not by design):
- python md5 being zero when there were dependencies because of variable re-usage;
- and pragma no cover being ignored.

As such this PR became a sort of longer one. However due to the iterative nature of these would be troublesome to split it up. As such I would appreciate to review and merge it as one.

Patch:
diff --git a/.gitignore b/.gitignore
index c80af931..83b4c440 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,8 @@ coverage.xml
 htmlcov
 
 .idea
+.vscode
+
 .eggs/
 py27/
 
diff --git a/changelog/1003.bugfix.rst b/changelog/1003.bugfix.rst
new file mode 100644
index 00000000..7303a0b3
--- /dev/null
+++ b/changelog/1003.bugfix.rst
@@ -0,0 +1,1 @@
+PEP-517 packaging fails with sdist already exists, fixed via ensuring the dist folder is empty before invoking the backend and `pypa/setuptools#1481 <https://github.com/pypa/setuptools/pull/1481>`_ - by :user:`gaborbernat`
diff --git a/changelog/1004.feature.rst b/changelog/1004.feature.rst
new file mode 100644
index 00000000..8e6f044a
--- /dev/null
+++ b/changelog/1004.feature.rst
@@ -0,0 +1,1 @@
+``-vv`` will print out why a virtual environment is re-created whenever this operation is triggered - by :user:`gaborbernat`
diff --git a/changelog/167.feature.rst b/changelog/167.feature.rst
new file mode 100644
index 00000000..0323c743
--- /dev/null
+++ b/changelog/167.feature.rst
@@ -0,0 +1,3 @@
+add :confval:`commands_pre` and :confval:`commands_post` that run before and after running
+the :confval:`commands` (setup runs always, commands only if setup suceeds, teardown always - all
+run until the first failing command)  - by :user:`gaborbernat`
diff --git a/doc/config.rst b/doc/config.rst
index 69db342c..61b6214a 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -171,15 +171,17 @@ Complete list of settings that you can put into ``testenv*`` sections:
 
 .. confval:: commands=ARGVLIST
 
-    The commands to be called for testing. Each line is interpreted as one command;
-    however a command can be split over multiple lines by ending the line with the ``\``
-    character.
+    The commands to be called for testing. Only execute if :confval:`commands_pre` succeed.
+
+    Each line is interpreted as one command; however a command can be split over
+    multiple lines by ending the line with the ``\`` character.
 
     Commands will execute one by one in sequential fashion until one of them fails (their exit
     code is non-zero) or all of them succeed. The exit code of a command may be ignored (meaning
     they are always considered successful) by prefixing the command with a dash (``-``) - this is
     similar to how ``make`` recipe lines work. The outcome of the environment is considered successful
-    only if all commands succeeded (exit code ignored via the ``-`` or success exit code value of zero).
+    only if all commands (these + setup + teardown) succeeded (exit code ignored via the
+    ``-`` or success exit code value of zero).
 
     :note: the virtual environment binary path (the ``bin`` folder within) is prepended to the os ``PATH``,
         meaning commands will first try to resolve to an executable from within the
@@ -187,6 +189,17 @@ Complete list of settings that you can put into ``testenv*`` sections:
         translates as the virtual environments ``python`` (having the same runtime version
         as the :confval:`basepython`), and ``pip`` translates as the virtual environments ``pip``.
 
+.. confval:: commands_pre=ARGVLIST
+
+    Commands to run before running the :confval:`commands`.
+    All evaluation and configuration logic applies from :confval:`commands`.
+
+.. confval:: commands_post=ARGVLIST
+
+    Commands to run after running the :confval:`commands`. Execute regardless of the outcome of
+    both :confval:`commands` and :confval:`commands_pre`.
+    All evaluation and configuration logic applies from :confval:`commands`.
+
 .. confval:: install_command=ARGV
 
     .. versionadded:: 1.6
diff --git a/pyproject.toml b/pyproject.toml
index 89839548..ed7af662 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [build-system]
 requires = [
-    "setuptools >= 35.0.2",
+    "setuptools >= 40.0.4",
     "setuptools_scm >= 2.0.0, <4",
     "wheel >= 0.29.0",
 ]
diff --git a/setup.py b/setup.py
index d9443290..90bd6d0a 100644
--- a/setup.py
+++ b/setup.py
@@ -110,7 +110,7 @@ def main():
         ]
         + [
             ("Programming Language :: Python :: {}".format(x))
-            for x in "2 2.7 3 3.4 3.5 3.6".split()
+            for x in "2 2.7 3 3.4 3.5 3.6 3.7".split()
         ],
     )
 
diff --git a/src/tox/config.py b/src/tox/config.py
index 62cb3768..e560c221 100755
--- a/src/tox/config.py
+++ b/src/tox/config.py
@@ -772,6 +772,20 @@ def tox_addoption(parser):
         help="each line specifies a test command and can use substitution.",
     )
 
+    parser.add_testenv_attribute(
+        name="commands_pre",
+        type="argvlist",
+        default="",
+        help="each line specifies a setup command action and can use substitution.",
+    )
+
+    parser.add_testenv_attribute(
+        name="commands_post",
+        type="argvlist",
+        default="",
+        help="each line specifies a teardown command and can use substitution.",
+    )
+
     parser.add_testenv_attribute(
         "ignore_outcome",
         type="bool",
diff --git a/src/tox/package.py b/src/tox/package.py
index 7f8d24a3..eb0ade34 100644
--- a/src/tox/package.py
+++ b/src/tox/package.py
@@ -183,7 +183,12 @@ def perform_isolated_build(build_info, package_venv, session, config):
                 build_info.backend_module, build_info.backend_object, "sdist", str(config.distdir)
             )
         )
+
+        # need to start with an empty (but existing) source distribution folder
+        if config.distdir.exists():
+            config.distdir.remove(rec=1, ignore_errors=True)
         config.distdir.ensure_dir()
+
         result = package_venv._pcall(
             [package_venv.envconfig.envpython, "-c", script],
             returnout=True,
diff --git a/src/tox/session.py b/src/tox/session.py
index 95716060..55ff0a87 100644
--- a/src/tox/session.py
+++ b/src/tox/session.py
@@ -178,7 +178,7 @@ class Action(object):
         popen.action = self
         self._popenlist.append(popen)
         try:
-            self.report.logpopen(popen, env=env)
+            self.report.logpopen(popen, cmd_args_shell)
             try:
                 if resultjson and not redirect:
                     if popen.stderr is not None:
@@ -290,13 +290,12 @@ class Reporter(object):
         else:
             return Verbosity.DEBUG
 
-    def logpopen(self, popen, env):
+    def logpopen(self, popen, cmd_args_shell):
         """ log information about the action.popen() created process. """
-        cmd = " ".join(map(str, popen.args))
         if popen.outpath:
-            self.verbosity1("  {}$ {} >{}".format(popen.cwd, cmd, popen.outpath))
+            self.verbosity1("  {}$ {} >{}".format(popen.cwd, cmd_args_shell, popen.outpath))
         else:
-            self.verbosity1("  {}$ {} ".format(popen.cwd, cmd))
+            self.verbosity1("  {}$ {} ".format(popen.cwd, cmd_args_shell))
 
     def logaction_start(self, action):
         msg = "{} {}".format(action.msg, " ".join(map(str, action.args)))
@@ -589,7 +588,8 @@ class Session:
             if venv.status:
                 return
             self.hook.tox_runtest_pre(venv=venv)
-            self.hook.tox_runtest(venv=venv, redirect=redirect)
+            if venv.status == 0:
+                self.hook.tox_runtest(venv=venv, redirect=redirect)
             self.hook.tox_runtest_post(venv=venv)
         else:
             venv.status = "skipped tests"
diff --git a/src/tox/venv.py b/src/tox/venv.py
index 8358cd1f..a89f96b1 100755
--- a/src/tox/venv.py
+++ b/src/tox/venv.py
@@ -1,5 +1,5 @@
-import ast
 import codecs
+import json
 import os
 import pipes
 import re
@@ -41,7 +41,6 @@ class CreationConfig:
         try:
             lines = path.readlines(cr=0)
             value = lines.pop(0).split(None, 1)
-            md5, python = value
             version, sitepackages, usedevelop, alwayscopy = lines.pop(0).split(None, 4)
             sitepackages = bool(int(sitepackages))
             usedevelop = bool(int(usedevelop))
@@ -50,25 +49,31 @@ class CreationConfig:
             for line in lines:
                 md5, depstring = line.split(None, 1)
                 deps.append((md5, depstring))
+            md5, python = value
             return CreationConfig(md5, python, version, sitepackages, usedevelop, deps, alwayscopy)
         except Exception:
             return None
 
+    def matches_with_reason(self, other, deps_matches_subset=False):
+        for attr in ("md5", "python", "version", "sitepackages", "usedevelop", "alwayscopy"):
+            left = getattr(self, attr)
+            right = getattr(other, attr)
+            if left != right:
+                return False, "attr {} {!r}!={!r}".format(attr, left, right)
+        self_deps = set(self.deps)
+        other_deps = set(other.deps)
+        if self_deps != other_deps:
+            if deps_matches_subset:
+                diff = other_deps - self_deps
+                if not diff:
+                    return False, "missing in previous {!r}".format(diff)
+            else:
+                return False, "{!r}!={!r}".format(self_deps, other_deps)
+        return True, None
+
     def matches(self, other, deps_matches_subset=False):
-        return (
-            other
-            and self.md5 == other.md5
-            and self.python == other.python
-            and self.version == other.version
-            and self.sitepackages == other.sitepackages
-            and self.usedevelop == other.usedevelop
-            and self.alwayscopy == other.alwayscopy
-            and (
-                all(d in self.deps for d in other.deps)
-                if deps_matches_subset is True
-                else self.deps == other.deps
-            )
-        )
+        outcome, _ = self.matches_with_reason(other, deps_matches_subset)
+        return outcome
 
 
 class VirtualEnv(object):
@@ -164,15 +169,19 @@ class VirtualEnv(object):
             if status string is empty, all is ok.
         """
         rconfig = CreationConfig.readconfig(self.path_config)
-        if (
-            not self.envconfig.recreate
-            and rconfig
-            and rconfig.matches(
-                self._getliveconfig(), getattr(self.envconfig, "deps_matches_subset", False)
-            )
-        ):
+        if self.envconfig.recreate:
+            reason = "-r flag"
+        else:
+            if rconfig is None:
+                reason = "no previous config {}".format(self.path_config)
+            else:
+                live_config = self._getliveconfig()
+                deps_subset_match = getattr(self.envconfig, "deps_matches_subset", False)
+                outcome, reason = rconfig.matches_with_reason(live_config, deps_subset_match)
+        if reason is None:
             action.info("reusing", self.envconfig.envdir)
             return
+        action.info("cannot reuse", reason)
         if rconfig is None:
             action.setactivity("create", self.envconfig.envdir)
         else:
@@ -189,7 +198,6 @@ class VirtualEnv(object):
 
     def _getliveconfig(self):
         python = self.envconfig.python_info.executable
-        md5 = getdigest(python)
         version = tox.__version__
         sitepackages = self.envconfig.sitepackages
         develop = self.envconfig.usedevelop
@@ -199,6 +207,7 @@ class VirtualEnv(object):
             raw_dep = dep.name
             md5 = getdigest(raw_dep)
             deps.append((md5, raw_dep))
+        md5 = getdigest(python)
         return CreationConfig(md5, python, version, sitepackages, develop, deps, alwayscopy)
 
     def _getresolvedeps(self):
@@ -226,7 +235,13 @@ class VirtualEnv(object):
         return re.match(self.envconfig.platform, sys.platform)
 
     def finish(self):
-        self._getliveconfig().writeconfig(self.path_config)
+        previous_config = CreationConfig.readconfig(self.path_config)
+        live_config = self._getliveconfig()
+        if previous_config is None or not previous_config.matches(live_config):
+            self.session.report.verbosity1(
+                "write config to {} as {!r}".format(self.path_config, live_config)
+            )
+            live_config.writeconfig(self.path_config)
 
     def _needs_reinstall(self, setupdir, action):
         setup_py = setupdir.join("setup.py")
@@ -234,12 +249,18 @@ class VirtualEnv(object):
         args = [self.envconfig.envpython, str(setup_py), "--name"]
         env = self._get_os_environ()
         output = action.popen(args, cwd=setupdir, redirect=False, returnout=True, env=env)
-        name = output.strip()
-        args = [self.envconfig.envpython, "-c", "import sys; print(sys.path)"]
+        name = next(
+            (i for i in output.split("\n") if i and not i.startswith("pydev debugger:")), ""
+        )
+        args = [
+            self.envconfig.envpython,
+            "-c",
+            "import sys;  import json; print(json.dumps(sys.path))",
+        ]
         out = action.popen(args, redirect=False, returnout=True, env=env)
         try:
-            sys_path = ast.literal_eval(out.strip())
-        except SyntaxError:
+            sys_path = json.loads(out)
+        except ValueError:
             sys_path = []
         egg_info_fname = ".".join((name, "egg-info"))
         for d in reversed(sys_path):
@@ -386,22 +407,34 @@ class VirtualEnv(object):
         env["VIRTUAL_ENV"] = str(self.path)
         return env
 
-    def test(self, redirect=False):
-        with self.session.newaction(self, "runtests") as action:
-            self.status = 0
-            self.session.make_emptydir(self.envconfig.envtmpdir)
-            self.envconfig.envtmpdir.ensure(dir=1)
+    def test(
+        self,
+        redirect=False,
+        name="runtests",
+        commands=None,
+        ignore_outcome=None,
+        ignore_errors=None,
+        display_hash_seed=False,
+    ):
+        if commands is None:
+            commands = self.envconfig.commands
+        if ignore_outcome is None:
+            ignore_outcome = self.envconfig.ignore_outcome
+        if ignore_errors is None:
+            ignore_errors = self.envconfig.ignore_errors
+        with self.session.newaction(self, name) as action:
             cwd = self.envconfig.changedir
-            env = self._get_os_environ(is_test_command=True)
-            # Display PYTHONHASHSEED to assist with reproducibility.
-            action.setactivity("runtests", "PYTHONHASHSEED={!r}".format(env.get("PYTHONHASHSEED")))
-            for i, argv in enumerate(self.envconfig.commands):
+            if display_hash_seed:
+                env = self._get_os_environ(is_test_command=True)
+                # Display PYTHONHASHSEED to assist with reproducibility.
+                action.setactivity(name, "PYTHONHASHSEED={!r}".format(env.get("PYTHONHASHSEED")))
+            for i, argv in enumerate(commands):
                 # have to make strings as _pcall changes argv[0] to a local()
                 # happens if the same environment is invoked twice
                 message = "commands[{}] | {}".format(
                     i, " ".join([pipes.quote(str(x)) for x in argv])
                 )
-                action.setactivity("runtests", message)
+                action.setactivity(name, message)
                 # check to see if we need to ignore the return code
                 # if so, we need to alter the command line arguments
                 if argv[0].startswith("-"):
@@ -423,7 +456,7 @@ class VirtualEnv(object):
                         is_test_command=True,
                     )
                 except tox.exception.InvocationError as err:
-                    if self.envconfig.ignore_outcome:
+                    if ignore_outcome:
                         msg = "command failed but result from testenv is ignored\ncmd:"
                         self.session.report.warning("{} {}".format(msg, err))
                         self.status = "ignored failed command"
@@ -431,7 +464,7 @@ class VirtualEnv(object):
 
                     self.session.report.error(str(err))
                     self.status = "commands failed"
-                    if not self.envconfig.ignore_errors:
+                    if not ignore_errors:
                         break  # Don't process remaining commands
                 except KeyboardInterrupt:
                     self.status = "keyboardinterrupt"
@@ -532,6 +565,32 @@ def tox_runtest(venv, redirect):
     return True  # Return non-None to indicate plugin has completed
 
 
+@tox.hookimpl
+def tox_runtest_pre(venv):
+    venv.status = 0
+    venv.session.make_emptydir(venv.envconfig.envtmpdir)
+    venv.envconfig.envtmpdir.ensure(dir=1)
+    venv.test(
+        name="run-test-pre",
+        commands=venv.envconfig.commands_pre,
+        redirect=False,
+        ignore_outcome=False,
+        ignore_errors=False,
+        display_hash_seed=True,
+    )
+
+
+@tox.hookimpl
+def tox_runtest_post(venv):
+    venv.test(
+        name="run-test-post",
+        commands=venv.envconfig.commands_post,
+        redirect=False,
+        ignore_outcome=False,
+        ignore_errors=False,
+    )
+
+
 @tox.hookimpl
 def tox_runenvreport(venv, action):
     # write out version dependency information
diff --git a/tox.ini b/tox.ini
index 53311495..f14c5bf0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -107,7 +107,20 @@ branch = true
 [coverage:report]
 skip_covered = True
 show_missing = True
-exclude_lines = if __name__ == ["']__main__["']:
+exclude_lines =
+    # Have to re-enable the standard pragma
+    \#\s*pragma: no cover
+    # We optionally substitute this
+    ${COVERAGE_IGNORE_WINDOWS}
+
+    # Don't complain if tests don't hit defensive assertion code:
+    ^\s*raise AssertionError\b
+    ^\s*raise NotImplementedError\b
+    ^\s*return NotImplemented\b
+    ^\s*raise$
+
+    # Don't complain if non-runnable code isn't run:
+    ^if __name__ == ['"]__main__['"]:$
 
 [coverage:paths]
 source = src/tox


Test patch:
diff --git a/src/tox/_pytestplugin.py b/src/tox/_pytestplugin.py
index f0f35ec7..c79a5fd1 100644
--- a/src/tox/_pytestplugin.py
+++ b/src/tox/_pytestplugin.py
@@ -69,8 +69,8 @@ def cmd(request, capfd, monkeypatch):
     request.addfinalizer(py.path.local().chdir)
 
     def run(*argv):
-        key = str(b"PYTHONPATH")
-        python_paths = (i for i in (str(os.getcwd()), os.getenv(key)) if i)
+        key = str("PYTHONPATH")
+        python_paths = (i for i in (os.getcwd(), os.getenv(key)) if i)
         monkeypatch.setenv(key, os.pathsep.join(python_paths))
 
         with RunResult(capfd, argv) as result:
@@ -403,34 +403,95 @@ def mock_venv(monkeypatch):
     Note: because we inherit, to keep things sane you must call the py environment and only that;
     and cannot install any packages. """
 
+    # first ensure we have a clean python path
+    monkeypatch.delenv("PYTHONPATH", raising=False)
+
+    # object to collect some data during the execution
+    class Result(object):
+        def __init__(self):
+            self.popens = []
+            self.cwd = None
+            self.session = None
+
+    res = Result()
+
+    # convince tox that the current running virtual environment is already the env we would create
     class ProxyCurrentPython:
         @classmethod
         def readconfig(cls, path):
-            assert path.dirname.endswith("{}py".format(os.sep))
-            return CreationConfig(
-                md5=getdigest(sys.executable),
-                python=sys.executable,
-                version=tox.__version__,
-                sitepackages=False,
-                usedevelop=False,
-                deps=[],
-                alwayscopy=False,
-            )
+            if path.dirname.endswith("{}py".format(os.sep)):
+                return CreationConfig(
+                    md5=getdigest(sys.executable),
+                    python=sys.executable,
+                    version=tox.__version__,
+                    sitepackages=False,
+                    usedevelop=False,
+                    deps=[],
+                    alwayscopy=False,
+                )
+            elif path.dirname.endswith("{}.package".format(os.sep)):
+                return CreationConfig(
+                    md5=getdigest(sys.executable),
+                    python=sys.executable,
+                    version=tox.__version__,
+                    sitepackages=False,
+                    usedevelop=False,
+                    deps=[(getdigest(""), "setuptools >= 35.0.2"), (getdigest(""), "wheel")],
+                    alwayscopy=False,
+                )
+            assert False  # pragma: no cover
 
     monkeypatch.setattr(CreationConfig, "readconfig", ProxyCurrentPython.readconfig)
 
+    # provide as Python the current python executable
     def venv_lookup(venv, name):
         assert name == "python"
+        venv.envconfig.envdir = py.path.local(sys.executable).join("..", "..")
         return sys.executable
 
     monkeypatch.setattr(VirtualEnv, "_venv_lookup", venv_lookup)
 
+    # don't allow overriding the tox config data for the host Python
+    def finish_venv(self):
+        return
+
+    monkeypatch.setattr(VirtualEnv, "finish", finish_venv)
+
+    # we lie that it's an environment with no packages in it
     @tox.hookimpl
     def tox_runenvreport(venv, action):
         return []
 
     monkeypatch.setattr(venv, "tox_runenvreport", tox_runenvreport)
 
+    # intercept the build session to save it and we intercept the popen invocations
+    prev_build = tox.session.build_session
+
+    def build_session(config):
+        res.session = prev_build(config)
+        res._popen = res.session.popen
+        monkeypatch.setattr(res.session, "popen", popen)
+        return res.session
+
+    monkeypatch.setattr(tox.session, "build_session", build_session)
+
+    # collect all popen calls
+    def popen(cmd, **kwargs):
+        # we don't want to perform installation of new packages, just replace with an always ok cmd
+        if "pip" in cmd and "install" in cmd:
+            cmd = ["python", "-c", "print({!r})".format(cmd)]
+        activity_id = res.session._actions[-1].id
+        activity_name = res.session._actions[-1].activity
+        try:
+            ret = res._popen(cmd, **kwargs)
+        except tox.exception.InvocationError as exception:  # pragma: no cover
+            ret = exception  # pragma: no cover
+        finally:
+            res.popens.append((activity_id, activity_name, kwargs.get("env"), ret, cmd))
+        return ret
+
+    return res
+
 
 @pytest.fixture(scope="session")
 def current_tox_py():
diff --git a/tests/unit/session/test_session.py b/tests/unit/session/test_session.py
index 565728d5..2436b1e6 100644
--- a/tests/unit/session/test_session.py
+++ b/tests/unit/session/test_session.py
@@ -1,5 +1,7 @@
 import os
 import re
+import sys
+import textwrap
 import uuid
 from threading import Thread
 
@@ -304,3 +306,72 @@ def assert_popen_env(res):
         if tox_id != "tox":
             assert env["TOX_ENV_NAME"] == tox_id
             assert env["TOX_ENV_DIR"] == os.path.join(res.cwd, ".tox", tox_id)
+
+
+def test_command_prev_post_ok(cmd, initproj, mock_venv):
+    initproj(
+        "pkg_command_test_123-0.7",
+        filedefs={
+            "tox.ini": """
+            [tox]
+            envlist = py
+
+            [testenv]
+            commands_pre = python -c 'print("pre")'
+            commands = python -c 'print("command")'
+            commands_post = python -c 'print("post")'
+        """
+        },
+    )
+    result = cmd()
+    assert result.ret == 0
+    expected = textwrap.dedent(
+        """
+        py run-test-pre: commands[0] | python -c 'print("pre")'
+        pre
+        py runtests: commands[0] | python -c 'print("command")'
+        command
+        py run-test-post: commands[0] | python -c 'print("post")'
+        post
+        ___________________________________ summary ___________________________________{}
+          py: commands succeeded
+          congratulations :)
+    """.format(
+            "_" if sys.platform != "win32" else ""
+        )
+    )
+    actual = result.out.replace(os.linesep, "\n")
+    assert expected in actual
+
+
+def test_command_prev_fail_command_skip_post_run(cmd, initproj, mock_venv):
+    initproj(
+        "pkg_command_test_123-0.7",
+        filedefs={
+            "tox.ini": """
+                [tox]
+                envlist = py
+
+                [testenv]
+                commands_pre = python -c 'raise SystemExit(2)'
+                commands = python -c 'print("command")'
+                commands_post = python -c 'print("post")'
+            """
+        },
+    )
+    result = cmd()
+    assert result.ret == 1
+    expected = textwrap.dedent(
+        """
+            py run-test-pre: commands[0] | python -c 'raise SystemExit(2)'
+            ERROR: InvocationError for command '{} -c raise SystemExit(2)' (exited with code 2)
+            py run-test-post: commands[0] | python -c 'print("post")'
+            post
+            ___________________________________ summary ___________________________________{}
+            ERROR:   py: commands failed
+        """.format(
+            sys.executable.replace("\\", "\\\\"), "_" if sys.platform != "win32" else ""
+        )
+    )
+    actual = result.out.replace(os.linesep, "\n")
+    assert expected in actual
diff --git a/tests/unit/test_package.py b/tests/unit/test_package.py
index ea885544..81605e86 100644
--- a/tests/unit/test_package.py
+++ b/tests/unit/test_package.py
@@ -229,3 +229,28 @@ def test_package_isolated_toml_bad_backend(initproj, cmd):
     build-backend = []
     """,
     )
+
+
+def test_dist_exists_version_change(mock_venv, initproj, cmd):
+    base = initproj(
+        "package_toml-{}".format("0.1"),
+        filedefs={
+            "tox.ini": """
+                [tox]
+                isolated_build = true
+                        """,
+            "pyproject.toml": """
+                [build-system]
+                requires = ["setuptools >= 35.0.2"]
+                build-backend = 'setuptools.build_meta'
+                            """,
+        },
+    )
+    result = cmd("-e", "py")
+    assert result.ret == 0, result.out
+
+    new_code = base.join("setup.py").read_text("utf-8").replace("0.1", "0.2")
+    base.join("setup.py").write_text(new_code, "utf-8")
+
+    result = cmd("-e", "py")
+    assert result.ret == 0, result.out
diff --git a/tests/unit/test_venv.py b/tests/unit/test_venv.py
index 5b830956..6c89119f 100644
--- a/tests/unit/test_venv.py
+++ b/tests/unit/test_venv.py
@@ -344,8 +344,8 @@ def test_test_hashseed_is_in_output(newmocksession, monkeypatch):
     venv = mocksession.getenv("python")
     action = mocksession.newaction(venv, "update")
     venv.update(action)
-    venv.test()
-    mocksession.report.expect("verbosity0", "runtests: PYTHONHASHSEED='{}'".format(seed))
+    tox.venv.tox_runtest_pre(venv)
+    mocksession.report.expect("verbosity0", "run-test-pre: PYTHONHASHSEED='{}'".format(seed))
 
 
 def test_test_runtests_action_command_is_in_output(newmocksession):
@@ -389,6 +389,7 @@ def test_install_command_not_installed(newmocksession):
     """,
     )
     venv = mocksession.getenv("python")
+    venv.status = 0
     venv.test()
     mocksession.report.expect("warning", "*test command found but not*")
     assert venv.status == 0
diff --git a/tests/unit/test_z_cmdline.py b/tests/unit/test_z_cmdline.py
index 2bcd57ae..95e628cb 100644
--- a/tests/unit/test_z_cmdline.py
+++ b/tests/unit/test_z_cmdline.py
@@ -639,6 +639,7 @@ def test_alwayscopy_default(initproj, cmd):
     assert "virtualenv --always-copy" not in result.out
 
 
+@pytest.mark.skipif("sys.platform == 'win32'")
 def test_empty_activity_ignored(initproj, cmd):
     initproj(
         "example123",
@@ -655,6 +656,7 @@ def test_empty_activity_ignored(initproj, cmd):
     assert "installed:" not in result.out
 
 
+@pytest.mark.skipif("sys.platform == 'win32'")
 def test_empty_activity_shown_verbose(initproj, cmd):
     initproj(
         "example123",