Robot Framework
jsmodelbuilders.py
Go to the documentation of this file.
1 # Copyright 2008-2015 Nokia Networks
2 # Copyright 2016- Robot Framework Foundation
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 from robot.output import LEVELS
17 
18 from .jsbuildingcontext import JsBuildingContext
19 from .jsexecutionresult import JsExecutionResult
20 
21 
22 STATUSES = {'FAIL': 0, 'PASS': 1, 'SKIP': 2, 'NOT RUN': 3}
23 KEYWORD_TYPES = {'KEYWORD': 0, 'SETUP': 1, 'TEARDOWN': 2,
24  'FOR': 3, 'ITERATION': 4,
25  'IF': 5, 'ELSE IF': 6, 'ELSE': 7,
26  'RETURN': 8, 'TRY': 9, 'EXCEPT': 10,
27  'FINALLY': 11, 'WHILE': 12, 'CONTINUE': 13, 'BREAK': 14}
28 
29 
31 
32  def __init__(self, log_path=None, split_log=False, expand_keywords=None,
33  prune_input_to_save_memory=False):
34  self._context_context = JsBuildingContext(log_path, split_log, expand_keywords,
35  prune_input_to_save_memory)
36 
37  def build_from(self, result_from_xml):
38  # Statistics must be build first because building suite may prune input.
39  return JsExecutionResult(
40  statistics=StatisticsBuilder().build(result_from_xml.statistics),
41  suite=SuiteBuilder(self._context_context).build(result_from_xml.suite),
42  errors=ErrorsBuilder(self._context_context).build(result_from_xml.errors),
43  strings=self._context_context.strings,
44  basemillis=self._context_context.basemillis,
45  split_results=self._context_context.split_results,
46  min_level=self._context_context.min_level,
47  expand_keywords=self._context_context.expand_keywords
48  )
49 
50 
51 class _Builder:
52 
53  def __init__(self, context):
54  self._context_context = context
55  self._string_string = self._context_context.string
56  self._html_html = self._context_context.html
57  self._timestamp_timestamp = self._context_context.timestamp
58 
59  def _get_status(self, item):
60  model = (STATUSES[item.status],
61  self._timestamp_timestamp(item.starttime),
62  item.elapsedtime)
63  msg = getattr(item, 'message', '')
64  if not msg:
65  return model
66  elif msg.startswith('*HTML*'):
67  msg = self._string_string(msg[6:].lstrip(), escape=False)
68  else:
69  msg = self._string_string(msg)
70  return model + (msg,)
71 
72  def _build_keywords(self, steps, split=False):
73  splitting = self._context_context.start_splitting_if_needed(split)
74  # tuple([<listcomp>>]) is faster than tuple(<genex>) with short lists.
75  model = tuple([self._build_keyword_build_keyword(step) for step in steps])
76  return model if not splitting else self._context_context.end_splitting(model)
77 
78  def _build_keyword(self, step):
79  raise NotImplementedError
80 
81 
83 
84  def __init__(self, context):
85  _Builder.__init__(self, context)
86  self._build_suite_build_suite = self.buildbuild
87  self._build_test_build_test = TestBuilder(context).build
88  self._build_keyword_build_keyword_build_keyword = KeywordBuilder(context).build
89 
90  def build(self, suite):
91  with self._context_context.prune_input(suite.tests, suite.suites):
92  stats = self._get_statistics_get_statistics(suite) # Must be done before pruning
93  kws = [kw for kw in (suite.setup, suite.teardown) if kw]
94  return (self._string_string(suite.name, attr=True),
95  self._string_string(suite.source),
96  self._context_context.relative_source(suite.source),
97  self._html_html(suite.doc),
98  tuple(self._yield_metadata_yield_metadata(suite)),
99  self._get_status_get_status(suite),
100  tuple(self._build_suite_build_suite(s) for s in suite.suites),
101  tuple(self._build_test_build_test(t) for t in suite.tests),
102  tuple(self._build_keyword_build_keyword_build_keyword(k, split=True) for k in kws),
103  stats)
104 
105  def _yield_metadata(self, suite):
106  for name, value in suite.metadata.items():
107  yield self._string_string(name)
108  yield self._html_html(value)
109 
110  def _get_statistics(self, suite):
111  stats = suite.statistics # Access property only once
112  return (stats.total, stats.passed, stats.failed, stats.skipped)
113 
114 
116 
117  def __init__(self, context):
118  _Builder.__init__(self, context)
119  self._build_keyword_build_keyword_build_keyword = KeywordBuilder(context).build
120 
121  def build(self, test):
122  kws = self._get_keywords_get_keywords(test)
123  with self._context_context.prune_input(test.body):
124  return (self._string_string(test.name, attr=True),
125  self._string_string(test.timeout),
126  self._html_html(test.doc),
127  tuple(self._string_string(t) for t in test.tags),
128  self._get_status_get_status(test),
129  self._build_keywords_build_keywords(kws, split=True))
130 
131  def _get_keywords(self, test):
132  kws = []
133  if test.setup:
134  kws.append(test.setup)
135  kws.extend(test.body.flatten())
136  if test.teardown:
137  kws.append(test.teardown)
138  return kws
139 
140 
142 
143  def __init__(self, context):
144  _Builder.__init__(self, context)
145  self._build_keyword_build_keyword_build_keyword = self.buildbuild
146  self._build_message_build_message = MessageBuilder(context).build
147 
148  def build(self, item, split=False):
149  if item.type == item.MESSAGE:
150  return self._build_message_build_message(item)
151  return self.build_keywordbuild_keyword(item, split)
152 
153  def build_keyword(self, kw, split=False):
154  self._context_context.check_expansion(kw)
155  items = kw.body.flatten()
156  if kw.has_teardown:
157  items.append(kw.teardown)
158  with self._context_context.prune_input(kw.body):
159  return (KEYWORD_TYPES[kw.type],
160  self._string_string(kw.kwname, attr=True),
161  self._string_string(kw.libname, attr=True),
162  self._string_string(kw.timeout),
163  self._html_html(kw.doc),
164  self._string_string(', '.join(kw.args)),
165  self._string_string(', '.join(kw.assign)),
166  self._string_string(', '.join(kw.tags)),
167  self._get_status_get_status(kw),
168  self._build_keywords_build_keywords(items, split))
169 
170 
172 
173  def build(self, msg):
174  if msg.level in ('WARN', 'ERROR'):
175  self._context_context.create_link_target(msg)
176  self._context_context.message_level(msg.level)
177  return self._build_build(msg)
178 
179  def _build(self, msg):
180  return (self._timestamp_timestamp(msg.timestamp),
181  LEVELS[msg.level],
182  self._string_string(msg.html_message, escape=False))
183 
184 
186 
187  def build(self, statistics):
188  return (self._build_stats_build_stats(statistics.total),
189  self._build_stats_build_stats(statistics.tags),
190  self._build_stats_build_stats(statistics.suite, exclude_empty=False))
191 
192  def _build_stats(self, stats, exclude_empty=True):
193  return tuple(stat.get_attributes(include_label=True,
194  include_elapsed=True,
195  exclude_empty=exclude_empty,
196  html_escape=True)
197  for stat in stats)
198 
199 
201 
202  def __init__(self, context):
203  _Builder.__init__(self, context)
204  self._build_message_build_message = ErrorMessageBuilder(context).build
205 
206  def build(self, errors):
207  with self._context_context.prune_input(errors.messages):
208  return tuple(self._build_message_build_message(msg) for msg in errors)
209 
210 
212 
213  def build(self, msg):
214  model = self._build_build(msg)
215  link = self._context_context.link(msg)
216  return model if link is None else model + (link,)
def __init__(self, log_path=None, split_log=False, expand_keywords=None, prune_input_to_save_memory=False)
def _build_stats(self, stats, exclude_empty=True)
def _build_keywords(self, steps, split=False)