summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lldb/packages/Python/lldbsuite/test/decorators.py7
-rw-r--r--lldb/packages/Python/lldbsuite/test/dosep.py15
-rw-r--r--lldb/packages/Python/lldbsuite/test/dotest.py18
-rw-r--r--lldb/packages/Python/lldbsuite/test/lldbtest.py7
-rw-r--r--lldb/packages/Python/lldbsuite/test/test_result.py3
-rw-r--r--lldb/packages/Python/lldbsuite/test_event/__init__.py0
-rw-r--r--lldb/packages/Python/lldbsuite/test_event/dotest_channels.py (renamed from lldb/packages/Python/lldbsuite/test/dotest_channels.py)0
-rw-r--r--lldb/packages/Python/lldbsuite/test_event/event_builder.py435
-rw-r--r--lldb/packages/Python/lldbsuite/test_event/formatter/__init__.py160
-rw-r--r--lldb/packages/Python/lldbsuite/test_event/formatter/curses.py (renamed from lldb/packages/Python/lldbsuite/test/curses_results.py)19
-rw-r--r--lldb/packages/Python/lldbsuite/test_event/formatter/dump_formatter.py23
-rw-r--r--lldb/packages/Python/lldbsuite/test_event/formatter/pickled.py57
-rw-r--r--lldb/packages/Python/lldbsuite/test_event/formatter/results_formatter.py (renamed from lldb/packages/Python/lldbsuite/test/result_formatter.py)635
-rw-r--r--lldb/packages/Python/lldbsuite/test_event/formatter/xunit.py (renamed from lldb/packages/Python/lldbsuite/test/xunit_formatter.py)29
14 files changed, 734 insertions, 674 deletions
diff --git a/lldb/packages/Python/lldbsuite/test/decorators.py b/lldb/packages/Python/lldbsuite/test/decorators.py
index e73bfe34173..84bce6e6452 100644
--- a/lldb/packages/Python/lldbsuite/test/decorators.py
+++ b/lldb/packages/Python/lldbsuite/test/decorators.py
@@ -1,10 +1,9 @@
-from __future__ import print_function
from __future__ import absolute_import
+from __future__ import print_function
# System modules
from distutils.version import LooseVersion, StrictVersion
from functools import wraps
-import itertools
import os
import re
import sys
@@ -19,7 +18,7 @@ import use_lldb_suite
import lldb
from . import configuration
from . import test_categories
-from .result_formatter import EventBuilder
+from lldbsuite.test_event.event_builder import EventBuilder
from lldbsuite.support import funcutils
from lldbsuite.test import lldbplatform
from lldbsuite.test import lldbplatformutil
@@ -77,7 +76,6 @@ def expectedFailure(expected_fn, bugnumber=None):
raise Exception("Decorator can only be used to decorate a test method")
@wraps(func)
def wrapper(*args, **kwargs):
- from unittest2 import case
self = args[0]
if funcutils.requires_self(expected_fn):
xfail_reason = expected_fn(self)
@@ -110,7 +108,6 @@ def skipTestIfFn(expected_fn, bugnumber=None):
@wraps(func)
def wrapper(*args, **kwargs):
- from unittest2 import case
self = args[0]
if funcutils.requires_self(expected_fn):
reason = expected_fn(self)
diff --git a/lldb/packages/Python/lldbsuite/test/dosep.py b/lldb/packages/Python/lldbsuite/test/dosep.py
index e58bcaf9574..7d9dcd938aa 100644
--- a/lldb/packages/Python/lldbsuite/test/dosep.py
+++ b/lldb/packages/Python/lldbsuite/test/dosep.py
@@ -30,8 +30,8 @@ ulimit -c unlimited
echo core.%p | sudo tee /proc/sys/kernel/core_pattern
"""
-from __future__ import print_function
from __future__ import absolute_import
+from __future__ import print_function
# system packages and modules
import asyncore
@@ -52,13 +52,12 @@ from six.moves import queue
import lldbsuite
import lldbsuite.support.seven as seven
-from lldbsuite.support import optional_with
from . import configuration
-from . import dotest_channels
from . import dotest_args
-from . import result_formatter
-
-from .result_formatter import EventBuilder
+from lldbsuite.support import optional_with
+from lldbsuite.test_event import dotest_channels
+from lldbsuite.test_event.event_builder import EventBuilder
+from lldbsuite.test_event import formatter
from .test_runner import process_control
@@ -299,9 +298,9 @@ def send_events_to_collector(events, command):
event_port = int(command[arg_index])
# Create results formatter connected back to collector via socket.
- config = result_formatter.FormatterConfig()
+ config = formatter.FormatterConfig()
config.port = event_port
- formatter_spec = result_formatter.create_results_formatter(config)
+ formatter_spec = formatter.create_results_formatter(config)
if formatter_spec is None or formatter_spec.formatter is None:
raise Exception(
"Failed to create socket-based ResultsFormatter "
diff --git a/lldb/packages/Python/lldbsuite/test/dotest.py b/lldb/packages/Python/lldbsuite/test/dotest.py
index 198e87c5807..fa164a61890 100644
--- a/lldb/packages/Python/lldbsuite/test/dotest.py
+++ b/lldb/packages/Python/lldbsuite/test/dotest.py
@@ -23,7 +23,6 @@ from __future__ import print_function
# System modules
import atexit
-import importlib
import os
import errno
import platform
@@ -31,7 +30,6 @@ import signal
import socket
import subprocess
import sys
-import inspect
# Third-party modules
import six
@@ -43,9 +41,9 @@ from . import configuration
from . import dotest_args
from . import lldbtest_config
from . import test_categories
-from . import result_formatter
+from lldbsuite.test_event import formatter
from . import test_result
-from .result_formatter import EventBuilder
+from lldbsuite.test_event.event_builder import EventBuilder
from ..support import seven
def is_exe(fpath):
@@ -359,7 +357,7 @@ def parseOptionsAndInitTestdirs():
# Capture test results-related args.
if args.curses and not args.inferior:
# Act as if the following args were set.
- args.results_formatter = "lldbsuite.test.curses_results.Curses"
+ args.results_formatter = "lldbsuite.test_event.formatter.curses.Curses"
args.results_file = "stdout"
if args.results_file:
@@ -383,7 +381,7 @@ def parseOptionsAndInitTestdirs():
# and we're not a test inferior.
if not args.inferior and configuration.results_formatter_name is None:
configuration.results_formatter_name = (
- "lldbsuite.test.result_formatter.ResultsFormatter")
+ "lldbsuite.test_event.formatter.results_formatter.ResultsFormatter")
# rerun-related arguments
configuration.rerun_all_issues = args.rerun_all_issues
@@ -412,7 +410,7 @@ def parseOptionsAndInitTestdirs():
# Tell the event builder to create all events with these
# key/val pairs in them.
if len(entries) > 0:
- result_formatter.EventBuilder.add_entries_to_all_events(entries)
+ EventBuilder.add_entries_to_all_events(entries)
# Gather all the dirs passed on the command line.
if len(args.args) > 0:
@@ -453,7 +451,7 @@ def createSocketToLocalPort(port):
def setupTestResults():
"""Sets up test results-related objects based on arg settings."""
# Setup the results formatter configuration.
- formatter_config = result_formatter.FormatterConfig()
+ formatter_config = formatter.FormatterConfig()
formatter_config.filename = configuration.results_filename
formatter_config.formatter_name = configuration.results_formatter_name
formatter_config.formatter_options = (
@@ -461,12 +459,12 @@ def setupTestResults():
formatter_config.port = configuration.results_port
# Create the results formatter.
- formatter_spec = result_formatter.create_results_formatter(
+ formatter_spec = formatter.create_results_formatter(
formatter_config)
if formatter_spec is not None and formatter_spec.formatter is not None:
configuration.results_formatter_object = formatter_spec.formatter
- # Send an intialize message to the formatter.
+ # Send an initialize message to the formatter.
initialize_event = EventBuilder.bare_event("initialize")
if isMultiprocessTestRunner():
if (configuration.test_runner_name is not None and
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index 196ee8fac80..492e136fe39 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -31,8 +31,8 @@ OK
$
"""
-from __future__ import print_function
from __future__ import absolute_import
+from __future__ import print_function
# System modules
import abc
@@ -42,12 +42,13 @@ import gc
import glob
import inspect
import io
-import os, sys, traceback
import os.path
import re
import signal
from subprocess import *
+import sys
import time
+import traceback
import types
# Third-party modules
@@ -68,8 +69,6 @@ from . import test_categories
from lldbsuite.support import encoded_file
from lldbsuite.support import funcutils
-from .result_formatter import EventBuilder
-
# dosep.py starts lots and lots of dotest instances
# This option helps you find if two (or more) dotest instances are using the same
# directory at the same time
diff --git a/lldb/packages/Python/lldbsuite/test/test_result.py b/lldb/packages/Python/lldbsuite/test/test_result.py
index 9665d672a79..949ebc84362 100644
--- a/lldb/packages/Python/lldbsuite/test/test_result.py
+++ b/lldb/packages/Python/lldbsuite/test/test_result.py
@@ -18,9 +18,8 @@ import inspect
import unittest2
# LLDB Modules
-import lldbsuite
from . import configuration
-from .result_formatter import EventBuilder
+from lldbsuite.test_event.event_builder import EventBuilder
class LLDBTestResult(unittest2.TextTestResult):
diff --git a/lldb/packages/Python/lldbsuite/test_event/__init__.py b/lldb/packages/Python/lldbsuite/test_event/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test_event/__init__.py
diff --git a/lldb/packages/Python/lldbsuite/test/dotest_channels.py b/lldb/packages/Python/lldbsuite/test_event/dotest_channels.py
index 72ff9bd85f1..72ff9bd85f1 100644
--- a/lldb/packages/Python/lldbsuite/test/dotest_channels.py
+++ b/lldb/packages/Python/lldbsuite/test_event/dotest_channels.py
diff --git a/lldb/packages/Python/lldbsuite/test_event/event_builder.py b/lldb/packages/Python/lldbsuite/test_event/event_builder.py
new file mode 100644
index 00000000000..3c43f00577a
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test_event/event_builder.py
@@ -0,0 +1,435 @@
+"""
+ The LLVM Compiler Infrastructure
+
+This file is distributed under the University of Illinois Open Source
+License. See LICENSE.TXT for details.
+
+Provides a class to build Python test event data structures.
+"""
+
+from __future__ import print_function
+from __future__ import absolute_import
+
+# System modules
+import inspect
+import time
+import traceback
+
+# Third-party modules
+
+# LLDB modules
+
+
+class EventBuilder(object):
+ """Helper class to build test result event dictionaries."""
+
+ BASE_DICTIONARY = None
+
+ # Test Event Types
+ TYPE_JOB_RESULT = "job_result"
+ TYPE_TEST_RESULT = "test_result"
+ TYPE_TEST_START = "test_start"
+ TYPE_MARK_TEST_RERUN_ELIGIBLE = "test_eligible_for_rerun"
+ TYPE_MARK_TEST_EXPECTED_FAILURE = "test_expected_failure"
+ TYPE_SESSION_TERMINATE = "terminate"
+
+ RESULT_TYPES = {TYPE_JOB_RESULT, TYPE_TEST_RESULT}
+
+ # Test/Job Status Tags
+ STATUS_EXCEPTIONAL_EXIT = "exceptional_exit"
+ STATUS_SUCCESS = "success"
+ STATUS_FAILURE = "failure"
+ STATUS_EXPECTED_FAILURE = "expected_failure"
+ STATUS_EXPECTED_TIMEOUT = "expected_timeout"
+ STATUS_UNEXPECTED_SUCCESS = "unexpected_success"
+ STATUS_SKIP = "skip"
+ STATUS_ERROR = "error"
+ STATUS_TIMEOUT = "timeout"
+
+ """Test methods or jobs with a status matching any of these
+ status values will cause a testrun failure, unless
+ the test methods rerun and do not trigger an issue when rerun."""
+ TESTRUN_ERROR_STATUS_VALUES = {STATUS_ERROR, STATUS_EXCEPTIONAL_EXIT, STATUS_FAILURE, STATUS_TIMEOUT}
+
+ @staticmethod
+ def _get_test_name_info(test):
+ """Returns (test-class-name, test-method-name) from a test case instance.
+
+ @param test a unittest.TestCase instance.
+
+ @return tuple containing (test class name, test method name)
+ """
+ test_class_components = test.id().split(".")
+ test_class_name = ".".join(test_class_components[:-1])
+ test_name = test_class_components[-1]
+ return test_class_name, test_name
+
+ @staticmethod
+ def bare_event(event_type):
+ """Creates an event with default additions, event type and timestamp.
+
+ @param event_type the value set for the "event" key, used
+ to distinguish events.
+
+ @returns an event dictionary with all default additions, the "event"
+ key set to the passed in event_type, and the event_time value set to
+ time.time().
+ """
+ if EventBuilder.BASE_DICTIONARY is not None:
+ # Start with a copy of the "always include" entries.
+ event = dict(EventBuilder.BASE_DICTIONARY)
+ else:
+ event = {}
+
+ event.update({
+ "event": event_type,
+ "event_time": time.time()
+ })
+ return event
+
+ @staticmethod
+ def _assert_is_python_sourcefile(test_filename):
+ if test_filename is not None:
+ if not test_filename.endswith(".py"):
+ raise Exception("source python filename has unexpected extension: {}".format(test_filename))
+ return test_filename
+
+ @staticmethod
+ def _event_dictionary_common(test, event_type):
+ """Returns an event dictionary setup with values for the given event type.
+
+ @param test the unittest.TestCase instance
+
+ @param event_type the name of the event type (string).
+
+ @return event dictionary with common event fields set.
+ """
+ test_class_name, test_name = EventBuilder._get_test_name_info(test)
+
+ # Determine the filename for the test case. If there is an attribute
+ # for it, use it. Otherwise, determine from the TestCase class path.
+ if hasattr(test, "test_filename"):
+ test_filename = EventBuilder._assert_is_python_sourcefile(test.test_filename)
+ else:
+ test_filename = EventBuilder._assert_is_python_sourcefile(inspect.getsourcefile(test.__class__))
+
+ event = EventBuilder.bare_event(event_type)
+ event.update({
+ "test_class": test_class_name,
+ "test_name": test_name,
+ "test_filename": test_filename
+ })
+
+ return event
+
+ @staticmethod
+ def _error_tuple_class(error_tuple):
+ """Returns the unittest error tuple's error class as a string.
+
+ @param error_tuple the error tuple provided by the test framework.
+
+ @return the error type (typically an exception) raised by the
+ test framework.
+ """
+ type_var = error_tuple[0]
+ module = inspect.getmodule(type_var)
+ if module:
+ return "{}.{}".format(module.__name__, type_var.__name__)
+ else:
+ return type_var.__name__
+
+ @staticmethod
+ def _error_tuple_message(error_tuple):
+ """Returns the unittest error tuple's error message.
+
+ @param error_tuple the error tuple provided by the test framework.
+
+ @return the error message provided by the test framework.
+ """
+ return str(error_tuple[1])
+
+ @staticmethod
+ def _error_tuple_traceback(error_tuple):
+ """Returns the unittest error tuple's error message.
+
+ @param error_tuple the error tuple provided by the test framework.
+
+ @return the error message provided by the test framework.
+ """
+ return error_tuple[2]
+
+ @staticmethod
+ def _event_dictionary_test_result(test, status):
+ """Returns an event dictionary with common test result fields set.
+
+ @param test a unittest.TestCase instance.
+
+ @param status the status/result of the test
+ (e.g. "success", "failure", etc.)
+
+ @return the event dictionary
+ """
+ event = EventBuilder._event_dictionary_common(
+ test, EventBuilder.TYPE_TEST_RESULT)
+ event["status"] = status
+ return event
+
+ @staticmethod
+ def _event_dictionary_issue(test, status, error_tuple):
+ """Returns an event dictionary with common issue-containing test result
+ fields set.
+
+ @param test a unittest.TestCase instance.
+
+ @param status the status/result of the test
+ (e.g. "success", "failure", etc.)
+
+ @param error_tuple the error tuple as reported by the test runner.
+ This is of the form (type<error>, error).
+
+ @return the event dictionary
+ """
+ event = EventBuilder._event_dictionary_test_result(test, status)
+ event["issue_class"] = EventBuilder._error_tuple_class(error_tuple)
+ event["issue_message"] = EventBuilder._error_tuple_message(error_tuple)
+ backtrace = EventBuilder._error_tuple_traceback(error_tuple)
+ if backtrace is not None:
+ event["issue_backtrace"] = traceback.format_tb(backtrace)
+ return event
+
+ @staticmethod
+ def event_for_start(test):
+ """Returns an event dictionary for the test start event.
+
+ @param test a unittest.TestCase instance.
+
+ @return the event dictionary
+ """
+ return EventBuilder._event_dictionary_common(
+ test, EventBuilder.TYPE_TEST_START)
+
+ @staticmethod
+ def event_for_success(test):
+ """Returns an event dictionary for a successful test.
+
+ @param test a unittest.TestCase instance.
+
+ @return the event dictionary
+ """
+ return EventBuilder._event_dictionary_test_result(
+ test, EventBuilder.STATUS_SUCCESS)
+
+ @staticmethod
+ def event_for_unexpected_success(test, bugnumber):
+ """Returns an event dictionary for a test that succeeded but was
+ expected to fail.
+
+ @param test a unittest.TestCase instance.
+
+ @param bugnumber the issue identifier for the bug tracking the
+ fix request for the test expected to fail (but is in fact
+ passing here).
+
+ @return the event dictionary
+
+ """
+ event = EventBuilder._event_dictionary_test_result(
+ test, EventBuilder.STATUS_UNEXPECTED_SUCCESS)
+ if bugnumber:
+ event["bugnumber"] = str(bugnumber)
+ return event
+
+ @staticmethod
+ def event_for_failure(test, error_tuple):
+ """Returns an event dictionary for a test that failed.
+
+ @param test a unittest.TestCase instance.
+
+ @param error_tuple the error tuple as reported by the test runner.
+ This is of the form (type<error>, error).
+
+ @return the event dictionary
+ """
+ return EventBuilder._event_dictionary_issue(
+ test, EventBuilder.STATUS_FAILURE, error_tuple)
+
+ @staticmethod
+ def event_for_expected_failure(test, error_tuple, bugnumber):
+ """Returns an event dictionary for a test that failed as expected.
+
+ @param test a unittest.TestCase instance.
+
+ @param error_tuple the error tuple as reported by the test runner.
+ This is of the form (type<error>, error).
+
+ @param bugnumber the issue identifier for the bug tracking the
+ fix request for the test expected to fail.
+
+ @return the event dictionary
+
+ """
+ event = EventBuilder._event_dictionary_issue(
+ test, EventBuilder.STATUS_EXPECTED_FAILURE, error_tuple)
+ if bugnumber:
+ event["bugnumber"] = str(bugnumber)
+ return event
+
+ @staticmethod
+ def event_for_skip(test, reason):
+ """Returns an event dictionary for a test that was skipped.
+
+ @param test a unittest.TestCase instance.
+
+ @param reason the reason why the test is being skipped.
+
+ @return the event dictionary
+ """
+ event = EventBuilder._event_dictionary_test_result(
+ test, EventBuilder.STATUS_SKIP)
+ event["skip_reason"] = reason
+ return event
+
+ @staticmethod
+ def event_for_error(test, error_tuple):
+ """Returns an event dictionary for a test that hit a test execution error.
+
+ @param test a unittest.TestCase instance.
+
+ @param error_tuple the error tuple as reported by the test runner.
+ This is of the form (type<error>, error).
+
+ @return the event dictionary
+ """
+ return EventBuilder._event_dictionary_issue(
+ test, EventBuilder.STATUS_ERROR, error_tuple)
+
+ @staticmethod
+ def event_for_cleanup_error(test, error_tuple):
+ """Returns an event dictionary for a test that hit a test execution error
+ during the test cleanup phase.
+
+ @param test a unittest.TestCase instance.
+
+ @param error_tuple the error tuple as reported by the test runner.
+ This is of the form (type<error>, error).
+
+ @return the event dictionary
+ """
+ event = EventBuilder._event_dictionary_issue(
+ test, EventBuilder.STATUS_ERROR, error_tuple)
+ event["issue_phase"] = "cleanup"
+ return event
+
+ @staticmethod
+ def event_for_job_exceptional_exit(
+ pid, worker_index, exception_code, exception_description,
+ test_filename, command_line):
+ """Creates an event for a job (i.e. process) exit due to signal.
+
+ @param pid the process id for the job that failed
+ @param worker_index optional id for the job queue running the process
+ @param exception_code optional code
+ (e.g. SIGTERM integer signal number)
+ @param exception_description optional string containing symbolic
+ representation of the issue (e.g. "SIGTERM")
+ @param test_filename the path to the test filename that exited
+ in some exceptional way.
+ @param command_line the Popen()-style list provided as the command line
+ for the process that timed out.
+
+ @return an event dictionary coding the job completion description.
+ """
+ event = EventBuilder.bare_event(EventBuilder.TYPE_JOB_RESULT)
+ event["status"] = EventBuilder.STATUS_EXCEPTIONAL_EXIT
+ if pid is not None:
+ event["pid"] = pid
+ if worker_index is not None:
+ event["worker_index"] = int(worker_index)
+ if exception_code is not None:
+ event["exception_code"] = exception_code
+ if exception_description is not None:
+ event["exception_description"] = exception_description
+ if test_filename is not None:
+ event["test_filename"] = EventBuilder._assert_is_python_sourcefile(test_filename)
+ if command_line is not None:
+ event["command_line"] = command_line
+ return event
+
+ @staticmethod
+ def event_for_job_timeout(pid, worker_index, test_filename, command_line):
+ """Creates an event for a job (i.e. process) timeout.
+
+ @param pid the process id for the job that timed out
+ @param worker_index optional id for the job queue running the process
+ @param test_filename the path to the test filename that timed out.
+ @param command_line the Popen-style list provided as the command line
+ for the process that timed out.
+
+ @return an event dictionary coding the job completion description.
+ """
+ event = EventBuilder.bare_event(EventBuilder.TYPE_JOB_RESULT)
+ event["status"] = "timeout"
+ if pid is not None:
+ event["pid"] = pid
+ if worker_index is not None:
+ event["worker_index"] = int(worker_index)
+ if test_filename is not None:
+ event["test_filename"] = EventBuilder._assert_is_python_sourcefile(test_filename)
+ if command_line is not None:
+ event["command_line"] = command_line
+ return event
+
+ @staticmethod
+ def event_for_mark_test_rerun_eligible(test):
+ """Creates an event that indicates the specified test is explicitly
+ eligible for rerun.
+
+ Note there is a mode that will enable test rerun eligibility at the
+ global level. These markings for explicit rerun eligibility are
+ intended for the mode of running where only explicitly re-runnable
+ tests are rerun upon hitting an issue.
+
+ @param test the TestCase instance to which this pertains.
+
+ @return an event that specifies the given test as being eligible to
+ be rerun.
+ """
+ event = EventBuilder._event_dictionary_common(
+ test,
+ EventBuilder.TYPE_MARK_TEST_RERUN_ELIGIBLE)
+ return event
+
+ @staticmethod
+ def event_for_mark_test_expected_failure(test):
+ """Creates an event that indicates the specified test is expected
+ to fail.
+
+ @param test the TestCase instance to which this pertains.
+
+ @return an event that specifies the given test is expected to fail.
+ """
+ event = EventBuilder._event_dictionary_common(
+ test,
+ EventBuilder.TYPE_MARK_TEST_EXPECTED_FAILURE)
+ return event
+
+ @staticmethod
+ def add_entries_to_all_events(entries_dict):
+ """Specifies a dictionary of entries to add to all test events.
+
+ This provides a mechanism for, say, a parallel test runner to
+ indicate to each inferior dotest.py that it should add a
+ worker index to each.
+
+ Calling this method replaces all previous entries added
+ by a prior call to this.
+
+ Event build methods will overwrite any entries that collide.
+ Thus, the passed in dictionary is the base, which gets merged
+ over by event building when keys collide.
+
+ @param entries_dict a dictionary containing key and value
+ pairs that should be merged into all events created by the
+ event generator. May be None to clear out any extra entries.
+ """
+ EventBuilder.BASE_DICTIONARY = dict(entries_dict)
diff --git a/lldb/packages/Python/lldbsuite/test_event/formatter/__init__.py b/lldb/packages/Python/lldbsuite/test_event/formatter/__init__.py
new file mode 100644
index 00000000000..f00766c5d38
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test_event/formatter/__init__.py
@@ -0,0 +1,160 @@
+"""
+ The LLVM Compiler Infrastructure
+
+This file is distributed under the University of Illinois Open Source
+License. See LICENSE.TXT for details.
+"""
+
+from __future__ import print_function
+from __future__ import absolute_import
+
+# System modules
+import importlib
+import socket
+import sys
+
+# Third-party modules
+
+# LLDB modules
+
+
+# Ignore method count on DTOs.
+# pylint: disable=too-few-public-methods
+class FormatterConfig(object):
+ """Provides formatter configuration info to create_results_formatter()."""
+
+ def __init__(self):
+ self.filename = None
+ self.port = None
+ self.formatter_name = None
+ self.formatter_options = None
+
+
+# Ignore method count on DTOs.
+# pylint: disable=too-few-public-methods
+class CreatedFormatter(object):
+ """Provides transfer object for returns from create_results_formatter()."""
+
+ def __init__(self, formatter, cleanup_func):
+ self.formatter = formatter
+ self.cleanup_func = cleanup_func
+
+
+SOCKET_ACK_BYTE_VALUE = b'*' # ASCII for chr(42)
+
+
+def create_results_formatter(config):
+ """Sets up a test results formatter.
+
+ @param config an instance of FormatterConfig
+ that indicates how to setup the ResultsFormatter.
+
+ @return an instance of CreatedFormatter.
+ """
+
+ def create_socket(port):
+ """Creates a socket to the localhost on the given port.
+
+ @param port the port number of the listening port on
+ the localhost.
+
+ @return (socket object, socket closing function)
+ """
+
+ def socket_closer(open_sock):
+ """Close down an opened socket properly."""
+ open_sock.shutdown(socket.SHUT_RDWR)
+ open_sock.close()
+
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.connect(("localhost", port))
+
+ # Wait for the ack from the listener side.
+ # This is needed to prevent a race condition
+ # in the main dosep.py processing loop: we
+ # can't allow a worker queue thread to die
+ # that has outstanding messages to a listener
+ # socket before the listener socket asyncore
+ # listener socket gets spun up; otherwise,
+ # we lose the test result info.
+ read_bytes = sock.recv(1)
+ if read_bytes is None or (len(read_bytes) < 1) or (read_bytes[0] != SOCKET_ACK_BYTE_VALUE):
+ raise Exception("listening socket did not respond with ack byte: response={}".format(read_bytes))
+
+ return sock, lambda: socket_closer(sock)
+
+ default_formatter_name = None
+ results_file_object = None
+ cleanup_func = None
+
+ if config.filename:
+ # Open the results file for writing.
+ if config.filename == 'stdout':
+ results_file_object = sys.stdout
+ cleanup_func = None
+ elif config.filename == 'stderr':
+ results_file_object = sys.stderr
+ cleanup_func = None
+ else:
+ results_file_object = open(config.filename, "w")
+ cleanup_func = results_file_object.close
+ default_formatter_name = (
+ "lldbsuite.test_event.formatter.xunit.XunitFormatter")
+ elif config.port:
+ # Connect to the specified localhost port.
+ results_file_object, cleanup_func = create_socket(config.port)
+ default_formatter_name = (
+ "lldbsuite.test_event.formatter.pickled.RawPickledFormatter")
+
+ # If we have a results formatter name specified and we didn't specify
+ # a results file, we should use stdout.
+ if config.formatter_name is not None and results_file_object is None:
+ # Use stdout.
+ results_file_object = sys.stdout
+ cleanup_func = None
+
+ if results_file_object:
+ # We care about the formatter. Choose user-specified or, if
+ # none specified, use the default for the output type.
+ if config.formatter_name:
+ formatter_name = config.formatter_name
+ else:
+ formatter_name = default_formatter_name
+
+ # Create an instance of the class.
+ # First figure out the package/module.
+ components = formatter_name.split(".")
+ module = importlib.import_module(".".join(components[:-1]))
+
+ # Create the class name we need to load.
+ cls = getattr(module, components[-1])
+
+ # Handle formatter options for the results formatter class.
+ formatter_arg_parser = cls.arg_parser()
+ if config.formatter_options and len(config.formatter_options) > 0:
+ command_line_options = config.formatter_options
+ else:
+ command_line_options = []
+
+ formatter_options = formatter_arg_parser.parse_args(
+ command_line_options)
+
+ # Create the TestResultsFormatter given the processed options.
+ results_formatter_object = cls(results_file_object, formatter_options)
+
+ def shutdown_formatter():
+ """Shuts down the formatter when it is no longer needed."""
+ # Tell the formatter to write out anything it may have
+ # been saving until the very end (e.g. xUnit results
+ # can't complete its output until this point).
+ results_formatter_object.send_terminate_as_needed()
+
+ # And now close out the output file-like object.
+ if cleanup_func is not None:
+ cleanup_func()
+
+ return CreatedFormatter(
+ results_formatter_object,
+ shutdown_formatter)
+ else:
+ return None
diff --git a/lldb/packages/Python/lldbsuite/test/curses_results.py b/lldb/packages/Python/lldbsuite/test_event/formatter/curses.py
index 9d480b946ea..85782599a40 100644
--- a/lldb/packages/Python/lldbsuite/test/curses_results.py
+++ b/lldb/packages/Python/lldbsuite/test_event/formatter/curses.py
@@ -1,16 +1,12 @@
-#!/usr/bin/env python
-
"""
The LLVM Compiler Infrastructure
This file is distributed under the University of Illinois Open Source
License. See LICENSE.TXT for details.
-
-Configuration options for lldbtest.py set by dotest.py during initialization
"""
-from __future__ import print_function
from __future__ import absolute_import
+from __future__ import print_function
# System modules
import curses
@@ -22,12 +18,13 @@ import time
# Third-party modules
# LLDB modules
-from . import lldbcurses
-from . import result_formatter
-from .result_formatter import EventBuilder
+from lldbsuite.test import lldbcurses
+
+from . import results_formatter
+from ..event_builder import EventBuilder
-class Curses(result_formatter.ResultsFormatter):
+class Curses(results_formatter.ResultsFormatter):
"""Receives live results from tests that are running and reports them to the terminal in a curses GUI"""
def __init__(self, out_file, options):
@@ -75,6 +72,10 @@ class Curses(result_formatter.ResultsFormatter):
return 'S'
elif status == EventBuilder.STATUS_ERROR:
return 'E'
+ elif status == EventBuilder.STATUS_TIMEOUT:
+ return 'T'
+ elif status == EventBuilder.STATUS_EXPECTED_TIMEOUT:
+ return 't'
else:
return status
diff --git a/lldb/packages/Python/lldbsuite/test_event/formatter/dump_formatter.py b/lldb/packages/Python/lldbsuite/test_event/formatter/dump_formatter.py
new file mode 100644
index 00000000000..d42dcb18bb4
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test_event/formatter/dump_formatter.py
@@ -0,0 +1,23 @@
+"""
+ The LLVM Compiler Infrastructure
+
+This file is distributed under the University of Illinois Open Source
+License. See LICENSE.TXT for details.
+"""
+
+from __future__ import print_function
+from __future__ import absolute_import
+
+# System modules
+import pprint
+
+# Our modules
+from .results_formatter import ResultsFormatter
+
+
+class DumpFormatter(ResultsFormatter):
+ """Formats events to the file as their raw python dictionary format."""
+
+ def handle_event(self, test_event):
+ super(DumpFormatter, self).handle_event(test_event)
+ self.out_file.write("\n" + pprint.pformat(test_event) + "\n")
diff --git a/lldb/packages/Python/lldbsuite/test_event/formatter/pickled.py b/lldb/packages/Python/lldbsuite/test_event/formatter/pickled.py
new file mode 100644
index 00000000000..9607e91fd55
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test_event/formatter/pickled.py
@@ -0,0 +1,57 @@
+"""
+ The LLVM Compiler Infrastructure
+
+This file is distributed under the University of Illinois Open Source
+License. See LICENSE.TXT for details.
+"""
+
+from __future__ import print_function
+from __future__ import absolute_import
+
+# System modules
+import os
+
+# Our modules
+from .results_formatter import ResultsFormatter
+from six.moves import cPickle
+
+
+class RawPickledFormatter(ResultsFormatter):
+ """Formats events as a pickled stream.
+
+ The parallel test runner has inferiors pickle their results and send them
+ over a socket back to the parallel test. The parallel test runner then
+ aggregates them into the final results formatter (e.g. xUnit).
+ """
+
+ @classmethod
+ def arg_parser(cls):
+ """@return arg parser used to parse formatter-specific options."""
+ parser = super(RawPickledFormatter, cls).arg_parser()
+ return parser
+
+ def __init__(self, out_file, options):
+ super(RawPickledFormatter, self).__init__(out_file, options)
+ self.pid = os.getpid()
+
+ def handle_event(self, test_event):
+ super(RawPickledFormatter, self).handle_event(test_event)
+
+ # Convert initialize/terminate events into job_begin/job_end events.
+ event_type = test_event["event"]
+ if event_type is None:
+ return
+
+ if event_type == "initialize":
+ test_event["event"] = "job_begin"
+ elif event_type == "terminate":
+ test_event["event"] = "job_end"
+
+ # Tack on the pid.
+ test_event["pid"] = self.pid
+
+ # Send it as {serialized_length_of_serialized_bytes}{serialized_bytes}
+ import struct
+ msg = cPickle.dumps(test_event)
+ packet = struct.pack("!I%ds" % len(msg), len(msg), msg)
+ self.out_file.send(packet)
diff --git a/lldb/packages/Python/lldbsuite/test/result_formatter.py b/lldb/packages/Python/lldbsuite/test_event/formatter/results_formatter.py
index 4ad39db3e43..0fc5649bad1 100644
--- a/lldb/packages/Python/lldbsuite/test/result_formatter.py
+++ b/lldb/packages/Python/lldbsuite/test_event/formatter/results_formatter.py
@@ -13,587 +13,24 @@ from __future__ import absolute_import
# System modules
import argparse
-import importlib
-import inspect
import os
-import pprint
-import socket
import sys
import threading
-import time
-import traceback
# Third-party modules
-import six
-from six.moves import cPickle
+
# LLDB modules
-from . import configuration
+from lldbsuite.test import configuration
+from ..event_builder import EventBuilder
import lldbsuite
-# Ignore method count on DTOs.
-# pylint: disable=too-few-public-methods
-class FormatterConfig(object):
- """Provides formatter configuration info to create_results_formatter()."""
- def __init__(self):
- self.filename = None
- self.port = None
- self.formatter_name = None
- self.formatter_options = None
-
-
-# Ignore method count on DTOs.
-# pylint: disable=too-few-public-methods
-class CreatedFormatter(object):
- """Provides transfer object for returns from create_results_formatter()."""
- def __init__(self, formatter, cleanup_func):
- self.formatter = formatter
- self.cleanup_func = cleanup_func
-
-
-def create_results_formatter(config):
- """Sets up a test results formatter.
-
- @param config an instance of FormatterConfig
- that indicates how to setup the ResultsFormatter.
-
- @return an instance of CreatedFormatter.
- """
- def create_socket(port):
- """Creates a socket to the localhost on the given port.
-
- @param port the port number of the listening port on
- the localhost.
-
- @return (socket object, socket closing function)
- """
- def socket_closer(open_sock):
- """Close down an opened socket properly."""
- open_sock.shutdown(socket.SHUT_RDWR)
- open_sock.close()
-
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.connect(("localhost", port))
-
- # Wait for the ack from the listener side.
- # This is needed to prevent a race condition
- # in the main dosep.py processing loop: we
- # can't allow a worker queue thread to die
- # that has outstanding messages to a listener
- # socket before the listener socket asyncore
- # listener socket gets spun up; otherwise,
- # we lose the test result info.
- read_bytes = sock.recv(1)
- # print("\n** socket creation: received ack: {}".format(ord(read_bytes[0])), file=sys.stderr)
-
- return (sock, lambda: socket_closer(sock))
-
- default_formatter_name = None
- results_file_object = None
- cleanup_func = None
-
- if config.filename:
- # Open the results file for writing.
- if config.filename == 'stdout':
- results_file_object = sys.stdout
- cleanup_func = None
- elif config.filename == 'stderr':
- results_file_object = sys.stderr
- cleanup_func = None
- else:
- results_file_object = open(config.filename, "w")
- cleanup_func = results_file_object.close
- default_formatter_name = (
- "lldbsuite.test.xunit_formatter.XunitFormatter")
- elif config.port:
- # Connect to the specified localhost port.
- results_file_object, cleanup_func = create_socket(config.port)
- default_formatter_name = (
- "lldbsuite.test.result_formatter.RawPickledFormatter")
-
- # If we have a results formatter name specified and we didn't specify
- # a results file, we should use stdout.
- if config.formatter_name is not None and results_file_object is None:
- # Use stdout.
- results_file_object = sys.stdout
- cleanup_func = None
-
- if results_file_object:
- # We care about the formatter. Choose user-specified or, if
- # none specified, use the default for the output type.
- if config.formatter_name:
- formatter_name = config.formatter_name
- else:
- formatter_name = default_formatter_name
-
- # Create an instance of the class.
- # First figure out the package/module.
- components = formatter_name.split(".")
- module = importlib.import_module(".".join(components[:-1]))
-
- # Create the class name we need to load.
- cls = getattr(module, components[-1])
-
- # Handle formatter options for the results formatter class.
- formatter_arg_parser = cls.arg_parser()
- if config.formatter_options and len(config.formatter_options) > 0:
- command_line_options = config.formatter_options
- else:
- command_line_options = []
-
- formatter_options = formatter_arg_parser.parse_args(
- command_line_options)
-
- # Create the TestResultsFormatter given the processed options.
- results_formatter_object = cls(results_file_object, formatter_options)
-
- def shutdown_formatter():
- """Shuts down the formatter when it is no longer needed."""
- # Tell the formatter to write out anything it may have
- # been saving until the very end (e.g. xUnit results
- # can't complete its output until this point).
- results_formatter_object.send_terminate_as_needed()
-
- # And now close out the output file-like object.
- if cleanup_func is not None:
- cleanup_func()
-
- return CreatedFormatter(
- results_formatter_object,
- shutdown_formatter)
- else:
- return None
-
-
-class EventBuilder(object):
- """Helper class to build test result event dictionaries."""
-
- BASE_DICTIONARY = None
-
- # Test Event Types
- TYPE_JOB_RESULT = "job_result"
- TYPE_TEST_RESULT = "test_result"
- TYPE_TEST_START = "test_start"
- TYPE_MARK_TEST_RERUN_ELIGIBLE = "test_eligible_for_rerun"
- TYPE_MARK_TEST_EXPECTED_FAILURE = "test_expected_failure"
- TYPE_SESSION_TERMINATE = "terminate"
-
- RESULT_TYPES = set([
- TYPE_JOB_RESULT,
- TYPE_TEST_RESULT
- ])
-
- # Test/Job Status Tags
- STATUS_EXCEPTIONAL_EXIT = "exceptional_exit"
- STATUS_SUCCESS = "success"
- STATUS_FAILURE = "failure"
- STATUS_EXPECTED_FAILURE = "expected_failure"
- STATUS_EXPECTED_TIMEOUT = "expected_timeout"
- STATUS_UNEXPECTED_SUCCESS = "unexpected_success"
- STATUS_SKIP = "skip"
- STATUS_ERROR = "error"
- STATUS_TIMEOUT = "timeout"
-
- """Test methods or jobs with a status matching any of these
- status values will cause a testrun failure, unless
- the test methods rerun and do not trigger an issue when rerun."""
- TESTRUN_ERROR_STATUS_VALUES = set([
- STATUS_ERROR,
- STATUS_EXCEPTIONAL_EXIT,
- STATUS_FAILURE,
- STATUS_TIMEOUT
- ])
-
- @staticmethod
- def _get_test_name_info(test):
- """Returns (test-class-name, test-method-name) from a test case instance.
-
- @param test a unittest.TestCase instance.
-
- @return tuple containing (test class name, test method name)
- """
- test_class_components = test.id().split(".")
- test_class_name = ".".join(test_class_components[:-1])
- test_name = test_class_components[-1]
- return (test_class_name, test_name)
-
- @staticmethod
- def bare_event(event_type):
- """Creates an event with default additions, event type and timestamp.
-
- @param event_type the value set for the "event" key, used
- to distinguish events.
-
- @returns an event dictionary with all default additions, the "event"
- key set to the passed in event_type, and the event_time value set to
- time.time().
- """
- if EventBuilder.BASE_DICTIONARY is not None:
- # Start with a copy of the "always include" entries.
- event = dict(EventBuilder.BASE_DICTIONARY)
- else:
- event = {}
-
- event.update({
- "event": event_type,
- "event_time": time.time()
- })
- return event
-
- @staticmethod
- def _assert_is_python_sourcefile(test_filename):
- if test_filename is not None:
- if not test_filename.endswith(".py"):
- raise Exception("source python filename has unexpected extension: {}".format(test_filename))
- return test_filename
-
- @staticmethod
- def _event_dictionary_common(test, event_type):
- """Returns an event dictionary setup with values for the given event type.
-
- @param test the unittest.TestCase instance
-
- @param event_type the name of the event type (string).
-
- @return event dictionary with common event fields set.
- """
- test_class_name, test_name = EventBuilder._get_test_name_info(test)
-
- # Determine the filename for the test case. If there is an attribute
- # for it, use it. Otherwise, determine from the TestCase class path.
- if hasattr(test, "test_filename"):
- test_filename = EventBuilder._assert_is_python_sourcefile(test.test_filename)
- else:
- test_filename = EventBuilder._assert_is_python_sourcefile(inspect.getsourcefile(test.__class__))
-
- event = EventBuilder.bare_event(event_type)
- event.update({
- "test_class": test_class_name,
- "test_name": test_name,
- "test_filename": test_filename
- })
-
- return event
-
- @staticmethod
- def _error_tuple_class(error_tuple):
- """Returns the unittest error tuple's error class as a string.
-
- @param error_tuple the error tuple provided by the test framework.
-
- @return the error type (typically an exception) raised by the
- test framework.
- """
- type_var = error_tuple[0]
- module = inspect.getmodule(type_var)
- if module:
- return "{}.{}".format(module.__name__, type_var.__name__)
- else:
- return type_var.__name__
-
- @staticmethod
- def _error_tuple_message(error_tuple):
- """Returns the unittest error tuple's error message.
-
- @param error_tuple the error tuple provided by the test framework.
-
- @return the error message provided by the test framework.
- """
- return str(error_tuple[1])
-
- @staticmethod
- def _error_tuple_traceback(error_tuple):
- """Returns the unittest error tuple's error message.
-
- @param error_tuple the error tuple provided by the test framework.
-
- @return the error message provided by the test framework.
- """
- return error_tuple[2]
-
- @staticmethod
- def _event_dictionary_test_result(test, status):
- """Returns an event dictionary with common test result fields set.
-
- @param test a unittest.TestCase instance.
-
- @param status the status/result of the test
- (e.g. "success", "failure", etc.)
-
- @return the event dictionary
- """
- event = EventBuilder._event_dictionary_common(
- test, EventBuilder.TYPE_TEST_RESULT)
- event["status"] = status
- return event
-
- @staticmethod
- def _event_dictionary_issue(test, status, error_tuple):
- """Returns an event dictionary with common issue-containing test result
- fields set.
-
- @param test a unittest.TestCase instance.
-
- @param status the status/result of the test
- (e.g. "success", "failure", etc.)
-
- @param error_tuple the error tuple as reported by the test runner.
- This is of the form (type<error>, error).
-
- @return the event dictionary
- """
- event = EventBuilder._event_dictionary_test_result(test, status)
- event["issue_class"] = EventBuilder._error_tuple_class(error_tuple)
- event["issue_message"] = EventBuilder._error_tuple_message(error_tuple)
- backtrace = EventBuilder._error_tuple_traceback(error_tuple)
- if backtrace is not None:
- event["issue_backtrace"] = traceback.format_tb(backtrace)
- return event
-
- @staticmethod
- def event_for_start(test):
- """Returns an event dictionary for the test start event.
-
- @param test a unittest.TestCase instance.
-
- @return the event dictionary
- """
- return EventBuilder._event_dictionary_common(
- test, EventBuilder.TYPE_TEST_START)
-
- @staticmethod
- def event_for_success(test):
- """Returns an event dictionary for a successful test.
-
- @param test a unittest.TestCase instance.
-
- @return the event dictionary
- """
- return EventBuilder._event_dictionary_test_result(
- test, EventBuilder.STATUS_SUCCESS)
-
- @staticmethod
- def event_for_unexpected_success(test, bugnumber):
- """Returns an event dictionary for a test that succeeded but was
- expected to fail.
-
- @param test a unittest.TestCase instance.
-
- @param bugnumber the issue identifier for the bug tracking the
- fix request for the test expected to fail (but is in fact
- passing here).
-
- @return the event dictionary
-
- """
- event = EventBuilder._event_dictionary_test_result(
- test, EventBuilder.STATUS_UNEXPECTED_SUCCESS)
- if bugnumber:
- event["bugnumber"] = str(bugnumber)
- return event
-
- @staticmethod
- def event_for_failure(test, error_tuple):
- """Returns an event dictionary for a test that failed.
-
- @param test a unittest.TestCase instance.
-
- @param error_tuple the error tuple as reported by the test runner.
- This is of the form (type<error>, error).
-
- @return the event dictionary
- """
- return EventBuilder._event_dictionary_issue(
- test, EventBuilder.STATUS_FAILURE, error_tuple)
-
- @staticmethod
- def event_for_expected_failure(test, error_tuple, bugnumber):
- """Returns an event dictionary for a test that failed as expected.
-
- @param test a unittest.TestCase instance.
-
- @param error_tuple the error tuple as reported by the test runner.
- This is of the form (type<error>, error).
-
- @param bugnumber the issue identifier for the bug tracking the
- fix request for the test expected to fail.
-
- @return the event dictionary
-
- """
- event = EventBuilder._event_dictionary_issue(
- test, EventBuilder.STATUS_EXPECTED_FAILURE, error_tuple)
- if bugnumber:
- event["bugnumber"] = str(bugnumber)
- return event
-
- @staticmethod
- def event_for_skip(test, reason):
- """Returns an event dictionary for a test that was skipped.
-
- @param test a unittest.TestCase instance.
-
- @param reason the reason why the test is being skipped.
-
- @return the event dictionary
- """
- event = EventBuilder._event_dictionary_test_result(
- test, EventBuilder.STATUS_SKIP)
- event["skip_reason"] = reason
- return event
-
- @staticmethod
- def event_for_error(test, error_tuple):
- """Returns an event dictionary for a test that hit a test execution error.
-
- @param test a unittest.TestCase instance.
-
- @param error_tuple the error tuple as reported by the test runner.
- This is of the form (type<error>, error).
-
- @return the event dictionary
- """
- return EventBuilder._event_dictionary_issue(
- test, EventBuilder.STATUS_ERROR, error_tuple)
-
- @staticmethod
- def event_for_cleanup_error(test, error_tuple):
- """Returns an event dictionary for a test that hit a test execution error
- during the test cleanup phase.
-
- @param test a unittest.TestCase instance.
-
- @param error_tuple the error tuple as reported by the test runner.
- This is of the form (type<error>, error).
-
- @return the event dictionary
- """
- event = EventBuilder._event_dictionary_issue(
- test, EventBuilder.STATUS_ERROR, error_tuple)
- event["issue_phase"] = "cleanup"
- return event
-
- @staticmethod
- def event_for_job_exceptional_exit(
- pid, worker_index, exception_code, exception_description,
- test_filename, command_line):
- """Creates an event for a job (i.e. process) exit due to signal.
-
- @param pid the process id for the job that failed
- @param worker_index optional id for the job queue running the process
- @param exception_code optional code
- (e.g. SIGTERM integer signal number)
- @param exception_description optional string containing symbolic
- representation of the issue (e.g. "SIGTERM")
- @param test_filename the path to the test filename that exited
- in some exceptional way.
- @param command_line the Popen-style list provided as the command line
- for the process that timed out.
-
- @return an event dictionary coding the job completion description.
- """
- event = EventBuilder.bare_event(EventBuilder.TYPE_JOB_RESULT)
- event["status"] = EventBuilder.STATUS_EXCEPTIONAL_EXIT
- if pid is not None:
- event["pid"] = pid
- if worker_index is not None:
- event["worker_index"] = int(worker_index)
- if exception_code is not None:
- event["exception_code"] = exception_code
- if exception_description is not None:
- event["exception_description"] = exception_description
- if test_filename is not None:
- event["test_filename"] = EventBuilder._assert_is_python_sourcefile(test_filename)
- if command_line is not None:
- event["command_line"] = command_line
- return event
-
- @staticmethod
- def event_for_job_timeout(pid, worker_index, test_filename, command_line):
- """Creates an event for a job (i.e. process) timeout.
-
- @param pid the process id for the job that timed out
- @param worker_index optional id for the job queue running the process
- @param test_filename the path to the test filename that timed out.
- @param command_line the Popen-style list provided as the command line
- for the process that timed out.
-
- @return an event dictionary coding the job completion description.
- """
- event = EventBuilder.bare_event(EventBuilder.TYPE_JOB_RESULT)
- event["status"] = "timeout"
- if pid is not None:
- event["pid"] = pid
- if worker_index is not None:
- event["worker_index"] = int(worker_index)
- if test_filename is not None:
- event["test_filename"] = EventBuilder._assert_is_python_sourcefile(test_filename)
- if command_line is not None:
- event["command_line"] = command_line
- return event
-
- @staticmethod
- def event_for_mark_test_rerun_eligible(test):
- """Creates an event that indicates the specified test is explicitly
- eligible for rerun.
-
- Note there is a mode that will enable test rerun eligibility at the
- global level. These markings for explicit rerun eligibility are
- intended for the mode of running where only explicitly rerunnable
- tests are rerun upon hitting an issue.
-
- @param test the TestCase instance to which this pertains.
-
- @return an event that specifies the given test as being eligible to
- be rerun.
- """
- event = EventBuilder._event_dictionary_common(
- test,
- EventBuilder.TYPE_MARK_TEST_RERUN_ELIGIBLE)
- return event
-
- @staticmethod
- def event_for_mark_test_expected_failure(test):
- """Creates an event that indicates the specified test is expected
- to fail.
-
- @param test the TestCase instance to which this pertains.
-
- @return an event that specifies the given test is expected to fail.
- """
- event = EventBuilder._event_dictionary_common(
- test,
- EventBuilder.TYPE_MARK_TEST_EXPECTED_FAILURE)
- return event
-
- @staticmethod
- def add_entries_to_all_events(entries_dict):
- """Specifies a dictionary of entries to add to all test events.
-
- This provides a mechanism for, say, a parallel test runner to
- indicate to each inferior dotest.py that it should add a
- worker index to each.
-
- Calling this method replaces all previous entries added
- by a prior call to this.
-
- Event build methods will overwrite any entries that collide.
- Thus, the passed in dictionary is the base, which gets merged
- over by event building when keys collide.
-
- @param entries_dict a dictionary containing key and value
- pairs that should be merged into all events created by the
- event generator. May be None to clear out any extra entries.
- """
- EventBuilder.BASE_DICTIONARY = dict(entries_dict)
-
-
class ResultsFormatter(object):
"""Provides interface to formatting test results out to a file-like object.
- This class allows the LLDB test framework's raw test-realted
+ This class allows the LLDB test framework's raw test-related
events to be processed and formatted in any manner desired.
Test events are represented by python dictionaries, formatted
as in the EventBuilder class above.
@@ -609,7 +46,7 @@ class ResultsFormatter(object):
# passed to dotest.py via the "--results-formatter-options"
# argument. See the help on that for syntactic requirements
# on getting that parsed correctly.
- formatter = SomeResultFormatter(file_like_object, argpared_options_dict)
+ formatter = SomeResultFormatter(file_like_object, argparse_options_dict)
# Single call to session start, before parsing any events.
formatter.begin_session()
@@ -625,7 +62,7 @@ class ResultsFormatter(object):
for event in zero_or_more_test_events():
formatter.handle_event(event)
- # Single call to terminate/wrap-up. Formatters that need all the
+ # Single call to terminate/wrap-up. For formatters that need all the
# data before they can print a correct result (e.g. xUnit/JUnit),
# this is where the final report can be generated.
formatter.handle_event({"event":"terminate",...})
@@ -755,6 +192,8 @@ class ResultsFormatter(object):
if "test_filename" in result_event:
key = result_event["test_filename"]
component_count += 1
+ else:
+ key = "<no_filename>"
if "test_class" in result_event:
if component_count > 0:
key += ":"
@@ -1010,6 +449,7 @@ class ResultsFormatter(object):
# Derived classes may require self access
# pylint: disable=no-self-use
+ # noinspection PyMethodMayBeStatic,PyMethodMayBeStatic
def replaces_summary(self):
"""Returns whether the results formatter includes a summary
suitable to replace the old lldb test run results.
@@ -1070,7 +510,8 @@ class ResultsFormatter(object):
key=lambda x: self._event_sort_key(x[1]))
return partitioned_events
- def _print_banner(self, out_file, banner_text):
+ @staticmethod
+ def _print_banner(out_file, banner_text):
"""Prints an ASCII banner around given text.
Output goes to the out file for the results formatter.
@@ -1161,7 +602,8 @@ class ResultsFormatter(object):
# details.
return False
- def _report_category_details(self, out_file, category, result_events_by_status):
+ @staticmethod
+ def _report_category_details(out_file, category, result_events_by_status):
"""Reports all test results matching the given category spec.
@param out_file a file-like object used to print output.
@@ -1266,58 +708,9 @@ class ResultsFormatter(object):
if self.options.dump_results:
# Debug dump of the key/result info for all categories.
- self._print_banner("Results Dump")
+ self._print_banner(out_file, "Results Dump")
for status, events_by_key in result_events_by_status.items():
out_file.write("\nSTATUS: {}\n".format(status))
for key, event in events_by_key:
out_file.write("key: {}\n".format(key))
out_file.write("event: {}\n".format(event))
-
-
-class RawPickledFormatter(ResultsFormatter):
- """Formats events as a pickled stream.
-
- The parallel test runner has inferiors pickle their results and send them
- over a socket back to the parallel test. The parallel test runner then
- aggregates them into the final results formatter (e.g. xUnit).
- """
-
- @classmethod
- def arg_parser(cls):
- """@return arg parser used to parse formatter-specific options."""
- parser = super(RawPickledFormatter, cls).arg_parser()
- return parser
-
- def __init__(self, out_file, options):
- super(RawPickledFormatter, self).__init__(out_file, options)
- self.pid = os.getpid()
-
- def handle_event(self, test_event):
- super(RawPickledFormatter, self).handle_event(test_event)
-
- # Convert initialize/terminate events into job_begin/job_end events.
- event_type = test_event["event"]
- if event_type is None:
- return
-
- if event_type == "initialize":
- test_event["event"] = "job_begin"
- elif event_type == "terminate":
- test_event["event"] = "job_end"
-
- # Tack on the pid.
- test_event["pid"] = self.pid
-
- # Send it as {serialized_length_of_serialized_bytes}{serialized_bytes}
- import struct
- msg = cPickle.dumps(test_event)
- packet = struct.pack("!I%ds" % len(msg), len(msg), msg)
- self.out_file.send(packet)
-
-
-class DumpFormatter(ResultsFormatter):
- """Formats events to the file as their raw python dictionary format."""
-
- def handle_event(self, test_event):
- super(DumpFormatter, self).handle_event(test_event)
- self.out_file.write("\n" + pprint.pformat(test_event) + "\n")
diff --git a/lldb/packages/Python/lldbsuite/test/xunit_formatter.py b/lldb/packages/Python/lldbsuite/test_event/formatter/xunit.py
index 41eac97b5c8..02a49ed0834 100644
--- a/lldb/packages/Python/lldbsuite/test/xunit_formatter.py
+++ b/lldb/packages/Python/lldbsuite/test_event/formatter/xunit.py
@@ -8,8 +8,8 @@ Provides an xUnit ResultsFormatter for integrating the LLDB
test suite with the Jenkins xUnit aggregator and other xUnit-compliant
test output processors.
"""
-from __future__ import print_function
from __future__ import absolute_import
+from __future__ import print_function
# System modules
import re
@@ -20,8 +20,8 @@ import xml.sax.saxutils
import six
# Local modules
-from .result_formatter import EventBuilder
-from .result_formatter import ResultsFormatter
+from ..event_builder import EventBuilder
+from .results_formatter import ResultsFormatter
class XunitFormatter(ResultsFormatter):
@@ -36,10 +36,10 @@ class XunitFormatter(ResultsFormatter):
@staticmethod
def _build_illegal_xml_regex():
- """Contructs a regex to match all illegal xml characters.
+ """Constructs a regex to match all illegal xml characters.
Expects to be used against a unicode string."""
- # Construct the range pairs of invalid unicode chareacters.
+ # Construct the range pairs of invalid unicode characters.
illegal_chars_u = [
(0x00, 0x08), (0x0B, 0x0C), (0x0E, 0x1F), (0x7F, 0x84),
(0x86, 0x9F), (0xFDD0, 0xFDDF), (0xFFFE, 0xFFFF)]
@@ -139,10 +139,10 @@ class XunitFormatter(ResultsFormatter):
@staticmethod
def _build_regex_list_from_patterns(patterns):
- """Builds a list of compiled regexes from option value.
+ """Builds a list of compiled regular expressions from option value.
- @param option string containing a comma-separated list of regex
- patterns. Zero-length or None will produce an empty regex list.
+ @param patterns contains a list of regular expression
+ patterns.
@return list of compiled regular expressions, empty if no
patterns provided.
@@ -156,7 +156,7 @@ class XunitFormatter(ResultsFormatter):
def __init__(self, out_file, options):
"""Initializes the XunitFormatter instance.
@param out_file file-like object where formatted output is written.
- @param options_dict specifies a dictionary of options for the
+ @param options specifies a dictionary of options for the
formatter.
"""
# Initialize the parent
@@ -198,9 +198,7 @@ class XunitFormatter(ResultsFormatter):
self._handle_timeout
}
- RESULT_TYPES = set(
- [EventBuilder.TYPE_TEST_RESULT,
- EventBuilder.TYPE_JOB_RESULT])
+ RESULT_TYPES = {EventBuilder.TYPE_TEST_RESULT, EventBuilder.TYPE_JOB_RESULT}
def handle_event(self, test_event):
super(XunitFormatter, self).handle_event(test_event)
@@ -401,7 +399,8 @@ class XunitFormatter(ResultsFormatter):
raise Exception(
"unknown xfail option: {}".format(self.options.xfail))
- def _handle_expected_timeout(self, test_event):
+ @staticmethod
+ def _handle_expected_timeout(test_event):
"""Handles expected_timeout.
@param test_event the test event to handle.
"""
@@ -418,7 +417,7 @@ class XunitFormatter(ResultsFormatter):
# test results viewer.
result = self._common_add_testcase_entry(
test_event,
- inner_content=("<unexpected-success />"))
+ inner_content="<unexpected-success />")
with self.lock:
self.elements["unexpected_successes"].append(result)
elif self.options.xpass == XunitFormatter.RM_SUCCESS:
@@ -519,7 +518,7 @@ class XunitFormatter(ResultsFormatter):
xUnit output is in XML. The reporting system cannot complete the
formatting of the output without knowing when there is no more input.
- This call addresses notifcation of the completed test run and thus is
+ This call addresses notification of the completed test run and thus is
when we can finish off the report output.
"""
OpenPOWER on IntegriCloud