Skip to content

Commit

Permalink
Refactor ASCII color handling into a fixture
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoddemus committed Feb 11, 2020
1 parent d218218 commit 84406db
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 65 deletions.
21 changes: 2 additions & 19 deletions testing/code/test_terminal_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,6 @@
from _pytest._io import TerminalWriter


# TODO: move this and the other two related attributes from test_terminal.py into conftest as a
# fixture
COLORS = {
"red": "\x1b[31m",
"green": "\x1b[32m",
"yellow": "\x1b[33m",
"bold": "\x1b[1m",
"reset": "\x1b[0m",
"kw": "\x1b[94m",
"hl-reset": "\x1b[39;49;00m",
"function": "\x1b[92m",
"number": "\x1b[94m",
"str": "\x1b[33m",
"print": "\x1b[96m",
}


@pytest.mark.parametrize(
"has_markup, expected",
[
Expand All @@ -31,12 +14,12 @@
pytest.param(False, "assert 0\n", id="no markup"),
],
)
def test_code_highlight(has_markup, expected):
def test_code_highlight(has_markup, expected, color_mapping):
f = StringIO()
tw = TerminalWriter(f)
tw.hasmarkup = has_markup
tw._write_source(["assert 0"])
assert f.getvalue() == expected.format(**COLORS)
assert f.getvalue().splitlines(keepends=True) == color_mapping.format([expected])

with pytest.raises(
ValueError,
Expand Down
46 changes: 46 additions & 0 deletions testing/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import re
import sys
from typing import List

import pytest
from _pytest.pytester import Testdir
Expand Down Expand Up @@ -127,7 +129,51 @@ def runtest(self):
testdir.makefile(".yaml", test1="")



@pytest.fixture
def testdir(testdir: Testdir) -> Testdir:
testdir.monkeypatch.setenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "1")
return testdir

@pytest.fixture(scope="session")
def color_mapping():
"""Returns a utility class which can replace keys in strings in the form "{NAME}"
by their equivalent ASCII codes in the terminal.
Used by tests which check the actual colors output by pytest.
"""
from colorama import Fore, Style

class ColorMapping:
COLORS = {
"red": Fore.RED,
"green": Fore.GREEN,
"yellow": Fore.YELLOW,
"bold": Style.BRIGHT,
"reset": Style.RESET_ALL,
"kw": Fore.LIGHTBLUE_EX,
"hl-reset": "\x1b[39;49;00m",
"function": Fore.LIGHTGREEN_EX,
"number": Fore.LIGHTBLUE_EX,
"str": Fore.YELLOW,
"print": Fore.LIGHTCYAN_EX,
}
RE_COLORS = {k: re.escape(v) for k, v in COLORS.items()}

@classmethod
def format(cls, lines: List[str]) -> List[str]:
"""Straightforward replacement of color names to their ASCII codes."""
return [line.format(**cls.COLORS) for line in lines]

@classmethod
def format_for_fnmatch(cls, lines: List[str]) -> List[str]:
"""Replace color names for use with LineMatcher.fnmatch_lines"""
return [line.format(**cls.COLORS).replace("[", "[[]") for line in lines]

@classmethod
def format_for_rematch(cls, lines: List[str]) -> List[str]:
"""Replace color names for use with LineMatcher.re_match_lines"""
return [line.format(**cls.RE_COLORS) for line in lines]

return ColorMapping

71 changes: 25 additions & 46 deletions testing/test_terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@
"""
import collections
import os
import re
import sys
import textwrap
from io import StringIO

import pluggy
import py
from colorama import Fore
from colorama import Style

import pytest
from _pytest.config import ExitCode
Expand All @@ -26,20 +23,6 @@

DistInfo = collections.namedtuple("DistInfo", ["project_name", "version"])

COLORS = {
"red": Fore.RED,
"green": Fore.GREEN,
"yellow": Fore.YELLOW,
"bold": Style.BRIGHT,
"reset": Style.RESET_ALL,
"kw": Fore.LIGHTBLUE_EX,
"hl-reset": "\x1b[39;49;00m",
"function": Fore.LIGHTGREEN_EX,
"number": Fore.LIGHTBLUE_EX,
"str": Fore.YELLOW,
"print": Fore.LIGHTCYAN_EX,
}
RE_COLORS = {k: re.escape(v) for k, v in COLORS.items()}

TRANS_FNMATCH = str.maketrans({"[": "[[]", "]": "[]]"})

Expand Down Expand Up @@ -897,7 +880,7 @@ def test_pass_no_output():
)


def test_color_yes(testdir):
def test_color_yes(testdir, color_mapping):
p1 = testdir.makepyfile(
"""
def fail():
Expand All @@ -915,9 +898,8 @@ def test_this():
assert "\x1b[1m" in output
return
result.stdout.fnmatch_lines(
[
line.format(**COLORS).replace("[", "[[]")
for line in [
color_mapping.format_for_fnmatch(
[
"{bold}=*= test session starts =*={reset}",
"collected 1 item",
"",
Expand All @@ -939,13 +921,12 @@ def test_this():
"{bold}{red}test_color_yes.py{reset}:2: AssertionError",
"{red}=*= {red}{bold}1 failed{reset}{red} in *s{reset}{red} =*={reset}",
]
]
)
)
result = testdir.runpytest("--color=yes", "--tb=short", str(p1))
result.stdout.fnmatch_lines(
[
line.format(**COLORS).replace("[", "[[]")
for line in [
color_mapping.format_for_fnmatch(
[
"{bold}=*= test session starts =*={reset}",
"collected 1 item",
"",
Expand All @@ -960,7 +941,7 @@ def test_this():
"{bold}{red}E assert 0{reset}",
"{red}=*= {red}{bold}1 failed{reset}{red} in *s{reset}{red} =*={reset}",
]
]
)
)


Expand Down Expand Up @@ -1681,7 +1662,7 @@ def test_normal(self, many_tests_files, testdir):
]
)

