From c06df509309d3d81ac75cb0587e2d18521d024bc Mon Sep 17 00:00:00 2001 From: Joseph Roitman Date: Tue, 20 Apr 2021 02:46:05 +0300 Subject: [PATCH 1/2] Add tests that fail due to broken newline support. --- README.rst | 2 +- tests/test_assert_match.py | 50 ++++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/README.rst b/README.rst index cb66910..b8d0b3b 100644 --- a/README.rst +++ b/README.rst @@ -43,7 +43,7 @@ This `pytest`_ plugin was generated with `Cookiecutter`_ along with `@hackebrot` Features -------- -* snapshot testing of strings ands bytes +* snapshot testing of strings and bytes * snapshot testing of collections of strings and bytes * the user has complete control over the snapshot file path and content diff --git a/tests/test_assert_match.py b/tests/test_assert_match.py index 6353260..0d1ffbd 100644 --- a/tests/test_assert_match.py +++ b/tests/test_assert_match.py @@ -1,3 +1,5 @@ +import os + import pytest from tests.utils import assert_pytest_passes @@ -6,17 +8,17 @@ @pytest.fixture def basic_case_dir(testdir): case_dir = testdir.mkdir('case_dir') - case_dir.join('snapshot1.txt').write_text('the valuÉ of snapshot1.txt', 'utf-8') + case_dir.join('snapshot1.txt').write_text('the valuÉ of snapshot1.txt\n', 'utf-8') return case_dir def test_assert_match_with_external_snapshot_path(testdir, basic_case_dir): - testdir.makepyfile(""" + testdir.makepyfile(r""" from pathlib import Path def test_sth(snapshot): snapshot.snapshot_dir = 'case_dir' - snapshot.assert_match('the value of snapshot1.txt', Path('not_case_dir/snapshot1.txt').absolute()) + snapshot.assert_match('the value of snapshot1.txt\n', Path('not_case_dir/snapshot1.txt').absolute()) """) result = testdir.runpytest('-v') result.stdout.fnmatch_lines([ @@ -27,28 +29,29 @@ def test_sth(snapshot): def test_assert_match_success_string(testdir, basic_case_dir): - testdir.makepyfile(""" + testdir.makepyfile(r""" def test_sth(snapshot): snapshot.snapshot_dir = 'case_dir' - snapshot.assert_match('the valuÉ of snapshot1.txt', 'snapshot1.txt') + snapshot.assert_match('the valuÉ of snapshot1.txt\n', 'snapshot1.txt') """) assert_pytest_passes(testdir) def test_assert_match_success_bytes(testdir, basic_case_dir): testdir.makepyfile(r""" + import os def test_sth(snapshot): snapshot.snapshot_dir = 'case_dir' - snapshot.assert_match(b'the valu\xc3\x89 of snapshot1.txt', 'snapshot1.txt') + snapshot.assert_match(b'the valu\xc3\x89 of snapshot1.txt' + os.linesep.encode(), 'snapshot1.txt') """) assert_pytest_passes(testdir) def test_assert_match_failure_string(testdir, basic_case_dir): - testdir.makepyfile(""" + testdir.makepyfile(r""" def test_sth(snapshot): snapshot.snapshot_dir = 'case_dir' - snapshot.assert_match('the INCORRECT value of snapshot1.txt', 'snapshot1.txt') + snapshot.assert_match('the INCORRECT value of snapshot1.txt\n', 'snapshot1.txt') """) result = testdir.runpytest('-v') result.stdout.fnmatch_lines([ @@ -65,10 +68,11 @@ def test_sth(snapshot): def test_assert_match_failure_bytes(testdir, basic_case_dir): - testdir.makepyfile(""" + testdir.makepyfile(r""" + import os def test_sth(snapshot): snapshot.snapshot_dir = 'case_dir' - snapshot.assert_match(b'the INCORRECT value of snapshot1.txt', 'snapshot1.txt') + snapshot.assert_match(b'the INCORRECT value of snapshot1.txt' + os.linesep.encode(), 'snapshot1.txt') """) result = testdir.runpytest('-v') result.stdout.fnmatch_lines([ @@ -78,14 +82,14 @@ def test_sth(snapshot): r"E* assert * == *", r"E* At index 4 diff: * != *", r"E* Full diff:", - r"E* - b'the valu\xc3\x89 of snapshot1.txt'", - r"E* + b'the INCORRECT value of snapshot1.txt'", + r"E* - b'the valu\xc3\x89 of snapshot1.txt{}'".format(repr(os.linesep)[1:-1]), + r"E* + b'the INCORRECT value of snapshot1.txt{}'".format(repr(os.linesep)[1:-1]), ]) assert result.ret == 1 def test_assert_match_invalid_type(testdir, basic_case_dir): - testdir.makepyfile(""" + testdir.makepyfile(r""" def test_sth(snapshot): snapshot.snapshot_dir = 'case_dir' snapshot.assert_match(123, 'snapshot1.txt') @@ -99,7 +103,7 @@ def test_sth(snapshot): def test_assert_match_missing_snapshot(testdir, basic_case_dir): - testdir.makepyfile(""" + testdir.makepyfile(r""" def test_sth(snapshot): snapshot.snapshot_dir = 'case_dir' snapshot.assert_match('something', 'snapshot_that_doesnt_exist.txt') @@ -114,10 +118,10 @@ def test_sth(snapshot): def test_assert_match_update_existing_snapshot_no_change(testdir, basic_case_dir): - testdir.makepyfile(""" + testdir.makepyfile(r""" def test_sth(snapshot): snapshot.snapshot_dir = 'case_dir' - snapshot.assert_match('the valuÉ of snapshot1.txt', 'snapshot1.txt') + snapshot.assert_match('the valuÉ of snapshot1.txt\n', 'snapshot1.txt') """) result = testdir.runpytest('-v', '--snapshot-update') result.stdout.fnmatch_lines([ @@ -152,12 +156,12 @@ def test_assert_match_update_existing_snapshot(testdir, basic_case_dir, case_dir Also tests that `Snapshot` supports absolute/relative str/Path snapshot directories and snapshot paths. """ - testdir.makepyfile(""" + testdir.makepyfile(r""" from pathlib import Path def test_sth(snapshot): snapshot.snapshot_dir = {case_dir_repr} - snapshot.assert_match('the NEW value of snapshot1.txt', {snapshot_name_repr}) + snapshot.assert_match('the NEW value of snapshot1.txt\n', {snapshot_name_repr}) """.format(case_dir_repr=case_dir_repr, snapshot_name_repr=snapshot_name_repr)) result = testdir.runpytest('-v', '--snapshot-update') result.stdout.fnmatch_lines([ @@ -177,12 +181,12 @@ def test_assert_match_update_existing_snapshot_and_exception_in_test(testdir, ba Tests that `Snapshot.assert_match` works when updating an existing snapshot and then the test function fails. In this case, both the snapshot update error and the test function error are printed out. """ - testdir.makepyfile(""" + testdir.makepyfile(r""" from pathlib import Path def test_sth(snapshot): snapshot.snapshot_dir = 'case_dir' - snapshot.assert_match('the NEW value of snapshot1.txt', 'snapshot1.txt') + snapshot.assert_match('the NEW value of snapshot1.txt\n', 'snapshot1.txt') assert False """) result = testdir.runpytest('-v', '--snapshot-update') @@ -198,7 +202,7 @@ def test_sth(snapshot): def test_assert_match_create_new_snapshot(testdir, basic_case_dir): - testdir.makepyfile(""" + testdir.makepyfile(r""" def test_sth(snapshot): snapshot.snapshot_dir = 'case_dir' snapshot.assert_match('the NEW value of new_snapshot1.txt', 'sub_dir/new_snapshot1.txt') @@ -217,7 +221,7 @@ def test_sth(snapshot): def test_assert_match_create_new_snapshot_in_default_dir(testdir): - testdir.makepyfile(""" + testdir.makepyfile(r""" def test_sth(snapshot): snapshot.assert_match('the value of new_snapshot1.txt', 'sub_dir/new_snapshot1.txt') """) @@ -239,7 +243,7 @@ def test_sth(snapshot): def test_assert_match_existing_snapshot_is_not_file(testdir, basic_case_dir): basic_case_dir.mkdir('directory1') - testdir.makepyfile(""" + testdir.makepyfile(r""" def test_sth(snapshot): snapshot.snapshot_dir = 'case_dir' snapshot.assert_match('something', 'directory1') From 7e41ec4562570b6180cecce7266d016ebe22ccdb Mon Sep 17 00:00:00 2001 From: Joseph Roitman Date: Tue, 20 Apr 2021 02:52:45 +0300 Subject: [PATCH 2/2] Fix regression in newline support. --- pytest_snapshot/plugin.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pytest_snapshot/plugin.py b/pytest_snapshot/plugin.py index b82ca3f..a65a423 100644 --- a/pytest_snapshot/plugin.py +++ b/pytest_snapshot/plugin.py @@ -44,6 +44,22 @@ def _assert_equal(value, expected_value) -> None: assert expected_value == value +def _file_encode(string: str) -> bytes: + """ + Returns the bytes that would be in a file created using ``path.write_text(string)``. + See universal newlines documentation. + """ + return string.replace('\n', os.linesep).encode() + + +def _file_decode(data: bytes) -> str: + """ + Returns the string that would be read from a file using ``path.read_text(string)``. + See universal newlines documentation. + """ + return data.decode().replace('\r\n', '\n').replace('\r', '\n') + + class Snapshot: _snapshot_update = None # type: bool _allow_snapshot_deletion = None # type: bool @@ -120,7 +136,7 @@ def _get_compare_encode_decode(self, value: Union[str, bytes]): * The decoding function should decode bytes from a snapshot file into a object. """ if isinstance(value, str): - return _assert_equal, str.encode, bytes.decode + return _assert_equal, _file_encode, _file_decode elif isinstance(value, bytes): return _assert_equal, lambda x: x, lambda x: x else: