Răsfoiți Sursa

tests: move tests to testsuite.helpers.parseformat_test

Thomas Waldmann 1 săptămână în urmă
părinte
comite
8458ed1965

+ 598 - 0
src/borg/testsuite/helpers/parseformat_test.py

@@ -0,0 +1,598 @@
+import base64
+import os
+from argparse import ArgumentTypeError
+from datetime import datetime, timezone
+
+import pytest
+
+from ...constants import *  # NOQA
+from ...helpers.parseformat import (
+    bin_to_hex,
+    binary_to_json,
+    text_to_json,
+    Location,
+    archivename_validator,
+    text_validator,
+    format_file_size,
+    parse_file_size,
+    interval,
+    partial_format,
+    clean_lines,
+    format_line,
+    PlaceholderError,
+    replace_placeholders,
+    swidth_slice,
+    eval_escapes,
+)
+from ...helpers.time import format_timedelta, parse_timestamp
+
+
+def test_bin_to_hex():
+    assert bin_to_hex(b"") == ""
+    assert bin_to_hex(b"\x00\x01\xff") == "0001ff"
+
+
+@pytest.mark.parametrize(
+    "key,value",
+    [("key", b"\x00\x01\x02\x03"), ("key", b"\x00\x01\x02"), ("key", b"\x00\x01"), ("key", b"\x00"), ("key", b"")],
+)
+def test_binary_to_json(key, value):
+    key_b64 = key + "_b64"
+    d = binary_to_json(key, value)
+    assert key_b64 in d
+    assert base64.b64decode(d[key_b64]) == value
+
+
+@pytest.mark.parametrize(
+    "key,value,strict",
+    [
+        ("key", "abc", True),
+        ("key", "äöü", True),
+        ("key", "", True),
+        ("key", b"\x00\xff".decode("utf-8", errors="surrogateescape"), False),
+        ("key", "äöü".encode("latin1").decode("utf-8", errors="surrogateescape"), False),
+    ],
+)
+def test_text_to_json(key, value, strict):
+    key_b64 = key + "_b64"
+    d = text_to_json(key, value)
+    value_b = value.encode("utf-8", errors="surrogateescape")
+    if strict:
+        # no surrogate-escapes, just unicode text
+        assert key in d
+        assert d[key] == value_b.decode("utf-8", errors="strict")
+        assert d[key].encode("utf-8", errors="strict") == value_b
+        assert key_b64 not in d  # not needed. pure valid unicode.
+    else:
+        # requiring surrogate-escapes. text has replacement chars, base64 representation is present.
+        assert key in d
+        assert d[key] == value.encode("utf-8", errors="replace").decode("utf-8", errors="strict")
+        assert d[key].encode("utf-8", errors="strict") == value.encode("utf-8", errors="replace")
+        assert key_b64 in d
+        assert base64.b64decode(d[key_b64]) == value_b
+
+
+class TestLocationWithoutEnv:
+    @pytest.fixture
+    def keys_dir(self, tmpdir, monkeypatch):
+        tmpdir = str(tmpdir)
+        monkeypatch.setenv("BORG_KEYS_DIR", tmpdir)
+        if not tmpdir.endswith(os.path.sep):
+            tmpdir += os.path.sep
+        return tmpdir
+
+    def test_ssh(self, monkeypatch, keys_dir):
+        monkeypatch.delenv("BORG_REPO", raising=False)
+        assert (
+            repr(Location("ssh://user@host:1234//absolute/path"))
+            == "Location(proto='ssh', user='user', host='host', port=1234, path='/absolute/path')"
+        )
+        assert Location("ssh://user@host:1234//absolute/path").to_key_filename() == keys_dir + "host___absolute_path"
+        assert (
+            repr(Location("ssh://user@host:1234/relative/path"))
+            == "Location(proto='ssh', user='user', host='host', port=1234, path='relative/path')"
+        )
+        assert Location("ssh://user@host:1234/relative/path").to_key_filename() == keys_dir + "host__relative_path"
+        assert (
+            repr(Location("ssh://user@host/relative/path"))
+            == "Location(proto='ssh', user='user', host='host', port=None, path='relative/path')"
+        )
+        assert (
+            repr(Location("ssh://user@[::]:1234/relative/path"))
+            == "Location(proto='ssh', user='user', host='::', port=1234, path='relative/path')"
+        )
+        assert Location("ssh://user@[::]:1234/relative/path").to_key_filename() == keys_dir + "____relative_path"
+        assert (
+            repr(Location("ssh://user@[::]/relative/path"))
+            == "Location(proto='ssh', user='user', host='::', port=None, path='relative/path')"
+        )
+        assert (
+            repr(Location("ssh://user@[2001:db8::]:1234/relative/path"))
+            == "Location(proto='ssh', user='user', host='2001:db8::', port=1234, path='relative/path')"
+        )
+        assert (
+            Location("ssh://user@[2001:db8::]:1234/relative/path").to_key_filename()
+            == keys_dir + "2001_db8____relative_path"
+        )
+        assert (
+            repr(Location("ssh://user@[2001:db8::]/relative/path"))
+            == "Location(proto='ssh', user='user', host='2001:db8::', port=None, path='relative/path')"
+        )
+        assert (
+            repr(Location("ssh://user@[2001:db8::c0:ffee]:1234/relative/path"))
+            == "Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=1234, path='relative/path')"
+        )
+        assert (
+            repr(Location("ssh://user@[2001:db8::c0:ffee]/relative/path"))
+            == "Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=None, path='relative/path')"
+        )
+        assert (
+            repr(Location("ssh://user@[2001:db8::192.0.2.1]:1234/relative/path"))
+            == "Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=1234, path='relative/path')"
+        )
+        assert (
+            repr(Location("ssh://user@[2001:db8::192.0.2.1]/relative/path"))
+            == "Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=None, path='relative/path')"
+        )
+        assert (
+            Location("ssh://user@[2001:db8::192.0.2.1]/relative/path").to_key_filename()
+            == keys_dir + "2001_db8__192_0_2_1__relative_path"
+        )
+        assert (
+            repr(Location("ssh://user@[2a02:0001:0002:0003:0004:0005:0006:0007]/relative/path"))
+            == "Location(proto='ssh', user='user', "
+            "host='2a02:0001:0002:0003:0004:0005:0006:0007', port=None, path='relative/path')"
+        )
+        assert (
+            repr(Location("ssh://user@[2a02:0001:0002:0003:0004:0005:0006:0007]:1234/relative/path"))
+            == "Location(proto='ssh', user='user', "
+            "host='2a02:0001:0002:0003:0004:0005:0006:0007', port=1234, path='relative/path')"
+        )
+
+    def test_rclone(self, monkeypatch, keys_dir):
+        monkeypatch.delenv("BORG_REPO", raising=False)
+        assert (
+            repr(Location("rclone:remote:path"))
+            == "Location(proto='rclone', user=None, host=None, port=None, path='remote:path')"
+        )
+        assert Location("rclone:remote:path").to_key_filename() == keys_dir + "remote_path"
+
+    def test_sftp(self, monkeypatch, keys_dir):
+        monkeypatch.delenv("BORG_REPO", raising=False)
+        # relative path
+        assert (
+            repr(Location("sftp://user@host:1234/rel/path"))
+            == "Location(proto='sftp', user='user', host='host', port=1234, path='rel/path')"
+        )
+        assert Location("sftp://user@host:1234/rel/path").to_key_filename() == keys_dir + "host__rel_path"
+        # absolute path
+        assert (
+            repr(Location("sftp://user@host:1234//abs/path"))
+            == "Location(proto='sftp', user='user', host='host', port=1234, path='/abs/path')"
+        )
+        assert Location("sftp://user@host:1234//abs/path").to_key_filename() == keys_dir + "host___abs_path"
+
+    def test_socket(self, monkeypatch, keys_dir):
+        monkeypatch.delenv("BORG_REPO", raising=False)
+        assert (
+            repr(Location("socket:///repo/path"))
+            == "Location(proto='socket', user=None, host=None, port=None, path='/repo/path')"
+        )
+        assert Location("socket:///some/path").to_key_filename() == keys_dir + "_some_path"
+
+    def test_file(self, monkeypatch, keys_dir):
+        monkeypatch.delenv("BORG_REPO", raising=False)
+        assert (
+            repr(Location("file:///some/path"))
+            == "Location(proto='file', user=None, host=None, port=None, path='/some/path')"
+        )
+        assert (
+            repr(Location("file:///some/path"))
+            == "Location(proto='file', user=None, host=None, port=None, path='/some/path')"
+        )
+        assert Location("file:///some/path").to_key_filename() == keys_dir + "_some_path"
+
+    def test_smb(self, monkeypatch, keys_dir):
+        monkeypatch.delenv("BORG_REPO", raising=False)
+        assert (
+            repr(Location("file:////server/share/path"))
+            == "Location(proto='file', user=None, host=None, port=None, path='//server/share/path')"
+        )
+        assert Location("file:////server/share/path").to_key_filename() == keys_dir + "__server_share_path"
+
+    def test_folder(self, monkeypatch, keys_dir):
+        monkeypatch.delenv("BORG_REPO", raising=False)
+        rel_path = "path"
+        abs_path = os.path.abspath(rel_path)
+        assert repr(Location(rel_path)) == f"Location(proto='file', user=None, host=None, port=None, path='{abs_path}')"
+        assert Location("path").to_key_filename().endswith(rel_path)
+
+    def test_abspath(self, monkeypatch, keys_dir):
+        monkeypatch.delenv("BORG_REPO", raising=False)
+        assert (
+            repr(Location("/some/absolute/path"))
+            == "Location(proto='file', user=None, host=None, port=None, path='/some/absolute/path')"
+        )
+        assert Location("/some/absolute/path").to_key_filename() == keys_dir + "_some_absolute_path"
+        assert (
+            repr(Location("/some/../absolute/path"))
+            == "Location(proto='file', user=None, host=None, port=None, path='/absolute/path')"
+        )
+        assert Location("/some/../absolute/path").to_key_filename() == keys_dir + "_absolute_path"
+
+    def test_relpath(self, monkeypatch, keys_dir):
+        monkeypatch.delenv("BORG_REPO", raising=False)
+        # for a local path, borg creates a Location instance with an absolute path
+        rel_path = "relative/path"
+        abs_path = os.path.abspath(rel_path)
+        assert repr(Location(rel_path)) == f"Location(proto='file', user=None, host=None, port=None, path='{abs_path}')"
+        assert Location(rel_path).to_key_filename().endswith("relative_path")
+        assert (
+            repr(Location("ssh://user@host/relative/path"))
+            == "Location(proto='ssh', user='user', host='host', port=None, path='relative/path')"
+        )
+        assert Location("ssh://user@host/relative/path").to_key_filename() == keys_dir + "host__relative_path"
+
+    def test_with_colons(self, monkeypatch, keys_dir):
+        monkeypatch.delenv("BORG_REPO", raising=False)
+        assert (
+            repr(Location("/abs/path:w:cols"))
+            == "Location(proto='file', user=None, host=None, port=None, path='/abs/path:w:cols')"
+        )
+        assert Location("/abs/path:w:cols").to_key_filename() == keys_dir + "_abs_path_w_cols"
+        assert (
+            repr(Location("file:///abs/path:w:cols"))
+            == "Location(proto='file', user=None, host=None, port=None, path='/abs/path:w:cols')"
+        )
+        assert Location("file:///abs/path:w:cols").to_key_filename() == keys_dir + "_abs_path_w_cols"
+        assert (
+            repr(Location("ssh://user@host/abs/path:w:cols"))
+            == "Location(proto='ssh', user='user', host='host', port=None, path='abs/path:w:cols')"
+        )
+        assert Location("ssh://user@host/abs/path:w:cols").to_key_filename() == keys_dir + "host__abs_path_w_cols"
+
+    def test_canonical_path(self, monkeypatch):
+        monkeypatch.delenv("BORG_REPO", raising=False)
+        locations = [
+            "relative/path",
+            "/absolute/path",
+            "file:///absolute/path",
+            "socket:///absolute/path",
+            "ssh://host/relative/path",
+            "ssh://host//absolute/path",
+            "ssh://user@host:1234/relative/path",
+            "sftp://host/relative/path",
+            "sftp://host//absolute/path",
+            "sftp://user@host:1234/relative/path",
+            "rclone:remote:path",
+        ]
+        for location in locations:
+            assert (
+                Location(location).canonical_path() == Location(Location(location).canonical_path()).canonical_path()
+            ), ("failed: %s" % location)
+
+    def test_bad_syntax(self):
+        with pytest.raises(ValueError):
+            # this is invalid due to the 2nd colon, correct: 'ssh://user@host/path'
+            Location("ssh://user@host:/path")
+
+
+@pytest.mark.parametrize(
+    "name",
+    [
+        "foo",
+        "foo bar",
+        "foo_bar",
+        "foo-bar",
+        "foo.bar",
+        "foo[bar]",
+        "foo@2020-01-01T12:34:56",
+        "foo{now}",
+        "foo{now:%Y-%m-%d}",
+        "foo{hostname}",
+        "foo{hostname}-{now}",
+        "foo{hostname}-{now:%Y-%m-%d}",
+        "foo{hostname}-{now:%Y-%m-%d}@{now:%H:%M:%S}",
+        "foo{hostname}-{now:%Y-%m-%d}@{now:%H:%M:%S}",
+        "foo{hostname}-{now:%Y-%m-%d}@{now:%H:%M:%S}",
+    ],
+)
+def test_archivename_ok(name):
+    assert archivename_validator(name) == name
+
+
+@pytest.mark.parametrize(
+    "name",
+    [
+        "",  # empty name
+        " ",  # just a space
+        " foo",  # leading space
+        "foo ",  # trailing space
+        "foo/bar",  # / not allowed
+        "foo\\bar",  # \ not allowed
+        "foo\nbar",  # \n not allowed
+        "foo\rbar",  # \r not allowed
+        "foo\tbar",  # \t not allowed
+        "foo\0bar",  # \0 not allowed
+        "foo\x01bar",  # \x01 not allowed
+        "foo\x02bar",  # \x02 not allowed
+        "foo\x03bar",  # \x03 not allowed
+        "foo\x04bar",  # \x04 not allowed
+        "foo\x05bar",  # \x05 not allowed
+        "foo\x06bar",  # \x06 not allowed
+        "foo\x07bar",  # \x07 not allowed
+        "foo\x08bar",  # \x08 not allowed
+        "foo\x09bar",  # \x09 not allowed
+        "foo\x0abar",  # \x0a not allowed
+        "foo\x0bbar",  # \x0b not allowed
+        "foo\x0cbar",  # \x0c not allowed
+        "foo\x0dbar",  # \x0d not allowed
+        "foo\x0ebar",  # \x0e not allowed
+        "foo\x0fbar",  # \x0f not allowed
+    ],
+)
+def test_archivename_invalid(name):
+    with pytest.raises(ArgumentTypeError):
+        archivename_validator(name)
+
+
+@pytest.mark.parametrize("text", ["foo", "bar", "baz"])
+def test_text_ok(text):
+    assert text_validator(name="text", max_length=100)(text) == text
+
+
+@pytest.mark.parametrize(
+    "text",
+    [
+        "",  # empty
+        "foo\0bar",  # contains null byte
+        "foo\nbar",  # contains newline
+        "foo\rbar",  # contains carriage return
+        "foo\tbar",  # contains tab
+        "foo\x01bar",  # contains control character
+        "foo\x02bar",  # contains control character
+        "foo\x03bar",  # contains control character
+        "foo\x04bar",  # contains control character
+        "foo\x05bar",  # contains control character
+        "foo\x06bar",  # contains control character
+        "foo\x07bar",  # contains control character
+        "foo\x08bar",  # contains control character
+    ],
+)
+def test_text_invalid(text):
+    invalid_ctrl_chars = "".join(chr(i) for i in range(32))
+    tv = text_validator(name="text", max_length=100, min_length=1, invalid_ctrl_chars=invalid_ctrl_chars)
+    with pytest.raises(ArgumentTypeError):
+        tv(text)
+
+
+def test_format_timedelta():
+    t0 = datetime(2001, 1, 1, 10, 20, 3, 0)
+    t1 = datetime(2001, 1, 1, 12, 20, 4, 100000)
+    assert format_timedelta(t1 - t0) == "2 hours 1.100 seconds"
+
+
+@pytest.mark.parametrize(
+    "timeframe, num_secs",
+    [
+        ("5S", 5),
+        ("2M", 2 * 60),
+        ("1H", 60 * 60),
+        ("1d", 24 * 60 * 60),
+        ("1w", 7 * 24 * 60 * 60),
+        ("1m", 31 * 24 * 60 * 60),
+        ("1y", 365 * 24 * 60 * 60),
+    ],
+)
+def test_interval(timeframe, num_secs):
+    assert interval(timeframe) == num_secs
+
+
+@pytest.mark.parametrize(
+    "invalid_interval, error_tuple",
+    [
+        ("H", ('Invalid number "": expected positive integer',)),
+        ("-1d", ('Invalid number "-1": expected positive integer',)),
+        ("food", ('Invalid number "foo": expected positive integer',)),
+    ],
+)
+def test_interval_time_unit(invalid_interval, error_tuple):
+    with pytest.raises(ArgumentTypeError) as exc:
+        interval(invalid_interval)
+    assert exc.value.args == error_tuple
+
+
+def test_interval_number():
+    with pytest.raises(ArgumentTypeError) as exc:
+        interval("5")
+    assert exc.value.args == ('Unexpected time unit "5": choose from y, m, w, d, H, M, S',)
+
+
+def test_parse_timestamp():
+    assert parse_timestamp("2015-04-19T20:25:00.226410") == datetime(2015, 4, 19, 20, 25, 0, 226410, timezone.utc)
+    assert parse_timestamp("2015-04-19T20:25:00") == datetime(2015, 4, 19, 20, 25, 0, 0, timezone.utc)
+
+
+@pytest.mark.parametrize(
+    "size, fmt",
+    [
+        (0, "0 B"),  # no rounding necessary for those
+        (1, "1 B"),
+        (142, "142 B"),
+        (999, "999 B"),
+        (1000, "1.00 kB"),  # rounding starts here
+        (1001, "1.00 kB"),  # should be rounded away
+        (1234, "1.23 kB"),  # should be rounded down
+        (1235, "1.24 kB"),  # should be rounded up
+        (1010, "1.01 kB"),  # rounded down as well
+        (999990000, "999.99 MB"),  # rounded down
+        (999990001, "999.99 MB"),  # rounded down
+        (999995000, "1.00 GB"),  # rounded up to next unit
+        (10**6, "1.00 MB"),  # and all the remaining units, megabytes
+        (10**9, "1.00 GB"),  # gigabytes
+        (10**12, "1.00 TB"),  # terabytes
+        (10**15, "1.00 PB"),  # petabytes
+        (10**18, "1.00 EB"),  # exabytes
+        (10**21, "1.00 ZB"),  # zottabytes
+        (10**24, "1.00 YB"),  # yottabytes
+        (-1, "-1 B"),  # negative value
+        (-1010, "-1.01 kB"),  # negative value with rounding
+    ],
+)
+def test_file_size(size, fmt):
+    """test the size formatting routines"""
+    assert format_file_size(size) == fmt
+
+
+@pytest.mark.parametrize(
+    "size, fmt",
+    [
+        (0, "0 B"),
+        (2**0, "1 B"),
+        (2**10, "1.00 KiB"),
+        (2**20, "1.00 MiB"),
+        (2**30, "1.00 GiB"),
+        (2**40, "1.00 TiB"),
+        (2**50, "1.00 PiB"),
+        (2**60, "1.00 EiB"),
+        (2**70, "1.00 ZiB"),
+        (2**80, "1.00 YiB"),
+        (-(2**0), "-1 B"),
+        (-(2**10), "-1.00 KiB"),
+        (-(2**20), "-1.00 MiB"),
+    ],
+)
+def test_file_size_iec(size, fmt):
+    """test the size formatting routines"""
+    assert format_file_size(size, iec=True) == fmt
+
+
+@pytest.mark.parametrize(
+    "original_size, formatted_size",
+    [
+        (1234, "1.2 kB"),  # rounded down
+        (1254, "1.3 kB"),  # rounded up
+        (999990000, "1.0 GB"),  # and not 999.9 MB or 1000.0 MB
+    ],
+)
+def test_file_size_precision(original_size, formatted_size):
+    assert format_file_size(original_size, precision=1) == formatted_size
+
+
+@pytest.mark.parametrize("size, fmt", [(0, "0 B"), (1, "+1 B"), (1234, "+1.23 kB"), (-1, "-1 B"), (-1234, "-1.23 kB")])
+def test_file_size_sign(size, fmt):
+    assert format_file_size(size, sign=True) == fmt
+
+
+@pytest.mark.parametrize(
+    "string, value", [("1", 1), ("20", 20), ("5K", 5000), ("1.75M", 1750000), ("1e+9", 1e9), ("-1T", -1e12)]
+)
+def test_parse_file_size(string, value):
+    assert parse_file_size(string) == int(value)
+
+
+@pytest.mark.parametrize("string", ("", "5 Äpfel", "4E", "2229 bit", "1B"))
+def test_parse_file_size_invalid(string):
+    with pytest.raises(ValueError):
+        parse_file_size(string)
+
+
+@pytest.mark.parametrize(
+    "fmt, items_map, expected_result",
+    [
+        ("{space:10}", {"space": " "}, " " * 10),
+        ("{foobar}", {"bar": "wrong", "foobar": "correct"}, "correct"),
+        ("{unknown_key}", {}, "{unknown_key}"),
+        ("{key}{{escaped_key}}", {}, "{key}{{escaped_key}}"),
+        ("{{escaped_key}}", {"escaped_key": 1234}, "{{escaped_key}}"),
+    ],
+)
+def test_partial_format(fmt, items_map, expected_result):
+    assert partial_format(fmt, items_map) == expected_result
+
+
+def test_clean_lines():
+    conf = """\
+#comment
+data1 #data1
+data2
+
+ data3
+""".splitlines(
+        keepends=True
+    )
+    assert list(clean_lines(conf)) == ["data1 #data1", "data2", "data3"]
+    assert list(clean_lines(conf, lstrip=False)) == ["data1 #data1", "data2", " data3"]
+    assert list(clean_lines(conf, rstrip=False)) == ["data1 #data1\n", "data2\n", "data3\n"]
+    assert list(clean_lines(conf, remove_empty=False)) == ["data1 #data1", "data2", "", "data3"]
+    assert list(clean_lines(conf, remove_comments=False)) == ["#comment", "data1 #data1", "data2", "data3"]
+
+
+def test_format_line():
+    data = dict(foo="bar baz")
+    assert format_line("", data) == ""
+    assert format_line("{foo}", data) == "bar baz"
+    assert format_line("foo{foo}foo", data) == "foobar bazfoo"
+
+
+def test_format_line_erroneous():
+    data = dict()
+    with pytest.raises(PlaceholderError):
+        assert format_line("{invalid}", data)
+    with pytest.raises(PlaceholderError):
+        assert format_line("{}", data)
+    with pytest.raises(PlaceholderError):
+        assert format_line("{now!r}", data)
+    with pytest.raises(PlaceholderError):
+        assert format_line("{now.__class__.__module__.__builtins__}", data)
+
+
+def test_replace_placeholders():
+    replace_placeholders.reset()  # avoid overrides are spoiled by previous tests
+    now = datetime.now()
+    assert " " not in replace_placeholders("{now}")
+    assert int(replace_placeholders("{now:%Y}")) == now.year
+
+
+def test_override_placeholders():
+    assert replace_placeholders("{uuid4}", overrides={"uuid4": "overridden"}) == "overridden"
+
+
+def working_swidth():
+    from ...platform import swidth
+
+    return swidth("선") == 2
+
+
+@pytest.mark.skipif(not working_swidth(), reason="swidth() is not supported / active")
+def test_swidth_slice():
+    string = "나윤선나윤선나윤선나윤선나윤선"
+    assert swidth_slice(string, 1) == ""
+    assert swidth_slice(string, -1) == ""
+    assert swidth_slice(string, 4) == "나윤"
+    assert swidth_slice(string, -4) == "윤선"
+
+
+@pytest.mark.skipif(not working_swidth(), reason="swidth() is not supported / active")
+def test_swidth_slice_mixed_characters():
+    string = "나윤a선나윤선나윤선나윤선나윤선"
+    assert swidth_slice(string, 5) == "나윤a"
+    assert swidth_slice(string, 6) == "나윤a"
+
+
+def test_eval_escapes():
+    assert eval_escapes("\\n") == "\n"
+    assert eval_escapes("\\t") == "\t"
+    assert eval_escapes("\\r") == "\r"
+    assert eval_escapes("\\f") == "\f"
+    assert eval_escapes("\\b") == "\b"
+    assert eval_escapes("\\a") == "\a"
+    assert eval_escapes("\\v") == "\v"
+    assert eval_escapes("\\\\") == "\\"
+    assert eval_escapes('\\"') == '"'
+    assert eval_escapes("\\'") == "'"
+    assert eval_escapes("\\101") == "A"  # ord('A') == 65 == 0o101
+    assert eval_escapes("\\x41") == "A"  # ord('A') == 65 == 0x41
+    assert eval_escapes("\\u0041") == "A"  # ord('A') == 65 == 0x41
+    assert eval_escapes("\\U00000041") == "A"  # ord('A') == 65 == 0x41
+    assert eval_escapes("äç\\n") == "äç\n"