def test_colored_progress(self, testdir, monkeypatch):
def test_colored_progress(self, testdir, monkeypatch, color_mapping):
monkeypatch.setenv("PY_COLORS", "1")
testdir.makepyfile(
test_bar="""
Expand All @@ -1705,14 +1686,13 @@ def test_foobar(i): raise ValueError()
)
result = testdir.runpytest()
result.stdout.re_match_lines(
[
line.format(**RE_COLORS)
for line in [
color_mapping.format_for_rematch(
[
r"test_bar.py ({green}\.{reset}){{10}}{green} \s+ \[ 50%\]{reset}",
r"test_foo.py ({green}\.{reset}){{5}}{yellow} \s+ \[ 75%\]{reset}",
r"test_foobar.py ({red}F{reset}){{5}}{red} \s+ \[100%\]{reset}",
]
]
)
)

def test_count(self, many_tests_files, testdir):
Expand Down Expand Up @@ -1864,20 +1844,21 @@ def test_teardown_many(self, testdir, many_files):
[r"test_bar.py (\.E){5}\s+\[ 25%\]", r"test_foo.py (\.E){15}\s+\[100%\]"]
)

def test_teardown_many_verbose(self, testdir: Testdir, many_files) -> None:
def test_teardown_many_verbose(
self, testdir: Testdir, many_files, color_mapping
) -> None:
result = testdir.runpytest("-v")
result.stdout.fnmatch_lines(
[
line.translate(TRANS_FNMATCH)
for line in [
color_mapping.format_for_fnmatch(
[
"test_bar.py::test_bar[0] PASSED * [ 5%]",
"test_bar.py::test_bar[0] ERROR * [ 5%]",
"test_bar.py::test_bar[4] PASSED * [ 25%]",
"test_foo.py::test_foo[14] PASSED * [100%]",
"test_foo.py::test_foo[14] ERROR * [100%]",
"=* 20 passed, 20 errors in *",
]
]
)
)

def test_xdist_normal(self, many_files, testdir, monkeypatch):
Expand Down Expand Up @@ -2032,7 +2013,7 @@ def test_via_exec(testdir: Testdir) -> None:


class TestCodeHighlight:
def test_code_highlight_simple(self, testdir: Testdir) -> None:
def test_code_highlight_simple(self, testdir: Testdir, color_mapping) -> None:
testdir.makepyfile(
"""
def test_foo():
Expand All @@ -2041,17 +2022,16 @@ def test_foo():
)
result = testdir.runpytest("--color=yes")
result.stdout.fnmatch_lines(
[
line.format(**COLORS).replace("[", "[[]")
for line in [
color_mapping.format_for_fnmatch(
[
" {kw}def{hl-reset} {function}test_foo{hl-reset}():",
"> {kw}assert{hl-reset} {number}1{hl-reset} == {number}10{hl-reset}",
"{bold}{red}E assert 1 == 10{reset}",
]
]
)
)

def test_code_highlight_continuation(self, testdir: Testdir) -> None:
def test_code_highlight_continuation(self, testdir: Testdir, color_mapping) -> None:
testdir.makepyfile(
"""
def test_foo():
Expand All @@ -2061,13 +2041,12 @@ def test_foo():
)
result = testdir.runpytest("--color=yes")
result.stdout.fnmatch_lines(
[
line.format(**COLORS).replace("[", "[[]")
for line in [
color_mapping.format_for_fnmatch(
[
" {kw}def{hl-reset} {function}test_foo{hl-reset}():",
" {print}print{hl-reset}({str}'''{hl-reset}{str}{hl-reset}",
"> {str} {hl-reset}{str}'''{hl-reset}); {kw}assert{hl-reset} {number}0{hl-reset}",
"{bold}{red}E assert 0{reset}",
]
]
)
)

0 comments on commit 84406db

Please sign in to comment.