]> git-server-git.apps.pok.os.sepia.ceph.com Git - s3-tests.git/commitdiff
S3 Fuzzer: Implmented headers and made random safe
authorKyle Marsh <kyle.marsh@dreamhost.com>
Thu, 11 Aug 2011 18:32:18 +0000 (11:32 -0700)
committerKyle Marsh <kyle.marsh@dreamhost.com>
Mon, 12 Sep 2011 19:53:18 +0000 (12:53 -0700)
Random can sometimes include } or { which will confuse the string formatter.
Formatter escapes those values when they're doubled: }}, {{ but this
required some slight hacking to the expander.

s3tests/functional/test_fuzzer.py
s3tests/fuzz_headers.py

index f6fb3b53d5fdd96e8271c463a911d109d1500ce7..d16627bede97dccb37a5c4e1c642391b20f96aa7 100644 (file)
@@ -1,3 +1,5 @@
+import sys
+import itertools
 import nose
 import random
 import string
@@ -30,12 +32,23 @@ def build_graph():
             'key1': 'value1',
             'key2': 'value2'
         },
+        'headers': [
+            ['1-2', 'random-header-{random 5-10 printable}', '{random 20-30 punctuation}']
+        ],
         'choices': []
     }
     graph['node1'] = {
         'set': {
-            'key3': 'value3'
+            'key3': 'value3',
+            'header_val': [
+                '3 h1',
+                '2 h2',
+                'h3'
+            ]
         },
+        'headers': [
+            ['1-1', 'my-header', '{header_val}'],
+        ],
         'choices': ['leaf']
     }
     graph['node2'] = {
@@ -137,9 +150,9 @@ def test_expand_key():
     }
     decision = SpecialVariables(test_decision, prng)
 
-    randkey = expand_key(decision, 'randkey')
-    indirect = expand_key(decision, 'indirect')
-    dbl_indirect = expand_key(decision, 'dbl_indirect')
+    randkey = expand_key(decision, test_decision['randkey'])
+    indirect = expand_key(decision, test_decision['indirect'])
+    dbl_indirect = expand_key(decision, test_decision['dbl_indirect'])
 
     eq(indirect, 'value1')
     eq(dbl_indirect, 'value1')
@@ -152,7 +165,7 @@ def test_expand_loop():
         'key2': '{key1}',
     }
     decision = SpecialVariables(test_decision, prng)
-    assert_raises(RuntimeError, expand_key, decision, 'key1')
+    assert_raises(RuntimeError, expand_key, decision, test_decision['key1'])
 
 def test_expand_decision():
     graph = build_graph()
@@ -166,7 +179,7 @@ def test_expand_decision():
     eq(request['key1'], 'value1')
     eq(request['indirect_key1'], 'value1')
     eq(request['path'], '/my-readable-bucket')
-    eq(request['randkey'], 'value-NI$;92@H/0I')
+    eq(request['randkey'], 'value-cx+*~G@&uW_[OW3')
     assert_raises(KeyError, lambda x: decision[x], 'key3')
 
 def test_weighted_choices():
@@ -207,3 +220,40 @@ def test_weighted_set():
     nose.tools.assert_almost_equal(bar_percentage, 0.50, 1)
     nose.tools.assert_almost_equal(baz_percentage, 0.25, 1)
 
