mirror of
https://github.com/ceph/s3-tests.git
synced 2024-11-25 13:47:27 +00:00
S3 Fuzzer: Added SpecialVariables dict subclass
Helper class to catch sentinal keys and turn them into random values. This will be used to generate garbage data when expanding a decision. Also add unit tests for expand_decision and assemble_decision
This commit is contained in:
parent
a9a41a2891
commit
7d9ec02686
2 changed files with 92 additions and 4 deletions
|
@ -23,7 +23,7 @@ def build_graph():
|
||||||
graph = {}
|
graph = {}
|
||||||
graph['start'] = {
|
graph['start'] = {
|
||||||
'set': {},
|
'set': {},
|
||||||
'choices': ['node1']
|
'choices': ['node2']
|
||||||
}
|
}
|
||||||
graph['leaf'] = {
|
graph['leaf'] = {
|
||||||
'set': {
|
'set': {
|
||||||
|
@ -38,6 +38,14 @@ def build_graph():
|
||||||
},
|
},
|
||||||
'choices': ['leaf']
|
'choices': ['leaf']
|
||||||
}
|
}
|
||||||
|
graph['node2'] = {
|
||||||
|
'set': {
|
||||||
|
'randkey': 'value-{random 10-15 printable}',
|
||||||
|
'path': '/{bucket_readable}',
|
||||||
|
'indirect_key1': '{key1}'
|
||||||
|
},
|
||||||
|
'choices': ['leaf']
|
||||||
|
}
|
||||||
graph['bad_node'] = {
|
graph['bad_node'] = {
|
||||||
'set': {
|
'set': {
|
||||||
'key1': 'value1'
|
'key1': 'value1'
|
||||||
|
@ -62,6 +70,7 @@ def test_descend_leaf_node():
|
||||||
eq(decision['key2'], 'value2')
|
eq(decision['key2'], 'value2')
|
||||||
e = assert_raises(KeyError, lambda x: decision[x], 'key3')
|
e = assert_raises(KeyError, lambda x: decision[x], 'key3')
|
||||||
|
|
||||||
|
|
||||||
def test_descend_node():
|
def test_descend_node():
|
||||||
graph = build_graph()
|
graph = build_graph()
|
||||||
prng = random.Random(1)
|
prng = random.Random(1)
|
||||||
|
@ -71,8 +80,45 @@ def test_descend_node():
|
||||||
eq(decision['key2'], 'value2')
|
eq(decision['key2'], 'value2')
|
||||||
eq(decision['key3'], 'value3')
|
eq(decision['key3'], 'value3')
|
||||||
|
|
||||||
|
|
||||||
def test_descend_bad_node():
|
def test_descend_bad_node():
|
||||||
graph = build_graph()
|
graph = build_graph()
|
||||||
prng = random.Random(1)
|
prng = random.Random(1)
|
||||||
assert_raises(KeyError, descend_graph, graph, 'bad_node', prng)
|
assert_raises(KeyError, descend_graph, graph, 'bad_node', prng)
|
||||||
|
|
||||||
|
|
||||||
|
def test_SpecialVariables_dict():
|
||||||
|
prng = random.Random(1)
|
||||||
|
testdict = {'foo': 'bar'}
|
||||||
|
tester = SpecialVariables(testdict, prng)
|
||||||
|
|
||||||
|
eq(tester['foo'], 'bar')
|
||||||
|
eq(tester['random 10-15 printable'], '[/pNI$;92@') #FIXME: how should I test pseudorandom content?
|
||||||
|
|
||||||
|
def test_assemble_decision():
|
||||||
|
graph = build_graph()
|
||||||
|
prng = random.Random(1)
|
||||||
|
decision = assemble_decision(graph, prng)
|
||||||
|
|
||||||
|
eq(decision['key1'], 'value1')
|
||||||
|
eq(decision['key2'], 'value2')
|
||||||
|
eq(decision['randkey'], 'value-{random 10-15 printable}')
|
||||||
|
eq(decision['indirect_key1'], '{key1}')
|
||||||
|
eq(decision['path'], '/{bucket_readable}')
|
||||||
|
assert_raises(KeyError, lambda x: decision[x], 'key3')
|
||||||
|
|
||||||
|
def test_expand_decision():
|
||||||
|
graph = build_graph()
|
||||||
|
prng = random.Random(1)
|
||||||
|
|
||||||
|
decision = assemble_decision(graph, prng)
|
||||||
|
decision.update({'bucket_readable': 'my-readable-bucket'})
|
||||||
|
|
||||||
|
request = expand_decision(decision, prng)
|
||||||
|
|
||||||
|
eq(request['key1'], 'value1')
|
||||||
|
eq(request['indirect_key1'], 'value1')
|
||||||
|
eq(request['path'], '/my-readable-bucket')
|
||||||
|
eq(request['randkey'], 'value-?') #FIXME: again, how to handle the pseudorandom content?
|
||||||
|
assert_raises(KeyError, lambda x: decision[x], 'key3')
|
||||||
|
|
||||||
|
|
|
@ -22,14 +22,16 @@ def descend_graph(decision_graph, node_name, prng):
|
||||||
the node's "set" list, pick a choice from the "choice" list, and
|
the node's "set" list, pick a choice from the "choice" list, and
|
||||||
recurse. Finally, return dictionary of values
|
recurse. Finally, return dictionary of values
|
||||||
"""
|
"""
|
||||||
|
node = decision_graph[node_name]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
choice = prng.choice(decision_graph[node_name]['choices'])
|
#TODO: Give weights to each choice
|
||||||
|
choice = prng.choice(node['choices'])
|
||||||
decision = descend_graph(decision_graph, choice, prng)
|
decision = descend_graph(decision_graph, choice, prng)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
decision = {}
|
decision = {}
|
||||||
|
|
||||||
node = decision_graph[node_name]
|
#TODO: Add in headers
|
||||||
|
|
||||||
for key in node['set']:
|
for key in node['set']:
|
||||||
if decision.has_key(key):
|
if decision.has_key(key):
|
||||||
raise KeyError("Node %s tried to set '%s', but that key was already set by a lower node!" %(node_name, key))
|
raise KeyError("Node %s tried to set '%s', but that key was already set by a lower node!" %(node_name, key))
|
||||||
|
@ -45,6 +47,46 @@ def expand_decision(decision, prng):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class SpecialVariables(dict):
|
||||||
|
charsets = {
|
||||||
|
'printable': string.printable,
|
||||||
|
'punctuation': string.punctuation,
|
||||||
|
'whitespace': string.whitespace
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, orig_dict, prng):
|
||||||
|
self.update(orig_dict)
|
||||||
|
self.prng = prng
|
||||||
|
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
fields = key.split(None, 1)
|
||||||
|
fn = getattr(self, 'special_{name}'.format(name=fields[0]), None)
|
||||||
|
if fn is None:
|
||||||
|
return super(SpecialVariables, self).__getitem__(key)
|
||||||
|
|
||||||
|
if len(fields) == 1:
|
||||||
|
fields.apppend('')
|
||||||
|
return fn(fields[1])
|
||||||
|
|
||||||
|
|
||||||
|
def special_random(self, args):
|
||||||
|
arg_list = args.split()
|
||||||
|
try:
|
||||||
|
size_min, size_max = [int(x) for x in arg_list[0].split('-')]
|
||||||
|
except IndexError:
|
||||||
|
size_min = 0
|
||||||
|
size_max = 1000
|
||||||
|
try:
|
||||||
|
charset = self.charsets[arg_list[1]]
|
||||||
|
except IndexError:
|
||||||
|
charset = self.charsets['printable']
|
||||||
|
|
||||||
|
length = self.prng.randint(size_min, size_max)
|
||||||
|
return ''.join([self.prng.choice(charset) for _ in xrange(length)]) # Won't scale nicely
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def parse_options():
|
def parse_options():
|
||||||
parser = OptionParser()
|
parser = OptionParser()
|
||||||
parser.add_option('-O', '--outfile', help='write output to FILE. Defaults to STDOUT', metavar='FILE')
|
parser.add_option('-O', '--outfile', help='write output to FILE. Defaults to STDOUT', metavar='FILE')
|
||||||
|
|
Loading…
Reference in a new issue