23 from pathlib
import Path
29 from robot.utils import (abspath, create_destination_directory, escape, format_time,
30 get_link_path, html_escape, is_list_like, plural_or_not
as s,
31 seq2str, split_args_from_name_or_path)
33 from .gatherfailed
import gather_failed_tests, gather_failed_suites
34 from .languages
import Languages
41 _cli_opts = {
'RPA' : (
'rpa',
None),
42 'Name' : (
'name',
None),
43 'Doc' : (
'doc',
None),
44 'Metadata' : (
'metadata', []),
45 'TestNames' : (
'test', []),
46 'TaskNames' : (
'task', []),
47 'SuiteNames' : (
'suite', []),
48 'SetTag' : (
'settag', []),
49 'Include' : (
'include', []),
50 'Exclude' : (
'exclude', []),
51 'OutputDir' : (
'outputdir',
abspath(
'.')),
52 'Log' : (
'log',
'log.html'),
53 'Report' : (
'report',
'report.html'),
54 'XUnit' : (
'xunit',
None),
55 'SplitLog' : (
'splitlog',
False),
56 'TimestampOutputs' : (
'timestampoutputs',
False),
57 'LogTitle' : (
'logtitle',
None),
58 'ReportTitle' : (
'reporttitle',
None),
59 'ReportBackground' : (
'reportbackground',
60 (
'#9e9',
'#f66',
'#fed84f')),
61 'SuiteStatLevel' : (
'suitestatlevel', -1),
62 'TagStatInclude' : (
'tagstatinclude', []),
63 'TagStatExclude' : (
'tagstatexclude', []),
64 'TagStatCombine' : (
'tagstatcombine', []),
65 'TagDoc' : (
'tagdoc', []),
66 'TagStatLink' : (
'tagstatlink', []),
67 'RemoveKeywords' : (
'removekeywords', []),
68 'ExpandKeywords' : (
'expandkeywords', []),
69 'FlattenKeywords' : (
'flattenkeywords', []),
70 'PreRebotModifiers': (
'prerebotmodifier', []),
71 'StatusRC' : (
'statusrc',
True),
72 'ConsoleColors' : (
'consolecolors',
'AUTO'),
73 'PythonPath' : (
'pythonpath', []),
74 'StdOut' : (
'stdout',
None),
75 'StdErr' : (
'stderr',
None)}
79 _output_opts = [
'Output',
'Log',
'Report',
'XUnit',
'DebugFile']
81 def __init__(self, options=None, **extra_options):
90 value = opts.pop(cli_name)
if cli_name
in opts
else default
91 if isinstance(default, list):
96 raise DataError(f
'Invalid option{s(opts)} {seq2str(opts)}.')
100 raise KeyError(f
"Non-existing option '{name}'.")
101 self.
_opts_opts[name] = value
104 if name ==
'LogLevel':
110 if name ==
'Metadata':
114 if name
in [
'Include',
'Exclude']:
116 if name
in self.
_output_opts_output_opts
or name
in [
'ReRunFailed',
'ReRunFailedSuites']:
117 if isinstance(value, Path):
119 return value
if value
and value.upper() !=
'NONE' else None
120 if name ==
'OutputDir':
122 if name
in [
'SuiteStatLevel',
'ConsoleWidth']:
124 if name ==
'VariableFiles':
126 if name ==
'ReportBackground':
128 if name ==
'TagStatCombine':
130 if name ==
'TagStatLink':
132 if name ==
'Randomize':
134 if name ==
'MaxErrorLines':
136 if name ==
'MaxAssignLength':
138 if name ==
'PythonPath':
140 if name ==
'RemoveKeywords':
142 if name ==
'FlattenKeywords':
144 if name ==
'ExpandKeywords':
146 if name ==
'Extension':
147 return tuple(ext.lower().lstrip(
'.')
for ext
in value.split(
':'))
151 if isinstance(value, Path)
or (os.path.isfile(value)
and value.strip() == value):
153 with open(value)
as f:
155 except (OSError, IOError)
as err:
156 self.
_raise_invalid_raise_invalid(
'Doc', f
"Reading documentation from '{value}' "
165 self.
_opts_opts[
'VisibleLogLevel'] = visible_level
170 log_level, visible_level = level.split(
':', 1)
172 log_level = visible_level = level
173 for level
in log_level, visible_level:
174 if level
not in loggerhelper.LEVELS:
175 self.
_raise_invalid_raise_invalid(
'LogLevel', f
"Invalid level '{level}'.")
176 if not loggerhelper.IsLogged(log_level)(visible_level):
177 self.
_raise_invalid_raise_invalid(
'LogLevel', f
"Level in log '{visible_level}' is lower "
178 f
"than execution level '{log_level}'.")
179 return log_level, visible_level
182 if not value
or value.upper() ==
'NONE':
187 f
"Expected integer greater than 10, got {value}.")
195 value = original.upper()
197 value, seed = value.split(
':', 1)
199 seed = random.randint(0, sys.maxsize)
200 if value
in (
'TEST',
'SUITE'):
202 valid = (
'TESTS',
'SUITES',
'ALL',
'NONE')
203 if value
not in valid:
204 valid =
seq2str(valid, lastsep=
' or ')
205 self.
_raise_invalid_raise_invalid(
'Randomize', f
"Expected {valid}, got '{value}'.")
209 self.
_raise_invalid_raise_invalid(
'Randomize', f
"Seed should be integer, got '{seed}'.")
213 if name
not in self.
_opts_opts:
214 raise KeyError(f
"Non-existing option '{name}'.")
217 return self.
_opts_opts[name]
226 name = self.
_opts_opts[option]
229 if option ==
'Log' and self._output_disabled():
231 LOGGER.error(
'Log file cannot be created if output.xml is disabled.')
234 path =
abspath(os.path.join(self[
'OutputDir'], name))
239 base, ext = os.path.splitext(name)
240 if self[
'TimestampOutputs']:
241 base = f
'{base}-{self.start_timestamp}'
248 if file_type
in [
'Output',
'XUnit']:
250 if file_type
in [
'Log',
'Report']:
252 if file_type ==
'DebugFile':
262 return value.split(
':', 1)
269 if colors.count(
':')
not in [1, 2]:
270 self.
_raise_invalid_raise_invalid(
'ReportBackground', f
"Expected format 'pass:fail:skip' "
271 f
"or 'pass:fail', got '{colors}'.")
272 colors = colors.split(
':')
274 return colors[0], colors[1],
'#fed84f'
279 pattern, title = pattern.rsplit(
':', 1)
285 for search, replace
in [(
'&',
'AND'), (
'AND',
' AND '), (
'OR',
' OR '),
286 (
'NOT',
' NOT '), (
'_',
' ')]:
287 if search
in pattern:
288 pattern = pattern.replace(search, replace)
289 while ' ' in pattern:
290 pattern = pattern.replace(
' ',
' ')
291 if pattern.startswith(
' NOT'):
292 pattern = pattern[1:]
296 tokens = value.split(
':')
298 return tokens[0],
':'.join(tokens[1:-1]), tokens[-1]
300 f
"Expected format 'tag:link:title', got '{value}'.")
310 self.
_raise_invalid_raise_invalid(name, f
"Expected integer, got '{value}'.")
316 return [os.path.abspath(globbed)
319 for globbed
in glob.glob(split)
or [split]]
322 path = path.replace(
'/', os.sep)
324 yield from path.split(
';')
326 yield from path.split(
':')
329 for item
in path.split(
':'):
331 if item.startswith(
'\\'):
332 yield f
'{drive}:{item}'
337 if len(item) == 1
and item
in string.ascii_letters:
348 except DataError
as err:
354 except DataError
as err:
359 if not opt.lower().startswith((
'name:',
'tag:')):
360 self.
_raise_invalid_raise_invalid(
'ExpandKeywords', f
"Expected 'TAG:<pattern>' or "
361 f
"'NAME:<pattern>', got '{opt}'.")
364 raise DataError(f
"Invalid value for option '--{option.lower()}': {error}")
367 return setting
in self.
_opts_opts
370 return '\n'.join(f
'{name}: {self._opts[name]}' for name
in sorted(self.
_opts_opts))
373 output_directory = property
376 return self[
'OutputDir']
382 return self[
'Output']
394 return self[
'Report']
406 return self[
'LogLevel']
412 return self[
'SplitLog']
415 suite_names = property
421 return [i
for i
in items
if i]
or None
424 test_names = property
427 return self.
_filter_empty_filter_empty(self[
'TestNames'] + self[
'TaskNames'])
442 pythonpath = property
445 return self[
'PythonPath']
451 return self[
'StatusRC']
454 statistics_config = property
458 'suite_stat_level': self[
'SuiteStatLevel'],
459 'tag_stat_include': self[
'TagStatInclude'],
460 'tag_stat_exclude': self[
'TagStatExclude'],
461 'tag_stat_combine': self[
'TagStatCombine'],
462 'tag_stat_link': self[
'TagStatLink'],
463 'tag_doc': self[
'TagDoc'],
467 remove_keywords = property
470 return self[
'RemoveKeywords']
473 flatten_keywords = property
476 return self[
'FlattenKeywords']
479 pre_rebot_modifiers = property
482 return self[
'PreRebotModifiers']
485 console_colors = property
488 return self[
'ConsoleColors']
506 _extra_cli_opts = {
'Extension' : (
'extension', (
'robot',)),
507 'Output' : (
'output',
'output.xml'),
508 'LogLevel' : (
'loglevel',
'INFO'),
509 'MaxErrorLines' : (
'maxerrorlines', 40),
510 'MaxAssignLength' : (
'maxassignlength', 200),
511 'DryRun' : (
'dryrun',
False),
512 'ExitOnFailure' : (
'exitonfailure',
False),
513 'ExitOnError' : (
'exitonerror',
False),
514 'Skip' : (
'skip', []),
515 'SkipOnFailure' : (
'skiponfailure', []),
516 'SkipTeardownOnExit' : (
'skipteardownonexit',
False),
517 'ReRunFailed' : (
'rerunfailed',
None),
518 'ReRunFailedSuites' : (
'rerunfailedsuites',
None),
519 'Randomize' : (
'randomize',
'NONE'),
520 'RunEmptySuite' : (
'runemptysuite',
False),
521 'Variables' : (
'variable', []),
522 'VariableFiles' : (
'variablefile', []),
523 'PreRunModifiers' : (
'prerunmodifier', []),
524 'Listeners' : (
'listener', []),
525 'ConsoleType' : (
'console',
'verbose'),
526 'ConsoleTypeDotted' : (
'dotted',
False),
527 'ConsoleTypeQuiet' : (
'quiet',
False),
528 'ConsoleWidth' : (
'consolewidth', 78),
529 'ConsoleMarkers' : (
'consolemarkers',
'AUTO'),
530 'DebugFile' : (
'debugfile',
None),
531 'Language' : (
'language', [])}
540 not_copied = {
'Include',
'Exclude',
'TestNames',
'SuiteNames',
'Name',
'Doc',
541 'Metadata',
'SetTag',
'Output',
'LogLevel',
'TimestampOutputs'}
542 for opt
in settings._opts:
543 if opt
in self
and opt
not in not_copied:
544 settings._opts[opt] = self[opt]
545 settings._opts[
'ProcessEmptySuite'] = self[
'RunEmptySuite']
558 return self[
'Listeners']
561 debug_file = property
564 return self[
'DebugFile']
573 except DataError
as err:
578 suite_config = property
582 'name': self[
'Name'],
584 'metadata': dict(self[
'Metadata']),
585 'set_tags': self[
'SetTag'],
597 suite_names = property
603 test_names = property
610 names = self[
'TestNames'] + self[
'TaskNames']
613 names = self[
'SuiteNames']
619 return names
or rerun
622 randomize_seed = property
625 return self[
'Randomize'][1]
628 randomize_suites = property
631 return self[
'Randomize'][0]
in (
'SUITES',
'ALL')
634 randomize_tests = property
637 return self[
'Randomize'][0]
in (
'TESTS',
'ALL')
643 return self[
'DryRun']
646 exit_on_failure = property
649 return self[
'ExitOnFailure']
652 exit_on_error = property
655 return self[
'ExitOnError']
664 skipped_tags = property
667 warnings.warn(
"'RobotSettings.skipped_tags' is deprecated. Use 'skip' instead.")
671 skip_on_failure = property
674 return self[
'SkipOnFailure']
677 skip_teardown_on_exit = property
680 return self[
'SkipTeardownOnExit']
683 console_output_config = property
691 'stdout': self[
'StdOut'],
692 'stderr': self[
'StdErr']
696 console_type = property
699 if self[
'ConsoleTypeQuiet']:
701 if self[
'ConsoleTypeDotted']:
703 return self[
'ConsoleType']
706 console_width = property
709 return self[
'ConsoleWidth']
712 console_markers = property
715 return self[
'ConsoleMarkers']
718 max_error_lines = property
721 return self[
'MaxErrorLines']
724 max_assign_length = property
727 return self[
'MaxAssignLength']
730 pre_run_modifiers = property
733 return self[
'PreRunModifiers']
736 run_empty_suite = property
739 return self[
'RunEmptySuite']
745 return self[
'Variables']
748 variable_files = property
751 return self[
'VariableFiles']
757 return self[
'Extension']
764 _extra_cli_opts = {
'Output' : (
'output',
None),
765 'LogLevel' : (
'loglevel',
'TRACE'),
766 'ProcessEmptySuite' : (
'processemptysuite',
False),
767 'StartTime' : (
'starttime',
None),
768 'EndTime' : (
'endtime',
None),
769 'Merge' : (
'merge',
False)}
775 suite_config = property
779 'name': self[
'Name'],
781 'metadata': dict(self[
'Metadata']),
782 'set_tags': self[
'SetTag'],
789 'log_level': self[
'LogLevel'],
790 'start_time': self[
'StartTime'],
791 'end_time': self[
'EndTime']
795 log_config = property
804 'splitLogBase': os.path.basename(os.path.splitext(self.
logloglog)[0]),
805 'defaultLevel': self[
'VisibleLogLevel']
809 report_config = property
827 colors = self[
'ReportBackground']
828 return {
'pass': colors[0],
'fail': colors[1],
'skip': colors[2]}
837 console_output_config = property
842 'stdout': self[
'StdOut'],
843 'stderr': self[
'StdErr']
847 process_empty_suite = property
850 return self[
'ProcessEmptySuite']
853 expand_keywords = property
856 return self[
'ExpandKeywords']
Keeps a list of languages and unifies the translations in the properties.
def process_empty_suite(self)
def _output_disabled(self)
def _resolve_background_colors(self)
def _url_from_path(self, source, destination)
def _output_disabled(self)
def run_empty_suite(self)
def get_rebot_settings(self)
def _names_and_rerun(self, for_test=False)
def randomize_tests(self)
def console_markers(self)
def randomize_suites(self)
def _escape_doc(self, value)
def _raise_invalid(self, option, error)
def _process_metadata(self, value)
def _process_max_assign_length(self, value)
def _process_tagdoc(self, value)
def __getitem__(self, name)
def _get_default_value(self, name)
def _process_doc(self, value)
def _convert_to_integer(self, name, value)
def _process_cli_opts(self, opts)
def _process_pythonpath(self, paths)
def _split_pythonpath(self, path)
def _process_report_background(self, colors)
def _convert_to_positive_integer_or_default(self, name, value)
def __setitem__(self, name, value)
def _validate_expandkeywords(self, values)
def __init__(self, options=None, **extra_options)
def _escape_doc(self, value)
def _process_tag_stat_link(self, value)
def _process_randomize_value(self, original)
def __contains__(self, setting)
def remove_keywords(self)
def _process_tag_stat_combine(self, pattern)
def _validate_flatten_keywords(self, values)
def _process_output_name(self, option, name)
def _filter_empty(self, items)
def _split_log_level(self, level)
def _format_tag_patterns(self, pattern)
def _process_value(self, name, value)
def _process_max_error_lines(self, value)
def _get_output_file(self, option)
Returns path of the requested output file and creates needed dirs.
def _split_from_colon(self, value)
def _get_output_extension(self, extension, file_type)
def _process_log_level(self, level)
def _validate_remove_keywords(self, values)
Can be used when the core framework goes to unexpected state.
def gather_failed_tests(output, empty_suite_ok=False)
def gather_failed_suites(output, empty_suite_ok=False)
def validate_flatten_keyword(options)
def html_escape(text, linkify=True)
def seq2str(sequence, quote="'", sep=', ', lastsep=' and ')
Returns sequence in format ‘'item 1’, 'item 2' and 'item 3'`.
def create_destination_directory(path, usage=None)
def abspath(path, case_normalize=False)
Replacement for os.path.abspath with some enhancements and bug fixes.
def get_link_path(target, base)
Returns a relative path to target from base.
def format_time(timetuple_or_epochsecs, daysep='', daytimesep=' ', timesep=':', millissep=None)
Returns a timestamp formatted from given time using separators.
def split_args_from_name_or_path(name)
Split arguments embedded to name or path like Example:arg1:arg2.