+def test_header_presence():
+    graph = build_graph()
+    prng = random.Random(1)
+    decision = descend_graph(graph, 'node1', prng)
+
+    c1 = itertools.count()
+    c2 = itertools.count()
+    for header, value in decision['headers']:
+        if header == 'my-header':
+            eq(value, '{header_val}')
+            nose.tools.assert_true(next(c1) < 1)
+        elif header == 'random-header-{random 5-10 printable}':
+            eq(value, '{random 20-30 punctuation}')
+            nose.tools.assert_true(next(c2) < 2)
+        else:
+            raise KeyError('unexpected header found: %s' % header)
+
+    nose.tools.assert_true(next(c1))
+    nose.tools.assert_true(next(c2))
+
+
+
+def test_header_expansion():
+    graph = build_graph()
+    prng = random.Random(1)
+    decision = descend_graph(graph, 'node1', prng)
+    expanded_decision = expand_decision(decision, prng)
+
+    for header, value in expanded_decision['headers']:
+        if header == 'my-header':
+            nose.tools.assert_true(value in ['h1', 'h2', 'h3'])
+        elif header.startswith('random-header-'):
+            nose.tools.assert_true(20 <= len(value) <= 30)
+            nose.tools.assert_true(string.strip(value, SpecialVariables.charsets['punctuation']) is '')
+        else:
+            raise KeyError('unexpected header found: "%s"' % header)
+
index d01da8c6b746fd1496d20126f9a777682f28f575..fe33b7c2478d2854ec51623386d713f7cac993e8 100644 (file)
@@ -10,6 +10,7 @@ import string
 import struct
 import yaml
 import sys
+import re
 
 
 def assemble_decision(decision_graph, prng):
@@ -32,15 +33,41 @@ def descend_graph(decision_graph, node_name, prng):
     except IndexError:
         decision = {}
 
-    #TODO: Add in headers
     for key in node['set']:
         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))
         decision[key] = make_choice(node['set'][key], prng)
+
+    if node.has_key('headers'):
+        if not decision.has_key('headers'):
+            decision['headers'] = []
+
+        for desc in node['headers']:
+            if len(desc) == 3:
+                repetition_range = desc.pop(0)
+                try:
+                    size_min, size_max = [int(x) for x in repetition_range.split('-')]
+                except IndexError:
+                    size_min = size_max = int(repetition_range)
+            else:
+                size_min = size_max = 1
+            num_reps = prng.randint(size_min, size_max)
+            for _ in xrange(num_reps):
+                header = desc[0]
+                value = desc[1]
+                if header in [h for h, v in decision['headers']]:
+                    if not re.search('{[a-zA-Z_0-9 -]+}', header):
+                        raise KeyError("Node %s tried to add header '%s', but that header already exists!" %(node_name, header))
+                decision['headers'].append([header, value])
+
     return decision
 
 
 def make_choice(choices, prng):
+    """ Given a list of (possibly weighted) options or just a single option!,
+        choose one of the options taking weights into account and return the
+        choice
+    """
     if isinstance(choices, str):
         return choices
     weighted_choices = []
@@ -65,24 +92,27 @@ def expand_decision(decision, prng):
     """
     special_decision = SpecialVariables(decision, prng)
     for key in special_decision:
-        decision[key] = expand_key(special_decision, key)
-
+        if not key == 'headers':
+            decision[key] = expand_key(special_decision, decision[key])
+        else:
+            for header in special_decision[key]:
+                header[0] = expand_key(special_decision, header[0])
+                header[1] = expand_key(special_decision, header[1])
     return decision
 
 
-def expand_key(decision, key):
+def expand_key(decision, value):
     c = itertools.count()
     fmt = string.Formatter()
-    old = decision[key]
+    old = value
     while True:
         new = fmt.vformat(old, [], decision)
-        if new == old:
+        if new == old.replace('{{', '{').replace('}}', '}'):
             return old
         if next(c) > 5:
             raise RuntimeError
         old = new
 
-
 class SpecialVariables(dict):
     charsets = {
         'binary': 'binary',
@@ -126,7 +156,8 @@ class SpecialVariables(dict):
             tmpstring = struct.pack((num_bytes / 8) * 'Q', *tmplist)
             return tmpstring[0:length]
         else:
-            return ''.join([self.prng.choice(charset) for _ in xrange(length)]) # Won't scale nicely; won't do binary
+            tmpstring = ''.join([self.prng.choice(charset) for _ in xrange(length)]) # Won't scale nicely; won't do binary
+            return tmpstring.replace('{', '{{').replace('}', '}}')