[PowerPC] Collect some CallLowering arguments into a struct. [NFC]
[llvm-project.git] / lldb / packages / Python / lldbsuite / test_event / event_builder.py
blob8759011abe5ef0e3b74adcee9db6639c3bfb4ecf
1 """
2 Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3 See https://llvm.org/LICENSE.txt for license information.
4 SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 Provides a class to build Python test event data structures.
7 """
9 from __future__ import print_function
10 from __future__ import absolute_import
12 # System modules
13 import inspect
14 import time
15 import traceback
17 # Third-party modules
19 # LLDB modules
20 from . import build_exception
23 class EventBuilder(object):
24 """Helper class to build test result event dictionaries."""
26 BASE_DICTIONARY = None
28 # Test Event Types
29 TYPE_JOB_RESULT = "job_result"
30 TYPE_TEST_RESULT = "test_result"
31 TYPE_TEST_START = "test_start"
32 TYPE_MARK_TEST_RERUN_ELIGIBLE = "test_eligible_for_rerun"
33 TYPE_MARK_TEST_EXPECTED_FAILURE = "test_expected_failure"
34 TYPE_SESSION_TERMINATE = "terminate"
36 RESULT_TYPES = {TYPE_JOB_RESULT, TYPE_TEST_RESULT}
38 # Test/Job Status Tags
39 STATUS_EXCEPTIONAL_EXIT = "exceptional_exit"
40 STATUS_SUCCESS = "success"
41 STATUS_FAILURE = "failure"
42 STATUS_EXPECTED_FAILURE = "expected_failure"
43 STATUS_EXPECTED_TIMEOUT = "expected_timeout"
44 STATUS_UNEXPECTED_SUCCESS = "unexpected_success"
45 STATUS_SKIP = "skip"
46 STATUS_ERROR = "error"
47 STATUS_TIMEOUT = "timeout"
49 """Test methods or jobs with a status matching any of these
50 status values will cause a testrun failure, unless
51 the test methods rerun and do not trigger an issue when rerun."""
52 TESTRUN_ERROR_STATUS_VALUES = {
53 STATUS_ERROR,
54 STATUS_EXCEPTIONAL_EXIT,
55 STATUS_FAILURE,
56 STATUS_TIMEOUT}
58 @staticmethod
59 def _get_test_name_info(test):
60 """Returns (test-class-name, test-method-name) from a test case instance.
62 @param test a unittest.TestCase instance.
64 @return tuple containing (test class name, test method name)
65 """
66 test_class_components = test.id().split(".")
67 test_class_name = ".".join(test_class_components[:-1])
68 test_name = test_class_components[-1]
69 return test_class_name, test_name
71 @staticmethod
72 def bare_event(event_type):
73 """Creates an event with default additions, event type and timestamp.
75 @param event_type the value set for the "event" key, used
76 to distinguish events.
78 @returns an event dictionary with all default additions, the "event"
79 key set to the passed in event_type, and the event_time value set to
80 time.time().
81 """
82 if EventBuilder.BASE_DICTIONARY is not None:
83 # Start with a copy of the "always include" entries.
84 event = dict(EventBuilder.BASE_DICTIONARY)
85 else:
86 event = {}
88 event.update({
89 "event": event_type,
90 "event_time": time.time()
92 return event
94 @staticmethod
95 def _assert_is_python_sourcefile(test_filename):
96 if test_filename is not None:
97 if not test_filename.endswith(".py"):
98 raise Exception(
99 "source python filename has unexpected extension: {}".format(test_filename))
100 return test_filename
102 @staticmethod
103 def _event_dictionary_common(test, event_type):
104 """Returns an event dictionary setup with values for the given event type.
106 @param test the unittest.TestCase instance
108 @param event_type the name of the event type (string).
110 @return event dictionary with common event fields set.
112 test_class_name, test_name = EventBuilder._get_test_name_info(test)
114 # Determine the filename for the test case. If there is an attribute
115 # for it, use it. Otherwise, determine from the TestCase class path.
116 if hasattr(test, "test_filename"):
117 test_filename = EventBuilder._assert_is_python_sourcefile(
118 test.test_filename)
119 else:
120 test_filename = EventBuilder._assert_is_python_sourcefile(
121 inspect.getsourcefile(test.__class__))
123 event = EventBuilder.bare_event(event_type)
124 event.update({
125 "test_class": test_class_name,
126 "test_name": test_name,
127 "test_filename": test_filename
130 return event
132 @staticmethod
133 def _error_tuple_class(error_tuple):
134 """Returns the unittest error tuple's error class as a string.
136 @param error_tuple the error tuple provided by the test framework.
138 @return the error type (typically an exception) raised by the
139 test framework.
141 type_var = error_tuple[0]
142 module = inspect.getmodule(type_var)
143 if module:
144 return "{}.{}".format(module.__name__, type_var.__name__)
145 else:
146 return type_var.__name__
148 @staticmethod
149 def _error_tuple_message(error_tuple):
150 """Returns the unittest error tuple's error message.
152 @param error_tuple the error tuple provided by the test framework.
154 @return the error message provided by the test framework.
156 return str(error_tuple[1])
158 @staticmethod
159 def _error_tuple_traceback(error_tuple):
160 """Returns the unittest error tuple's error message.
162 @param error_tuple the error tuple provided by the test framework.
164 @return the error message provided by the test framework.
166 return error_tuple[2]
168 @staticmethod
169 def _event_dictionary_test_result(test, status):
170 """Returns an event dictionary with common test result fields set.
172 @param test a unittest.TestCase instance.
174 @param status the status/result of the test
175 (e.g. "success", "failure", etc.)
177 @return the event dictionary
179 event = EventBuilder._event_dictionary_common(
180 test, EventBuilder.TYPE_TEST_RESULT)
181 event["status"] = status
182 return event
184 @staticmethod
185 def _event_dictionary_issue(test, status, error_tuple):
186 """Returns an event dictionary with common issue-containing test result
187 fields set.
189 @param test a unittest.TestCase instance.
191 @param status the status/result of the test
192 (e.g. "success", "failure", etc.)
194 @param error_tuple the error tuple as reported by the test runner.
195 This is of the form (type<error>, error).
197 @return the event dictionary
199 event = EventBuilder._event_dictionary_test_result(test, status)
200 event["issue_class"] = EventBuilder._error_tuple_class(error_tuple)
201 event["issue_message"] = EventBuilder._error_tuple_message(error_tuple)
202 backtrace = EventBuilder._error_tuple_traceback(error_tuple)
203 if backtrace is not None:
204 event["issue_backtrace"] = traceback.format_tb(backtrace)
205 return event
207 @staticmethod
208 def event_for_start(test):
209 """Returns an event dictionary for the test start event.
211 @param test a unittest.TestCase instance.
213 @return the event dictionary
215 return EventBuilder._event_dictionary_common(
216 test, EventBuilder.TYPE_TEST_START)
218 @staticmethod
219 def event_for_success(test):
220 """Returns an event dictionary for a successful test.
222 @param test a unittest.TestCase instance.
224 @return the event dictionary
226 return EventBuilder._event_dictionary_test_result(
227 test, EventBuilder.STATUS_SUCCESS)
229 @staticmethod
230 def event_for_unexpected_success(test, bugnumber):
231 """Returns an event dictionary for a test that succeeded but was
232 expected to fail.
234 @param test a unittest.TestCase instance.
236 @param bugnumber the issue identifier for the bug tracking the
237 fix request for the test expected to fail (but is in fact
238 passing here).
240 @return the event dictionary
243 event = EventBuilder._event_dictionary_test_result(
244 test, EventBuilder.STATUS_UNEXPECTED_SUCCESS)
245 if bugnumber:
246 event["bugnumber"] = str(bugnumber)
247 return event
249 @staticmethod
250 def event_for_failure(test, error_tuple):
251 """Returns an event dictionary for a test that failed.
253 @param test a unittest.TestCase instance.
255 @param error_tuple the error tuple as reported by the test runner.
256 This is of the form (type<error>, error).
258 @return the event dictionary
260 return EventBuilder._event_dictionary_issue(
261 test, EventBuilder.STATUS_FAILURE, error_tuple)
263 @staticmethod
264 def event_for_expected_failure(test, error_tuple, bugnumber):
265 """Returns an event dictionary for a test that failed as expected.
267 @param test a unittest.TestCase instance.
269 @param error_tuple the error tuple as reported by the test runner.
270 This is of the form (type<error>, error).
272 @param bugnumber the issue identifier for the bug tracking the
273 fix request for the test expected to fail.
275 @return the event dictionary
278 event = EventBuilder._event_dictionary_issue(
279 test, EventBuilder.STATUS_EXPECTED_FAILURE, error_tuple)
280 if bugnumber:
281 event["bugnumber"] = str(bugnumber)
282 return event
284 @staticmethod
285 def event_for_skip(test, reason):
286 """Returns an event dictionary for a test that was skipped.
288 @param test a unittest.TestCase instance.
290 @param reason the reason why the test is being skipped.
292 @return the event dictionary
294 event = EventBuilder._event_dictionary_test_result(
295 test, EventBuilder.STATUS_SKIP)
296 event["skip_reason"] = reason
297 return event
299 @staticmethod
300 def event_for_error(test, error_tuple):
301 """Returns an event dictionary for a test that hit a test execution error.
303 @param test a unittest.TestCase instance.
305 @param error_tuple the error tuple as reported by the test runner.
306 This is of the form (type<error>, error).
308 @return the event dictionary
310 event = EventBuilder._event_dictionary_issue(
311 test, EventBuilder.STATUS_ERROR, error_tuple)
312 event["issue_phase"] = "test"
313 return event
315 @staticmethod
316 def event_for_build_error(test, error_tuple):
317 """Returns an event dictionary for a test that hit a test execution error
318 during the test cleanup phase.
320 @param test a unittest.TestCase instance.
322 @param error_tuple the error tuple as reported by the test runner.
323 This is of the form (type<error>, error).
325 @return the event dictionary
327 event = EventBuilder._event_dictionary_issue(
328 test, EventBuilder.STATUS_ERROR, error_tuple)
329 event["issue_phase"] = "build"
331 build_error = error_tuple[1]
332 event["build_command"] = build_error.command
333 event["build_error"] = build_error.build_error
334 return event
336 @staticmethod
337 def event_for_cleanup_error(test, error_tuple):
338 """Returns an event dictionary for a test that hit a test execution error
339 during the test cleanup phase.
341 @param test a unittest.TestCase instance.
343 @param error_tuple the error tuple as reported by the test runner.
344 This is of the form (type<error>, error).
346 @return the event dictionary
348 event = EventBuilder._event_dictionary_issue(
349 test, EventBuilder.STATUS_ERROR, error_tuple)
350 event["issue_phase"] = "cleanup"
351 return event
353 @staticmethod
354 def event_for_job_test_add_error(test_filename, exception, backtrace):
355 event = EventBuilder.bare_event(EventBuilder.TYPE_JOB_RESULT)
356 event["status"] = EventBuilder.STATUS_ERROR
357 if test_filename is not None:
358 event["test_filename"] = EventBuilder._assert_is_python_sourcefile(
359 test_filename)
360 if exception is not None and "__class__" in dir(exception):
361 event["issue_class"] = exception.__class__
362 event["issue_message"] = exception
363 if backtrace is not None:
364 event["issue_backtrace"] = backtrace
365 return event
367 @staticmethod
368 def event_for_job_exceptional_exit(
369 pid, worker_index, exception_code, exception_description,
370 test_filename, command_line):
371 """Creates an event for a job (i.e. process) exit due to signal.
373 @param pid the process id for the job that failed
374 @param worker_index optional id for the job queue running the process
375 @param exception_code optional code
376 (e.g. SIGTERM integer signal number)
377 @param exception_description optional string containing symbolic
378 representation of the issue (e.g. "SIGTERM")
379 @param test_filename the path to the test filename that exited
380 in some exceptional way.
381 @param command_line the Popen()-style list provided as the command line
382 for the process that timed out.
384 @return an event dictionary coding the job completion description.
386 event = EventBuilder.bare_event(EventBuilder.TYPE_JOB_RESULT)
387 event["status"] = EventBuilder.STATUS_EXCEPTIONAL_EXIT
388 if pid is not None:
389 event["pid"] = pid
390 if worker_index is not None:
391 event["worker_index"] = int(worker_index)
392 if exception_code is not None:
393 event["exception_code"] = exception_code
394 if exception_description is not None:
395 event["exception_description"] = exception_description
396 if test_filename is not None:
397 event["test_filename"] = EventBuilder._assert_is_python_sourcefile(
398 test_filename)
399 if command_line is not None:
400 event["command_line"] = command_line
401 return event
403 @staticmethod
404 def event_for_job_timeout(pid, worker_index, test_filename, command_line):
405 """Creates an event for a job (i.e. process) timeout.
407 @param pid the process id for the job that timed out
408 @param worker_index optional id for the job queue running the process
409 @param test_filename the path to the test filename that timed out.
410 @param command_line the Popen-style list provided as the command line
411 for the process that timed out.
413 @return an event dictionary coding the job completion description.
415 event = EventBuilder.bare_event(EventBuilder.TYPE_JOB_RESULT)
416 event["status"] = "timeout"
417 if pid is not None:
418 event["pid"] = pid
419 if worker_index is not None:
420 event["worker_index"] = int(worker_index)
421 if test_filename is not None:
422 event["test_filename"] = EventBuilder._assert_is_python_sourcefile(
423 test_filename)
424 if command_line is not None:
425 event["command_line"] = command_line
426 return event
428 @staticmethod
429 def event_for_mark_test_rerun_eligible(test):
430 """Creates an event that indicates the specified test is explicitly
431 eligible for rerun.
433 Note there is a mode that will enable test rerun eligibility at the
434 global level. These markings for explicit rerun eligibility are
435 intended for the mode of running where only explicitly re-runnable
436 tests are rerun upon hitting an issue.
438 @param test the TestCase instance to which this pertains.
440 @return an event that specifies the given test as being eligible to
441 be rerun.
443 event = EventBuilder._event_dictionary_common(
444 test,
445 EventBuilder.TYPE_MARK_TEST_RERUN_ELIGIBLE)
446 return event
448 @staticmethod
449 def event_for_mark_test_expected_failure(test):
450 """Creates an event that indicates the specified test is expected
451 to fail.
453 @param test the TestCase instance to which this pertains.
455 @return an event that specifies the given test is expected to fail.
457 event = EventBuilder._event_dictionary_common(
458 test,
459 EventBuilder.TYPE_MARK_TEST_EXPECTED_FAILURE)
460 return event
462 @staticmethod
463 def add_entries_to_all_events(entries_dict):
464 """Specifies a dictionary of entries to add to all test events.
466 This provides a mechanism for, say, a parallel test runner to
467 indicate to each inferior dotest.py that it should add a
468 worker index to each.
470 Calling this method replaces all previous entries added
471 by a prior call to this.
473 Event build methods will overwrite any entries that collide.
474 Thus, the passed in dictionary is the base, which gets merged
475 over by event building when keys collide.
477 @param entries_dict a dictionary containing key and value
478 pairs that should be merged into all events created by the
479 event generator. May be None to clear out any extra entries.
481 EventBuilder.BASE_DICTIONARY = dict(entries_dict)