Skip to content

Commit

Permalink
Update from upstream
Browse files Browse the repository at this point in the history
* Better Python 3 support
* Reformatting with Black
  • Loading branch information
Aigars Mahinovs authored and Aigars Mahinovs committed Dec 7, 2019
1 parent b97a7ee commit d65834f
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 93 deletions.
52 changes: 31 additions & 21 deletions dltlyse/core/analyser.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@
import os.path
import signal
import sys

from contextlib import contextmanager
import six

from dlt import dlt

from dltlyse.core.report import XUnitReport, Result
from dltlyse.core.plugin_base import Plugin

from dlt import dlt

# pylint: disable= too-many-nested-blocks, no-member

logger = logging.getLogger(__name__)
stdoutlogger = logging.getLogger("summary")
Expand All @@ -25,19 +27,20 @@
DEFAULT_PLUGINS_DIRS = [
os.path.join(os.path.dirname(__file__), "../plugins"), # installation folder
# e.g. /usr/bin/pythonX.X/site-packages/dltlyse/plugins
os.path.join(os.getcwd(), "plugins") # plugins folder in current working directory
os.path.join(os.getcwd(), "plugins"), # plugins folder in current working directory
]

# Traces to buffer since they might be stored before lifecycle start message
buffer_matches = [{"apid": b"DA1", "ctid": b"DC1",
"payload_decoded": b"[connection_info ok] connected \x00\x00\x00\x00"},
{"ecuid": "XORA"}]
buffer_matches = [
{"apid": "DA1", "ctid": "DC1", "payload_decoded": "[connection_info ok] connected \00\00\00\00"},
{"ecuid": "XORA"},
]
MAX_BUFFER_SIZE = 50

DLT_LIFECYCLE_START = {
"apid": b"DLTD",
"ctid": b"INTM",
"payload_decoded": b"Daemon launched. Starting to output traces...",
"apid": "DLTD",
"ctid": "INTM",
"payload_decoded": "Daemon launched. Starting to output traces...",
}


Expand Down Expand Up @@ -130,7 +133,7 @@ def _scan_folder(root, plugin_classes):
return

filenames = os.listdir(root)
if '__NO_PLUGINS__' in filenames: # If the folder hasn't plugins, we skip it.
if "__NO_PLUGINS__" in filenames: # If the folder hasn't plugins, we skip it.
return

sys.path.insert(0, root)
Expand All @@ -148,8 +151,14 @@ def _scan_folder(root, plugin_classes):
module = sys.modules[module_name]
for class_name in dir(module):
cls = getattr(module, class_name)
if hasattr(cls, "__mro__") and issubclass(cls, Plugin) and not cls.__abstractmethods__:
plugin_classes.append(cls)
if six.PY3:
if (hasattr(cls, "__mro__") and issubclass(cls, Plugin) and
(not any(hasattr(getattr(cls, item), "__isabstractmethod__") and
not isinstance(getattr(cls, item), property) for item in dir(cls)))):
plugin_classes.append(cls)
else:
if hasattr(cls, "__mro__") and issubclass(cls, Plugin) and not cls.__abstractmethods__:
plugin_classes.append(cls)
except (ImportError, ValueError):
logger.error("Could not load plugin %s\n%s", module_name, traceback.format_exc())

Expand All @@ -168,6 +177,7 @@ def get_plugin_classes(plugin_dirs): # pylint: disable=too-many-locals

class DLTAnalyser(object):
"""DLT Analyser"""

