add more datatype tests

check from_string and to_string are counterparts
redesign standard value checks for this

Change-Id: I825cdba5955596096fd11ab52de571f05845d7ae
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34737
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
zolliker 2024-10-04 16:07:29 +02:00
parent f8d8cbd76d
commit 25986d67aa

View File

@ -36,6 +36,41 @@ def copytest(dt):
assert repr(dt) == repr(dt.copy())
assert dt.export_datatype() == dt.copy().export_datatype()
assert dt != dt.copy()
with pytest.raises(KeyError):
dt.setProperty('visibility', 0)
def valid(dt, *args, exported=None, formatted=()):
for value in args:
v = dt(value)
assert dt.import_value(dt.export_value(v)) == v
vv = dt.from_string(dt.to_string(v))
if isinstance(vv, float):
assert abs(vv - v) <= max(dt.absolute_resolution, (vv + v) * dt.relative_resolution)
else:
assert vv == v
if exported is None:
exported = args
for value, ex in zip(args, exported):
assert dt.export_value(value) == ex
for value, fm in zip(args, formatted):
assert dt.format_value(dt(value)) == fm
def invalid(dt, *args, test_import=True):
for value in args:
with pytest.raises(WrongTypeError):
dt(value)
if test_import:
with pytest.raises(WrongTypeError):
dt.import_value(value)
def out_of_range(dt, *args):
for value in args:
dt(value)
with pytest.raises(RangeError):
dt.validate(value)
def test_DataType():
@ -52,33 +87,19 @@ def test_FloatRange():
copytest(dt)
assert dt.export_datatype() == {'type': 'double', 'min':-3.14, 'max':3.14}
with pytest.raises(RangeError):
dt.validate(9)
with pytest.raises(RangeError):
dt.validate(-9)
dt(9) # convert, but do not check limits
dt(-9) # convert, but do not check limits
with pytest.raises(WrongTypeError):
dt('XX')
with pytest.raises(WrongTypeError):
dt.import_value('XX')
with pytest.raises(WrongTypeError):
dt([19, 'X'])
with pytest.raises(WrongTypeError):
dt.import_value([19, 'X'])
dt(1)
dt(0)
valid(dt, -2.718, 1, 0)
dt(13.14 - 10) # raises an error, if resolution is not handled correctly
assert dt.export_value(-2.718) == -2.718
assert dt.import_value(-2.718) == -2.718
with pytest.raises(ProgrammingError):
FloatRange('x', 'Y')
invalid(dt, 'XX', [19, 'XX'])
out_of_range(dt, -9, 9)
# check that unit can be changed
dt.setProperty('unit', 'K')
assert dt.export_datatype() == {'type': 'double', 'min':-3.14, 'max':3.14, 'unit': 'K'}
with pytest.raises(KeyError):
dt.setProperty('visibility', 0)
dt.setProperty('absolute_resolution', 0)
valid(dt, 1.25, formatted=['1.25 K'])
with pytest.raises(ProgrammingError):
FloatRange('x', 'Y')
dt = FloatRange()
copytest(dt)
@ -90,8 +111,9 @@ def test_FloatRange():
assert dt.export_datatype() == {'type': 'double', 'unit':'X', 'fmtstr':'%.2f',
'absolute_resolution':1.0,
'relative_resolution':0.1}
assert dt(4) == 4
assert dt.format_value(3.14) == '3.14 X'
valid(dt, 4, 3.1392,
formatted=['4.00 X', '3.14 X'])
assert dt.format_value(3.14, '') == '3.14'
assert dt.format_value(3.14, '#') == '3.14 #'
@ -109,30 +131,10 @@ def test_IntRange():
copytest(dt)
assert dt.export_datatype() == {'type': 'int', 'min':-3, 'max':3}
with pytest.raises(RangeError):
dt.validate(9)
with pytest.raises(RangeError):
dt.validate(-9)
dt(9) # convert, but do not check limits
dt(-9) # convert, but do not check limits
with pytest.raises(WrongTypeError):
dt('XX')
with pytest.raises(WrongTypeError):
dt.import_value('XX')
with pytest.raises(WrongTypeError):
dt([19, 'X'])
with pytest.raises(WrongTypeError):
dt.import_value([19, 'X'])
with pytest.raises(WrongTypeError):
dt(1.3)
with pytest.raises(WrongTypeError):
dt.import_value(1.3)
with pytest.raises(WrongTypeError):
dt('1.3')
with pytest.raises(WrongTypeError):
dt.import_value('1.3')
dt(1)
dt(0)
out_of_range(dt, 9, -9)
invalid(dt, 'XX', [19, 'X'], 1.3, '1.3')
valid(dt, 0, 1)
with pytest.raises(ProgrammingError):
IntRange('xc', 'Yx')
@ -155,22 +157,10 @@ def test_ScaledInteger():
# serialisation of datatype contains limits on the 'integer' value
assert dt.export_datatype() == {'type': 'scaled', 'scale':0.01, 'min':-300, 'max':300}
with pytest.raises(RangeError):
dt.validate(9)
with pytest.raises(RangeError):
dt.validate(-9)
dt(9) # convert, but do not check limits
dt(-9) # convert, but do not check limits
with pytest.raises(WrongTypeError):
dt('XX')
with pytest.raises(WrongTypeError):
dt.import_value('XX')
with pytest.raises(WrongTypeError):
dt([19, 'X'])
with pytest.raises(WrongTypeError):
dt.import_value([19, 'X'])
dt(1)
dt(0)
out_of_range(dt, 9, -9)
invalid(dt, 'XX', [19, 'X'], '1.3')
valid(dt, 0, 1, 0.0001, 2.71819, exported=[0, 100, 0, 272])
with pytest.raises(ProgrammingError):
ScaledInteger('xc', 'Yx')
with pytest.raises(ProgrammingError):
@ -182,10 +172,6 @@ def test_ScaledInteger():
assert dt.export_datatype() == {'type': 'scaled', 'scale':0.01, 'min':-300, 'max':300,
'unit': 'A'}
assert dt.export_value(0.0001) == int(0)
assert dt.export_value(2.71819) == int(272)
assert dt.import_value(272) == 2.72
dt.setProperty('scale', 0.1)
assert dt.export_datatype() == {'type': 'scaled', 'scale':0.1, 'min':-30, 'max':30,
'unit':'A'}
@ -203,11 +189,8 @@ def test_ScaledInteger():
assert dt.format_value(0.6, '') == '0.6'
assert dt.format_value(0.6, 'Z') == '0.6 Z'
assert round(dt.validate(1.0004), 5) == 0.999 # rounded value within limit
with pytest.raises(RangeError):
dt.validate(1.006) # rounded value outside limit
out_of_range(dt, 1.006, 0.395) # rounded value outside limit
assert round(dt.validate(0.398), 5) == 0.399 # rounded value within rounded limit
with pytest.raises(RangeError):
dt.validate(0.395) # rounded value outside limit
dt.setProperty('min', 1)
dt.setProperty('max', 0)
@ -230,39 +213,14 @@ def test_EnumType():
assert dt.export_datatype() == {'type': 'enum', 'members': {'a': 3, 'c': 7, 'stuff': 1}}
invalid(dt, 2.3, [19, 'X'])
with pytest.raises(RangeError):
dt(9)
with pytest.raises(RangeError):
dt.import_value(9)
with pytest.raises(RangeError):
dt(-9)
with pytest.raises(RangeError):
dt.import_value(-9)
with pytest.raises(RangeError):
dt('XX')
with pytest.raises(RangeError):
dt.import_value('XX')
with pytest.raises(WrongTypeError):
dt([19, 'X'])
with pytest.raises(WrongTypeError):
dt.import_value([19, 'X'])
assert dt('a') == 3
assert dt('stuff') == 1
assert dt(1) == 1
with pytest.raises(RangeError):
dt(2)
assert dt.export_value('c') == 7
assert dt.export_value('stuff') == 1
assert dt.export_value(1) == 1
assert dt.import_value('c') == 7
assert dt.import_value('a') == 3
assert dt.import_value('stuff') == 1
with pytest.raises(RangeError):
dt.export_value(2)
with pytest.raises(RangeError):
dt.import_value('A')
valid(dt, 'a', 'stuff', 1, 'c', exported=[3, 1, 1, 7])
assert dt.format_value(dt(3)) == 'a<3>'
@ -280,33 +238,19 @@ def test_BLOBType():
copytest(dt)
assert dt.export_datatype() == {'type': 'blob', 'minbytes':3, 'maxbytes':10}
with pytest.raises(WrongTypeError):
dt(9)
valid(dt, b'abcd', b'ert', b'123456789a', exported=['YWJjZA=='])
invalid(dt, 9, 'abcd', test_import=False)
with pytest.raises(RangeError):
dt(b'av')
with pytest.raises(RangeError):
dt(b'abcdefghijklmno')
with pytest.raises(WrongTypeError):
dt('abcd')
with pytest.raises(WrongTypeError):
dt.import_value('ab')
with pytest.raises(WrongTypeError):
dt.import_value(b'xxx')
assert dt(b'abcd') == b'abcd'
dt.setProperty('minbytes', 1)
dt.setProperty('maxbytes', 0)
with pytest.raises(ConfigError):
dt.checkProperties()
assert dt.export_value(b'abcd') == 'YWJjZA=='
assert dt.export_value(b'abcd') == 'YWJjZA=='
# assert dt.export_value('abcd') == 'YWJjZA=='
assert dt.import_value('YWJjZA==') == b'abcd'
# XXX: right? or different format?
# to be added after migration to py3
# assert dt.format_value(b'ab\0cd') == "b'ab\\x00cd\'"
assert dt.format_value(b'ab\0cd') == "b'ab\\x00cd\'"
def test_StringType():
@ -322,8 +266,8 @@ def test_StringType():
copytest(dt)
assert dt.export_datatype() == {'type': 'string', 'minchars':4, 'maxchars':11}
with pytest.raises(WrongTypeError):
dt(9)
invalid(dt, 9, b'abcd')
valid(dt, 'abcd', exported=['abcd'])
with pytest.raises(RangeError):
dt('av')
with pytest.raises(RangeError):
@ -331,14 +275,9 @@ def test_StringType():
with pytest.raises(RangeError):
dt('abcdefg\0')
assert dt('abcd') == 'abcd'
# tests with bytes have to be added after migration to py3
#assert dt(b'abcd') == 'abcd'
assert dt.export_value('abcd') == 'abcd'
# assert dt.export_value(b'abcd') == 'abcd'
assert dt.import_value('abcd') == 'abcd'
assert dt.format_value('abcd') == "'abcd'"
assert dt.to_string('abcd') == 'abcd'
dt.setProperty('minchars', 1)
dt.setProperty('maxchars', 0)
@ -352,19 +291,13 @@ def test_TextType():
copytest(dt)
assert dt.export_datatype() == {'type': 'string', 'maxchars':12}
with pytest.raises(WrongTypeError):
dt(9)
invalid(dt, 9, b'abcd')
with pytest.raises(RangeError):
dt('abcdefghijklmno')
with pytest.raises(RangeError):
dt('abcdefg\0')
valid(dt, 'abcd', exported=['abcd'])
assert dt('ab\n\ncd\n') == 'ab\n\ncd\n'
# assert dt(b'ab\n\ncd\n') == 'ab\n\ncd\n'
assert dt.export_value('abcd') == 'abcd'
# assert dt.export_value(b'abcd') == b'abcd'
assert dt.export_value('abcd') == 'abcd'
assert dt.import_value('abcd') == 'abcd'
def test_BoolType():
@ -373,26 +306,11 @@ def test_BoolType():
copytest(dt)
assert dt.export_datatype() == {'type': 'bool'}
with pytest.raises(WrongTypeError):
dt(9)
with pytest.raises(WrongTypeError):
dt('av')
valid(dt, 1, True, 0, False, exported=[1, 1, 0, 0],
formatted=['True', 'True', 'False', 'False'])
assert dt.from_string('true') is True
assert dt.from_string('off') is False
assert dt(1) is True
assert dt.export_value(False) is False
assert dt.export_value(0) is False
assert dt.export_value(1) is True
assert dt.import_value(False) is False
assert dt.import_value(True) is True
with pytest.raises(WrongTypeError):
dt.import_value('av')
assert dt.format_value(dt(0)) == "False"
assert dt.format_value(True) == "True"
invalid(dt, 2, 'av')
with pytest.raises(TypeError):
# pylint: disable=unexpected-keyword-arg
@ -421,6 +339,7 @@ def test_ArrayOf():
with pytest.raises(WrongTypeError):
dt('av')
valid(dt, [1, 2, 3])
assert dt([1, 2, 3]) == (1, 2, 3)
assert dt.export_value([1, 2, 3]) == [1, 2, 3]
@ -448,6 +367,13 @@ def test_ArrayOf():
dt = ArrayOf(ArrayOf(FloatRange(unit='m')))
assert dt.format_value([[0, 1], [2, 3]]) == '[[0, 1], [2, 3]] m'
dt = ArrayOf(StructOf(f=FloatRange(unit='K')))
assert dt.format_value([{'f': 1.5}]) == "[{f=1.5 K}]"
assert dt.to_string([{'f': 1.5}]) == "[{'f': 1.5}]"
dt = ArrayOf(ArrayOf(EnumType(a=1, b=2)))
assert dt.to_string(dt([[1, 2]])) == "[['a', 'b']]"
def test_TupleOf():
# test constructor catching illegal arguments
@ -463,6 +389,7 @@ def test_TupleOf():
with pytest.raises(WrongTypeError):
dt([99, 'X'])
valid(dt, [1, True])
assert dt([1, True]) == (1, True)
assert dt.export_value([1, True]) == [1, True]
@ -496,6 +423,7 @@ def test_StructOf():
with pytest.raises(RangeError):
dt.validate({'a_string': 'XXX', 'an_int': 1811})
valid(dt, {'a_string': 'XXX', 'an_int': 8})
assert dt({'a_string': 'XXX', 'an_int': 8}) == {'a_string': 'XXX',
'an_int': 8}
assert dt.export_value({'an_int': 13, 'a_string': 'WFEC'}) == {
@ -504,6 +432,7 @@ def test_StructOf():
'a_string': 'WFEC', 'an_int': 13}
assert dt.format_value({'an_int': 2, 'a_string': 'Z'}) == "{an_int=2, a_string='Z'}"
assert dt.to_string({'an_int': 2, 'a_string': 'Z'}) == "{'an_int': 2, 'a_string': 'Z'}"
dt = StructOf(['optionalmember'], optionalmember=EnumType('myenum', single=0))
copytest(dt)