Commit 0a457745 authored by Maxime Perrotin's avatar Maxime Perrotin

ValueGenerator fills up ctypes instances

parent cdcfd4e8
......@@ -17,6 +17,7 @@
__all__ = ['compute_random_value', 'compute_combinations']
import sys
import random
import itertools
import opengeode
......@@ -84,73 +85,105 @@ def compute_random_sequenceof(asn1_ty, pool):
# Set of functions used by the simulator to compute the combination input
# parameters for each ASN.1 data type. Yield ASN.1 Value Notation strings
# ASN.1 types must be in the format generated by ASN1SCC Python backend
def compute_combinations(asn1_ty, pool):
# dest is a ctypes instance of the corresponding ASN.1 type
# offset is an optional state to apply to dest using the Reset(offset)
def compute_combinations(asn1_ty, pool, dest, offset=None):
''' Top-level, type-dispatching function
"pool" is the set of types, found in the process.dataview attribute '''
basic = opengeode.ogParser.find_basic_type(asn1_ty.type, pool)
state = dest.GetState()
if offset:
dest.Reset(offset)
# Python2 has no "yield from"...
if basic.kind.startswith('Integer'):
for each in compute_integer_combinations(basic):
for each in compute_integer_combinations(basic, dest=dest):
yield each
elif basic.kind == 'BooleanType':
for each in compute_boolean_combinations(basic):
for each in compute_boolean_combinations(basic, dest):
yield each
elif basic.kind.startswith('Real'):
for each in compute_real_combinations(basic):
for each in compute_real_combinations(basic, dest):
yield each
elif basic.kind == 'EnumeratedType':
for each in compute_enumerated_combinations(basic):
for each in compute_enumerated_combinations(basic, dest):
yield each
elif basic.kind == 'ChoiceType':
for each in compute_choice_combinations(basic, pool):
for each in compute_choice_combinations(basic, pool, dest):
yield each
elif basic.kind in ('SequenceType', 'SetType'):
for each in compute_sequence_combinations(basic, pool):
for each in compute_sequence_combinations(basic, pool, dest):
yield each
elif basic.kind in ('SequenceOfType', 'SetOfType'):
for each in compute_sequenceof_combinations(basic, pool):
for each in compute_sequenceof_combinations(basic, pool, dest):
yield each
elif basic.kind.endswith('StringType'):
# Strings
for n in xrange(int(basic.Min), int(basic.Max) + 1):
yield '"' + 'X' * n + '"'
dest.Reset(state)
def compute_integer_combinations(asn1_ty, max_iter=0):
def compute_integer_combinations(asn1_ty, dest, max_iter=0):
''' Generator returning all integer values, with optional limit '''
state = dest.GetState()
# Do not use xrange, it needs a value that fits in a C long
max_iter = (long(asn1_ty.Min)
+ max_iter) if max_iter != 0 else long(asn1_ty.Max)
for each in itertools.count(long(asn1_ty.Min)):
if each > max_iter:
break
dest.Set(each)
yield str(each)
dest.Reset(state)
def compute_real_combinations(asn1_ty):
def compute_real_combinations(asn1_ty, dest):
''' Generator returning three real values only (set is infinite) '''
state = dest.GetState()
dest.Set(float(asn1_ty.Min))
yield asn1_ty.Min
dest.Reset(state)
dest.Set((float(asn1_ty.Max) + float(asn1_ty.Min)) / 2.0)
yield str((float(asn1_ty.Max) + float(asn1_ty.Min)) / 2.0)
dest.Reset(state)
dest.Set(float(asn1_ty.Max))
yield asn1_ty.Max
dest.Reset(state)
def compute_boolean_combinations(asn1_ty):
def compute_boolean_combinations(asn1_ty, dest):
''' Generator returning all combinations of boolean '''
state = dest.GetState()
dest.Set(True)
yield 'TRUE'
dest.Reset(state)
dest.Set(False)
yield 'FALSE'
dest.Reset(state)
def compute_enumerated_combinations(asn1_ty):
def compute_enumerated_combinations(asn1_ty, dest):
''' Generator returning all combinations of enumerated '''
DV = sys.modules[dest.__module__].DV
state = dest.GetState()
for each in asn1_ty.EnumValues.viewkeys():
enum_id = asn1_ty.EnumValues[each].EnumID
dest.Set(getattr(DV, enum_id.replace('-', '_')))
dest.Reset(state)
yield each
def compute_choice_combinations(asn1_ty, pool):
def compute_choice_combinations(asn1_ty, pool, dest):
''' Generator returning all combinations of choice components '''
state = dest.GetState()
DV = sys.modules[dest.__module__].DV
for discr, value_ty in asn1_ty.Children.viewitems():
for each in compute_combinations(value_ty, pool):
enum_id = asn1_ty.Children[discr].EnumID
dest.kind.Set(getattr(DV, enum_id.replace('-', '_')))
dest = getattr(dest, discr.replace('-', '_'))
for each in compute_combinations(value_ty, pool, dest):
yield '{}: {}'.format(discr, each)
dest.Reset(state)
def myproduct(*iterables):
......@@ -164,12 +197,21 @@ def myproduct(*iterables):
for items in myproduct(*iterables[1:]):
yield (item, ) + items
def compute_sequence_combinations(asn1_ty, pool):
def compute_sequence_combinations(asn1_ty, pool, dest):
''' Generator returning all combinations of SEQUENCE types '''
# Prepare generators to compute combinations of each field
# Using partial (closure) here allows to have "resettable" generators
elems = [partial(compute_combinations, sort, pool)
for sort in asn1_ty.Children.viewvalues()]
state = dest.GetState()
# elems = [partial(compute_combinations, sort, pool, dest)
# for sort in asn1_ty.Children.viewvalues()]
elems = []
for field, sort in asn1_ty.Children.viewitems():
# move the ctypes pointer to the position of the field
getattr(dest, field.replace('-', '_'))
offset = dest.GetState()
dest.Reset(state)
elems.append(partial(compute_combinations, sort, pool, dest, offset))
for each in myproduct(*elems):
# each is a tuple with values for the sequence, join with fieldnames
......@@ -177,13 +219,26 @@ def compute_sequence_combinations(asn1_ty, pool):
res = ('{} {}'.format(name, value) for name, value in pairs)
res_yield = '{{ {} }}'.format(', '.join(res))
yield res_yield
dest.Reset(state)
def compute_sequenceof_combinations(asn1_ty, pool):
def compute_sequenceof_combinations(asn1_ty, pool, dest):
''' Generator returning all combinations of arrays '''
state = dest.GetState()
for size in xrange(int(asn1_ty.Min), int(asn1_ty.Max) + 1):
elems = [partial(compute_combinations, asn1_ty, pool)
for _ in xrange(size)]
dest.SetLength(size)
dest.Reset(state)
# elems = [partial(compute_combinations, asn1_ty, pool, dest)
# for _ in xrange(size)]
elems = []
for idx in xrange(size):
# move the ctypes pointer to the right position in the array
dest[idx]
offset = dest.GetState()
dest.Reset(state)
elems.append(partial(compute_combinations,
asn1_ty, pool, dest, offset))
for each in myproduct(*elems):
res = '{{ {} }}'.format(', '.join(each))
yield res
dest.Reset(state)
......@@ -669,16 +669,18 @@ class sdlHandler(QObject):
the resulting state hash plus the transition that caused it
This function does not filter out the states that were already
visited - it is exhaustive. '''
def exhaust_interface(name, asn1_ty):
def exhaust_interface(name, asn1_ty=None, asn1_inst=None):
''' For all combinations of a given interface parameter, execute
the PI, save the resulting state, undo, and yield the state '''
if asn1_ty:
print 'Exhausting', name
for arg in compute_combinations(asn1_ty, self.proc.dataview):
self.click_tc(name, arg)
for arg_gser in compute_combinations(asn1_ty,
self.proc.dataview,
asn1_inst):
self.click_tc(name, arg_gser)
new_hash = self.current_hash
self.undo()
yield new_hash, (name, arg)
yield new_hash, (name, arg_gser)
else:
self.click_tc(name)
new_hash = self.current_hash
......@@ -686,13 +688,15 @@ class sdlHandler(QObject):
yield new_hash, (name, None)
for name in self.active_tc:
# Start from the current state
asn1_ty = None
asn1_ty, asn1_inst = None, None
for inp in self.proc.input_signals:
if inp['name'].lower() == name.lower():
sort = inp.get('type', None)
if sort:
asn1_ty = self.proc.dataview[sort.ReferencedTypeName]
for each in exhaust_interface(name, asn1_ty):
asn1_inst = getattr(ASN1,
sort.ReferencedTypeName.replace('-', '_'))()
for each in exhaust_interface(name, asn1_ty, asn1_inst):
yield each
def breadth_first(self, max_states=1000):
......
......@@ -10,10 +10,7 @@ from asn1_value_editor.ValueGenerator import (compute_random_value,
from asn1_value_editor.vn import fromValueNotationToPySide as parse_gser
from opengeode import Asn1scc as asn1scc
'''
Use py.test-2.7 to run these tests
'''
asn1scc.LOG.setLevel(asn1scc.logging.DEBUG)
TEST = ['data/dv1.asn']
......@@ -22,7 +19,9 @@ dataview = asn1scc.parse_asn1(TEST,
ast_version=asn1scc.ASN1.UniqueEnumeratedNames,
flags=[asn1scc.ASN1.AstOnly])
pool = dataview.types
ASN1 = asn1scc.asn2dataModel(*TEST)
CName = lambda name: name.replace('-', '_')
Inst = lambda name: getattr(ASN1, CName(name))()
def test_random_bool():
''' Test boolean values '''
......@@ -37,7 +36,7 @@ def test_random_bool():
def test_exhaustive_bool():
''' Test boolean values '''
typeName = "Type-SingleBool"
result = list(compute_combinations(pool[typeName], pool))
result = list(compute_combinations(pool[typeName], pool, Inst(typeName)))
assert result == ['TRUE', 'FALSE']
def test_random_int():
......@@ -50,7 +49,7 @@ def test_random_int():
def test_exhaustive_int():
''' Test integer value '''
typeName = "Type-SingleInt"
result = list(compute_combinations(pool[typeName], pool))
result = list(compute_combinations(pool[typeName], pool, Inst(typeName)))
assert result == [str(i) for i in range(256)]
def test_random_real():
......@@ -63,7 +62,7 @@ def test_random_real():
def test_exhaustive_real():
''' Test float value '''
typeName = "Type-SingleReal"
result = list(compute_combinations(pool[typeName], pool))
result = list(compute_combinations(pool[typeName], pool, Inst(typeName)))
asFloat = [float(i) for i in result]
assert asFloat == [-5.0, 0.0, 5.0]
......@@ -80,7 +79,7 @@ def test_random_enum():
def test_exhaustive_enum():
''' Test enumerated value '''
typeName = "Type-SingleEnum"
result = list(compute_combinations(pool[typeName], pool))
result = list(compute_combinations(pool[typeName], pool, Inst(typeName)))
assert result == ['enum-one', 'enum-two']
def test_random_string():
......@@ -93,7 +92,7 @@ def test_random_string():
def test_exhaustive_string():
''' Test octet string value '''
typeName = "Type-SingleString"
result = list(compute_combinations(pool[typeName], pool))
result = list(compute_combinations(pool[typeName], pool, Inst(typeName)))
expected_len = [i+2 for i in range(21)]
assert result[0] == '""'
assert result[1] == '"X"'
......@@ -114,7 +113,7 @@ def test_exhaustive_tinysequence():
typeName = "Type-TinySeq"
expected = [{'a': 1, 'b': True}, {'a': 1, 'b': False},
{'a': 2, 'b': True}, {'a': 2, 'b': False}]
for each in compute_combinations(pool[typeName], pool):
for each in compute_combinations(pool[typeName], pool, Inst(typeName)):
value = parse_gser('result', each)['result']
assert value in expected
expected.remove(value)
......@@ -138,7 +137,7 @@ def test_random_simplechoice():
def test_exhaustive_simplechoice():
''' Test simple choice (only made of basic types, not sequences '''
typeName = "Type-SingleChoice"
result = compute_combinations(pool[typeName], pool)
result = compute_combinations(pool[typeName], pool, Inst(typeName))
choiceA = choiceB = choiceC = 0
expected = {'choice-A': range(256),
'choice-B': [True, False],
......@@ -165,7 +164,7 @@ def test_random_simpleseqof():
def test_exhaustive_tinyseqof():
''' Test SEQOF '''
typeName = "Type-TinySeqOf" # Size-2 seq of boolean -> 00 01 10 11
result = compute_combinations(pool[typeName], pool)
result = compute_combinations(pool[typeName], pool, Inst(typeName))
expected = [[False, False], [False, True], [True, False], [True, True]]
for each in result:
value = parse_gser('result', each)['result']
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment