More tests for vobject (#541)

* More tests for vobject

* wip

* wip

* stylefix
This commit is contained in:
Markus Unterwaditzer 2017-01-22 23:46:14 +01:00 committed by GitHub
parent f756366081
commit 3514d7348c
2 changed files with 152 additions and 6 deletions

View file

@ -3,7 +3,8 @@
from textwrap import dedent
import hypothesis.strategies as st
from hypothesis import given
from hypothesis import assume, given
from hypothesis.stateful import Bundle, RuleBasedStateMachine, rule
import pytest
@ -214,3 +215,128 @@ def test_replace_uid(template, uid):
assert item.raw.count('\nUID:{}'.format(uid)) == 1
else:
assert '\nUID:' not in item.raw
def test_broken_item():
with pytest.raises(ValueError) as excinfo:
vobject._Component.parse('END:FOO')
assert 'Parsing error at line 1' in str(excinfo.value)
item = vobject.Item('END:FOO')
assert item.parsed is None
def test_multiple_items():
with pytest.raises(ValueError) as excinfo:
vobject._Component.parse([
'BEGIN:FOO',
'END:FOO',
'BEGIN:FOO',
'END:FOO',
])
assert 'Found 2 components, expected one' in str(excinfo.value)
c1, c2 = vobject._Component.parse([
'BEGIN:FOO',
'END:FOO',
'BEGIN:FOO',
'END:FOO',
], multiple=True)
assert c1.name == c2.name == 'FOO'
def test_input_types():
lines = ['BEGIN:FOO', 'FOO:BAR', 'END:FOO']
for x in (lines, '\r\n'.join(lines), '\r\n'.join(lines).encode('ascii')):
c = vobject._Component.parse(x)
assert c.name == 'FOO'
assert c.props == ['FOO:BAR']
assert not c.subcomponents
value_strategy = st.text(
st.characters(blacklist_categories=(
'Zs', 'Zl', 'Zp',
'Cc', 'Cs'
), blacklist_characters=':='),
min_size=1
).filter(lambda x: x.strip() == x)
class VobjectMachine(RuleBasedStateMachine):
Unparsed = Bundle('unparsed')
Parsed = Bundle('parsed')
@rule(target=Unparsed,
joined=st.booleans(),
encoded=st.booleans())
def get_unparsed_lines(self, joined, encoded):
rv = ['BEGIN:FOO', 'FOO:YES', 'END:FOO']
if joined:
rv = '\r\n'.join(rv)
if encoded:
rv = rv.encode('utf-8')
elif encoded:
assume(False)
return rv
@rule(unparsed=Unparsed, target=Parsed)
def parse(self, unparsed):
return vobject._Component.parse(unparsed)
@rule(parsed=Parsed, target=Unparsed)
def serialize(self, parsed):
return list(parsed.dump_lines())
@rule(c=Parsed,
key=uid_strategy,
value=uid_strategy)
def add_prop(self, c, key, value):
c[key] = value
assert c[key] == value
assert key in c
assert c.get(key) == value
dump = '\r\n'.join(c.dump_lines())
assert key in dump and value in dump
@rule(c=Parsed,
key=uid_strategy,
value=uid_strategy,
params=st.lists(st.tuples(value_strategy, value_strategy)))
def add_prop_raw(self, c, key, value, params):
params_str = ','.join(k + '=' + v for k, v in params)
c.props.append('{};{}:{}'.format(key, params_str, value))
assert c[key] == value
assert key in c
assert c.get(key) == value
@rule(c=Parsed, sub_c=Parsed)
def add_component(self, c, sub_c):
assume(sub_c is not c and sub_c not in c)
c.subcomponents.append(sub_c)
assert '\r\n'.join(sub_c.dump_lines()) in '\r\n'.join(c.dump_lines())
@rule(c=Parsed)
def sanity_check(self, c):
c1 = vobject._Component.parse(c.dump_lines())
assert c1 == c
TestVobjectMachine = VobjectMachine.TestCase
def test_component_contains():
item = vobject._Component.parse([
'BEGIN:FOO',
'FOO:YES',
'END:FOO'
])
assert 'FOO' in item
assert 'BAZ' not in item
with pytest.raises(ValueError):
42 in item

View file

@ -272,8 +272,7 @@ class _Component(object):
if line.strip():
stack[-1].props.append(line)
except IndexError:
raise ValueError('Parsing error at line {}. Check the debug log '
'for more information.'.format(_i + 1))
raise ValueError('Parsing error at line {}'.format(_i + 1))
if multiple:
return rv
@ -319,12 +318,25 @@ class _Component(object):
line = u'{}:{}'.format(key, val)
self.props.append(line)
def __contains__(self, obj):
if isinstance(obj, type(self)):
return obj not in self.subcomponents and \
not any(obj in x for x in self.subcomponents)
elif isinstance(obj, str):
return self.get(obj, None) is not None
else:
raise ValueError(obj)
def __getitem__(self, key):
prefix = (u'{}:'.format(key), u'{};'.format(key))
prefix_without_params = '{}:'.format(key)
prefix_with_params = '{};'.format(key)
iterlines = iter(self.props)
for line in iterlines:
if line.startswith(prefix):
rv = line.split(u':', 1)[-1]
if line.startswith(prefix_without_params):
rv = line[len(prefix_without_params):]
break
elif line.startswith(prefix_with_params):
rv = line[len(prefix_with_params):].split(':', 1)[-1]
break
else:
raise KeyError()
@ -342,3 +354,11 @@ class _Component(object):
return self[key]
except KeyError:
return default
def __eq__(self, other):
return (
isinstance(other, type(self)) and
self.name == other.name and
self.props == other.props and
self.subcomponents == other.subcomponents
)