def __init__(self):
self.plugins = []
self.file_exceptions = {}
Expand All @@ -192,7 +202,7 @@ def load_plugins(self, plugin_dirs, plugins=None, exclude=None, no_default_dir=F
for cls in plugin_classes:
if plugins is None:
if cls.manually_executed and \
os.environ.get("DLTLYSE_ALL_INCLUDES_MANUAL", "false").lower() not in ('1', 'true', 'yes'):
os.environ.get("DLTLYSE_ALL_INCLUDES_MANUAL", "false").lower() not in ('1', 'true', 'yes',):
continue
else:
if not cls.get_plugin_name() in plugins:
Expand Down Expand Up @@ -245,9 +255,9 @@ def process_message(self, message):
"""Pass on the message to plugins that need it"""
for plugin in self.plugins:
if plugin.message_filters == "all" or \
(message.apid, message.ctid) in plugin.message_filters or \
("", message.ctid) in plugin.message_filters or \
(message.apid, "") in plugin.message_filters:
(message.apid, message.ctid) in plugin.message_filters or \
("", message.ctid) in plugin.message_filters or \
(message.apid, "") in plugin.message_filters:
with handle_plugin_exceptions(plugin, "calling"):
plugin(message)

Expand All @@ -267,7 +277,7 @@ def run_analyse(self, traces, xunit, no_sort, is_live, testsuite_name="dltlyse")
filters = self.get_filters()
# add filter for lifecycle start message in case it is missing
# filters == None means no filtering is done at all
flt = (DLT_LIFECYCLE_START["apid"], DLT_LIFECYCLE_START["ctid"])
flt = (DLT_LIFECYCLE_START["apid"].encode("utf-8"), DLT_LIFECYCLE_START["ctid"].encode("utf-8"))
if filters and flt not in filters:
filters.append(flt)

Expand Down Expand Up @@ -362,23 +372,23 @@ def generate_reports(self, xunit, testsuite_name):
output = "Report for file"
if filename in self.file_exceptions:
stdoutlogger.debug(self.file_exceptions[filename])
stdoutlogger.info(output + " %s ... = failed", filename)
stdoutlogger.info("%s %s ... = failed", output, filename)
file_results.append(Result(
classname="DLTAnalyser",
testname="File Sanity Checks During Execution",
state="error",
stdout=self.file_exceptions[filename],
message=self.file_exceptions[filename]
))
))
else:
stdoutlogger.info(output + " %s ... = passed", filename)
stdoutlogger.info("%s %s ... = passed", output, filename)
file_results.append(Result(
classname="DLTAnalyser",
testname="File Sanity Checks During Execution",
state="success",
stdout="File Parsed Successfully",
message="File Parsed Successfully"
))
))

xreport.add_results(file_results)

Expand Down
49 changes: 25 additions & 24 deletions dltlyse/core/plugin_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@

from abc import ABCMeta, abstractmethod
from collections import defaultdict
from six import string_types

from dltlyse.core.report import Result
from dltlyse.core.utils import round_float

# pylint: disable= unsupported-membership-test

EXTRACT_DIR = "extracted_files"
logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -66,9 +70,7 @@ def add_result(self, **kwargs):
except AttributeError:
testname = ""
kwargs.setdefault("testname", testname)
self.__results.append(Result(
**kwargs
))
self.__results.append(Result(**kwargs))

def add_attachments(self, attachments):
"""Adds attachments to the last result, creating a result if none exist"""
Expand Down Expand Up @@ -118,7 +120,7 @@ class CSVPlugin(Plugin): # pylint: disable=abstract-method

# If you have only one file you can use these two lines
csv_filename = None # If you only have one file, you can use this. Set to "subdir/example.csv" in subclass
csv_fields = None # If using only one file, set this to the list of column headers
csv_fields = None # If using only one file, set this to the list of column headers

# If you want to use multiple CSV files, please use csv_filenames and provide columns per file
csv_filenames = None
Expand All @@ -137,30 +139,28 @@ def __init__(self):

def _create_csvfile(self, filename=None):
"""Create csv file and add first row with column names"""
filename = filename or self.csv_filenames.keys()[0]
filename = filename or list(self.csv_filenames)[0]
pathname = os.path.join("extracted_files", filename)
if not os.path.exists(os.path.dirname(pathname)):
os.makedirs(os.path.dirname(pathname))

