mbligh | 99c2c6f | 2008-07-11 18:15:46 +0000 | [diff] [blame] | 1 | # |
| 2 | # Copyright 2008 Google Inc. Released under the GPL v2 |
| 3 | |
mbligh | 4b5c31e | 2009-07-11 00:55:34 +0000 | [diff] [blame] | 4 | import compiler, textwrap, types |
mbligh | 99c2c6f | 2008-07-11 18:15:46 +0000 | [diff] [blame] | 5 | |
| 6 | |
| 7 | REQUIRED_VARS = set(['author', 'doc', 'name', 'time', 'test_class', |
| 8 | 'test_category', 'test_type']) |
| 9 | |
| 10 | class ControlVariableException(Exception): |
| 11 | pass |
| 12 | |
| 13 | |
| 14 | class ControlData(object): |
mbligh | d7cd983 | 2008-10-02 16:20:37 +0000 | [diff] [blame] | 15 | def __init__(self, vars, path, raise_warnings=False): |
mbligh | 99c2c6f | 2008-07-11 18:15:46 +0000 | [diff] [blame] | 16 | # Defaults |
mbligh | d7cd983 | 2008-10-02 16:20:37 +0000 | [diff] [blame] | 17 | self.path = path |
mbligh | 99c2c6f | 2008-07-11 18:15:46 +0000 | [diff] [blame] | 18 | self.dependencies = set() |
| 19 | self.experimental = False |
| 20 | self.run_verify = True |
| 21 | self.sync_count = 1 |
| 22 | |
| 23 | diff = REQUIRED_VARS - set(vars) |
| 24 | if len(diff) > 0: |
| 25 | warning = ("WARNING: Not all required control " |
mbligh | d7cd983 | 2008-10-02 16:20:37 +0000 | [diff] [blame] | 26 | "variables were specified in %s. Please define " |
| 27 | "%s.") % (self.path, ', '.join(diff)) |
mbligh | 99c2c6f | 2008-07-11 18:15:46 +0000 | [diff] [blame] | 28 | if raise_warnings: |
| 29 | raise ControlVariableException(warning) |
| 30 | print textwrap.wrap(warning, 80) |
| 31 | |
| 32 | for key, val in vars.iteritems(): |
| 33 | try: |
| 34 | self.set_attr(key, val, raise_warnings) |
| 35 | except Exception, e: |
| 36 | if raise_warnings: |
| 37 | raise |
| 38 | print "WARNING: %s; skipping" % e |
| 39 | |
| 40 | |
| 41 | def set_attr(self, attr, val, raise_warnings=False): |
| 42 | attr = attr.lower() |
| 43 | try: |
| 44 | set_fn = getattr(self, 'set_%s' % attr) |
| 45 | set_fn(val) |
| 46 | except AttributeError: |
| 47 | # This must not be a variable we care about |
| 48 | pass |
| 49 | |
| 50 | |
| 51 | def _set_string(self, attr, val): |
| 52 | val = str(val) |
| 53 | setattr(self, attr, val) |
| 54 | |
| 55 | |
| 56 | def _set_option(self, attr, val, options): |
| 57 | val = str(val) |
| 58 | if val.lower() not in [x.lower() for x in options]: |
| 59 | raise ValueError("%s must be one of the following " |
| 60 | "options: %s" % (attr, |
| 61 | ', '.join(options))) |
| 62 | setattr(self, attr, val) |
| 63 | |
| 64 | |
| 65 | def _set_bool(self, attr, val): |
| 66 | val = str(val).lower() |
| 67 | if val == "false": |
| 68 | val = False |
| 69 | elif val == "true": |
| 70 | val = True |
| 71 | else: |
| 72 | msg = "%s must be either true or false" % attr |
| 73 | raise ValueError(msg) |
| 74 | setattr(self, attr, val) |
| 75 | |
| 76 | |
| 77 | def _set_int(self, attr, val, min=None, max=None): |
| 78 | val = int(val) |
| 79 | if min is not None and min > val: |
| 80 | raise ValueError("%s is %d, which is below the " |
| 81 | "minimum of %d" % (attr, val, min)) |
| 82 | if max is not None and max < val: |
| 83 | raise ValueError("%s is %d, which is above the " |
| 84 | "maximum of %d" % (attr, val, max)) |
| 85 | setattr(self, attr, val) |
| 86 | |
| 87 | |
| 88 | def _set_set(self, attr, val): |
| 89 | val = str(val) |
| 90 | items = [x.strip() for x in val.split(',')] |
| 91 | setattr(self, attr, set(items)) |
| 92 | |
| 93 | |
| 94 | def set_author(self, val): |
| 95 | self._set_string('author', val) |
| 96 | |
| 97 | |
| 98 | def set_dependencies(self, val): |
| 99 | self._set_set('dependencies', val) |
| 100 | |
| 101 | |
| 102 | def set_doc(self, val): |
| 103 | self._set_string('doc', val) |
| 104 | |
| 105 | |
| 106 | def set_experimental(self, val): |
| 107 | self._set_bool('experimental', val) |
| 108 | |
| 109 | |
| 110 | def set_name(self, val): |
| 111 | self._set_string('name', val) |
| 112 | |
| 113 | |
| 114 | def set_run_verify(self, val): |
| 115 | self._set_bool('run_verify', val) |
| 116 | |
| 117 | |
| 118 | def set_sync_count(self, val): |
| 119 | self._set_int('sync_count', val, min=1) |
| 120 | |
| 121 | |
| 122 | def set_time(self, val): |
| 123 | self._set_option('time', val, ['short', 'medium', 'long']) |
| 124 | |
| 125 | |
| 126 | def set_test_class(self, val): |
| 127 | self._set_string('test_class', val.lower()) |
| 128 | |
| 129 | |
| 130 | def set_test_category(self, val): |
| 131 | self._set_string('test_category', val.lower()) |
| 132 | |
| 133 | |
| 134 | def set_test_type(self, val): |
| 135 | self._set_option('test_type', val, ['client', 'server']) |
| 136 | |
Eric Li | d3e8a3b | 2010-12-23 10:02:07 -0800 | [diff] [blame] | 137 | def set_test_parameters(self, val): |
| 138 | self._set_set('test_parameters', val) |
| 139 | |
mbligh | 99c2c6f | 2008-07-11 18:15:46 +0000 | [diff] [blame] | 140 | |
mbligh | 93f4209 | 2008-07-18 01:01:58 +0000 | [diff] [blame] | 141 | def _extract_const(n): |
| 142 | assert(n.__class__ == compiler.ast.Assign) |
| 143 | assert(n.expr.__class__ == compiler.ast.Const) |
| 144 | assert(n.expr.value.__class__ in (str, int, float, unicode)) |
| 145 | assert(n.nodes.__class__ == list) |
| 146 | assert(len(n.nodes) == 1) |
| 147 | assert(n.nodes[0].__class__ == compiler.ast.AssName) |
| 148 | assert(n.nodes[0].flags.__class__ == str) |
| 149 | assert(n.nodes[0].name.__class__ == str) |
| 150 | |
| 151 | key = n.nodes[0].name.lower() |
| 152 | val = str(n.expr.value).strip() |
| 153 | |
| 154 | return (key, val) |
| 155 | |
| 156 | |
| 157 | def _extract_name(n): |
| 158 | assert(n.__class__ == compiler.ast.Assign) |
| 159 | assert(n.expr.__class__ == compiler.ast.Name) |
| 160 | assert(n.nodes.__class__ == list) |
| 161 | assert(len(n.nodes) == 1) |
| 162 | assert(n.nodes[0].__class__ == compiler.ast.AssName) |
| 163 | assert(n.nodes[0].flags.__class__ == str) |
| 164 | assert(n.nodes[0].name.__class__ == str) |
| 165 | assert(n.expr.name in ('False', 'True', 'None')) |
| 166 | |
| 167 | key = n.nodes[0].name.lower() |
| 168 | val = str(n.expr.name) |
| 169 | |
| 170 | return (key, val) |
| 171 | |
| 172 | |
mbligh | 99c2c6f | 2008-07-11 18:15:46 +0000 | [diff] [blame] | 173 | def parse_control(path, raise_warnings=False): |
mbligh | d7cd983 | 2008-10-02 16:20:37 +0000 | [diff] [blame] | 174 | try: |
| 175 | mod = compiler.parseFile(path) |
| 176 | except SyntaxError, e: |
mbligh | 4b5c31e | 2009-07-11 00:55:34 +0000 | [diff] [blame] | 177 | raise ControlVariableException("Error parsing %s because %s" % |
| 178 | (path, e)) |
mbligh | 99c2c6f | 2008-07-11 18:15:46 +0000 | [diff] [blame] | 179 | |
| 180 | assert(mod.__class__ == compiler.ast.Module) |
| 181 | assert(mod.node.__class__ == compiler.ast.Stmt) |
| 182 | assert(mod.node.nodes.__class__ == list) |
| 183 | |
| 184 | vars = {} |
| 185 | for n in mod.node.nodes: |
mbligh | 93f4209 | 2008-07-18 01:01:58 +0000 | [diff] [blame] | 186 | for fn in (_extract_const, _extract_name): |
| 187 | try: |
mbligh | 93f4209 | 2008-07-18 01:01:58 +0000 | [diff] [blame] | 188 | key, val = fn(n) |
mbligh | 99c2c6f | 2008-07-11 18:15:46 +0000 | [diff] [blame] | 189 | |
mbligh | 93f4209 | 2008-07-18 01:01:58 +0000 | [diff] [blame] | 190 | vars[key] = val |
| 191 | except AssertionError, e: |
| 192 | pass |
mbligh | 99c2c6f | 2008-07-11 18:15:46 +0000 | [diff] [blame] | 193 | |
mbligh | d7cd983 | 2008-10-02 16:20:37 +0000 | [diff] [blame] | 194 | return ControlData(vars, path, raise_warnings) |