control_data.py revision 89d5d21ec72e55ca0ca9e50d488210e2c5bb34e7
1# pylint: disable-msg=C0111 2# Copyright 2008 Google Inc. Released under the GPL v2 3 4import warnings 5with warnings.catch_warnings(): 6 # The 'compiler' module is gone in Python 3.0. Let's not say 7 # so in every log file. 8 warnings.simplefilter("ignore", DeprecationWarning) 9 import compiler 10import logging, textwrap 11 12from autotest_lib.client.common_lib import enum 13 14REQUIRED_VARS = set(['author', 'doc', 'name', 'time', 'test_type']) 15 16CONTROL_TYPE = enum.Enum('Server', 'Client', start_value=1) 17CONTROL_TYPE_NAMES = enum.Enum(*CONTROL_TYPE.names, string_values=True) 18 19class ControlVariableException(Exception): 20 pass 21 22 23class ControlData(object): 24 # Available TIME settings in control file, the list must be in lower case 25 # and in ascending order, test running faster comes first. 26 TEST_TIME_LIST = ['fast', 'short', 'medium', 'long', 'lengthy'] 27 TEST_TIME = enum.Enum(*TEST_TIME_LIST, string_values=False) 28 29 @staticmethod 30 def get_test_time_index(time): 31 """ 32 Get the order of estimated test time, based on the TIME setting in 33 Control file. Faster test gets a lower index number. 34 """ 35 try: 36 return ControlData.TEST_TIME.get_value(time.lower()) 37 except AttributeError: 38 # Raise exception if time value is not a valid TIME setting. 39 error_msg = '%s is not a valid TIME.' % time 40 logging.error(error_msg) 41 raise ControlVariableException(error_msg) 42 43 44 def __init__(self, vars, path, raise_warnings=False): 45 # Defaults 46 self.path = path 47 self.dependencies = set() 48 self.experimental = False 49 self.run_verify = True 50 self.sync_count = 1 51 self.test_parameters = set() 52 self.test_category = '' 53 self.test_class = '' 54 self.retries = 0 55 56 diff = REQUIRED_VARS - set(vars) 57 if len(diff) > 0: 58 warning = ("WARNING: Not all required control " 59 "variables were specified in %s. Please define " 60 "%s.") % (self.path, ', '.join(diff)) 61 if raise_warnings: 62 raise ControlVariableException(warning) 63 print textwrap.wrap(warning, 80) 64 65 for key, val in vars.iteritems(): 66 try: 67 self.set_attr(key, val, raise_warnings) 68 except Exception, e: 69 if raise_warnings: 70 raise 71 print "WARNING: %s; skipping" % e 72 73 74 def set_attr(self, attr, val, raise_warnings=False): 75 attr = attr.lower() 76 try: 77 set_fn = getattr(self, 'set_%s' % attr) 78 set_fn(val) 79 except AttributeError: 80 # This must not be a variable we care about 81 pass 82 83 84 def _set_string(self, attr, val): 85 val = str(val) 86 setattr(self, attr, val) 87 88 89 def _set_option(self, attr, val, options): 90 val = str(val) 91 if val.lower() not in [x.lower() for x in options]: 92 raise ValueError("%s must be one of the following " 93 "options: %s" % (attr, 94 ', '.join(options))) 95 setattr(self, attr, val) 96 97 98 def _set_bool(self, attr, val): 99 val = str(val).lower() 100 if val == "false": 101 val = False 102 elif val == "true": 103 val = True 104 else: 105 msg = "%s must be either true or false" % attr 106 raise ValueError(msg) 107 setattr(self, attr, val) 108 109 110 def _set_int(self, attr, val, min=None, max=None): 111 val = int(val) 112 if min is not None and min > val: 113 raise ValueError("%s is %d, which is below the " 114 "minimum of %d" % (attr, val, min)) 115 if max is not None and max < val: 116 raise ValueError("%s is %d, which is above the " 117 "maximum of %d" % (attr, val, max)) 118 setattr(self, attr, val) 119 120 121 def _set_set(self, attr, val): 122 val = str(val) 123 items = [x.strip() for x in val.split(',')] 124 setattr(self, attr, set(items)) 125 126 127 def set_author(self, val): 128 self._set_string('author', val) 129 130 131 def set_dependencies(self, val): 132 self._set_set('dependencies', val) 133 134 135 def set_doc(self, val): 136 self._set_string('doc', val) 137 138 139 def set_experimental(self, val): 140 self._set_bool('experimental', val) 141 142 143 def set_name(self, val): 144 self._set_string('name', val) 145 146 147 def set_run_verify(self, val): 148 self._set_bool('run_verify', val) 149 150 151 def set_sync_count(self, val): 152 self._set_int('sync_count', val, min=1) 153 154 155 def set_suite(self, val): 156 self._set_string('suite', val) 157 158 159 def set_time(self, val): 160 self._set_option('time', val, ControlData.TEST_TIME_LIST) 161 162 163 def set_test_class(self, val): 164 self._set_string('test_class', val.lower()) 165 166 167 def set_test_category(self, val): 168 self._set_string('test_category', val.lower()) 169 170 171 def set_test_type(self, val): 172 self._set_option('test_type', val, list(CONTROL_TYPE.names)) 173 174 175 def set_test_parameters(self, val): 176 self._set_set('test_parameters', val) 177 178 179 def set_retries(self, val): 180 self._set_int('retries', val) 181 182 183def _extract_const(n): 184 assert(n.__class__ == compiler.ast.Assign) 185 assert(n.expr.__class__ == compiler.ast.Const) 186 assert(n.expr.value.__class__ in (str, int, float, unicode)) 187 assert(n.nodes.__class__ == list) 188 assert(len(n.nodes) == 1) 189 assert(n.nodes[0].__class__ == compiler.ast.AssName) 190 assert(n.nodes[0].flags.__class__ == str) 191 assert(n.nodes[0].name.__class__ == str) 192 193 key = n.nodes[0].name.lower() 194 val = str(n.expr.value).strip() 195 196 return (key, val) 197 198 199def _extract_name(n): 200 assert(n.__class__ == compiler.ast.Assign) 201 assert(n.expr.__class__ == compiler.ast.Name) 202 assert(n.nodes.__class__ == list) 203 assert(len(n.nodes) == 1) 204 assert(n.nodes[0].__class__ == compiler.ast.AssName) 205 assert(n.nodes[0].flags.__class__ == str) 206 assert(n.nodes[0].name.__class__ == str) 207 assert(n.expr.name in ('False', 'True', 'None')) 208 209 key = n.nodes[0].name.lower() 210 val = str(n.expr.name) 211 212 return (key, val) 213 214 215def parse_control_string(control, raise_warnings=False): 216 try: 217 mod = compiler.parse(control) 218 except SyntaxError, e: 219 raise ControlVariableException("Error parsing data because %s" % e) 220 return finish_parse(mod, '', raise_warnings) 221 222 223def parse_control(path, raise_warnings=False): 224 try: 225 mod = compiler.parseFile(path) 226 except SyntaxError, e: 227 raise ControlVariableException("Error parsing %s because %s" % 228 (path, e)) 229 return finish_parse(mod, path, raise_warnings) 230 231 232def finish_parse(mod, path, raise_warnings): 233 assert(mod.__class__ == compiler.ast.Module) 234 assert(mod.node.__class__ == compiler.ast.Stmt) 235 assert(mod.node.nodes.__class__ == list) 236 237 vars = {} 238 for n in mod.node.nodes: 239 for fn in (_extract_const, _extract_name): 240 try: 241 key, val = fn(n) 242 243 vars[key] = val 244 except AssertionError, e: 245 pass 246 247 return ControlData(vars, path, raise_warnings) 248