blob: 5b8caf225882278fd790bd8b283a2ed1959ec13a [file] [log] [blame]
mbligh99c2c6f2008-07-11 18:15:46 +00001#
2# Copyright 2008 Google Inc. Released under the GPL v2
3
mbligh4b5c31e2009-07-11 00:55:34 +00004import compiler, textwrap, types
mbligh99c2c6f2008-07-11 18:15:46 +00005
6
7REQUIRED_VARS = set(['author', 'doc', 'name', 'time', 'test_class',
8 'test_category', 'test_type'])
9
10class ControlVariableException(Exception):
11 pass
12
13
14class ControlData(object):
mblighd7cd9832008-10-02 16:20:37 +000015 def __init__(self, vars, path, raise_warnings=False):
mbligh99c2c6f2008-07-11 18:15:46 +000016 # Defaults
mblighd7cd9832008-10-02 16:20:37 +000017 self.path = path
mbligh99c2c6f2008-07-11 18:15:46 +000018 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 "
mblighd7cd9832008-10-02 16:20:37 +000026 "variables were specified in %s. Please define "
27 "%s.") % (self.path, ', '.join(diff))
mbligh99c2c6f2008-07-11 18:15:46 +000028 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 Lid3e8a3b2010-12-23 10:02:07 -0800137 def set_test_parameters(self, val):
138 self._set_set('test_parameters', val)
139
mbligh99c2c6f2008-07-11 18:15:46 +0000140
mbligh93f42092008-07-18 01:01:58 +0000141def _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
157def _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
mbligh99c2c6f2008-07-11 18:15:46 +0000173def parse_control(path, raise_warnings=False):
mblighd7cd9832008-10-02 16:20:37 +0000174 try:
175 mod = compiler.parseFile(path)
176 except SyntaxError, e:
mbligh4b5c31e2009-07-11 00:55:34 +0000177 raise ControlVariableException("Error parsing %s because %s" %
178 (path, e))
mbligh99c2c6f2008-07-11 18:15:46 +0000179
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:
mbligh93f42092008-07-18 01:01:58 +0000186 for fn in (_extract_const, _extract_name):
187 try:
mbligh93f42092008-07-18 01:01:58 +0000188 key, val = fn(n)
mbligh99c2c6f2008-07-11 18:15:46 +0000189
mbligh93f42092008-07-18 01:01:58 +0000190 vars[key] = val
191 except AssertionError, e:
192 pass
mbligh99c2c6f2008-07-11 18:15:46 +0000193
mblighd7cd9832008-10-02 16:20:37 +0000194 return ControlData(vars, path, raise_warnings)