1import pytest
2
3from jinja2 import Environment
4from jinja2 import Markup
5
6
7class MyDict(dict):
8    pass
9
10
11class TestTestsCase:
12    def test_defined(self, env):
13        tmpl = env.from_string("{{ missing is defined }}|{{ true is defined }}")
14        assert tmpl.render() == "False|True"
15
16    def test_even(self, env):
17        tmpl = env.from_string("""{{ 1 is even }}|{{ 2 is even }}""")
18        assert tmpl.render() == "False|True"
19
20    def test_odd(self, env):
21        tmpl = env.from_string("""{{ 1 is odd }}|{{ 2 is odd }}""")
22        assert tmpl.render() == "True|False"
23
24    def test_lower(self, env):
25        tmpl = env.from_string("""{{ "foo" is lower }}|{{ "FOO" is lower }}""")
26        assert tmpl.render() == "True|False"
27
28    # Test type checks
29    @pytest.mark.parametrize(
30        "op,expect",
31        (
32            ("none is none", True),
33            ("false is none", False),
34            ("true is none", False),
35            ("42 is none", False),
36            ("none is true", False),
37            ("false is true", False),
38            ("true is true", True),
39            ("0 is true", False),
40            ("1 is true", False),
41            ("42 is true", False),
42            ("none is false", False),
43            ("false is false", True),
44            ("true is false", False),
45            ("0 is false", False),
46            ("1 is false", False),
47            ("42 is false", False),
48            ("none is boolean", False),
49            ("false is boolean", True),
50            ("true is boolean", True),
51            ("0 is boolean", False),
52            ("1 is boolean", False),
53            ("42 is boolean", False),
54            ("0.0 is boolean", False),
55            ("1.0 is boolean", False),
56            ("3.14159 is boolean", False),
57            ("none is integer", False),
58            ("false is integer", False),
59            ("true is integer", False),
60            ("42 is integer", True),
61            ("3.14159 is integer", False),
62            ("(10 ** 100) is integer", True),
63            ("none is float", False),
64            ("false is float", False),
65            ("true is float", False),
66            ("42 is float", False),
67            ("4.2 is float", True),
68            ("(10 ** 100) is float", False),
69            ("none is number", False),
70            ("false is number", True),
71            ("true is number", True),
72            ("42 is number", True),
73            ("3.14159 is number", True),
74            ("complex is number", True),
75            ("(10 ** 100) is number", True),
76            ("none is string", False),
77            ("false is string", False),
78            ("true is string", False),
79            ("42 is string", False),
80            ('"foo" is string', True),
81            ("none is sequence", False),
82            ("false is sequence", False),
83            ("42 is sequence", False),
84            ('"foo" is sequence', True),
85            ("[] is sequence", True),
86            ("[1, 2, 3] is sequence", True),
87            ("{} is sequence", True),
88            ("none is mapping", False),
89            ("false is mapping", False),
90            ("42 is mapping", False),
91            ('"foo" is mapping', False),
92            ("[] is mapping", False),
93            ("{} is mapping", True),
94            ("mydict is mapping", True),
95            ("none is iterable", False),
96            ("false is iterable", False),
97            ("42 is iterable", False),
98            ('"foo" is iterable', True),
99            ("[] is iterable", True),
100            ("{} is iterable", True),
101            ("range(5) is iterable", True),
102            ("none is callable", False),
103            ("false is callable", False),
104            ("42 is callable", False),
105            ('"foo" is callable', False),
106            ("[] is callable", False),
107            ("{} is callable", False),
108            ("range is callable", True),
109        ),
110    )
111    def test_types(self, env, op, expect):
112        t = env.from_string(f"{{{{ {op} }}}}")
113        assert t.render(mydict=MyDict(), complex=complex(1, 2)) == str(expect)
114
115    def test_upper(self, env):
116        tmpl = env.from_string('{{ "FOO" is upper }}|{{ "foo" is upper }}')
117        assert tmpl.render() == "True|False"
118
119    def test_equalto(self, env):
120        tmpl = env.from_string(
121            "{{ foo is eq 12 }}|"
122            "{{ foo is eq 0 }}|"
123            "{{ foo is eq (3 * 4) }}|"
124            '{{ bar is eq "baz" }}|'
125            '{{ bar is eq "zab" }}|'
126            '{{ bar is eq ("ba" + "z") }}|'
127            "{{ bar is eq bar }}|"
128            "{{ bar is eq foo }}"
129        )
130        assert (
131            tmpl.render(foo=12, bar="baz")
132            == "True|False|True|True|False|True|True|False"
133        )
134
135    @pytest.mark.parametrize(
136        "op,expect",
137        (
138            ("eq 2", True),
139            ("eq 3", False),
140            ("ne 3", True),
141            ("ne 2", False),
142            ("lt 3", True),
143            ("lt 2", False),
144            ("le 2", True),
145            ("le 1", False),
146            ("gt 1", True),
147            ("gt 2", False),
148            ("ge 2", True),
149            ("ge 3", False),
150        ),
151    )
152    def test_compare_aliases(self, env, op, expect):
153        t = env.from_string(f"{{{{ 2 is {op} }}}}")
154        assert t.render() == str(expect)
155
156    def test_sameas(self, env):
157        tmpl = env.from_string("{{ foo is sameas false }}|{{ 0 is sameas false }}")
158        assert tmpl.render(foo=False) == "True|False"
159
160    def test_no_paren_for_arg1(self, env):
161        tmpl = env.from_string("{{ foo is sameas none }}")
162        assert tmpl.render(foo=None) == "True"
163
164    def test_escaped(self, env):
165        env = Environment(autoescape=True)
166        tmpl = env.from_string("{{ x is escaped }}|{{ y is escaped }}")
167        assert tmpl.render(x="foo", y=Markup("foo")) == "False|True"
168
169    def test_greaterthan(self, env):
170        tmpl = env.from_string("{{ 1 is greaterthan 0 }}|{{ 0 is greaterthan 1 }}")
171        assert tmpl.render() == "True|False"
172
173    def test_lessthan(self, env):
174        tmpl = env.from_string("{{ 0 is lessthan 1 }}|{{ 1 is lessthan 0 }}")
175        assert tmpl.render() == "True|False"
176
177    def test_multiple_tests(self):
178        items = []
179
180        def matching(x, y):
181            items.append((x, y))
182            return False
183
184        env = Environment()
185        env.tests["matching"] = matching
186        tmpl = env.from_string(
187            "{{ 'us-west-1' is matching '(us-east-1|ap-northeast-1)'"
188            " or 'stage' is matching '(dev|stage)' }}"
189        )
190        assert tmpl.render() == "False"
191        assert items == [
192            ("us-west-1", "(us-east-1|ap-northeast-1)"),
193            ("stage", "(dev|stage)"),
194        ]
195
196    def test_in(self, env):
197        tmpl = env.from_string(
198            '{{ "o" is in "foo" }}|'
199            '{{ "foo" is in "foo" }}|'
200            '{{ "b" is in "foo" }}|'
201            "{{ 1 is in ((1, 2)) }}|"
202            "{{ 3 is in ((1, 2)) }}|"
203            "{{ 1 is in [1, 2] }}|"
204            "{{ 3 is in [1, 2] }}|"
205            '{{ "foo" is in {"foo": 1}}}|'
206            '{{ "baz" is in {"bar": 1}}}'
207        )
208        assert tmpl.render() == "True|True|False|True|False|True|False|True|False"
209