1 # SPDX-License-Identifier: GPL-2.0
3 from __future__
import print_function
16 import ConfigParser
as configparser
19 # Allow multiple values in assignment separated by '|'
25 if (a_item
== b_item
):
27 elif (a_item
== '*') or (b_item
== '*'):
32 class Fail(Exception):
33 def __init__(self
, test
, msg
):
37 return '\'%s\' - %s' % (self
.test
.path
, self
.msg
)
39 class Notest(Exception):
40 def __init__(self
, test
, arch
):
44 return '[%s] \'%s\'' % (self
.arch
, self
.test
.path
)
46 class Unsup(Exception):
47 def __init__(self
, test
):
50 return '\'%s\'' % self
.test
.path
82 'exclude_callchain_kernel',
83 'exclude_callchain_user',
95 log
.debug(" %s = %s" % (key
, val
))
98 def __init__(self
, name
, data
, base
):
99 log
.debug(" Event %s" % name
);
105 def equal(self
, other
):
106 for t
in Event
.terms
:
107 log
.debug(" [%s] %s %s" % (t
, self
[t
], other
[t
]));
108 if t
not in self
or t
not in other
:
110 if not data_equal(self
[t
], other
[t
]):
115 if 'optional' in self
and self
['optional'] == '1':
119 def diff(self
, other
):
120 for t
in Event
.terms
:
121 if t
not in self
or t
not in other
:
123 if not data_equal(self
[t
], other
[t
]):
124 log
.warning("expected %s=%s, got %s" % (t
, self
[t
], other
[t
]))
126 # Test file description needs to have following sections:
128 # - just single instance in file
129 # - needs to specify:
130 # 'command' - perf command name
131 # 'args' - special command arguments
132 # 'ret' - expected command return value (0 by default)
133 # 'arch' - architecture specific test (optional)
134 # comma separated list, ! at the beginning
138 # - one or multiple instances in file
139 # - expected values assignments
141 def __init__(self
, path
, options
):
142 parser
= configparser
.SafeConfigParser()
145 log
.warning("running '%s'" % path
)
148 self
.test_dir
= options
.test_dir
149 self
.perf
= options
.perf
150 self
.command
= parser
.get('config', 'command')
151 self
.args
= parser
.get('config', 'args')
154 self
.ret
= parser
.get('config', 'ret')
159 self
.arch
= parser
.get('config', 'arch')
160 log
.warning("test limitation '%s'" % self
.arch
)
166 log
.debug(" loading expected events");
167 self
.load_events(path
, self
.expect
)
169 def is_event(self
, name
):
170 if name
.find("event") == -1:
175 def skip_test(self
, myarch
):
176 # If architecture not set always run test
178 # log.warning("test for arch %s is ok" % myarch)
181 # Allow multiple values in assignment separated by ','
182 arch_list
= self
.arch
.split(',')
184 # Handle negated list such as !s390x,ppc
185 if arch_list
[0][0] == '!':
186 arch_list
[0] = arch_list
[0][1:]
187 log
.warning("excluded architecture list %s" % arch_list
)
188 for arch_item
in arch_list
:
189 # log.warning("test for %s arch is %s" % (arch_item, myarch))
190 if arch_item
== myarch
:
194 for arch_item
in arch_list
:
195 # log.warning("test for architecture '%s' current '%s'" % (arch_item, myarch))
196 if arch_item
== myarch
:
200 def load_events(self
, path
, events
):
201 parser_event
= configparser
.SafeConfigParser()
202 parser_event
.read(path
)
204 # The event record section header contains 'event' word,
205 # optionaly followed by ':' allowing to load 'parent
206 # event' first as a base
207 for section
in filter(self
.is_event
, parser_event
.sections()):
209 parser_items
= parser_event
.items(section
);
212 # Read parent event if there's any
214 base
= section
[section
.index(':') + 1:]
215 parser_base
= configparser
.SafeConfigParser()
216 parser_base
.read(self
.test_dir
+ '/' + base
)
217 base_items
= parser_base
.items('event')
219 e
= Event(section
, parser_items
, base_items
)
222 def run_cmd(self
, tempdir
):
223 junk1
, junk2
, junk3
, junk4
, myarch
= (os
.uname())
225 if self
.skip_test(myarch
):
226 raise Notest(self
, myarch
)
228 cmd
= "PERF_TEST_ATTR=%s %s %s -o %s/perf.data %s" % (tempdir
,
229 self
.perf
, self
.command
, tempdir
, self
.args
)
230 ret
= os
.WEXITSTATUS(os
.system(cmd
))
232 log
.info(" '%s' ret '%s', expected '%s'" % (cmd
, str(ret
), str(self
.ret
)))
234 if not data_equal(str(ret
), str(self
.ret
)):
237 def compare(self
, expect
, result
):
240 log
.debug(" compare");
242 # For each expected event find all matching
243 # events in result. Fail if there's not any.
244 for exp_name
, exp_event
in expect
.items():
247 log
.debug(" matching [%s]" % exp_name
)
248 for res_name
, res_event
in result
.items():
249 log
.debug(" to [%s]" % res_name
)
250 if (exp_event
.equal(res_event
)):
251 exp_list
.append(res_name
)
254 log
.debug(" ->FAIL");
256 log
.debug(" match: [%s] matches %s" % (exp_name
, str(exp_list
)))
258 # we did not any matching event - fail
260 if exp_event
.optional():
261 log
.debug(" %s does not match, but is optional" % exp_name
)
264 log
.debug(" res_event is empty");
266 exp_event
.diff(res_event
)
267 raise Fail(self
, 'match failure');
269 match
[exp_name
] = exp_list
271 # For each defined group in the expected events
272 # check we match the same group in the result.
273 for exp_name
, exp_event
in expect
.items():
274 group
= exp_event
.group
279 for res_name
in match
[exp_name
]:
280 res_group
= result
[res_name
].group
281 if res_group
not in match
[group
]:
282 raise Fail(self
, 'group failure')
284 log
.debug(" group: [%s] matches group leader %s" %
285 (exp_name
, str(match
[group
])))
287 log
.debug(" matched")
289 def resolve_groups(self
, events
):
290 for name
, event
in events
.items():
291 group_fd
= event
['group_fd'];
295 for iname
, ievent
in events
.items():
296 if (ievent
['fd'] == group_fd
):
298 log
.debug('[%s] has group leader [%s]' % (name
, iname
))
302 tempdir
= tempfile
.mkdtemp();
305 # run the test script
306 self
.run_cmd(tempdir
);
308 # load events expectation for the test
309 log
.debug(" loading result events");
310 for f
in glob
.glob(tempdir
+ '/event*'):
311 self
.load_events(f
, self
.result
);
313 # resolve group_fd to event names
314 self
.resolve_groups(self
.expect
);
315 self
.resolve_groups(self
.result
);
317 # do the expectation - results matching - both ways
318 self
.compare(self
.expect
, self
.result
)
319 self
.compare(self
.result
, self
.expect
)
323 shutil
.rmtree(tempdir
)
326 def run_tests(options
):
327 for f
in glob
.glob(options
.test_dir
+ '/' + options
.test
):
329 Test(f
, options
).run()
331 log
.warning("unsupp %s" % obj
.getMsg())
332 except Notest
as obj
:
333 log
.warning("skipped %s" % obj
.getMsg())
335 def setup_log(verbose
):
337 level
= logging
.CRITICAL
340 level
= logging
.WARNING
344 level
= logging
.DEBUG
346 log
= logging
.getLogger('test')
348 ch
= logging
.StreamHandler()
350 formatter
= logging
.Formatter('%(message)s')
351 ch
.setFormatter(formatter
)
354 USAGE
= '''%s [OPTIONS]
356 -p path # perf binary
357 -t test # single test
362 parser
= optparse
.OptionParser(usage
=USAGE
)
364 parser
.add_option("-t", "--test",
365 action
="store", type="string", dest
="test")
366 parser
.add_option("-d", "--test-dir",
367 action
="store", type="string", dest
="test_dir")
368 parser
.add_option("-p", "--perf",
369 action
="store", type="string", dest
="perf")
370 parser
.add_option("-v", "--verbose",
371 default
=0, action
="count", dest
="verbose")
373 options
, args
= parser
.parse_args()
375 parser
.error('FAILED wrong arguments %s' % ' '.join(args
))
378 setup_log(options
.verbose
)
380 if not options
.test_dir
:
381 print('FAILED no -d option specified')
385 options
.test
= 'test*'
391 print("FAILED %s" % obj
.getMsg())
396 if __name__
== '__main__':