+ 5 - 585
src/borg/testsuite/helpers_test.py

@@ -1,364 +1,27 @@
-import base64
 import getpass
-import os
 import shutil
 import sys
 from argparse import ArgumentTypeError
-from datetime import datetime, timezone, timedelta
+from datetime import datetime, timezone
 from io import StringIO, BytesIO
 
 import pytest
 
-from ..archiver.prune_cmd import prune_within, prune_split
-from .. import platform
+from ..archiver.prune_cmd import prune_split
 from ..constants import *  # NOQA
-from ..helpers import Location
-from ..helpers import (
-    partial_format,
-    format_file_size,
-    parse_file_size,
-    format_timedelta,
-    format_line,
-    PlaceholderError,
-    replace_placeholders,
-)
-from ..helpers import clean_lines
-from ..helpers import interval
-from ..helpers import is_slow_msgpack
-from ..helpers import bin_to_hex
-from ..helpers import parse_timestamp, ChunkIteratorFileWrapper, ChunkerParams
-from ..helpers import archivename_validator, text_validator
+from ..helpers import ChunkIteratorFileWrapper, ChunkerParams
 from ..helpers import ProgressIndicatorPercent
-from ..helpers import swidth_slice
 from ..helpers import chunkit
 from ..helpers import safe_ns, safe_s, SUPPORT_32BIT_PLATFORMS
 from ..helpers import popen_with_error_handling
 from ..helpers import iter_separated
