# Copyright 2008-2015 Nokia Networks
# Copyright 2016- Robot Framework Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from robot.output import LEVELS
from .jsbuildingcontext import JsBuildingContext
from .jsexecutionresult import JsExecutionResult
[docs]class JsModelBuilder(object):
def __init__(self, log_path=None, split_log=False, expand_keywords=None,
prune_input_to_save_memory=False):
self._context = JsBuildingContext(log_path, split_log, expand_keywords,
prune_input_to_save_memory)
[docs] def build_from(self, result_from_xml):
# Statistics must be build first because building suite may prune input.
return JsExecutionResult(
statistics=StatisticsBuilder().build(result_from_xml.statistics),
suite=SuiteBuilder(self._context).build(result_from_xml.suite),
errors=ErrorsBuilder(self._context).build(result_from_xml.errors),
strings=self._context.strings,
basemillis=self._context.basemillis,
split_results=self._context.split_results,
min_level=self._context.min_level,
expand_keywords=self._context.expand_keywords
)
class _Builder(object):
_statuses = {'FAIL': 0, 'PASS': 1, 'NOT_RUN': 2}
def __init__(self, context):
self._context = context
self._string = self._context.string
self._html = self._context.html
self._timestamp = self._context.timestamp
def _get_status(self, item):
model = (self._statuses[item.status],
self._timestamp(item.starttime),
item.elapsedtime)
msg = getattr(item, 'message', '')
if not msg:
return model
elif msg.startswith('*HTML*'):
msg = self._string(msg[6:].lstrip(), escape=False)
else:
msg = self._string(msg)
return model + (msg,)
def _build_keywords(self, kws, split=False):
splitting = self._context.start_splitting_if_needed(split)
model = tuple(self._build_keyword(k) for k in kws)
return model if not splitting else self._context.end_splitting(model)
[docs]class SuiteBuilder(_Builder):
def __init__(self, context):
_Builder.__init__(self, context)
self._build_suite = self.build
self._build_test = TestBuilder(context).build
self._build_keyword = KeywordBuilder(context).build
[docs] def build(self, suite):
with self._context.prune_input(suite.suites, suite.tests, suite.keywords):
stats = self._get_statistics(suite) # Must be done before pruning
return (self._string(suite.name, attr=True),
self._string(suite.source),
self._context.relative_source(suite.source),
self._html(suite.doc),
tuple(self._yield_metadata(suite)),
self._get_status(suite),
tuple(self._build_suite(s) for s in suite.suites),
tuple(self._build_test(t) for t in suite.tests),
tuple(self._build_keyword(k, split=True) for k in suite.keywords),
stats)
def _yield_metadata(self, suite):
for name, value in suite.metadata.items():
yield self._string(name)
yield self._html(value)
def _get_statistics(self, suite):
stats = suite.statistics # Access property only once
return (stats.all.total,
stats.all.passed,
stats.critical.total,
stats.critical.passed)
[docs]class TestBuilder(_Builder):
def __init__(self, context):
_Builder.__init__(self, context)
self._build_keyword = KeywordBuilder(context).build
[docs] def build(self, test):
with self._context.prune_input(test.keywords):
return (self._string(test.name, attr=True),
self._string(test.timeout),
int(test.critical),
self._html(test.doc),
tuple(self._string(t) for t in test.tags),
self._get_status(test),
self._build_keywords(test.keywords, split=True))
[docs]class KeywordBuilder(_Builder):
_types = {'kw': 0, 'setup': 1, 'teardown': 2, 'for': 3, 'foritem': 4}
def __init__(self, context):
_Builder.__init__(self, context)
self._build_keyword = self.build
self._build_message = MessageBuilder(context).build
[docs] def build(self, kw, split=False):
self._context.check_expansion(kw)
with self._context.prune_input(kw.messages, kw.keywords):
return (self._types[kw.type],
self._string(kw.kwname, attr=True),
self._string(kw.libname, attr=True),
self._string(kw.timeout),
self._html(kw.doc),
self._string(', '.join(kw.args)),
self._string(', '.join(kw.assign)),
self._string(', '.join(kw.tags)),
self._get_status(kw),
self._build_keywords(kw.keywords, split),
tuple(self._build_message(m) for m in kw.messages))
[docs]class MessageBuilder(_Builder):
[docs] def build(self, msg):
if msg.level in ('WARN','ERROR'):
self._context.create_link_target(msg)
self._context.message_level(msg.level)
return self._build(msg)
def _build(self, msg):
return (self._timestamp(msg.timestamp),
LEVELS[msg.level],
self._string(msg.html_message, escape=False))
[docs]class StatisticsBuilder(object):
[docs] def build(self, statistics):
return (self._build_stats(statistics.total),
self._build_stats(statistics.tags),
self._build_stats(statistics.suite, exclude_empty=False))
def _build_stats(self, stats, exclude_empty=True):
return tuple(stat.get_attributes(include_label=True,
include_elapsed=True,
exclude_empty=exclude_empty,
html_escape=True)
for stat in stats)
[docs]class ErrorsBuilder(_Builder):
def __init__(self, context):
_Builder.__init__(self, context)
self._build_message = ErrorMessageBuilder(context).build
[docs] def build(self, errors):
with self._context.prune_input(errors.messages):
return tuple(self._build_message(msg) for msg in errors)
[docs]class ErrorMessageBuilder(MessageBuilder):
[docs] def build(self, msg):
model = self._build(msg)
link = self._context.link(msg)
return model if link is None else model + (link,)