165 lines
6.4 KiB
Python
165 lines
6.4 KiB
Python
from pytest import raises
|
|
from lxml.etree import DTD
|
|
from os.path import isfile
|
|
from copy import copy
|
|
|
|
from rougail import RougailConvert, RougailConfig
|
|
from rougail.error import DictConsistencyError
|
|
|
|
def parse_dtd_subelement(obj_name, content, default_required=False):
|
|
if not content:
|
|
return
|
|
if content.type == 'pcdata':
|
|
if obj_name in ['value', 'param']:
|
|
type_ = 'any'
|
|
else:
|
|
type_ = 'str'
|
|
yield {'name': 'text', 'type': type_, 'required': False}
|
|
elif content.name:
|
|
yield {'name': content.name, 'type': None, 'required': default_required}
|
|
if content.left:
|
|
if content.left.type == 'pcdata':
|
|
# choice
|
|
yield {'name': 'text', 'type': 'any', 'required': False}
|
|
else:
|
|
yield {'name': content.left.name, 'type': None, 'required': default_required}
|
|
if content.right:
|
|
if content.occur == 'once':
|
|
if content.right and content.right.name:
|
|
yield {'name': content.right.name, 'type': None, 'required': default_required}
|
|
else:
|
|
yield from parse_dtd_subelement(obj_name, content.right, default_required=False)
|
|
elif content.occur == 'mult':
|
|
yield from parse_dtd_subelement(obj_name, content.right, default_required=False)
|
|
elif content.occur == 'plus':
|
|
yield from parse_dtd_subelement(obj_name, content.right, default_required=False)
|
|
else:
|
|
raise Exception('pffff')
|
|
|
|
def parse_dtd(elt_name, elts, space=0):
|
|
elt = elts.get(elt_name)
|
|
if not elt:
|
|
return ''
|
|
schema = ''
|
|
if space:
|
|
schema += " " * space + "type: seq\n"
|
|
schema += " " * space + "sequence:\n"
|
|
space = space + 2
|
|
schema += " " * space + "- type: map\n"
|
|
space = space + 2
|
|
schema += " " * space + "mapping:\n"
|
|
if elt_name == 'variables':
|
|
# type
|
|
subspace = space + 2
|
|
schema += " " * subspace + "variable:\n"
|
|
schema += parse_dtd('variable', elts, subspace + 2)
|
|
# variables
|
|
if subspace < 50:
|
|
schema += " " * subspace + "family:\n"
|
|
schema += parse_dtd('family', elts, subspace + 2)
|
|
subspace = subspace + 8
|
|
schema += " " * subspace + "variables:\n"
|
|
schema += parse_dtd(elt_name, elts, subspace + 2)
|
|
else:
|
|
schema += " " * space + "type: map\n"
|
|
schema += " " * space + "mapping:\n"
|
|
space = space + 2
|
|
if elt_name != 'variables':
|
|
if 'subelts' in elt:
|
|
for subelt in elt['subelts']:
|
|
schema += " " * space + subelt['name'] + ":\n"
|
|
subspace = space + 2
|
|
schema += " " * subspace + 'required: ' + {False: 'false', True: 'true'}[subelt['required']] + "\n"
|
|
if subelt['type']:
|
|
schema += " " * subspace + 'type: ' + subelt['type'] + "\n"
|
|
if subelt['name'] != 'family' or subspace < 26:
|
|
schema += parse_dtd(subelt['name'], elts, subspace)
|
|
attributes = {attr.name: attr for attr in elt['elt'].iterattributes()}
|
|
for name, dtd_attr in attributes.items():
|
|
enum = list(dtd_attr.itervalues())
|
|
if set(enum) == {'True', 'False'} or set(enum) == {'True', 'False', 'nil'}:
|
|
# it's a boolean
|
|
type_ = 'bool'
|
|
elif enum == ['unix_permissions']:
|
|
continue
|
|
elif f'{name}_type' in attributes and list(attributes[f'{name}_type'].itervalues()) == ['unix_permissions']:
|
|
type_ = 'int'
|
|
else:
|
|
type_ = 'str'
|
|
schema += " " * space + name + ':\n'
|
|
subspace = space + 2
|
|
schema += " " * subspace + "type: " + type_ + '\n'
|
|
if type_ != 'bool' and enum:
|
|
enumspace = subspace + 2
|
|
schema += " " * subspace + 'enum:\n'
|
|
for value in enum:
|
|
schema += " " * enumspace + '- "' + value + '"\n'
|
|
return schema
|
|
|
|
|
|
def test_valid_dtd_schema_equal():
|
|
with open(RougailConfig['dtdfilename'], 'r') as dtdfd:
|
|
dtd = DTD(dtdfd)
|
|
elts = {}
|
|
for dtd_elt in dtd.iterelements():
|
|
subelts = tuple(parse_dtd_subelement(dtd_elt.name, dtd_elt.content))
|
|
ret = {'elt': dtd_elt}
|
|
if subelts:
|
|
ret['subelts'] = subelts
|
|
elts[dtd_elt.name] = ret
|
|
schema = parse_dtd('rougail', elts)
|
|
if not isfile(RougailConfig['yamlschema_filename']):
|
|
with open(RougailConfig['yamlschema_filename'], 'w') as ymldf:
|
|
ymldf.write(schema)
|
|
with open(RougailConfig['yamlschema_filename'], 'r') as ymldf:
|
|
ori_schema = ymldf.read()
|
|
assert schema == ori_schema, f'DTD and YAML schema ({RougailConfig["yamlschema_filename"]}) are different'
|
|
|
|
|
|
def test_no_dtd():
|
|
cfg = RougailConfig.copy()
|
|
cfg['dtdfilename'] = 'notexists.dtd'
|
|
with raises(IOError):
|
|
eolobj = RougailConvert(cfg)
|
|
|
|
|
|
def test_mode_invalid_default():
|
|
# default variable mode is not in modes_level
|
|
RougailConfig['dictionaries_dir'] = ['tests/personalize_mode/dictionary']
|
|
RougailConfig['modes_level'] = ('level1', 'level2')
|
|
eolobj = RougailConvert()
|
|
with raises(DictConsistencyError) as err:
|
|
eolobj.reflexion()
|
|
assert err.value.errno == 72
|
|
|
|
|
|
def test_mode_invalid_default_family():
|
|
# default family mode is not in modes_level
|
|
RougailConfig['dictionaries_dir'] = ['tests/personalize_mode/dictionary']
|
|
RougailConfig['modes_level'] = ('level1', 'level2')
|
|
RougailConfig['default_variable_mode'] = 'level1'
|
|
eolobj = RougailConvert()
|
|
with raises(DictConsistencyError) as err:
|
|
eolobj.reflexion()
|
|
assert err.value.errno == 73
|
|
|
|
|
|
def test_personalize_mode():
|
|
RougailConfig['dictionaries_dir'] = ['tests/personalize_mode/dictionary']
|
|
RougailConfig['modes_level'] = ('level1', 'level2')
|
|
RougailConfig['default_variable_mode'] = 'level1'
|
|
RougailConfig['default_family_mode'] = 'level1'
|
|
eolobj = RougailConvert()
|
|
eolobj.save(None)
|
|
|
|
|
|
def test_personalize_mode_unknown():
|
|
# a variable has an unknown mode
|
|
RougailConfig['dictionaries_dir'] = ['tests/personalize_mode/dictionary']
|
|
RougailConfig['modes_level'] = ('level1',)
|
|
RougailConfig['default_variable_mode'] = 'level1'
|
|
RougailConfig['default_family_mode'] = 'level1'
|
|
eolobj = RougailConvert()
|
|
with raises(DictConsistencyError) as err:
|
|
eolobj.reflexion()
|
|
assert err.value.errno == 71
|