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 from textwrap import dedent
import hypothesis.strategies as st import hypothesis.strategies as st
from hypothesis import given from hypothesis import assume, given
from hypothesis.stateful import Bundle, RuleBasedStateMachine, rule
import pytest import pytest
@ -214,3 +215,128 @@ def test_replace_uid(template, uid):
assert item.raw.count('\nUID:{}'.format(uid)) == 1 assert item.raw.count('\nUID:{}'.format(uid)) == 1
else: else:
assert '\nUID:' not in item.raw 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(): if line.strip():
stack[-1].props.append(line) stack[-1].props.append(line)
except IndexError: except IndexError:
raise ValueError('Parsing error at line {}. Check the debug log ' raise ValueError('Parsing error at line {}'.format(_i + 1))
'for more information.'.format(_i + 1))
if multiple: if multiple:
return rv return rv
@ -319,12 +318,25 @@ class _Component(object):
line = u'{}:{}'.format(key, val) line = u'{}:{}'.format(key, val)
self.props.append(line) 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): 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) iterlines = iter(self.props)
for line in iterlines: for line in iterlines:
if line.startswith(prefix): if line.startswith(prefix_without_params):
rv = line.split(u':', 1)[-1] rv = line[len(prefix_without_params):]
break
elif line.startswith(prefix_with_params):
rv = line[len(prefix_with_params):].split(':', 1)[-1]
break break
else: else:
raise KeyError() raise KeyError()
@ -342,3 +354,11 @@ class _Component(object):
return self[key] return self[key]
except KeyError: except KeyError:
return default 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
)