diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 16:10:54 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 16:10:54 +0000 |
commit | 3b2c8da6b3117ca186e27a7f94fa44b17f6a82ed (patch) | |
tree | d8d343b76ef070019c69531e4bd0f0964804e4d4 /tests | |
parent | Initial commit. (diff) | |
download | markupsafe-upstream.tar.xz markupsafe-upstream.zip |
Adding upstream version 2.1.2.upstream/2.1.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/conftest.py | 37 | ||||
-rw-r--r-- | tests/test_escape.py | 29 | ||||
-rw-r--r-- | tests/test_exception_custom_html.py | 18 | ||||
-rw-r--r-- | tests/test_leak.py | 28 | ||||
-rw-r--r-- | tests/test_markupsafe.py | 196 |
5 files changed, 308 insertions, 0 deletions
diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..d040ea8 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,37 @@ +import pytest + +from markupsafe import _native + +try: + from markupsafe import _speedups +except ImportError: + _speedups = None # type: ignore + + +@pytest.fixture( + scope="session", + params=( + _native, + pytest.param( + _speedups, + marks=pytest.mark.skipif(_speedups is None, reason="speedups unavailable"), + ), + ), +) +def _mod(request): + return request.param + + +@pytest.fixture(scope="session") +def escape(_mod): + return _mod.escape + + +@pytest.fixture(scope="session") +def escape_silent(_mod): + return _mod.escape_silent + + +@pytest.fixture(scope="session") +def soft_str(_mod): + return _mod.soft_str diff --git a/tests/test_escape.py b/tests/test_escape.py new file mode 100644 index 0000000..bf53fac --- /dev/null +++ b/tests/test_escape.py @@ -0,0 +1,29 @@ +import pytest + +from markupsafe import Markup + + +@pytest.mark.parametrize( + ("value", "expect"), + ( + # empty + ("", ""), + # ascii + ("abcd&><'\"efgh", "abcd&><'"efgh"), + ("&><'\"efgh", "&><'"efgh"), + ("abcd&><'\"", "abcd&><'""), + # 2 byte + ("こんにちは&><'\"こんばんは", "こんにちは&><'"こんばんは"), + ("&><'\"こんばんは", "&><'"こんばんは"), + ("こんにちは&><'\"", "こんにちは&><'""), + # 4 byte + ( + "\U0001F363\U0001F362&><'\"\U0001F37A xyz", + "\U0001F363\U0001F362&><'"\U0001F37A xyz", + ), + ("&><'\"\U0001F37A xyz", "&><'"\U0001F37A xyz"), + ("\U0001F363\U0001F362&><'\"", "\U0001F363\U0001F362&><'""), + ), +) +def test_escape(escape, value, expect): + assert escape(value) == Markup(expect) diff --git a/tests/test_exception_custom_html.py b/tests/test_exception_custom_html.py new file mode 100644 index 0000000..ec2f10b --- /dev/null +++ b/tests/test_exception_custom_html.py @@ -0,0 +1,18 @@ +import pytest + + +class CustomHtmlThatRaises: + def __html__(self): + raise ValueError(123) + + +def test_exception_custom_html(escape): + """Checks whether exceptions in custom __html__ implementations are + propagated correctly. + + There was a bug in the native implementation at some point: + https://github.com/pallets/markupsafe/issues/108 + """ + obj = CustomHtmlThatRaises() + with pytest.raises(ValueError): + escape(obj) diff --git a/tests/test_leak.py b/tests/test_leak.py new file mode 100644 index 0000000..55b10b9 --- /dev/null +++ b/tests/test_leak.py @@ -0,0 +1,28 @@ +import gc +import platform + +import pytest + +from markupsafe import escape + + +@pytest.mark.skipif( + escape.__module__ == "markupsafe._native", + reason="only test memory leak with speedups", +) +def test_markup_leaks(): + counts = set() + + for _i in range(20): + for _j in range(1000): + escape("foo") + escape("<foo>") + escape("foo") + escape("<foo>") + + if platform.python_implementation() == "PyPy": + gc.collect() + + counts.add(len(gc.get_objects())) + + assert len(counts) == 1 diff --git a/tests/test_markupsafe.py b/tests/test_markupsafe.py new file mode 100644 index 0000000..a62ebf9 --- /dev/null +++ b/tests/test_markupsafe.py @@ -0,0 +1,196 @@ +import pytest + +from markupsafe import Markup + + +def test_adding(escape): + unsafe = '<script type="application/x-some-script">alert("foo");</script>' + safe = Markup("<em>username</em>") + assert unsafe + safe == str(escape(unsafe)) + str(safe) + + +@pytest.mark.parametrize( + ("template", "data", "expect"), + ( + ("<em>%s</em>", "<bad user>", "<em><bad user></em>"), + ( + "<em>%(username)s</em>", + {"username": "<bad user>"}, + "<em><bad user></em>", + ), + ("%i", 3.14, "3"), + ("%.2f", 3.14, "3.14"), + ), +) +def test_string_interpolation(template, data, expect): + assert Markup(template) % data == expect + + +def test_type_behavior(): + assert type(Markup("foo") + "bar") is Markup + x = Markup("foo") + assert x.__html__() is x + + +def test_html_interop(): + class Foo: + def __html__(self): + return "<em>awesome</em>" + + def __str__(self): + return "awesome" + + assert Markup(Foo()) == "<em>awesome</em>" + result = Markup("<strong>%s</strong>") % Foo() + assert result == "<strong><em>awesome</em></strong>" + + +@pytest.mark.parametrize("args", ["foo", 42, ("foo", 42)]) +def test_missing_interpol(args): + with pytest.raises(TypeError): + Markup("<em></em>") % args + + +def test_tuple_interpol(): + result = Markup("<em>%s:%s</em>") % ("<foo>", "<bar>") + expect = Markup("<em><foo>:<bar></em>") + assert result == expect + + +def test_dict_interpol(): + result = Markup("<em>%(foo)s</em>") % {"foo": "<foo>"} + expect = Markup("<em><foo></em>") + assert result == expect + + result = Markup("<em>%(foo)s:%(bar)s</em>") % {"foo": "<foo>", "bar": "<bar>"} + expect = Markup("<em><foo>:<bar></em>") + assert result == expect + + +def test_escaping(escape): + assert escape("\"<>&'") == ""<>&'" + assert ( + Markup( + "<!-- outer comment -->" + "<em>Foo & Bar" + "<!-- inner comment about <em> -->" + "</em>" + "<!-- comment\nwith\nnewlines\n-->" + "<meta content='tag\nwith\nnewlines'>" + ).striptags() + == "Foo & Bar" + ) + + +def test_unescape(): + assert Markup("<test>").unescape() == "<test>" + + result = Markup("jack & tavi are cooler than mike & russ").unescape() + expect = "jack & tavi are cooler than mike & russ" + assert result == expect + + original = "&foo;" + once = Markup(original).unescape() + twice = Markup(once).unescape() + expect = "&foo;" + assert once == expect + assert twice == expect + + +def test_format(): + result = Markup("<em>{awesome}</em>").format(awesome="<awesome>") + assert result == "<em><awesome></em>" + + result = Markup("{0[1][bar]}").format([0, {"bar": "<bar/>"}]) + assert result == "<bar/>" + + result = Markup("{0[1][bar]}").format([0, {"bar": Markup("<bar/>")}]) + assert result == "<bar/>" + + +def test_formatting_empty(): + formatted = Markup("{}").format(0) + assert formatted == Markup("0") + + +def test_custom_formatting(): + class HasHTMLOnly: + def __html__(self): + return Markup("<foo>") + + class HasHTMLAndFormat: + def __html__(self): + return Markup("<foo>") + + def __html_format__(self, spec): + return Markup("<FORMAT>") + + assert Markup("{0}").format(HasHTMLOnly()) == Markup("<foo>") + assert Markup("{0}").format(HasHTMLAndFormat()) == Markup("<FORMAT>") + + +def test_complex_custom_formatting(): + class User: + def __init__(self, id, username): + self.id = id + self.username = username + + def __html_format__(self, format_spec): + if format_spec == "link": + return Markup('<a href="/user/{0}">{1}</a>').format( + self.id, self.__html__() + ) + elif format_spec: + raise ValueError("Invalid format spec") + + return self.__html__() + + def __html__(self): + return Markup("<span class=user>{0}</span>").format(self.username) + + user = User(1, "foo") + result = Markup("<p>User: {0:link}").format(user) + expect = Markup('<p>User: <a href="/user/1"><span class=user>foo</span></a>') + assert result == expect + + +def test_formatting_with_objects(): + class Stringable: + def __str__(self): + return "строка" + + assert Markup("{s}").format(s=Stringable()) == Markup("строка") + + +def test_escape_silent(escape, escape_silent): + assert escape_silent(None) == Markup() + assert escape(None) == Markup(None) + assert escape_silent("<foo>") == Markup("<foo>") + + +def test_splitting(): + expect = [Markup("a"), Markup("b")] + assert Markup("a b").split() == expect + assert Markup("a b").rsplit() == expect + assert Markup("a\nb").splitlines() == expect + + +def test_mul(): + assert Markup("a") * 3 == Markup("aaa") + + +def test_escape_return_type(escape): + assert isinstance(escape("a"), Markup) + assert isinstance(escape(Markup("a")), Markup) + + class Foo: + def __html__(self): + return "<strong>Foo</strong>" + + assert isinstance(escape(Foo()), Markup) + + +def test_soft_str(soft_str): + assert type(soft_str("")) is str + assert type(soft_str(Markup())) is Markup + assert type(soft_str(15)) is str |