1import pickle
2import random
3from collections import deque
4from copy import copy as shallow_copy
5
6import pytest
7from markupsafe import Markup
8
9from jinja2.utils import consume
10from jinja2.utils import generate_lorem_ipsum
11from jinja2.utils import LRUCache
12from jinja2.utils import missing
13from jinja2.utils import object_type_repr
14from jinja2.utils import select_autoescape
15from jinja2.utils import urlize
16
17
18class TestLRUCache:
19    def test_simple(self):
20        d = LRUCache(3)
21        d["a"] = 1
22        d["b"] = 2
23        d["c"] = 3
24        d["a"]
25        d["d"] = 4
26        assert len(d) == 3
27        assert "a" in d and "c" in d and "d" in d and "b" not in d
28
29    def test_itervalues(self):
30        cache = LRUCache(3)
31        cache["b"] = 1
32        cache["a"] = 2
33        values = [v for v in cache.values()]
34        assert len(values) == 2
35        assert 1 in values
36        assert 2 in values
37
38    def test_itervalues_empty(self):
39        cache = LRUCache(2)
40        values = [v for v in cache.values()]
41        assert len(values) == 0
42
43    def test_pickleable(self):
44        cache = LRUCache(2)
45        cache["foo"] = 42
46        cache["bar"] = 23
47        cache["foo"]
48
49        for protocol in range(3):
50            copy = pickle.loads(pickle.dumps(cache, protocol))
51            assert copy.capacity == cache.capacity
52            assert copy._mapping == cache._mapping
53            assert copy._queue == cache._queue
54
55    @pytest.mark.parametrize("copy_func", [LRUCache.copy, shallow_copy])
56    def test_copy(self, copy_func):
57        cache = LRUCache(2)
58        cache["a"] = 1
59        cache["b"] = 2
60        copy = copy_func(cache)
61        assert copy._queue == cache._queue
62        copy["c"] = 3
63        assert copy._queue != cache._queue
64        assert "a" not in copy and "b" in copy and "c" in copy
65
66    def test_clear(self):
67        d = LRUCache(3)
68        d["a"] = 1
69        d["b"] = 2
70        d["c"] = 3
71        d.clear()
72        assert d.__getstate__() == {"capacity": 3, "_mapping": {}, "_queue": deque([])}
73
74    def test_repr(self):
75        d = LRUCache(3)
76        d["a"] = 1
77        d["b"] = 2
78        d["c"] = 3
79        # Sort the strings - mapping is unordered
80        assert sorted(repr(d)) == sorted("<LRUCache {'a': 1, 'b': 2, 'c': 3}>")
81
82    def test_items(self):
83        """Test various items, keys, values and iterators of LRUCache."""
84        d = LRUCache(3)
85        d["a"] = 1
86        d["b"] = 2
87        d["c"] = 3
88        assert d.items() == [("c", 3), ("b", 2), ("a", 1)]
89        assert d.keys() == ["c", "b", "a"]
90        assert d.values() == [3, 2, 1]
91        assert list(reversed(d)) == ["a", "b", "c"]
92
93        # Change the cache a little
94        d["b"]
95        d["a"] = 4
96        assert d.items() == [("a", 4), ("b", 2), ("c", 3)]
97        assert d.keys() == ["a", "b", "c"]
98        assert d.values() == [4, 2, 3]
99        assert list(reversed(d)) == ["c", "b", "a"]
100
101    def test_setdefault(self):
102        d = LRUCache(3)
103        assert len(d) == 0
104        assert d.setdefault("a") is None
105        assert d.setdefault("a", 1) is None
106        assert len(d) == 1
107        assert d.setdefault("b", 2) == 2
108        assert len(d) == 2
109
110
111class TestHelpers:
112    def test_object_type_repr(self):
113        class X:
114            pass
115
116        assert object_type_repr(42) == "int object"
117        assert object_type_repr([]) == "list object"
118        assert object_type_repr(X()) == "test_utils.X object"
119        assert object_type_repr(None) == "None"
120        assert object_type_repr(Ellipsis) == "Ellipsis"
121
122    def test_autoescape_select(self):
123        func = select_autoescape(
124            enabled_extensions=("html", ".htm"),
125            disabled_extensions=("txt",),
126            default_for_string="STRING",
127            default="NONE",
128        )
129
130        assert func(None) == "STRING"
131        assert func("unknown.foo") == "NONE"
132        assert func("foo.html")
133        assert func("foo.htm")
134        assert not func("foo.txt")
135        assert func("FOO.HTML")
136        assert not func("FOO.TXT")
137
138
139class TestEscapeUrlizeTarget:
140    def test_escape_urlize_target(self):
141        url = "http://example.org"
142        target = "<script>"
143        assert urlize(url, target=target) == (
144            '<a href="http://example.org"'
145            ' target="&lt;script&gt;">'
146            "http://example.org</a>"
147        )
148
149
150class TestLoremIpsum:
151    def test_lorem_ipsum_markup(self):
152        """Test that output of lorem_ipsum is Markup by default."""
153        assert isinstance(generate_lorem_ipsum(), Markup)
154
155    def test_lorem_ipsum_html(self):
156        """Test that output of lorem_ipsum is a string_type when not html."""
157        assert isinstance(generate_lorem_ipsum(html=False), str)
158
159    def test_lorem_ipsum_n(self):
160        """Test that the n (number of lines) works as expected."""
161        assert generate_lorem_ipsum(n=0, html=False) == ""
162        for n in range(1, 50):
163            assert generate_lorem_ipsum(n=n, html=False).count("\n") == (n - 1) * 2
164
165    def test_lorem_ipsum_min(self):
166        """Test that at least min words are in the output of each line"""
167        for _ in range(5):
168            m = random.randrange(20, 99)
169            for _ in range(10):
170                assert generate_lorem_ipsum(n=1, min=m, html=False).count(" ") >= m - 1
171
172    def test_lorem_ipsum_max(self):
173        """Test that at least max words are in the output of each line"""
174        for _ in range(5):
175            m = random.randrange(21, 100)
176            for _ in range(10):
177                assert generate_lorem_ipsum(n=1, max=m, html=False).count(" ") < m - 1
178
179
180def test_missing():
181    """Test the repr of missing."""
182    assert repr(missing) == "missing"
183
184
185def test_consume():
186    """Test that consume consumes an iterator."""
187    x = iter([1, 2, 3, 4, 5])
188    consume(x)
189    with pytest.raises(StopIteration):
190        next(x)
191