Add more strict type tests

This PR adds a few more type-related tests.

- boolean
    Testing of an object is a boolean required 2 tests.

- false
    Make this similar to testing none value

- true
    Make this similar to testing none value

- integer
    The existing 'number' test does not make a distinction between
    integer, float or even booleans

- float
    The existing 'number' test does not make a distinction between
    integer, float or even booleans
This commit is contained in:
Dag Wieers 2018-03-17 04:00:11 +01:00 committed by David Lord
parent 9aa8296781
commit 9bd3cb22c1
No known key found for this signature in database
GPG Key ID: 7A1C87E3F5BC42A8
3 changed files with 143 additions and 36 deletions

View File

@ -32,6 +32,8 @@ Unreleased
- Parentheses around comparisons are preserved, so
``{{ 2 * (3 < 5) }}`` outputs "2" instead of "False".
:issue:`755`, :pr:`938`
- Add new ``boolean``, ``false``, ``true``, ``integer`` and ``float``
tests. :pr:`824`
Version 2.10.3

View File

@ -63,6 +63,48 @@ def test_none(value):
return value is None
def test_boolean(value):
"""Return true if the object is a boolean value.
.. versionadded:: 2.11
"""
return value is True or value is False
def test_false(value):
"""Return true if the object is False.
.. versionadded:: 2.11
"""
return value is False
def test_true(value):
"""Return true if the object is True.
.. versionadded:: 2.11
"""
return value is True
# NOTE: The existing Jinja2 'number' test matches booleans and floats
def test_integer(value):
"""Return true if the object is an integer.
.. versionadded:: 2.11
"""
return isinstance(value, integer_types) and value is not True and value is not False
# NOTE: The existing Jinja2 'number' test matches booleans and integers
def test_float(value):
"""Return true if the object is a float.
.. versionadded:: 2.11
"""
return isinstance(value, float)
def test_lower(value):
"""Return true if the variable is lowercased."""
return text_type(value).islower()
@ -145,6 +187,11 @@ TESTS = {
'defined': test_defined,
'undefined': test_undefined,
'none': test_none,
'boolean': test_boolean,
'false': test_false,
'true': test_true,
'integer': test_integer,
'float': test_float,
'lower': test_lower,
'upper': test_upper,
'string': test_string,

View File

@ -12,6 +12,8 @@ import pytest
from jinja2 import Markup, Environment
class MyDict(dict):
pass
@pytest.mark.test_tests
class TestTestsCase(object):
@ -33,45 +35,101 @@ class TestTestsCase(object):
tmpl = env.from_string('''{{ "foo" is lower }}|{{ "FOO" is lower }}''')
assert tmpl.render() == 'True|False'
def test_typechecks(self, env):
tmpl = env.from_string('''
{{ 42 is undefined }}
{{ 42 is defined }}
{{ 42 is none }}
{{ none is none }}
{{ 42 is number }}
{{ 42 is string }}
{{ "foo" is string }}
{{ "foo" is sequence }}
{{ [1] is sequence }}
{{ range is callable }}
{{ 42 is callable }}
{{ range(5) is iterable }}
{{ {} is mapping }}
{{ mydict is mapping }}
{{ [] is mapping }}
{{ 10 is number }}
{{ (10 ** 100) is number }}
{{ 3.14159 is number }}
{{ complex is number }}
''')
# Test type checks
@pytest.mark.parametrize('op,expect', (
('none is none', True),
('false is none', False),
('true is none', False),
('42 is none', False),
class MyDict(dict):
pass
('none is true', False),
('false is true', False),
('true is true', True),
('0 is true', False),
('1 is true', False),
('42 is true', False),
assert tmpl.render(mydict=MyDict(), complex=complex(1, 2)).split() == [
'False', 'True', 'False', 'True', 'True', 'False',
'True', 'True', 'True', 'True', 'False', 'True',
'True', 'True', 'False', 'True', 'True', 'True', 'True'
]
('none is false', False),
('false is false', True),
('true is false', False),
('0 is false', False),
('1 is false', False),
('42 is false', False),
('none is boolean', False),
('false is boolean', True),
('true is boolean', True),
('0 is boolean', False),
('1 is boolean', False),
('42 is boolean', False),
('0.0 is boolean', False),
('1.0 is boolean', False),
('3.14159 is boolean', False),
('none is integer', False),
('false is integer', False),
('true is integer', False),
('42 is integer', True),
('3.14159 is integer', False),
('(10 ** 100) is integer', True),
('none is float', False),
('false is float', False),
('true is float', False),
('42 is float', False),
('4.2 is float', True),
('(10 ** 100) is float', False),
('none is number', False),
('false is number', True),
('true is number', True),
('42 is number', True),
('3.14159 is number', True),
('complex is number', True),
('(10 ** 100) is number', True),
('none is string', False),
('false is string', False),
('true is string', False),
('42 is string', False),
('"foo" is string', True),
('none is sequence', False),
('false is sequence', False),
('42 is sequence', False),
('"foo" is sequence', True),
('[] is sequence', True),
('[1, 2, 3] is sequence', True),
('{} is sequence', True),
('none is mapping', False),
('false is mapping', False),
('42 is mapping', False),
('"foo" is mapping', False),
('[] is mapping', False),
('{} is mapping', True),
('mydict is mapping', True),
('none is iterable', False),
('false is iterable', False),
('42 is iterable', False),
('"foo" is iterable', True),
('[] is iterable', True),
('{} is iterable', True),
('range(5) is iterable', True),
('none is callable', False),
('false is callable', False),
('42 is callable', False),
('"foo" is callable', False),
('[] is callable', False),
('{} is callable', False),
('range is callable', True),
))
def test_types(self, env, op, expect):
t = env.from_string('{{{{ {op} }}}}'.format(op=op))
assert t.render(mydict=MyDict(), complex=complex(1, 2)) == str(expect)
def test_sequence(self, env):
tmpl = env.from_string(
'{{ [1, 2, 3] is sequence }}|'
'{{ "foo" is sequence }}|'
'{{ 42 is sequence }}'
)
assert tmpl.render() == 'True|True|False'
def test_upper(self, env):
tmpl = env.from_string('{{ "FOO" is upper }}|{{ "foo" is upper }}')