Robot Framework Integrated Development Environment (RIDE)
testdoc.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 # Copyright 2008-2015 Nokia Networks
4 # Copyright 2016- Robot Framework Foundation
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 
18 
31 
32 import os.path
33 import sys
34 import time
35 
36 # Allows running as a script. __name__ check needed with multiprocessing:
37 # https://github.com/robotframework/robotframework/issues/1137
38 if 'robot' not in sys.modules and __name__ == '__main__':
39  import pythonpathsetter
40 
41 from robotide.lib.robot.conf import RobotSettings
42 from robotide.lib.robot.htmldata import HtmlFileWriter, ModelWriter, JsonWriter, TESTDOC
43 from robotide.lib.robot.parsing import disable_curdir_processing
44 from robotide.lib.robot.running import TestSuiteBuilder
45 from robotide.lib.robot.utils import (abspath, Application, file_writer, get_link_path,
46  html_escape, html_format, IRONPYTHON, is_string,
47  PY_VERSION, secs_to_timestr, seq2str2,
48  timestr_to_secs, unescape)
49 
50 
51 # http://ironpython.codeplex.com/workitem/31549
52 if IRONPYTHON and PY_VERSION < (2, 7, 2):
53  int = long
54 
55 
56 USAGE = """robot.testdoc -- Robot Framework test data documentation tool
57 
58 Version: <VERSION>
59 
60 Usage: python -m robot.testdoc [options] data_sources output_file
61 
62 Testdoc generates a high level test documentation based on Robot Framework
63 test data. Generated documentation includes name, documentation and other
64 metadata of each test suite and test case, as well as the top-level keywords
65 and their arguments.
66 
67 Options
68 =======
69 
70  -T --title title Set the title of the generated documentation.
71  Underscores in the title are converted to spaces.
72  The default title is the name of the top level suite.
73  -N --name name Override the name of the top level suite.
74  -D --doc document Override the documentation of the top level suite.
75  -M --metadata name:value * Set/override metadata of the top level suite.
76  -G --settag tag * Set given tag(s) to all test cases.
77  -t --test name * Include tests by name.
78  -s --suite name * Include suites by name.
79  -i --include tag * Include tests by tags.
80  -e --exclude tag * Exclude tests by tags.
81  -A --argumentfile path * Text file to read more arguments from. Use special
82  path `STDIN` to read contents from the standard input
83  stream. File can have both options and data sources
84  one per line. Contents do not need to be escaped but
85  spaces in the beginning and end of lines are removed.
86  Empty lines and lines starting with a hash character
87  (#) are ignored. New in Robot Framework 3.0.2.
88  Example file:
89  | --name Example
90  | # This is a comment line
91  | my_tests.robot
92  | output.html
93  Examples:
94  --argumentfile argfile.txt --argumentfile STDIN
95  -h -? --help Print this help.
96 
97 All options except --title have exactly same semantics as same options have
98 when executing test cases.
99 
100 Execution
101 =========
102 
103 Data can be given as a single file, directory, or as multiple files and
104 directories. In all these cases, the last argument must be the file where
105 to write the output. The output is always created in HTML format.
106 
107 Testdoc works with all interpreters supported by Robot Framework (Python,
108 Jython and IronPython). It can be executed as an installed module like
109 `python -m robot.testdoc` or as a script like `python path/robot/testdoc.py`.
110 
111 Examples:
112 
113  python -m robot.testdoc my_test.html testdoc.html
114  jython -m robot.testdoc -N smoke_tests -i smoke path/to/my_tests smoke.html
115  ipy path/to/robot/testdoc.py first_suite.txt second_suite.txt output.html
116 
117 For more information about Testdoc and other built-in tools, see
118 http://robotframework.org/robotframework/#built-in-tools.
119 """
120 
121 
122 class TestDoc(Application):
123 
124  def __init__(self):
125  Application.__init__(self, USAGE, arg_limits=(2,))
126 
127  def main(self, datasources, title=None, **options):
128  outfile = abspath(datasources.pop())
129  suite = TestSuiteFactory(datasources, **options)
130  self._write_test_doc_write_test_doc(suite, outfile, title)
131  self.console(outfile)
132 
133  def _write_test_doc(self, suite, outfile, title):
134  with file_writer(outfile) as output:
135  model_writer = TestdocModelWriter(output, suite, title)
136  HtmlFileWriter(output, model_writer).write(TESTDOC)
137 
138 
139 @disable_curdir_processing
140 def TestSuiteFactory(datasources, **options):
141  settings = RobotSettings(options)
142  if is_string(datasources):
143  datasources = [datasources]
144  suite = TestSuiteBuilder().build(*datasources)
145  suite.configure(**settings.suite_config)
146  return suite
147 
148 
150 
151  def __init__(self, output, suite, title=None):
152  self._output_output = output
153  self._output_path_output_path = getattr(output, 'name', None)
154  self._suite_suite = suite
155  self._title_title = title.replace('_', ' ') if title else suite.name
156 
157  def write(self, line):
158  self._output_output.write('<script type="text/javascript">\n')
159  self.write_datawrite_data()
160  self._output_output.write('</script>\n')
161 
162  def write_data(self):
163  model = {
164  'suite': JsonConverter(self._output_path_output_path).convert(self._suite_suite),
165  'title': self._title_title,
166  'generated': int(time.time() * 1000)
167  }
168  JsonWriter(self._output_output).write_json('testdoc = ', model)
169 
170 
172 
173  def __init__(self, output_path=None):
174  self._output_path_output_path = output_path
175 
176  def convert(self, suite):
177  return self._convert_suite_convert_suite(suite)
178 
179  def _convert_suite(self, suite):
180  return {
181  'source': suite.source or '',
182  'relativeSource': self._get_relative_source_get_relative_source(suite.source),
183  'id': suite.id,
184  'name': self._escape_escape(suite.name),
185  'fullName': self._escape_escape(suite.longname),
186  'doc': self._html_html(suite.doc),
187  'metadata': [(self._escape_escape(name), self._html_html(value))
188  for name, value in suite.metadata.items()],
189  'numberOfTests': suite.test_count ,
190  'suites': self._convert_suites_convert_suites(suite),
191  'tests': self._convert_tests_convert_tests(suite),
192  'keywords': list(self._convert_keywords_convert_keywords(suite))
193  }
194 
195  def _get_relative_source(self, source):
196  if not source or not self._output_path_output_path:
197  return ''
198  return get_link_path(source, os.path.dirname(self._output_path_output_path))
199 
200  def _escape(self, item):
201  return html_escape(item)
202 
203  def _html(self, item):
204  return html_format(unescape(item))
205 
206  def _convert_suites(self, suite):
207  return [self._convert_suite_convert_suite(s) for s in suite.suites]
208 
209  def _convert_tests(self, suite):
210  return [self._convert_test_convert_test(t) for t in suite.tests]
211 
212  def _convert_test(self, test):
213  return {
214  'name': self._escape_escape(test.name),
215  'fullName': self._escape_escape(test.longname),
216  'id': test.id,
217  'doc': self._html_html(test.doc),
218  'tags': [self._escape_escape(t) for t in test.tags],
219  'timeout': self._get_timeout_get_timeout(test.timeout),
220  'keywords': list(self._convert_keywords_convert_keywords(test))
221  }
222 
223  def _convert_keywords(self, item):
224  for kw in getattr(item, 'keywords', []):
225  if kw.type == kw.SETUP_TYPE:
226  yield self._convert_keyword_convert_keyword(kw, 'SETUP')
227  elif kw.type == kw.TEARDOWN_TYPE:
228  yield self._convert_keyword_convert_keyword(kw, 'TEARDOWN')
229  elif kw.type == kw.FOR_LOOP_TYPE:
230  yield self._convert_for_loop_convert_for_loop(kw)
231  else:
232  yield self._convert_keyword_convert_keyword(kw, 'KEYWORD')
233 
234  def _convert_for_loop(self, kw):
235  return {
236  'name': self._escape_escape(self._get_for_loop_get_for_loop(kw)),
237  'arguments': '',
238  'type': 'FOR'
239  }
240 
241  def _convert_keyword(self, kw, kw_type):
242  return {
243  'name': self._escape_escape(self._get_kw_name_get_kw_name(kw)),
244  'arguments': self._escape_escape(', '.join(kw.args)),
245  'type': kw_type
246  }
247 
248  def _get_kw_name(self, kw):
249  if kw.assign:
250  return '%s = %s' % (', '.join(a.rstrip('= ') for a in kw.assign), kw.name)
251  return kw.name
252 
253  def _get_for_loop(self, kw):
254  joiner = ' %s ' % kw.flavor
255  return ', '.join(kw.variables) + joiner + seq2str2(kw.values)
256 
257  def _get_timeout(self, timeout):
258  if timeout is None:
259  return ''
260  try:
261  tout = secs_to_timestr(timestr_to_secs(timeout.value))
262  except ValueError:
263  tout = timeout.value
264  if timeout.message:
265  tout += ' :: ' + timeout.message
266  return tout
267 
268 
269 
283 def testdoc_cli(arguments):
284  TestDoc().execute_cli(arguments)
285 
286 
287 
298 def testdoc(*arguments, **options):
299  TestDoc().execute(*arguments, **options)
300 
301 
302 if __name__ == '__main__':
303  testdoc_cli(sys.argv[1:])
Creates executable :class:~robot.running.model.TestSuite objects.
Definition: builder.py:36
def _get_relative_source(self, source)
Definition: testdoc.py:195
def __init__(self, output_path=None)
Definition: testdoc.py:173
def _convert_keyword(self, kw, kw_type)
Definition: testdoc.py:241
def main(self, datasources, title=None, **options)
Definition: testdoc.py:127
def _write_test_doc(self, suite, outfile, title)
Definition: testdoc.py:133
def __init__(self, output, suite, title=None)
Definition: testdoc.py:151
def write(msg, level='INFO', html=False)
Writes the message to the log file using the given level.
Definition: logger.py:86
def testdoc_cli(arguments)
Executes Testdoc similarly as from the command line.
Definition: testdoc.py:283
def TestSuiteFactory(datasources, **options)
Definition: testdoc.py:140
def testdoc(*arguments, **options)
Executes Testdoc programmatically.
Definition: testdoc.py:298
def html_escape(text, linkify=True)
Definition: markuputils.py:40
def seq2str2(sequence)
Returns sequence in format [ item 1 | item 2 | ...
Definition: misc.py:126
def file_writer(path=None, encoding='UTF-8', newline=None)
Definition: robotio.py:21
def abspath(path, case_normalize=False)
Replacement for os.path.abspath with some enhancements and bug fixes.
Definition: robotpath.py:87
def get_link_path(target, base)
Returns a relative path to target from base.
Definition: robotpath.py:100
def secs_to_timestr(secs, compact=False)
Converts time in seconds to a string representation.
Definition: robottime.py:126
def timestr_to_secs(timestr, round_to=3)
Parses time like '1h 10s', '01:00:10' or '42' and returns seconds.
Definition: robottime.py:45