self._csv_fileobj[filename] = open(pathname, "w")
self._csv[filename] = csv.writer(
self._csv_fileobj[filename],
)
self._csv[filename] = csv.writer(self._csv_fileobj[filename])
if self.csv_filenames[filename]: # Only write header line if columns are defined.
self._csv[filename].writerow(self.csv_filenames[filename])
else:
logger.debug("No header line written to file %s", filename)

def writerow(self, data_row, filename=None):
"""Write a row to CSV file"""
filename = filename or self.csv_filenames.keys()[0]
filename = filename or list(self.csv_filenames)[0]
if filename not in self._csv:
self._create_csvfile(filename)
self._csv[filename].writerow(data_row)

def writerows(self, data_rows, filename=None):
"""Write several rows to csv file"""
filename = filename or self.csv_filenames.keys()[0]
filename = filename or list(self.csv_filenames)[0]
if filename not in self._csv:
self._create_csvfile(filename)
self._csv[filename].writerows(data_rows)
Expand All @@ -172,7 +172,7 @@ def report(self):

def _close_csv_file(self, filename=None):
"""Close CSV file"""
filename = filename or self.csv_filenames.keys()[0]
filename = filename or list(self.csv_filenames)[0]
if self._csv[filename]:
self._csv_fileobj[filename].close()

Expand Down Expand Up @@ -222,8 +222,9 @@ def dlt_callback(app_id=None, ctx_id=None):
app_id(str): if defined, is the app_id that we want to catch.
ctx_id(str): if defined, is the ctx_id that we want to catch.
"""

def wrapper(func): # pylint: disable=missing-docstring
func.filter_condition = app_id or '', ctx_id or ''
func.filter_condition = app_id or "", ctx_id or ""

return func

Expand Down Expand Up @@ -255,6 +256,7 @@ def gather_version_info(self, frame):
Finally, it automatically sets the log level to DEBUG, and creates a logger using the class
name. The logger is available as the logger member.
"""

def __init__(self):
"""Automatically sets a default for report (None -> no report) and logger."""
self.collect_and_register_callbacks()
Expand All @@ -274,14 +276,14 @@ def collect_and_register_callbacks(self):
self.dlt_greedy_callbacks = []
for member_name in dir(self): # Scans the class members.
member = getattr(self, member_name)
filter_condition = getattr(member, 'filter_condition', None)
filter_condition = getattr(member, "filter_condition", None)
if filter_condition:
if filter_condition[0] or filter_condition[1]:
if self.message_filters != 'all':
if self.message_filters != "all":
self.message_filters.append(filter_condition) # pylint: disable=no-member
self.dlt_callbacks[filter_condition].append(member)
else:
self.message_filters = 'all'
self.message_filters = "all"
self.dlt_greedy_callbacks.append(member)

