Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
260 changes: 260 additions & 0 deletions tests/test_makepyfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
import os
from encodings.aliases import aliases
from pathlib import Path
from textwrap import dedent
from typing import Tuple

import pytest

pytest_plugins = ["pytester"]


def not_debug():
"""run tests only when not in CI and debug is enabled."""
return os.getenv("CI", False) or not os.getenv("PYTEST_DEBUG_MAKEPYFILE", False)


def enc(enc):
try:
_ = "abc".encode(enc)
except LookupError:
return False

return True


all_encodings = set(filter(enc, aliases.values()))


@pytest.fixture(params=all_encodings)
def encoding(request):
return request.param


@pytest.fixture(
params=[
("3", "4"),
("A", "B"),
("!", "?"),
("☆", "☁"),
("✅", "💥"),
("é", "ü"),
("Я", "ж"), # Cyrillic characters
("Ф", "Щ"), # More Cyrillic
("電腦", "电脑"), # Chinese: traditional vs simplified for "computer"
("学校", "がっこう"), # Japanese: Kanji vs Hiragana for "school"
],
ids=[
"numbers",
"letters",
"punctuation",
"stars",
"check-explosion",
"accented_letters",
"cyrillic_basic",
"cyrillic_complex",
"chinese_computer",
"japanese_school",
],
)
def any_charset(request):
return request.param


@pytest.fixture
def makepyfile_encoded(testdir, encoding, any_charset):
a, b = any_charset

original_file = testdir.makepyfile(
f"""
def f():
'''
>>> print('{a}')
{b}
'''
pass
""",
encoding=encoding,
)

expected_diff = dedent(
f"""
>>> print('{a}')
- {b}
+ {a}
"""
).strip("\n")

yield original_file, expected_diff, encoding


def makebasicfile(tmp_path: Path, a, b, encoding: str) -> Tuple[Path, str]:
"""alternative implementation without the use of `testdir.makepyfile`."""

original_file = Path(tmp_path).joinpath("test_basic.py")
code = dedent(
f"""
def f():
'''
>>> print('{a}')
{b}
'''
pass
"""
)

original_file.write_text(code, encoding=encoding)

expected_diff = dedent(
f"""
>>> print('{a}')
- {b}
+ {a}
"""
).strip("\n")

return original_file, expected_diff


@pytest.fixture
def basic_encoded(tmp_path, encoding, any_charset):

a, b = any_charset

file, diff = makebasicfile(tmp_path, a, b, encoding)

yield file, diff, encoding


@pytest.mark.skipif(not_debug(), reason="skipped in CI or debugging is not enabled.")
def test_makepyfile(makepyfile_encoded):
"""
Test is expected to fail because of UnicodeDecodeError.

Just to compare visually with `test_basicfile` to see the difference
"""

file, diff, enc = makepyfile_encoded

text = Path(file).read_text(enc)

print(text, diff)


@pytest.mark.skipif(not_debug(), reason="skipped in CI or debugging is not enabled.")
def test_basicfile(basic_encoded):
"""
Test is expected to fail because of UnicodeDecodeError.

Just to compare visually with `test_makepyfile` to see the difference
"""
file, diff, enc = basic_encoded

text = Path(file).read_text(enc)

print(text, diff)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set env variable PYTEST_DEBUG_MAKEPYFILE to run test function


@pytest.mark.skip(reason="makepyfile uses encoding only on bytes objects.")
def test_compare_make_basic_file(testdir, encoding, any_charset):
"""
Compare testdir.makepyfile with pathlib.Path.writetext to create python files.

Expected behavior is when `testdir.makepyfile` created a file with given encoding,
`makebasicfile` also should be able to encode the charset.

If a UnicodeEncodeError is raised, it means `testdir.makepyfile` screwed up the
encoding.
"""

a, b = any_charset

# create a python file with testdir.makepyfile in the old fashioned way

make_file = testdir.makepyfile(
f"""
def f():
'''
>>> print('{a}')
{b}
'''
pass
""",
encoding=encoding,
)

assert Path(
make_file
).is_file(), f"`testdir.makepyfile` failed encode {repr((a,b))} with {encoding=}"

make_diff = dedent(
f"""
>>> print('{a}')
- {b}
+ {a}
"""
).strip("\n")

# try to check if makepyfile screwed up and create python file in a different way

try:
basic_file, basic_diff = makebasicfile(str(testdir), a, b, encoding)
except UnicodeEncodeError:
pytest.fail(
f"testdir.makepyfile encoding {repr((a,b))} with {encoding=}, but it sould have failed."
)

assert Path(
basic_file
).is_file(), f"`makebasicfile` failed encode {repr((a,b))} with {encoding=}"
assert (
make_diff == basic_diff
), "sanity check - diffs always should be the same, if not my test implementation is wrong."


def test_compare_write_bytes(testdir, encoding, any_charset):
"""
`makeypfile` ignores keyword arguments `encoding` if content is not bytes.

1. create test data and convert to bytes, if encoding failes the test is skipped.
2. use both functions to create python file

- if both functions raise the same error, everything is as expected
- if Path.write_bytes succeeds so should `testdir.makepyfile` and error should be None
- both files sould exist after the test
"""

a, b = any_charset

try:
bytes_content = dedent(f"""
def f():
'''
>>> print('{a}')
{b}
'''
pass
"""
).encode(encoding)
except UnicodeEncodeError:
# skip test do testdata could not prepared
pytest.xfail(f"{repr(any_charset)} cannot be encoded with {encoding=}")

try:
make_file = testdir.makepyfile(
bytes_content,
encoding=encoding,
)
except Exception as e:
error = e # if both function fail the same, everything is as expected
else:
error = None

basic_file = Path(str(testdir)).joinpath("test_basic.py")
try:
basic_file.write_bytes(bytes_content)
except Exception as e:
assert type(error) is type(e), f"basically never should happen, but {e=} was raised."
else:
assert error is None, f"makepyfile screwed up {encoding=} and raised {error=}"

assert Path(make_file).is_file() and Path(basic_file).is_file(), "files are missing."
Loading