]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - packages/Python/lldbsuite/test_event/event_builder.py
Vendor import of lldb release_39 branch r276489:
[FreeBSD/FreeBSD.git] / packages / Python / lldbsuite / test_event / event_builder.py
1 """
2     The LLVM Compiler Infrastructure
3
4 This file is distributed under the University of Illinois Open Source
5 License. See LICENSE.TXT for details.
6
7 Provides a class to build Python test event data structures.
8 """
9
10 from __future__ import print_function
11 from __future__ import absolute_import
12
13 # System modules
14 import inspect
15 import time
16 import traceback
17
18 # Third-party modules
19
20 # LLDB modules
21 from . import build_exception
22
23 class EventBuilder(object):
24     """Helper class to build test result event dictionaries."""
25
26     BASE_DICTIONARY = None
27
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"
35
36     RESULT_TYPES = {TYPE_JOB_RESULT, TYPE_TEST_RESULT}
37
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"
48
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}
57
58     @staticmethod
59     def _get_test_name_info(test):
60         """Returns (test-class-name, test-method-name) from a test case instance.
61
62         @param test a unittest.TestCase instance.
63
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
70
71     @staticmethod
72     def bare_event(event_type):
73         """Creates an event with default additions, event type and timestamp.
74
75         @param event_type the value set for the "event" key, used
76         to distinguish events.
77
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 = {}
87
88         event.update({
89             "event": event_type,
90             "event_time": time.time()
91         })
92         return event
93
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("source python filename has unexpected extension: {}".format(test_filename))
99         return test_filename
100
101     @staticmethod
102     def _event_dictionary_common(test, event_type):
103         """Returns an event dictionary setup with values for the given event type.
104
105         @param test the unittest.TestCase instance
106
107         @param event_type the name of the event type (string).
108
109         @return event dictionary with common event fields set.
110         """
111         test_class_name, test_name = EventBuilder._get_test_name_info(test)
112
113         # Determine the filename for the test case.  If there is an attribute
114         # for it, use it.  Otherwise, determine from the TestCase class path.
115         if hasattr(test, "test_filename"):
116             test_filename = EventBuilder._assert_is_python_sourcefile(test.test_filename)
117         else:
118             test_filename = EventBuilder._assert_is_python_sourcefile(inspect.getsourcefile(test.__class__))
119
120         event = EventBuilder.bare_event(event_type)
121         event.update({
122             "test_class": test_class_name,
123             "test_name": test_name,
124             "test_filename": test_filename
125         })
126
127         return event
128
129     @staticmethod
130     def _error_tuple_class(error_tuple):
131         """Returns the unittest error tuple's error class as a string.
132
133         @param error_tuple the error tuple provided by the test framework.
134
135         @return the error type (typically an exception) raised by the
136         test framework.
137         """
138         type_var = error_tuple[0]
139         module = inspect.getmodule(type_var)
140         if module:
141             return "{}.{}".format(module.__name__, type_var.__name__)
142         else:
143             return type_var.__name__
144
145     @staticmethod
146     def _error_tuple_message(error_tuple):
147         """Returns the unittest error tuple's error message.
148
149         @param error_tuple the error tuple provided by the test framework.
150
151         @return the error message provided by the test framework.
152         """
153         return str(error_tuple[1])
154
155     @staticmethod
156     def _error_tuple_traceback(error_tuple):
157         """Returns the unittest error tuple's error message.
158
159         @param error_tuple the error tuple provided by the test framework.
160
161         @return the error message provided by the test framework.
162         """
163         return error_tuple[2]
164
165     @staticmethod
166     def _event_dictionary_test_result(test, status):
167         """Returns an event dictionary with common test result fields set.
168
169         @param test a unittest.TestCase instance.
170
171         @param status the status/result of the test
172         (e.g. "success", "failure", etc.)
173
174         @return the event dictionary
175         """
176         event = EventBuilder._event_dictionary_common(
177             test, EventBuilder.TYPE_TEST_RESULT)
178         event["status"] = status
179         return event
180
181     @staticmethod
182     def _event_dictionary_issue(test, status, error_tuple):
183         """Returns an event dictionary with common issue-containing test result
184         fields set.
185
186         @param test a unittest.TestCase instance.
187
188         @param status the status/result of the test
189         (e.g. "success", "failure", etc.)
190
191         @param error_tuple the error tuple as reported by the test runner.
192         This is of the form (type<error>, error).
193
194         @return the event dictionary
195         """
196         event = EventBuilder._event_dictionary_test_result(test, status)
197         event["issue_class"] = EventBuilder._error_tuple_class(error_tuple)
198         event["issue_message"] = EventBuilder._error_tuple_message(error_tuple)
199         backtrace = EventBuilder._error_tuple_traceback(error_tuple)
200         if backtrace is not None:
201             event["issue_backtrace"] = traceback.format_tb(backtrace)
202         return event
203
204     @staticmethod
205     def event_for_start(test):
206         """Returns an event dictionary for the test start event.
207
208         @param test a unittest.TestCase instance.
209
210         @return the event dictionary
211         """
212         return EventBuilder._event_dictionary_common(
213             test, EventBuilder.TYPE_TEST_START)
214
215     @staticmethod
216     def event_for_success(test):
217         """Returns an event dictionary for a successful test.
218
219         @param test a unittest.TestCase instance.
220
221         @return the event dictionary
222         """
223         return EventBuilder._event_dictionary_test_result(
224             test, EventBuilder.STATUS_SUCCESS)
225
226     @staticmethod
227     def event_for_unexpected_success(test, bugnumber):
228         """Returns an event dictionary for a test that succeeded but was
229         expected to fail.
230
231         @param test a unittest.TestCase instance.
232
233         @param bugnumber the issue identifier for the bug tracking the
234         fix request for the test expected to fail (but is in fact
235         passing here).
236
237         @return the event dictionary
238
239         """
240         event = EventBuilder._event_dictionary_test_result(
241             test, EventBuilder.STATUS_UNEXPECTED_SUCCESS)
242         if bugnumber:
243             event["bugnumber"] = str(bugnumber)
244         return event
245
246     @staticmethod
247     def event_for_failure(test, error_tuple):
248         """Returns an event dictionary for a test that failed.
249
250         @param test a unittest.TestCase instance.
251
252         @param error_tuple the error tuple as reported by the test runner.
253         This is of the form (type<error>, error).
254
255         @return the event dictionary
256         """
257         return EventBuilder._event_dictionary_issue(
258             test, EventBuilder.STATUS_FAILURE, error_tuple)
259
260     @staticmethod
261     def event_for_expected_failure(test, error_tuple, bugnumber):
262         """Returns an event dictionary for a test that failed as expected.
263
264         @param test a unittest.TestCase instance.
265
266         @param error_tuple the error tuple as reported by the test runner.
267         This is of the form (type<error>, error).
268
269         @param bugnumber the issue identifier for the bug tracking the
270         fix request for the test expected to fail.
271
272         @return the event dictionary
273
274         """
275         event = EventBuilder._event_dictionary_issue(
276             test, EventBuilder.STATUS_EXPECTED_FAILURE, error_tuple)
277         if bugnumber:
278             event["bugnumber"] = str(bugnumber)
279         return event
280
281     @staticmethod
282     def event_for_skip(test, reason):
283         """Returns an event dictionary for a test that was skipped.
284
285         @param test a unittest.TestCase instance.
286
287         @param reason the reason why the test is being skipped.
288
289         @return the event dictionary
290         """
291         event = EventBuilder._event_dictionary_test_result(
292             test, EventBuilder.STATUS_SKIP)
293         event["skip_reason"] = reason
294         return event
295
296     @staticmethod
297     def event_for_error(test, error_tuple):
298         """Returns an event dictionary for a test that hit a test execution error.
299
300         @param test a unittest.TestCase instance.
301
302         @param error_tuple the error tuple as reported by the test runner.
303         This is of the form (type<error>, error).
304
305         @return the event dictionary
306         """
307         event = EventBuilder._event_dictionary_issue(
308             test, EventBuilder.STATUS_ERROR, error_tuple)
309         event["issue_phase"] = "test"
310         return event
311
312     @staticmethod
313     def event_for_build_error(test, error_tuple):
314         """Returns an event dictionary for a test that hit a test execution error
315         during the test cleanup phase.
316
317         @param test a unittest.TestCase instance.
318
319         @param error_tuple the error tuple as reported by the test runner.
320         This is of the form (type<error>, error).
321
322         @return the event dictionary
323         """
324         event = EventBuilder._event_dictionary_issue(
325             test, EventBuilder.STATUS_ERROR, error_tuple)
326         event["issue_phase"] = "build"
327
328         build_error = error_tuple[1]
329         event["build_command"] = build_error.command
330         event["build_error"] = build_error.build_error
331         return event
332
333     @staticmethod
334     def event_for_cleanup_error(test, error_tuple):
335         """Returns an event dictionary for a test that hit a test execution error
336         during the test cleanup phase.
337
338         @param test a unittest.TestCase instance.
339
340         @param error_tuple the error tuple as reported by the test runner.
341         This is of the form (type<error>, error).
342
343         @return the event dictionary
344         """
345         event = EventBuilder._event_dictionary_issue(
346             test, EventBuilder.STATUS_ERROR, error_tuple)
347         event["issue_phase"] = "cleanup"
348         return event
349
350     @staticmethod
351     def event_for_job_test_add_error(test_filename, exception, backtrace):
352         event = EventBuilder.bare_event(EventBuilder.TYPE_JOB_RESULT)
353         event["status"] = EventBuilder.STATUS_ERROR
354         if test_filename is not None:
355             event["test_filename"] = EventBuilder._assert_is_python_sourcefile(test_filename)
356         if exception is not None and "__class__" in dir(exception):
357             event["issue_class"] = exception.__class__
358         event["issue_message"] = exception
359         if backtrace is not None:
360             event["issue_backtrace"] = backtrace
361         return event
362
363     @staticmethod
364     def event_for_job_exceptional_exit(
365             pid, worker_index, exception_code, exception_description,
366             test_filename, command_line):
367         """Creates an event for a job (i.e. process) exit due to signal.
368
369         @param pid the process id for the job that failed
370         @param worker_index optional id for the job queue running the process
371         @param exception_code optional code
372         (e.g. SIGTERM integer signal number)
373         @param exception_description optional string containing symbolic
374         representation of the issue (e.g. "SIGTERM")
375         @param test_filename the path to the test filename that exited
376         in some exceptional way.
377         @param command_line the Popen()-style list provided as the command line
378         for the process that timed out.
379
380         @return an event dictionary coding the job completion description.
381         """
382         event = EventBuilder.bare_event(EventBuilder.TYPE_JOB_RESULT)
383         event["status"] = EventBuilder.STATUS_EXCEPTIONAL_EXIT
384         if pid is not None:
385             event["pid"] = pid
386         if worker_index is not None:
387             event["worker_index"] = int(worker_index)
388         if exception_code is not None:
389             event["exception_code"] = exception_code
390         if exception_description is not None:
391             event["exception_description"] = exception_description
392         if test_filename is not None:
393             event["test_filename"] = EventBuilder._assert_is_python_sourcefile(test_filename)
394         if command_line is not None:
395             event["command_line"] = command_line
396         return event
397
398     @staticmethod
399     def event_for_job_timeout(pid, worker_index, test_filename, command_line):
400         """Creates an event for a job (i.e. process) timeout.
401
402         @param pid the process id for the job that timed out
403         @param worker_index optional id for the job queue running the process
404         @param test_filename the path to the test filename that timed out.
405         @param command_line the Popen-style list provided as the command line
406         for the process that timed out.
407
408         @return an event dictionary coding the job completion description.
409         """
410         event = EventBuilder.bare_event(EventBuilder.TYPE_JOB_RESULT)
411         event["status"] = "timeout"
412         if pid is not None:
413             event["pid"] = pid
414         if worker_index is not None:
415             event["worker_index"] = int(worker_index)
416         if test_filename is not None:
417             event["test_filename"] = EventBuilder._assert_is_python_sourcefile(test_filename)
418         if command_line is not None:
419             event["command_line"] = command_line
420         return event
421
422     @staticmethod
423     def event_for_mark_test_rerun_eligible(test):
424         """Creates an event that indicates the specified test is explicitly
425         eligible for rerun.
426
427         Note there is a mode that will enable test rerun eligibility at the
428         global level.  These markings for explicit rerun eligibility are
429         intended for the mode of running where only explicitly re-runnable
430         tests are rerun upon hitting an issue.
431
432         @param test the TestCase instance to which this pertains.
433
434         @return an event that specifies the given test as being eligible to
435         be rerun.
436         """
437         event = EventBuilder._event_dictionary_common(
438             test,
439             EventBuilder.TYPE_MARK_TEST_RERUN_ELIGIBLE)
440         return event
441
442     @staticmethod
443     def event_for_mark_test_expected_failure(test):
444         """Creates an event that indicates the specified test is expected
445         to fail.
446
447         @param test the TestCase instance to which this pertains.
448
449         @return an event that specifies the given test is expected to fail.
450         """
451         event = EventBuilder._event_dictionary_common(
452             test,
453             EventBuilder.TYPE_MARK_TEST_EXPECTED_FAILURE)
454         return event
455
456     @staticmethod
457     def add_entries_to_all_events(entries_dict):
458         """Specifies a dictionary of entries to add to all test events.
459
460         This provides a mechanism for, say, a parallel test runner to
461         indicate to each inferior dotest.py that it should add a
462         worker index to each.
463
464         Calling this method replaces all previous entries added
465         by a prior call to this.
466
467         Event build methods will overwrite any entries that collide.
468         Thus, the passed in dictionary is the base, which gets merged
469         over by event building when keys collide.
470
471         @param entries_dict a dictionary containing key and value
472         pairs that should be merged into all events created by the
473         event generator.  May be None to clear out any extra entries.
474         """
475         EventBuilder.BASE_DICTIONARY = dict(entries_dict)