# pylint: disable=invalid-name
Expand All @@ -298,17 +300,17 @@ def add_callback_from_template_function(self, template_function, app_id, ctx_id,
"""
# Data should be converted to strings, since dltlyse fails to register a filter if it's using unicode strings.
app_id, ctx_id, userdata = (str(app_id), str(ctx_id),
str(userdata) if isinstance(userdata, basestring) else userdata)
str(userdata) if isinstance(userdata, string_types) else userdata)

callback = functools.partial(template_function, app_id=app_id, ctx_id=ctx_id, userdata=userdata)
callback = dlt_callback(app_id, ctx_id)(callback)
filter_condition = app_id, ctx_id
if filter_condition[0] or filter_condition[1]:
if self.message_filters != 'all':
if self.message_filters != "all":
self.message_filters.append(filter_condition) # pylint: disable=no-member
self.dlt_callbacks[filter_condition].append(callback)
else:
self.message_filters = 'all'
self.message_filters = "all"
self.dlt_greedy_callbacks.append(callback)

def get_result_dir(self):
Expand All @@ -321,9 +323,9 @@ def get_result_dir(self):
def report_filename(self):
"""Builds & returns a standard/base filename for the report."""
# Converts all uppercase letters in lowercase, pre-pending them with a '_'.
report_filename = re.sub(r'([A-Z])', r'_\1', self.get_plugin_name())
report_filename = re.sub(r"([A-Z])", r"_\1", self.get_plugin_name())

return report_filename.lower().strip('_') + '.txt'
return report_filename.lower().strip("_") + ".txt"

def prepare_report(self):
"""It's invoked just before writing the report to file, in case that some operation needs
Expand All @@ -341,9 +343,8 @@ def get_report(self):
"""
self.prepare_report()
if self.report_output is None:
return 'No report is generated!'
else:
return self.write_to_domain_file(self.report_filename(), str(self.report_output))
return "No report is generated!"
return self.write_to_domain_file(self.report_filename(), str(self.report_output))

def write_to_domain_file(self, filename, report):
"""Write the given report to a file.
Expand All @@ -353,7 +354,7 @@ def write_to_domain_file(self, filename, report):
report(str): the string with the report to be saved.
"""
fullpath = os.path.join(self.get_result_dir(), filename)
with open(fullpath, 'wb') as report_file:
with open(fullpath, "w") as report_file:
report_file.write(report)
self.logger.info("See %s", fullpath)

Expand Down
33 changes: 17 additions & 16 deletions dltlyse/core/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,41 @@
'<?xml version="1.0" encoding="UTF-8"?>'
'<testsuite name="{testsuite_name}" tests="{number_of_tests}" errors="{number_of_errors}" '
'failures="{number_of_failures}" skip="{number_of_skipped}">'
'{testcases}'
'</testsuite>'
"{testcases}"
"</testsuite>"
)

xunit_tc_template = dict(
error=(
'<testcase classname="{classname}" name="{testname}" time="0">'
'<error type="error" message="{message}"></error>'
'<system-out><![CDATA[{stdout}]]></system-out>'
'{attach}'
'</testcase>'
"<system-out><![CDATA[{stdout}]]></system-out>"
"{attach}"
"</testcase>"
),
failure=(
'<testcase classname="{classname}" name="{testname}" time="0">'
'<failure type="failure" message="{message}"></failure>'
'<system-out><![CDATA[{stdout}]]></system-out>'
'{attach}'
'</testcase>'
"<system-out><![CDATA[{stdout}]]></system-out>"
"{attach}"
"</testcase>"
),
skipped=(
'<testcase classname="{classname}" name="{testname}" time="0">'
'<skipped type="skip" message="{message}"></skipped>'
'<system-out><![CDATA[{stdout}]]></system-out>'
'{attach}'
'</testcase>'
"<system-out><![CDATA[{stdout}]]></system-out>"
"{attach}"
"</testcase>"
),
success=(
'<testcase classname="{classname}" name="{testname}" time="0">'
'<system-out><![CDATA[{stdout}]]></system-out>'
'{attach}'
'</testcase>'
"<system-out><![CDATA[{stdout}]]></system-out>"
"{attach}"
"</testcase>"
),
)

attachment_template = ('[[ATTACHMENT|{filename}]]')
attachment_template = "[[ATTACHMENT|{filename}]]"


def xunit_render(result):
Expand All @@ -53,6 +53,7 @@ def xunit_render(result):

class Result(object):
"""Class representing a single testcase result"""

def __init__(self, classname="Unknown", testname="Unknown", state="success", stdout="", stderr="", message="",
attach=None):
self.classname = classname
Expand Down Expand Up @@ -97,4 +98,4 @@ def render(self):
report = xunit_template.format(**kwargs)
if self.outfile:
with open(self.outfile, "w") as reportfile:
reportfile.write(report.encode("utf8"))
reportfile.write(report)
Loading

0 comments on commit d65834f

Please sign in to comment.