-from ..helpers import eval_escapes
-from ..helpers import text_to_json, binary_to_json
+from ..helpers import is_slow_msgpack
 from ..helpers import classify_ec, max_ec
+from ..helpers.parseformat import bin_to_hex
 from ..helpers.passphrase import Passphrase, PasswordRetriesExceeded
 from ..platform import is_cygwin
 
 
-def test_bin_to_hex():
-    assert bin_to_hex(b"") == ""
-    assert bin_to_hex(b"\x00\x01\xff") == "0001ff"
-
-
-@pytest.mark.parametrize(
-    "key,value",
-    [("key", b"\x00\x01\x02\x03"), ("key", b"\x00\x01\x02"), ("key", b"\x00\x01"), ("key", b"\x00"), ("key", b"")],
-)
-def test_binary_to_json(key, value):
-    key_b64 = key + "_b64"
-    d = binary_to_json(key, value)
-    assert key_b64 in d
-    assert base64.b64decode(d[key_b64]) == value
-
-
-@pytest.mark.parametrize(
-    "key,value,strict",
-    [
-        ("key", "abc", True),
-        ("key", "äöü", True),
-        ("key", "", True),
-        ("key", b"\x00\xff".decode("utf-8", errors="surrogateescape"), False),
-        ("key", "äöü".encode("latin1").decode("utf-8", errors="surrogateescape"), False),
-    ],
-)
-def test_text_to_json(key, value, strict):
-    key_b64 = key + "_b64"
-    d = text_to_json(key, value)
-    value_b = value.encode("utf-8", errors="surrogateescape")
-    if strict:
-        # no surrogate-escapes, just unicode text
-        assert key in d
-        assert d[key] == value_b.decode("utf-8", errors="strict")
-        assert d[key].encode("utf-8", errors="strict") == value_b
-        assert key_b64 not in d  # not needed. pure valid unicode.
-    else:
-        # requiring surrogate-escapes. text has replacement chars, base64 representation is present.
-        assert key in d
-        assert d[key] == value.encode("utf-8", errors="replace").decode("utf-8", errors="strict")
-        assert d[key].encode("utf-8", errors="strict") == value.encode("utf-8", errors="replace")
-        assert key_b64 in d
-        assert base64.b64decode(d[key_b64]) == value_b
-
-
-class TestLocationWithoutEnv:
-    @pytest.fixture
-    def keys_dir(self, tmpdir, monkeypatch):
-        tmpdir = str(tmpdir)
-        monkeypatch.setenv("BORG_KEYS_DIR", tmpdir)
-        if not tmpdir.endswith(os.path.sep):
-            tmpdir += os.path.sep
-        return tmpdir
-
-    def test_ssh(self, monkeypatch, keys_dir):
-        monkeypatch.delenv("BORG_REPO", raising=False)
-        assert (
-            repr(Location("ssh://user@host:1234//absolute/path"))
-            == "Location(proto='ssh', user='user', host='host', port=1234, path='/absolute/path')"
-        )
-        assert Location("ssh://user@host:1234//absolute/path").to_key_filename() == keys_dir + "host___absolute_path"
-        assert (
-            repr(Location("ssh://user@host:1234/relative/path"))
-            == "Location(proto='ssh', user='user', host='host', port=1234, path='relative/path')"
-        )
-        assert Location("ssh://user@host:1234/relative/path").to_key_filename() == keys_dir + "host__relative_path"
-        assert (
-            repr(Location("ssh://user@host/relative/path"))
-            == "Location(proto='ssh', user='user', host='host', port=None, path='relative/path')"
-        )
-        assert (
-            repr(Location("ssh://user@[::]:1234/relative/path"))
-            == "Location(proto='ssh', user='user', host='::', port=1234, path='relative/path')"
-        )
-        assert Location("ssh://user@[::]:1234/relative/path").to_key_filename() == keys_dir + "____relative_path"
-        assert (
-            repr(Location("ssh://user@[::]/relative/path"))
-            == "Location(proto='ssh', user='user', host='::', port=None, path='relative/path')"
-        )
-        assert (
-            repr(Location("ssh://user@[2001:db8::]:1234/relative/path"))
-            == "Location(proto='ssh', user='user', host='2001:db8::', port=1234, path='relative/path')"
-        )
-        assert (
-            Location("ssh://user@[2001:db8::]:1234/relative/path").to_key_filename()
-            == keys_dir + "2001_db8____relative_path"
-        )
-        assert (
-            repr(Location("ssh://user@[2001:db8::]/relative/path"))
-            == "Location(proto='ssh', user='user', host='2001:db8::', port=None, path='relative/path')"
-        )
-        assert (
-            repr(Location("ssh://user@[2001:db8::c0:ffee]:1234/relative/path"))
-            == "Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=1234, path='relative/path')"
-        )
-        assert (
-            repr(Location("ssh://user@[2001:db8::c0:ffee]/relative/path"))
-            == "Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=None, path='relative/path')"
-        )
-        assert (
-            repr(Location("ssh://user@[2001:db8::192.0.2.1]:1234/relative/path"))
-            == "Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=1234, path='relative/path')"
-        )
-        assert (
-            repr(Location("ssh://user@[2001:db8::192.0.2.1]/relative/path"))
-            == "Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=None, path='relative/path')"
-        )
-        assert (
-            Location("ssh://user@[2001:db8::192.0.2.1]/relative/path").to_key_filename()
-            == keys_dir + "2001_db8__192_0_2_1__relative_path"
-        )
-        assert (
-            repr(Location("ssh://user@[2a02:0001:0002:0003:0004:0005:0006:0007]/relative/path"))
-            == "Location(proto='ssh', user='user', "
-            "host='2a02:0001:0002:0003:0004:0005:0006:0007', port=None, path='relative/path')"
-        )
-        assert (
-            repr(Location("ssh://user@[2a02:0001:0002:0003:0004:0005:0006:0007]:1234/relative/path"))
-            == "Location(proto='ssh', user='user', "
-            "host='2a02:0001:0002:0003:0004:0005:0006:0007', port=1234, path='relative/path')"
-        )
-
-    def test_rclone(self, monkeypatch, keys_dir):
-        monkeypatch.delenv("BORG_REPO", raising=False)
-        assert (
-            repr(Location("rclone:remote:path"))
-            == "Location(proto='rclone', user=None, host=None, port=None, path='remote:path')"
-        )
-        assert Location("rclone:remote:path").to_key_filename() == keys_dir + "remote_path"
-
-    def test_sftp(self, monkeypatch, keys_dir):
-        monkeypatch.delenv("BORG_REPO", raising=False)
-        # relative path
-        assert (
-            repr(Location("sftp://user@host:1234/rel/path"))
-            == "Location(proto='sftp', user='user', host='host', port=1234, path='rel/path')"
-        )
-        assert Location("sftp://user@host:1234/rel/path").to_key_filename() == keys_dir + "host__rel_path"
-        # absolute path
-        assert (
-            repr(Location("sftp://user@host:1234//abs/path"))
-            == "Location(proto='sftp', user='user', host='host', port=1234, path='/abs/path')"
-        )
-        assert Location("sftp://user@host:1234//abs/path").to_key_filename() == keys_dir + "host___abs_path"
-
-    def test_socket(self, monkeypatch, keys_dir):
-        monkeypatch.delenv("BORG_REPO", raising=False)
-        assert (
-            repr(Location("socket:///repo/path"))
-            == "Location(proto='socket', user=None, host=None, port=None, path='/repo/path')"
-        )
-        assert Location("socket:///some/path").to_key_filename() == keys_dir + "_some_path"
-
-    def test_file(self, monkeypatch, keys_dir):
-        monkeypatch.delenv("BORG_REPO", raising=False)
-        assert (
-            repr(Location("file:///some/path"))
-            == "Location(proto='file', user=None, host=None, port=None, path='/some/path')"
-        )
-        assert (
-            repr(Location("file:///some/path"))
-            == "Location(proto='file', user=None, host=None, port=None, path='/some/path')"
-        )
-        assert Location("file:///some/path").to_key_filename() == keys_dir + "_some_path"
-
-    def test_smb(self, monkeypatch, keys_dir):
-        monkeypatch.delenv("BORG_REPO", raising=False)
-        assert (
-            repr(Location("file:////server/share/path"))
-            == "Location(proto='file', user=None, host=None, port=None, path='//server/share/path')"
-        )
-        assert Location("file:////server/share/path").to_key_filename() == keys_dir + "__server_share_path"
-
-    def test_folder(self, monkeypatch, keys_dir):
-        monkeypatch.delenv("BORG_REPO", raising=False)
-        rel_path = "path"
-        abs_path = os.path.abspath(rel_path)
-        assert repr(Location(rel_path)) == f"Location(proto='file', user=None, host=None, port=None, path='{abs_path}')"
-        assert Location("path").to_key_filename().endswith(rel_path)
-
-    def test_abspath(self, monkeypatch, keys_dir):
-        monkeypatch.delenv("BORG_REPO", raising=False)
-        assert (
-            repr(Location("/absolute/path"))
-            == "Location(proto='file', user=None, host=None, port=None, path='/absolute/path')"
-        )
-        assert Location("/absolute/path").to_key_filename() == keys_dir + "_absolute_path"
-        assert (
-            repr(Location("ssh://user@host//absolute/path"))
-            == "Location(proto='ssh', user='user', host='host', port=None, path='/absolute/path')"
-        )
-        assert Location("ssh://user@host//absolute/path").to_key_filename() == keys_dir + "host___absolute_path"
-
-    def test_relpath(self, monkeypatch, keys_dir):
-        monkeypatch.delenv("BORG_REPO", raising=False)
-        # for a local path, borg creates a Location instance with an absolute path
-        rel_path = "relative/path"
-        abs_path = os.path.abspath(rel_path)
-        assert repr(Location(rel_path)) == f"Location(proto='file', user=None, host=None, port=None, path='{abs_path}')"
-        assert Location(rel_path).to_key_filename().endswith("relative_path")
-        assert (
-            repr(Location("ssh://user@host/relative/path"))
-            == "Location(proto='ssh', user='user', host='host', port=None, path='relative/path')"
-        )
-        assert Location("ssh://user@host/relative/path").to_key_filename() == keys_dir + "host__relative_path"
-
-    def test_with_colons(self, monkeypatch, keys_dir):
-        monkeypatch.delenv("BORG_REPO", raising=False)
-        assert (
-            repr(Location("/abs/path:w:cols"))
-            == "Location(proto='file', user=None, host=None, port=None, path='/abs/path:w:cols')"
-        )
-        assert (
-            repr(Location("/abs/path:with:colons"))
-            == "Location(proto='file', user=None, host=None, port=None, path='/abs/path:with:colons')"
-        )
-        assert (
-            repr(Location("/abs/path:with:colons"))
-            == "Location(proto='file', user=None, host=None, port=None, path='/abs/path:with:colons')"
-        )
-        assert Location("/abs/path:with:colons").to_key_filename() == keys_dir + "_abs_path_with_colons"
-
-    def test_canonical_path(self, monkeypatch):
-        monkeypatch.delenv("BORG_REPO", raising=False)
-        locations = [
-            "relative/path",
-            "/absolute/path",
-            "file:///absolute/path",
-            "socket:///absolute/path",
-            "ssh://host/relative/path",
-            "ssh://host//absolute/path",
-            "ssh://user@host:1234/relative/path",
-            "sftp://host/relative/path",
-            "sftp://host//absolute/path",
-            "sftp://user@host:1234/relative/path",
-            "rclone:remote:path",
-        ]
-        for location in locations:
-            assert (
-                Location(location).canonical_path() == Location(Location(location).canonical_path()).canonical_path()
-            ), ("failed: %s" % location)
-
-    def test_bad_syntax(self):
-        with pytest.raises(ValueError):
-            # this is invalid due to the 2nd colon, correct: 'ssh://user@host/path'
-            Location("ssh://user@host:/path")
-
-
-@pytest.mark.parametrize(
-    "name",
-    [
-        "foobar",
-        # placeholders
-        "foobar-{now}",
-    ],
-)
-def test_archivename_ok(name):
-    archivename_validator(name)  # must not raise an exception
-
-
-@pytest.mark.parametrize(
-    "name",
-    [
-        "",  # too short
-        "x" * 201,  # too long
-        # invalid chars:
-        "foo/bar",
-        "foo\\bar",
-        ">foo",
-        "<foo",
-        "|foo",
-        'foo"bar',
-        "foo?",
-        "*bar",
-        "foo\nbar",
-        "foo\0bar",
-        # leading/trailing blanks
-        " foo",
-        "bar  ",
-        # contains surrogate-escapes
-        "foo\udc80bar",
-        "foo\udcffbar",
-    ],
-)
-def test_archivename_invalid(name):
-    with pytest.raises(ArgumentTypeError):
-        archivename_validator(name)
-
-
-@pytest.mark.parametrize("text", ["", "single line", "multi\nline\ncomment"])
-def test_text_ok(text):
-    tv = text_validator(max_length=100, name="name")
-    tv(text)  # must not raise an exception
-
-
-@pytest.mark.parametrize(
-    "text",
-    [
-        "x" * 101,  # too long
-        # invalid chars:
-        "foo\0bar",
-        # contains surrogate-escapes
-        "foo\udc80bar",
-        "foo\udcffbar",
-    ],
-)
-def test_text_invalid(text):
-    tv = text_validator(max_length=100, name="name")
-    with pytest.raises(ArgumentTypeError):
-        tv(text)
-
-
-def test_format_timedelta():
-    t0 = datetime(2001, 1, 1, 10, 20, 3, 0)
-    t1 = datetime(2001, 1, 1, 12, 20, 4, 100000)
-    assert format_timedelta(t1 - t0) == "2 hours 1.100 seconds"
-
-
 @pytest.mark.parametrize(
     "chunker_params, expected_return",
     [
@@ -498,163 +161,6 @@ def test_prune_split_no_archives():
     assert kept_because == {}
 
 
-@pytest.mark.parametrize(
-    "timeframe, num_secs",
-    [
-        ("5S", 5),
-        ("2M", 2 * 60),
-        ("1H", 60 * 60),
-        ("1d", 24 * 60 * 60),
-        ("1w", 7 * 24 * 60 * 60),
-        ("1m", 31 * 24 * 60 * 60),
-        ("1y", 365 * 24 * 60 * 60),
-    ],
-)
-def test_interval(timeframe, num_secs):
-    assert interval(timeframe) == num_secs
-
-
-@pytest.mark.parametrize(
-    "invalid_interval, error_tuple",
-    [
-        ("H", ('Invalid number "": expected positive integer',)),
-        ("-1d", ('Invalid number "-1": expected positive integer',)),
-        ("food", ('Invalid number "foo": expected positive integer',)),
-    ],
-)
-def test_interval_time_unit(invalid_interval, error_tuple):
-    with pytest.raises(ArgumentTypeError) as exc:
-        interval(invalid_interval)
-    assert exc.value.args == error_tuple
-
-
-def test_interval_number():
-    with pytest.raises(ArgumentTypeError) as exc:
-        interval("5")
-    assert exc.value.args == ('Unexpected time unit "5": choose from y, m, w, d, H, M, S',)
-
-
-def test_prune_within():
-    def subset(lst, indices):
-        return {lst[i] for i in indices}
-
-    def dotest(test_archives, within, indices):
-        for ta in test_archives, reversed(test_archives):
-            kept_because = {}
-            keep = prune_within(ta, interval(within), kept_because)
-            assert set(keep) == subset(test_archives, indices)
-            assert all("within" == kept_because[a.id][0] for a in keep)
-
-    # 1 minute, 1.5 hours, 2.5 hours, 3.5 hours, 25 hours, 49 hours
-    test_offsets = [60, 90 * 60, 150 * 60, 210 * 60, 25 * 60 * 60, 49 * 60 * 60]
-    now = datetime.now(timezone.utc)
-    test_dates = [now - timedelta(seconds=s) for s in test_offsets]
-    test_archives = [MockArchive(date, i) for i, date in enumerate(test_dates)]
-
-    dotest(test_archives, "15S", [])
-    dotest(test_archives, "2M", [0])
-    dotest(test_archives, "1H", [0])
-    dotest(test_archives, "2H", [0, 1])
-    dotest(test_archives, "3H", [0, 1, 2])
-    dotest(test_archives, "24H", [0, 1, 2, 3])
-    dotest(test_archives, "26H", [0, 1, 2, 3, 4])
-    dotest(test_archives, "2d", [0, 1, 2, 3, 4])
-    dotest(test_archives, "50H", [0, 1, 2, 3, 4, 5])
-    dotest(test_archives, "3d", [0, 1, 2, 3, 4, 5])
-    dotest(test_archives, "1w", [0, 1, 2, 3, 4, 5])
-    dotest(test_archives, "1m", [0, 1, 2, 3, 4, 5])
-    dotest(test_archives, "1y", [0, 1, 2, 3, 4, 5])
-
-
-def test_parse_timestamp():
-    assert parse_timestamp("2015-04-19T20:25:00.226410") == datetime(2015, 4, 19, 20, 25, 0, 226410, timezone.utc)
-    assert parse_timestamp("2015-04-19T20:25:00") == datetime(2015, 4, 19, 20, 25, 0, 0, timezone.utc)
-
-
-@pytest.mark.parametrize(
-    "size, fmt",
-    [
-        (0, "0 B"),  # no rounding necessary for those
-        (1, "1 B"),
-        (142, "142 B"),
-        (999, "999 B"),
-        (1000, "1.00 kB"),  # rounding starts here
-        (1001, "1.00 kB"),  # should be rounded away
-        (1234, "1.23 kB"),  # should be rounded down
-        (1235, "1.24 kB"),  # should be rounded up
-        (1010, "1.01 kB"),  # rounded down as well
-        (999990000, "999.99 MB"),  # rounded down
-        (999990001, "999.99 MB"),  # rounded down
-        (999995000, "1.00 GB"),  # rounded up to next unit
-        (10**6, "1.00 MB"),  # and all the remaining units, megabytes
-        (10**9, "1.00 GB"),  # gigabytes
-        (10**12, "1.00 TB"),  # terabytes
-        (10**15, "1.00 PB"),  # petabytes
-        (10**18, "1.00 EB"),  # exabytes
-        (10**21, "1.00 ZB"),  # zottabytes
-        (10**24, "1.00 YB"),  # yottabytes
-        (-1, "-1 B"),  # negative value
-        (-1010, "-1.01 kB"),  # negative value with rounding
-    ],
-)
-def test_file_size(size, fmt):
-    """test the size formatting routines"""
-    assert format_file_size(size) == fmt
-
-
-@pytest.mark.parametrize(
-    "size, fmt",
-    [
-        (0, "0 B"),
-        (2**0, "1 B"),
-        (2**10, "1.00 KiB"),
-        (2**20, "1.00 MiB"),
-        (2**30, "1.00 GiB"),
-        (2**40, "1.00 TiB"),
-        (2**50, "1.00 PiB"),
-        (2**60, "1.00 EiB"),
-        (2**70, "1.00 ZiB"),
-        (2**80, "1.00 YiB"),
-        (-(2**0), "-1 B"),
-        (-(2**10), "-1.00 KiB"),
-        (-(2**20), "-1.00 MiB"),
-    ],
-)
-def test_file_size_iec(size, fmt):
-    """test the size formatting routines"""
-    assert format_file_size(size, iec=True) == fmt
-
-
-@pytest.mark.parametrize(
-    "original_size, formatted_size",
-    [
-        (1234, "1.2 kB"),  # rounded down
-        (1254, "1.3 kB"),  # rounded up
-        (999990000, "1.0 GB"),  # and not 999.9 MB or 1000.0 MB
-    ],
-)
-def test_file_size_precision(original_size, formatted_size):
-    assert format_file_size(original_size, precision=1) == formatted_size
-
-
-@pytest.mark.parametrize("size, fmt", [(0, "0 B"), (1, "+1 B"), (1234, "+1.23 kB"), (-1, "-1 B"), (-1234, "-1.23 kB")])
-def test_file_size_sign(size, fmt):
-    assert format_file_size(size, sign=True) == fmt
-
-
-@pytest.mark.parametrize(
-    "string, value", [("1", 1), ("20", 20), ("5K", 5000), ("1.75M", 1750000), ("1e+9", 1e9), ("-1T", -1e12)]
-)
-def test_parse_file_size(string, value):
-    assert parse_file_size(string) == int(value)
-
-
-@pytest.mark.parametrize("string", ("", "5 Äpfel", "4E", "2229 bit", "1B"))
-def test_parse_file_size_invalid(string):
-    with pytest.raises(ValueError):
-        parse_file_size(string)
-
-
 def expected_py_mp_slow_combination():
     """do we expect msgpack to be slow in this environment?"""
     # we need to import upstream msgpack package here, not helpers.msgpack:
@@ -732,20 +238,6 @@ def test_progress_percentage_quiet(capfd):
     assert err == ""
 
 
-@pytest.mark.parametrize(
-    "fmt, items_map, expected_result",
-    [
-        ("{space:10}", {"space": " "}, " " * 10),
-        ("{foobar}", {"bar": "wrong", "foobar": "correct"}, "correct"),
-        ("{unknown_key}", {}, "{unknown_key}"),
-        ("{key}{{escaped_key}}", {}, "{key}{{escaped_key}}"),
-        ("{{escaped_key}}", {"escaped_key": 1234}, "{{escaped_key}}"),
-    ],
-)
-def test_partial_format(fmt, items_map, expected_result):
-    assert partial_format(fmt, items_map) == expected_result
-
-
 def test_chunk_file_wrapper():
     cfw = ChunkIteratorFileWrapper(iter([b"abc", b"def"]))
     assert cfw.read(2) == b"ab"
@@ -774,73 +266,6 @@ def test_chunkit():
     assert list(it) == []
 
 
-def test_clean_lines():
-    conf = """\
-#comment
-data1 #data1
-data2
-
- data3
-""".splitlines(
-        keepends=True
-    )
-    assert list(clean_lines(conf)) == ["data1 #data1", "data2", "data3"]
-    assert list(clean_lines(conf, lstrip=False)) == ["data1 #data1", "data2", " data3"]
-    assert list(clean_lines(conf, rstrip=False)) == ["data1 #data1\n", "data2\n", "data3\n"]
-    assert list(clean_lines(conf, remove_empty=False)) == ["data1 #data1", "data2", "", "data3"]
-    assert list(clean_lines(conf, remove_comments=False)) == ["#comment", "data1 #data1", "data2", "data3"]
-
-
-def test_format_line():
-    data = dict(foo="bar baz")
-    assert format_line("", data) == ""
-    assert format_line("{foo}", data) == "bar baz"
-    assert format_line("foo{foo}foo", data) == "foobar bazfoo"
-
-
-def test_format_line_erroneous():
-    data = dict()
-    with pytest.raises(PlaceholderError):
-        assert format_line("{invalid}", data)
-    with pytest.raises(PlaceholderError):
-        assert format_line("{}", data)
-    with pytest.raises(PlaceholderError):
-        assert format_line("{now!r}", data)
-    with pytest.raises(PlaceholderError):
-        assert format_line("{now.__class__.__module__.__builtins__}", data)
-
-
-def test_replace_placeholders():
-    replace_placeholders.reset()  # avoid overrides are spoiled by previous tests
-    now = datetime.now()
-    assert " " not in replace_placeholders("{now}")
-    assert int(replace_placeholders("{now:%Y}")) == now.year
-
-
-def test_override_placeholders():
-    assert replace_placeholders("{uuid4}", overrides={"uuid4": "overridden"}) == "overridden"
-
-
-def working_swidth():
-    return platform.swidth("선") == 2
-
-
-@pytest.mark.skipif(not working_swidth(), reason="swidth() is not supported / active")
-def test_swidth_slice():
-    string = "나윤선나윤선나윤선나윤선나윤선"
-    assert swidth_slice(string, 1) == ""
-    assert swidth_slice(string, -1) == ""
-    assert swidth_slice(string, 4) == "나윤"
-    assert swidth_slice(string, -4) == "윤선"
-
-
-@pytest.mark.skipif(not working_swidth(), reason="swidth() is not supported / active")
-def test_swidth_slice_mixed_characters():
-    string = "나윤a선나윤선나윤선나윤선나윤선"
-    assert swidth_slice(string, 5) == "나윤a"
-    assert swidth_slice(string, 6) == "나윤a"
-
-
 def utcfromtimestamp(timestamp):
     """Returns a naive datetime instance representing the timestamp in the UTC timezone"""
     return datetime.fromtimestamp(timestamp, timezone.utc).replace(tzinfo=None)
@@ -917,11 +342,6 @@ def test_iter_separated():
     assert list(iter_separated(fd)) == items
 
 
-def test_eval_escapes():
-    assert eval_escapes("\\n\\0\\x23") == "\n\0#"
-    assert eval_escapes("äç\\n") == "äç\n"
-
-
 class TestPassphrase:
     def test_passphrase_new_verification(self, capsys, monkeypatch):
         monkeypatch.setattr(getpass, "getpass", lambda prompt: "1234aöäü")