<commit_before>#  Copyright 2008-2012 Nokia Siemens Networks Oyj
#
#  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.

import os

from robot import utils
from robot.errors import DataError, FrameworkError
from robot.output import LOGGER, loggerhelper

from .gatherfailed import gather_failed_tests

class _BaseSettings(object):
    _cli_opts = {'Name'             : ('name', None),
                 'Doc'              : ('doc', None),
                 'Metadata'         : ('metadata', []),
                 'TestNames'        : ('test', []),
                 'RunFailed'        : ('runfailed', 'NONE'),
                 'SuiteNames'       : ('suite', []),
                 'SetTag'           : ('settag', []),
                 'Include'          : ('include', []),
                 'Exclude'          : ('exclude', []),
                 'Critical'         : ('critical', None),
                 'NonCritical'      : ('noncritical', None),
                 'OutputDir'        : ('outputdir', utils.abspath('.')),
                 'Log'              : ('log', 'log.html'),
                 'Report'           : ('report', 'report.html'),
                 'XUnitFile'        : ('xunitfile', 'NONE'),
                 'SplitLog'         : ('splitlog', False),
                 'TimestampOutputs' : ('timestampoutputs', False),
                 'LogTitle'         : ('logtitle', None),
                 'ReportTitle'      : ('reporttitle', None),
                 'ReportBackground' : ('reportbackground',
                                       ('#99FF66', '#99FF66', '#FF3333')),
                 'SuiteStatLevel'   : ('suitestatlevel', -1),
                 'TagStatInclude'   : ('tagstatinclude', []),
                 'TagStatExclude'   : ('tagstatexclude', []),
                 'TagStatCombine'   : ('tagstatcombine', []),
                 'TagDoc'           : ('tagdoc', []),
                 'TagStatLink'      : ('tagstatlink', []),
                 'RemoveKeywords'   : ('removekeywords', []),
                 'NoStatusRC'       : ('nostatusrc', False),
                 'MonitorColors'    : ('monitorcolors', 'AUTO'),
                 'StdOut'           : ('stdout', None),
                 'StdErr'           : ('stderr', None)}
    _output_opts = ['Output', 'Log', 'Report', 'DebugFile', 'XUnitFile']

    def __init__(self, options=None, log=True):
        self._opts = {}
        self._cli_opts = self._cli_opts.copy()
        self._cli_opts.update(self._extra_cli_opts)
        self._process_cli_opts(options or {}, log)
        if log: LOGGER.info('Settings:\n%s' % unicode(self))

    def _process_cli_opts(self, opts, log):
        for name, (cli_name, default) in self._cli_opts.items():
            value = opts.get(cli_name, default)
            if value in [None, []]:
                value = default
            elif default == [] and isinstance(value, basestring):
                value = [value]
            self[name] = self._process_value(name, value, log)
        self['TestNames'] += self['RunFailed']

    def __setitem__(self, name, value):
        if name not in self._cli_opts:
            raise KeyError("Non-existing settings '%s'" % name)
        self._opts[name] = value

    def _process_value(self, name, value, log):
        if name == 'RunFailed':
            return gather_failed_tests(value)
        if name == 'LogLevel':
            return self._process_log_level(value)
        if value == self._get_default_value(name):
            return value
        if name in ['Name', 'Doc', 'LogTitle', 'ReportTitle']:
            if name == 'Doc': value = self._escape(value)
            return value.replace('_', ' ')
        if name in ['Metadata', 'TagDoc']:
            if name == 'Metadata': value = [self._escape(v) for v in value]
            return [self._process_metadata_or_tagdoc(v) for v in value]
        if name in ['Include', 'Exclude']:
            return [v.replace('AND', '&').replace('_', ' ') for v in value]
        if name in self._output_opts and utils.eq(value, 'NONE'):
            return 'NONE'
        if name == 'OutputDir':
            return utils.abspath(value)
        if name in ['SuiteStatLevel', 'MonitorWidth']:
            return self._convert_to_positive_integer_or_default(name, value)
        if name in ['Listeners', 'VariableFiles']:
            return [self._split_args_from_name_or_path(item) for item in value]
        if name == 'ReportBackground':
            return self._process_report_background(value)
        if name == 'TagStatCombine':
            return [self._process_tag_stat_combine(v) for v in value]
        if name == 'TagStatLink':
            return [v for v in [self._process_tag_stat_link(v) for v in value] if v]
        if name == 'RemoveKeywords':
            return [v.upper() for v in value]
        return value

    def _process_log_level(self, level):
        level, visible_level = self._split_log_level(level.upper())
        self._opts['VisibleLogLevel'] = visible_level
        return level

    def _split_log_level(self, level):
        if ':' in level:
            level, visible_level = level.split(':', 1)
        else:
            visible_level = level
        self._validate_log_level_and_default(level, visible_level)
        return level, visible_level

    def _validate_log_level_and_default(self, log_level, default):
        if log_level not in loggerhelper.LEVELS:
            raise DataError("Invalid log level '%s'" % log_level)
        if default not in loggerhelper.LEVELS:
            raise DataError("Invalid log level '%s'" % default)
        if not loggerhelper.IsLogged(log_level)(default):
            raise DataError("Default visible log level '%s' is lower than "
                            "log level '%s'" % (default, log_level))

    def __getitem__(self, name):
        if name not in self._opts:
            raise KeyError("Non-existing setting '%s'" % name)
        if name in self._output_opts:
            return self._get_output_file(name)
        return self._opts[name]

    def _get_output_file(self, type_):
        """Returns path of the requested output file and creates needed dirs.

        `type_` can be 'Output', 'Log', 'Report', 'DebugFile' or 'XUnitFile'.
        """
        name = self._opts[type_]
        if self._outputfile_disabled(type_, name):
            return 'NONE'
        name = self._process_output_name(name, type_)
        path = utils.abspath(os.path.join(self['OutputDir'], name))
        self._create_output_dir(os.path.dirname(path), type_)
        return path

    def _process_output_name(self, name, type_):
        base, ext = os.path.splitext(name)
        if self['TimestampOutputs']:
            base = '%s-%s' % (base, utils.get_start_timestamp('', '-', ''))
        ext = self._get_output_extension(ext, type_)
        return base + ext

    def _get_output_extension(self, ext, type_):
        if ext != '':
            return ext
        if type_ in ['Output', 'XUnitFile']:
            return '.xml'
        if type_ in ['Log', 'Report']:
            return '.html'
        if type_ == 'DebugFile':
            return '.txt'
        raise FrameworkError("Invalid output file type: %s" % type_)

    def _create_output_dir(self, path, type_):
        try:
            if not os.path.exists(path):
                os.makedirs(path)
        except EnvironmentError, err:
            raise DataError("Creating %s file directory '%s' failed: %s"
                            % (type_.lower(), path, err.strerror))

    def _process_metadata_or_tagdoc(self, value):
        value = value.replace('_', ' ')
        if ':' in value:
            return value.split(':', 1)
        return value, ''

    def _process_report_background(self, colors):
        if colors.count(':') not in [1, 2]:
            LOGGER.error("Invalid report background colors '%s'." % colors)
            return self._get_default_value('ReportBackground')
        colors = colors.split(':')
        if len(colors) == 2:
            return colors[0], colors[0], colors[1]
        return tuple(colors)

    def _process_tag_stat_combine(self, value):
        for replwhat, replwith in [('&', 'AND'), ('AND', ' AND '), ('NOT', ' NOT ')]:
            value = value.replace(replwhat, replwith)
        if ':' not in value:
            return value, ''
        pattern, title = value.rsplit(':', 1)
        return pattern, title.replace('_', ' ')

    def _process_tag_stat_link(self, value):
        tokens = value.split(':')
        if len(tokens) >= 3:
            return tokens[0], ':'.join(tokens[1:-1]), tokens[-1]
        LOGGER.error("Invalid format for option '--tagstatlink'. "
                     "Expected 'tag:link:title' but got '%s'." % value)
        return None

    def _convert_to_positive_integer_or_default(self, name, value):
        value = self._convert_to_integer(name, value)
        return value if value > 0 else self._get_default_value(name)

    def _convert_to_integer(self, name, value):
        try:
            return int(value)
        except ValueError:
            LOGGER.error("Option '--%s' expected integer value but got '%s'. "
                         "Default value used instead." % (name.lower(), value))
            return self._get_default_value(name)

    def _get_default_value(self, name):
        return self._cli_opts[name][1]

    def _split_args_from_name_or_path(self, name):
        if ':' not in name or os.path.exists(name):
            args = []
        else:
            args = name.split(':')
            name = args.pop(0)
            # Handle absolute Windows paths with arguments
            if len(name) == 1 and args[0].startswith(('/', '\\')):
                name = name + ':' + args.pop(0)
        if os.path.exists(name):
            name = os.path.abspath(name)
        return name, args

    def __contains__(self, setting):
        return setting in self._cli_opts

    def __unicode__(self):
        return '\n'.join('%s: %s' % (name, self._opts[name])
                         for name in sorted(self._opts))

    @property
    def output(self):
        return self._get_file('Output')

    @property
    def log(self):
        return self._get_file('Log')

    @property
    def report(self):
        return self._get_file('Report')

    @property
    def xunit(self):
        return self._get_file('XUnitFile')

    def _get_file(self, name):
        value = self[name]
        return value if value != 'NONE' else None

    @property
    def split_log(self):
        return self['SplitLog']

    @property
    def status_rc(self):
        return not self['NoStatusRC']

class RobotSettings(_BaseSettings):
    _extra_cli_opts = {'Output'        : ('output', 'output.xml'),
                       'LogLevel'      : ('loglevel', 'INFO'),
                       'RunMode'       : ('runmode', []),
                       'RunEmptySuite' : ('runemptysuite', False),
                       'WarnOnSkipped' : ('warnonskippedfiles', False),
                       'Variables'     : ('variable', []),
                       'VariableFiles' : ('variablefile', []),
                       'Listeners'     : ('listener', []),
                       'MonitorWidth'  : ('monitorwidth', 78),
                       'MonitorMarkers': ('monitormarkers', 'AUTO'),
                       'DebugFile'     : ('debugfile', 'NONE')}

    def is_rebot_needed(self):
        return not ('NONE' == self['Log'] == self['Report'] == self['XUnitFile'])

    def get_rebot_datasource_and_settings(self):
        datasource = self['Output']
        settings = RebotSettings(log=False)
        settings._opts.update(self._opts)
        for name in ['Variables', 'VariableFiles', 'Listeners']:
            del(settings._opts[name])
        for name in ['Include', 'Exclude', 'TestNames', 'SuiteNames', 'Metadata']:
            settings._opts[name] = []
        for name in ['Name', 'Doc']:
            settings._opts[name] = None
        settings._opts['Output'] = 'NONE'
        settings._opts['LogLevel'] = 'TRACE'
        settings._opts['ProcessEmptySuite'] = self['RunEmptySuite']
        return datasource, settings

    def _outputfile_disabled(self, type_, name):
        if name == 'NONE':
            return True
        return self._opts['Output'] == 'NONE' and type_ != 'DebugFile'

    def _escape(self, value):
        return utils.escape(value)

class RebotSettings(_BaseSettings):
    _extra_cli_opts = {'Output'            : ('output', 'NONE'),
                       'LogLevel'          : ('loglevel', 'TRACE'),
                       'ProcessEmptySuite' : ('processemptysuite', False),
                       'StartTime'         : ('starttime', None),
                       'EndTime'           : ('endtime', None),
                       'RunFailed'         : ('runfailed', 'NONE')}

    def _outputfile_disabled(self, type_, name):
        return name == 'NONE'

    def _escape(self, value):
        return value

    @property
    def suite_config(self):
        return {
            'name': self['Name'],
            'doc': self['Doc'],
            'metadata': dict(self['Metadata']),
            'set_tags': self['SetTag'],
            'include_tags': self['Include'],
            'exclude_tags': self['Exclude'],
            'include_suites': self['SuiteNames'],
            'include_tests': self['TestNames'],
            'process_empty_suite': self['ProcessEmptySuite'],
            'remove_keywords': self['RemoveKeywords'],
            'log_level': self['LogLevel'],
            'critical': self['Critical'],
            'noncritical': self['NonCritical'],
            'starttime': self['StartTime'],
            'endtime': self['EndTime']
        }

    @property
    def statistics_config(self):
        return {
            'suite_stat_level': self['SuiteStatLevel'],
            'tag_stat_include': self['TagStatInclude'],
            'tag_stat_exclude': self['TagStatExclude'],
            'tag_stat_combine': self['TagStatCombine'],
            'tag_stat_link': self['TagStatLink'],
            'tag_doc': self['TagDoc'],
        }

    @property
    def log_config(self):
        if not self.log:
            return {}
        return {
            'title': utils.html_escape(self['LogTitle'] or ''),
            'reportURL': self._url_from_path(self.log, self.report),
            'splitLogBase': os.path.basename(os.path.splitext(self.log)[0]),
            'defaultLevel': self['VisibleLogLevel']
        }

    @property
    def report_config(self):
        if not self.report:
            return {}
        return {
            'title': utils.html_escape(self['ReportTitle'] or ''),
            'logURL': self._url_from_path(self.report, self.log),
            'background' : self._resolve_background_colors(),
        }

    def _url_from_path(self, source, destination):
        if not destination:
            return None
        return utils.get_link_path(destination, os.path.dirname(source))

    def _resolve_background_colors(self):
        colors = self['ReportBackground']
        return {'pass': colors[0], 'nonCriticalFail': colors[1], 'fail': colors[2]}
<commit_msg>added todo<commit_after>#  Copyright 2008-2012 Nokia Siemens Networks Oyj
#
#  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.

import os

from robot import utils
from robot.errors import DataError, FrameworkError
from robot.output import LOGGER, loggerhelper

from .gatherfailed import gather_failed_tests

class _BaseSettings(object):
    _cli_opts = {'Name'             : ('name', None),
                 'Doc'              : ('doc', None),
                 'Metadata'         : ('metadata', []),
                 'TestNames'        : ('test', []),
                 'RunFailed'        : ('runfailed', 'NONE'),
                 'SuiteNames'       : ('suite', []),
                 'SetTag'           : ('settag', []),
                 'Include'          : ('include', []),
                 'Exclude'          : ('exclude', []),
                 'Critical'         : ('critical', None),
                 'NonCritical'      : ('noncritical', None),
                 'OutputDir'        : ('outputdir', utils.abspath('.')),
                 'Log'              : ('log', 'log.html'),
                 'Report'           : ('report', 'report.html'),
                 'XUnitFile'        : ('xunitfile', 'NONE'),
                 'SplitLog'         : ('splitlog', False),
                 'TimestampOutputs' : ('timestampoutputs', False),
                 'LogTitle'         : ('logtitle', None),
                 'ReportTitle'      : ('reporttitle', None),
                 'ReportBackground' : ('reportbackground',
                                       ('#99FF66', '#99FF66', '#FF3333')),
                 'SuiteStatLevel'   : ('suitestatlevel', -1),
                 'TagStatInclude'   : ('tagstatinclude', []),
                 'TagStatExclude'   : ('tagstatexclude', []),
                 'TagStatCombine'   : ('tagstatcombine', []),
                 'TagDoc'           : ('tagdoc', []),
                 'TagStatLink'      : ('tagstatlink', []),
                 'RemoveKeywords'   : ('removekeywords', []),
                 'NoStatusRC'       : ('nostatusrc', False),
                 'MonitorColors'    : ('monitorcolors', 'AUTO'),
                 'StdOut'           : ('stdout', None),
                 'StdErr'           : ('stderr', None)}
    _output_opts = ['Output', 'Log', 'Report', 'DebugFile', 'XUnitFile']

    # TODO: Is log used? Accept options as **kws instead/in addition?
    def __init__(self, options=None, log=True):
        self._opts = {}
        self._cli_opts = self._cli_opts.copy()
        self._cli_opts.update(self._extra_cli_opts)
        self._process_cli_opts(options or {}, log)
        if log: LOGGER.info('Settings:\n%s' % unicode(self))

    def _process_cli_opts(self, opts, log):
        for name, (cli_name, default) in self._cli_opts.items():
            value = opts.get(cli_name, default)
            if value in [None, []]:
                value = default
            elif default == [] and isinstance(value, basestring):
                value = [value]
            self[name] = self._process_value(name, value, log)
        self['TestNames'] += self['RunFailed']

    def __setitem__(self, name, value):
        if name not in self._cli_opts:
            raise KeyError("Non-existing settings '%s'" % name)
        self._opts[name] = value

    def _process_value(self, name, value, log):
        if name == 'RunFailed':
            return gather_failed_tests(value)
        if name == 'LogLevel':
            return self._process_log_level(value)
        if value == self._get_default_value(name):
            return value
        if name in ['Name', 'Doc', 'LogTitle', 'ReportTitle']:
            if name == 'Doc': value = self._escape(value)
            return value.replace('_', ' ')
        if name in ['Metadata', 'TagDoc']:
            if name == 'Metadata': value = [self._escape(v) for v in value]
            return [self._process_metadata_or_tagdoc(v) for v in value]
        if name in ['Include', 'Exclude']:
            return [v.replace('AND', '&').replace('_', ' ') for v in value]
        if name in self._output_opts and utils.eq(value, 'NONE'):
            return 'NONE'
        if name == 'OutputDir':
            return utils.abspath(value)
        if name in ['SuiteStatLevel', 'MonitorWidth']:
            return self._convert_to_positive_integer_or_default(name, value)
        if name in ['Listeners', 'VariableFiles']:
            return [self._split_args_from_name_or_path(item) for item in value]
        if name == 'ReportBackground':
            return self._process_report_background(value)
        if name == 'TagStatCombine':
            return [self._process_tag_stat_combine(v) for v in value]
        if name == 'TagStatLink':
            return [v for v in [self._process_tag_stat_link(v) for v in value] if v]
        if name == 'RemoveKeywords':
            return [v.upper() for v in value]
        return value

    def _process_log_level(self, level):
        level, visible_level = self._split_log_level(level.upper())
        self._opts['VisibleLogLevel'] = visible_level
        return level

    def _split_log_level(self, level):
        if ':' in level:
            level, visible_level = level.split(':', 1)
        else:
            visible_level = level
        self._validate_log_level_and_default(level, visible_level)
        return level, visible_level

    def _validate_log_level_and_default(self, log_level, default):
        if log_level not in loggerhelper.LEVELS:
            raise DataError("Invalid log level '%s'" % log_level)
        if default not in loggerhelper.LEVELS:
            raise DataError("Invalid log level '%s'" % default)
        if not loggerhelper.IsLogged(log_level)(default):
            raise DataError("Default visible log level '%s' is lower than "
                            "log level '%s'" % (default, log_level))

    def __getitem__(self, name):
        if name not in self._opts:
            raise KeyError("Non-existing setting '%s'" % name)
        if name in self._output_opts:
            return self._get_output_file(name)
        return self._opts[name]

    def _get_output_file(self, type_):
        """Returns path of the requested output file and creates needed dirs.

        `type_` can be 'Output', 'Log', 'Report', 'DebugFile' or 'XUnitFile'.
        """
        name = self._opts[type_]
        if self._outputfile_disabled(type_, name):
            return 'NONE'
        name = self._process_output_name(name, type_)
        path = utils.abspath(os.path.join(self['OutputDir'], name))
        self._create_output_dir(os.path.dirname(path), type_)
        return path

    def _process_output_name(self, name, type_):
        base, ext = os.path.splitext(name)
        if self['TimestampOutputs']:
            base = '%s-%s' % (base, utils.get_start_timestamp('', '-', ''))
        ext = self._get_output_extension(ext, type_)
        return base + ext

    def _get_output_extension(self, ext, type_):
        if ext != '':
            return ext
        if type_ in ['Output', 'XUnitFile']:
            return '.xml'
        if type_ in ['Log', 'Report']:
            return '.html'
        if type_ == 'DebugFile':
            return '.txt'
        raise FrameworkError("Invalid output file type: %s" % type_)

    def _create_output_dir(self, path, type_):
        try:
            if not os.path.exists(path):
                os.makedirs(path)
        except EnvironmentError, err:
            raise DataError("Creating %s file directory '%s' failed: %s"
                            % (type_.lower(), path, err.strerror))

    def _process_metadata_or_tagdoc(self, value):
        value = value.replace('_', ' ')
        if ':' in value:
            return value.split(':', 1)
        return value, ''

    def _process_report_background(self, colors):
        if colors.count(':') not in [1, 2]:
            LOGGER.error("Invalid report background colors '%s'." % colors)
            return self._get_default_value('ReportBackground')
        colors = colors.split(':')
        if len(colors) == 2:
            return colors[0], colors[0], colors[1]
        return tuple(colors)

    def _process_tag_stat_combine(self, value):
        for replwhat, replwith in [('&', 'AND'), ('AND', ' AND '), ('NOT', ' NOT ')]:
            value = value.replace(replwhat, replwith)
        if ':' not in value:
            return value, ''
        pattern, title = value.rsplit(':', 1)
        return pattern, title.replace('_', ' ')

    def _process_tag_stat_link(self, value):
        tokens = value.split(':')
        if len(tokens) >= 3:
            return tokens[0], ':'.join(tokens[1:-1]), tokens[-1]
        LOGGER.error("Invalid format for option '--tagstatlink'. "
                     "Expected 'tag:link:title' but got '%s'." % value)
        return None

    def _convert_to_positive_integer_or_default(self, name, value):
        value = self._convert_to_integer(name, value)
        return value if value > 0 else self._get_default_value(name)

    def _convert_to_integer(self, name, value):
        try:
            return int(value)
        except ValueError:
            LOGGER.error("Option '--%s' expected integer value but got '%s'. "
                         "Default value used instead." % (name.lower(), value))
            return self._get_default_value(name)

    def _get_default_value(self, name):
        return self._cli_opts[name][1]

    def _split_args_from_name_or_path(self, name):
        if ':' not in name or os.path.exists(name):
            args = []
        else:
            args = name.split(':')
            name = args.pop(0)
            # Handle absolute Windows paths with arguments
            if len(name) == 1 and args[0].startswith(('/', '\\')):
                name = name + ':' + args.pop(0)
        if os.path.exists(name):
            name = os.path.abspath(name)
        return name, args

    def __contains__(self, setting):
        return setting in self._cli_opts

    def __unicode__(self):
        return '\n'.join('%s: %s' % (name, self._opts[name])
                         for name in sorted(self._opts))

    @property
    def output(self):
        return self._get_file('Output')

    @property
    def log(self):
        return self._get_file('Log')

    @property
    def report(self):
        return self._get_file('Report')

    @property
    def xunit(self):
        return self._get_file('XUnitFile')

    def _get_file(self, name):
        value = self[name]
        return value if value != 'NONE' else None

    @property
    def split_log(self):
        return self['SplitLog']

    @property
    def status_rc(self):
        return not self['NoStatusRC']

class RobotSettings(_BaseSettings):
    _extra_cli_opts = {'Output'        : ('output', 'output.xml'),
                       'LogLevel'      : ('loglevel', 'INFO'),
                       'RunMode'       : ('runmode', []),
                       'RunEmptySuite' : ('runemptysuite', False),
                       'WarnOnSkipped' : ('warnonskippedfiles', False),
                       'Variables'     : ('variable', []),
                       'VariableFiles' : ('variablefile', []),
                       'Listeners'     : ('listener', []),
                       'MonitorWidth'  : ('monitorwidth', 78),
                       'MonitorMarkers': ('monitormarkers', 'AUTO'),
                       'DebugFile'     : ('debugfile', 'NONE')}

    def is_rebot_needed(self):
        return not ('NONE' == self['Log'] == self['Report'] == self['XUnitFile'])

    def get_rebot_datasource_and_settings(self):
        datasource = self['Output']
        settings = RebotSettings(log=False)
        settings._opts.update(self._opts)
        for name in ['Variables', 'VariableFiles', 'Listeners']:
            del(settings._opts[name])
        for name in ['Include', 'Exclude', 'TestNames', 'SuiteNames', 'Metadata']:
            settings._opts[name] = []
        for name in ['Name', 'Doc']:
            settings._opts[name] = None
        settings._opts['Output'] = 'NONE'
        settings._opts['LogLevel'] = 'TRACE'
        settings._opts['ProcessEmptySuite'] = self['RunEmptySuite']
        return datasource, settings

    def _outputfile_disabled(self, type_, name):
        if name == 'NONE':
            return True
        return self._opts['Output'] == 'NONE' and type_ != 'DebugFile'

    def _escape(self, value):
        return utils.escape(value)

class RebotSettings(_BaseSettings):
    _extra_cli_opts = {'Output'            : ('output', 'NONE'),
                       'LogLevel'          : ('loglevel', 'TRACE'),
                       'ProcessEmptySuite' : ('processemptysuite', False),
                       'StartTime'         : ('starttime', None),
                       'EndTime'           : ('endtime', None),
                       'RunFailed'         : ('runfailed', 'NONE')}

    def _outputfile_disabled(self, type_, name):
        return name == 'NONE'

    def _escape(self, value):
        return value

    @property
    def suite_config(self):
        return {
            'name': self['Name'],
            'doc': self['Doc'],
            'metadata': dict(self['Metadata']),
            'set_tags': self['SetTag'],
            'include_tags': self['Include'],
            'exclude_tags': self['Exclude'],
            'include_suites': self['SuiteNames'],
            'include_tests': self['TestNames'],
            'process_empty_suite': self['ProcessEmptySuite'],
            'remove_keywords': self['RemoveKeywords'],
            'log_level': self['LogLevel'],
            'critical': self['Critical'],
            'noncritical': self['NonCritical'],
            'starttime': self['StartTime'],
            'endtime': self['EndTime']
        }

    @property
    def statistics_config(self):
        return {
            'suite_stat_level': self['SuiteStatLevel'],
            'tag_stat_include': self['TagStatInclude'],
            'tag_stat_exclude': self['TagStatExclude'],
            'tag_stat_combine': self['TagStatCombine'],
            'tag_stat_link': self['TagStatLink'],
            'tag_doc': self['TagDoc'],
        }

    @property
    def log_config(self):
        if not self.log:
            return {}
        return {
            'title': utils.html_escape(self['LogTitle'] or ''),
            'reportURL': self._url_from_path(self.log, self.report),
            'splitLogBase': os.path.basename(os.path.splitext(self.log)[0]),
            'defaultLevel': self['VisibleLogLevel']
        }

    @property
    def report_config(self):
        if not self.report:
            return {}
        return {
            'title': utils.html_escape(self['ReportTitle'] or ''),
            'logURL': self._url_from_path(self.report, self.log),
            'background' : self._resolve_background_colors(),
        }

    def _url_from_path(self, source, destination):
        if not destination:
            return None
        return utils.get_link_path(destination, os.path.dirname(source))

    def _resolve_background_colors(self):
        colors = self['ReportBackground']
        return {'pass': colors[0], 'nonCriticalFail': colors[1], 'fail': colors[2]}
<|endoftext|>


<commit_before>#  Copyright 2008-2009 Nokia Siemens Networks Oyj
#
#  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.

import os

from robot.errors import DataError
from robot.variables import is_var
from robot.output import LOGGER
from robot import utils

from settings import (Documentation, Fixture, Timeout, Tags, Metadata,
                      Library, Resource, Variables, Arguments, Return)
from datareader import FromFilePopulator, FromDirectoryPopulator

def TestData(parent=None, source=None):
    if os.path.isdir(source):
        return TestDataDirectory(parent, source)
    return TestCaseFile(parent, source)

class _TestData(object):

    def __init__(self, parent=None, source=None):
        self.parent = parent
        self.source = os.path.abspath(source) if source else None
        self.children = []

    @property
    def name(self):
        if not self.source:
            return None
        name = os.path.splitext(os.path.basename(self.source))[0]
        name = name.split('__', 1)[-1]  # Strip possible prefix
        name = name.replace('_', ' ').strip()
        return name.title() if name.islower() else name

    def report_invalid_syntax(self, table, message, level='ERROR'):
        initfile = getattr(self, 'initfile', None)
        path = os.path.join(self.source, initfile) if initfile else self.source
        LOGGER.write("Invalid syntax in file '%s' in table '%s': %s"
                     % (path, table, message), level)

class TestCaseFile(_TestData):

    def __init__(self, parent=None, source=None):
        _TestData.__init__(self, parent, source)
        self.directory = os.path.dirname(self.source) if self.source else None
        self.setting_table = TestCaseFileSettingTable(self)
        self.variable_table = VariableTable(self)
        self.testcase_table = TestCaseTable(self)
        self.keyword_table = KeywordTable(self)
        if source:
            FromFilePopulator(self).populate(source)

    def __iter__(self):
        for table in [self.setting_table, self.variable_table,
                      self.testcase_table, self.keyword_table]:
            yield table

class ResourceFile(_TestData):

    def __init__(self, source=None):
        _TestData.__init__(self, source=source)
        self.directory = os.path.dirname(self.source) if self.source else None
        self.setting_table = ResourceFileSettingTable(self)
        self.variable_table = VariableTable(self)
        self.testcase_table = TestCaseTableNotAllowed('resource file')
        self.keyword_table = KeywordTable(self)
        if self.source:
            FromFilePopulator(self).populate(source)

    def __iter__(self):
        for table in [self.setting_table, self.variable_table,
                      self.keyword_table]:
            yield table

class TestDataDirectory(_TestData):

    def __init__(self, parent=None, source=None):
        _TestData.__init__(self, parent, source)
        self.directory = self.source
        self.initfile = None
        self.setting_table = InitFileSettingTable(self)
        self.variable_table = VariableTable(self)
        self.testcase_table = TestCaseTableNotAllowed('test suite init file')
        self.keyword_table = KeywordTable(self)
        if self.source:
            FromDirectoryPopulator().populate(self.source, self)

    def add_child(self, path):
        self.children.append(TestData(parent=self,source=path))

    def __iter__(self):
        for table in [self.setting_table, self.variable_table,
                      self.keyword_table]:
            yield table

class _Table(object):

    @property
    def name(self):
        return self.header[0]

    def __init__(self, parent):
        self.parent = parent
        self.header = None

    def set_header(self, header):
        self.header = header

    @property
    def source(self):
        return self.parent.source

    @property
    def directory(self):
        return self.parent.directory

    def report_invalid_syntax(self, message, level='ERROR'):
        self.parent.report_invalid_syntax(self.name, message, level)

class _WithSettings(object):
    def setter_for(self, setting_name):
        return self._setters[setting_name]

    def is_setting(self, setting_name):
        return setting_name in self._setters

class _SettingTable(_Table, _WithSettings):

    def __init__(self, parent):
        _Table.__init__(self, parent)
        self.doc = Documentation()
        self.suite_setup = Fixture()
        self.suite_teardown = Fixture()
        self.test_setup = Fixture()
        self.test_teardown = Fixture()
        self.test_timeout = Timeout()
        self.force_tags = Tags()
        self.default_tags = Tags()
        self.metadata = []
        self.imports = []
        self._setters = self._get_setters()

    def add_metadata(self, name, value, comment=None):
        self.metadata.append(Metadata(self, name, value, comment))
        return self.metadata[-1]

    def add_library(self, name, args=None, comment=None):
        self.imports.append(Library(self, name, args, comment=comment))
        return self.imports[-1]

    def add_resource(self, name, invalid_args=None, comment=None):
        self.imports.append(Resource(self, name, invalid_args, comment=comment))
        return self.imports[-1]

    def add_variables(self, name, args=None, comment=None):
        self.imports.append(Variables(self, name, args, comment=comment))
        return self.imports[-1]

    def __iter__(self):
        for setting in [self.doc, self.suite_setup, self.suite_teardown,
                        self.test_setup, self.test_teardown, self.test_timeout,
                        self.force_tags, self.default_tags] \
                        + self.metadata + self.imports:
            yield setting

class TestCaseFileSettingTable(_SettingTable):

    def _get_setters(self):
        return utils.NormalizedDict({'Documentation': self.doc.set,
                                     'Document': self.doc.set,
                                     'Suite Setup': self.suite_setup.set,
                                     'Suite Precondition': self.suite_setup.set,
                                     'Suite Teardown': self.suite_teardown.set,
                                     'Suite Postcondition': self.suite_teardown.set,
                                     'Test Setup': self.test_setup.set,
                                     'Test Precondition': self.test_setup.set,
                                     'Test Teardown': self.test_teardown.set,
                                     'Test Postcondition': self.test_teardown.set,
                                     'Force Tags': self.force_tags.set,
                                     'Default Tags': self.default_tags.set,
                                     'Test Timeout': self.test_timeout.set,
                                     'Library': ImportSetter(self.add_library),
                                     'Resource': ImportSetter(self.add_resource),
                                     'Variables': ImportSetter(self.add_variables),
                                     'Metadata': ImportSetter(self.add_metadata)})

class ImportSetter(object):
    def __init__(self, setter):
        self._setter = setter
    def __call__(self, datacells, comment):
        self._setter(datacells[0], datacells[1:], comment)

class ResourceFileSettingTable(_SettingTable):
    def _get_setters(self):
        return utils.NormalizedDict({'Documentation': self.doc.set,
                                     'Document': self.doc.set,
                                     'Library': ImportSetter(self.add_library),
                                     'Resource': ImportSetter(self.add_resource),
                                     'Variables': ImportSetter(self.add_variables)})

class InitFileSettingTable(_SettingTable):
    def _get_setters(self):
        return utils.NormalizedDict({'Documentation': self.doc.set,
                                     'Document': self.doc.set,
                                     'Suite Setup': self.suite_setup.set,
                                     'Suite Precondition': self.suite_setup.set,
                                     'Suite Teardown': self.suite_teardown.set,
                                     'Suite Postcondition': self.suite_teardown.set,
                                     'Test Setup': self.test_setup.set,
                                     'Test Precondition': self.test_setup.set,
                                     'Test Teardown': self.test_teardown.set,
                                     'Test Postcondition': self.test_teardown.set,
                                     'Force Tags': self.force_tags.set,
                                     'Library': ImportSetter(self.add_library),
                                     'Resource': ImportSetter(self.add_resource),
                                     'Variables': ImportSetter(self.add_variables),
                                     'Metadata': ImportSetter(self.add_metadata)})

class VariableTable(_Table):

    def __init__(self, parent):
        _Table.__init__(self, parent)
        self.variables = []

    def add(self, name, value, comment=None):
        self.variables.append(Variable(name, value, comment))

    def __iter__(self):
        return iter(self.variables)

class TestCaseTable(_Table):

    def __init__(self, parent):
        _Table.__init__(self, parent)
        self.tests = []

    def add(self, name):
        self.tests.append(TestCase(self, name))
        return self.tests[-1]

    def __iter__(self):
        return iter(self.tests)

class KeywordTable(_Table):

    def __init__(self, parent):
        _Table.__init__(self, parent)
        self.keywords = []

    def add(self, name):
        self.keywords.append(UserKeyword(name))
        return self.keywords[-1]

    def __iter__(self):
        return iter(self.keywords)

class TestCaseTableNotAllowed(object):

    def __init__(self, where):
        self.message = 'Test case table not allowed in %s.' % where

    def __getattr__(self, name):
        raise DataError(self.message)

    def __nonzero__(self):
        return False

    def __iter__(self):
        return iter([])

class Variable(object):

    def __init__(self, name, value, comment=None):
        self.name = name.rstrip('= ')
        if name.startswith('$') and value == []:
            value = ''
        if isinstance(value, basestring):
            value = [value]  # Need to support scalar lists until RF 2.6
        self.value = value
        self.comment = comment

class _WithSteps(object):

    def add_step(self, content, comment=None):
        self.steps.append(Step(content, comment))
        return self.steps[-1]

class TestCase(_WithSteps, _WithSettings):

    def __init__(self, parent, name):
        self.parent = parent
        self.name = name
        self.doc = Documentation()
        self.tags = Tags()
        self.setup = Fixture()
        self.teardown = Fixture()
        self.timeout = Timeout()
        self.steps = []
        self._setters = self._get_setters()

    def _get_setters(self):
        return utils.NormalizedDict({'Documentation': self.doc.set,
                                     'Document': self.doc.set,
                                     'Setup': self.setup.set,
                                     'Precondition': self.setup.set,
                                     'Teardown': self.teardown.set,
                                     'Postcondition': self.teardown.set,
                                     'Tags': self.tags.set,
                                     'Timeout': self.timeout.set})

    def add_for_loop(self, data):
        self.steps.append(ForLoop(data))
        return self.steps[-1]

class UserKeyword(TestCase):

    def __init__(self, name):
        self.name = name
        self.doc = Documentation()
        self.args = Arguments()
        self.return_ = Return()
        self.timeout = Timeout()
        self.steps = []
        self._setters = self._get_setters()

    def _get_setters(self):
        return utils.NormalizedDict({'Documentation': self.doc.set,
                                     'Document': self.doc.set,
                                     'Arguments': self.args.set,
                                     'Return': self.return_.set,
                                     'Timeout': self.timeout.set})

class ForLoop(_WithSteps):

    def __init__(self, content):
        self.range, index = self._get_range_and_index(content)
        self.vars = content[:index]
        self.items = content[index+1:]
        self.steps = []

    def _get_range_and_index(self, content):
        for index, item in enumerate(content):
            if item.upper() in ['IN', 'IN RANGE']:
                return item.upper() == 'IN RANGE', index
        return False, len(content)

    def is_comment(self):
        return False

    def is_for_loop(self):
        return True

class Step(object):

    def __init__(self, content, comment=None):
        self.assign = self._get_assigned_vars(content)
        try:
            self.keyword = content[len(self.assign)]
        except IndexError:
            self.keyword = None
        self.args = content[len(self.assign)+1:]
        self.comment = comment

    def _get_assigned_vars(self, content):
        vars = []
        for item in content:
            item = item.rstrip('= ')
            if not is_var(item):
                break
            vars.append(item)
        return vars

    def is_comment(self):
        return not (self.assign or self.keyword or self.args)

    def is_for_loop(self):
        return False
<commit_msg>cleanup<commit_after>#  Copyright 2008-2009 Nokia Siemens Networks Oyj
#
#  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.

import os

from robot.errors import DataError
from robot.variables import is_var
from robot.output import LOGGER
from robot import utils

from settings import (Documentation, Fixture, Timeout, Tags, Metadata,
                      Library, Resource, Variables, Arguments, Return)
from datareader import FromFilePopulator, FromDirectoryPopulator

def TestData(parent=None, source=None):
    if os.path.isdir(source):
        return TestDataDirectory(parent, source)
    return TestCaseFile(parent, source)

class _TestData(object):

    def __init__(self, parent=None, source=None):
        self.parent = parent
        self.source = os.path.abspath(source) if source else None
        self.children = []

    @property
    def name(self):
        if not self.source:
            return None
        name = os.path.splitext(os.path.basename(self.source))[0]
        name = name.split('__', 1)[-1]  # Strip possible prefix
        name = name.replace('_', ' ').strip()
        return name.title() if name.islower() else name

    def report_invalid_syntax(self, table, message, level='ERROR'):
        initfile = getattr(self, 'initfile', None)
        path = os.path.join(self.source, initfile) if initfile else self.source
        LOGGER.write("Invalid syntax in file '%s' in table '%s': %s"
                     % (path, table, message), level)

class TestCaseFile(_TestData):

    def __init__(self, parent=None, source=None):
        _TestData.__init__(self, parent, source)
        self.directory = os.path.dirname(self.source) if self.source else None
        self.setting_table = TestCaseFileSettingTable(self)
        self.variable_table = VariableTable(self)
        self.testcase_table = TestCaseTable(self)
        self.keyword_table = KeywordTable(self)
        if source:
            FromFilePopulator(self).populate(source)

    def __iter__(self):
        for table in [self.setting_table, self.variable_table,
                      self.testcase_table, self.keyword_table]:
            yield table

class ResourceFile(_TestData):

    def __init__(self, source=None):
        _TestData.__init__(self, source=source)
        self.directory = os.path.dirname(self.source) if self.source else None
        self.setting_table = ResourceFileSettingTable(self)
        self.variable_table = VariableTable(self)
        self.testcase_table = TestCaseTableNotAllowed('resource file')
        self.keyword_table = KeywordTable(self)
        if self.source:
            FromFilePopulator(self).populate(source)

    def __iter__(self):
        for table in [self.setting_table, self.variable_table,
                      self.keyword_table]:
            yield table

class TestDataDirectory(_TestData):

    def __init__(self, parent=None, source=None):
        _TestData.__init__(self, parent, source)
        self.directory = self.source
        self.initfile = None
        self.setting_table = InitFileSettingTable(self)
        self.variable_table = VariableTable(self)
        self.testcase_table = TestCaseTableNotAllowed('test suite init file')
        self.keyword_table = KeywordTable(self)
        if self.source:
            FromDirectoryPopulator().populate(self.source, self)

    def add_child(self, path):
        self.children.append(TestData(parent=self,source=path))

    def __iter__(self):
        for table in [self.setting_table, self.variable_table,
                      self.keyword_table]:
            yield table

class _Table(object):

    def __init__(self, parent):
        self.parent = parent
        self.header = None

    def set_header(self, header):
        self.header = header

    @property
    def name(self):
        return self.header[0]

    @property
    def source(self):
        return self.parent.source

    @property
    def directory(self):
        return self.parent.directory

    def report_invalid_syntax(self, message, level='ERROR'):
        self.parent.report_invalid_syntax(self.name, message, level)

class _WithSettings(object):
    def setter_for(self, setting_name):
        return self._setters[setting_name]

    def is_setting(self, setting_name):
        return setting_name in self._setters

class _SettingTable(_Table, _WithSettings):

    def __init__(self, parent):
        _Table.__init__(self, parent)
        self.doc = Documentation()
        self.suite_setup = Fixture()
        self.suite_teardown = Fixture()
        self.test_setup = Fixture()
        self.test_teardown = Fixture()
        self.test_timeout = Timeout()
        self.force_tags = Tags()
        self.default_tags = Tags()
        self.metadata = []
        self.imports = []
        self._setters = self._get_setters()

    def add_metadata(self, name, value, comment=None):
        self.metadata.append(Metadata(self, name, value, comment))
        return self.metadata[-1]

    def add_library(self, name, args=None, comment=None):
        self.imports.append(Library(self, name, args, comment=comment))
        return self.imports[-1]

    def add_resource(self, name, invalid_args=None, comment=None):
        self.imports.append(Resource(self, name, invalid_args, comment=comment))
        return self.imports[-1]

    def add_variables(self, name, args=None, comment=None):
        self.imports.append(Variables(self, name, args, comment=comment))
        return self.imports[-1]

    def __iter__(self):
        for setting in [self.doc, self.suite_setup, self.suite_teardown,
                        self.test_setup, self.test_teardown, self.test_timeout,
                        self.force_tags, self.default_tags] \
                        + self.metadata + self.imports:
            yield setting

class TestCaseFileSettingTable(_SettingTable):

    def _get_setters(self):
        return utils.NormalizedDict({'Documentation': self.doc.set,
                                     'Document': self.doc.set,
                                     'Suite Setup': self.suite_setup.set,
                                     'Suite Precondition': self.suite_setup.set,
                                     'Suite Teardown': self.suite_teardown.set,
                                     'Suite Postcondition': self.suite_teardown.set,
                                     'Test Setup': self.test_setup.set,
                                     'Test Precondition': self.test_setup.set,
                                     'Test Teardown': self.test_teardown.set,
                                     'Test Postcondition': self.test_teardown.set,
                                     'Force Tags': self.force_tags.set,
                                     'Default Tags': self.default_tags.set,
                                     'Test Timeout': self.test_timeout.set,
                                     'Library': ImportSetter(self.add_library),
                                     'Resource': ImportSetter(self.add_resource),
                                     'Variables': ImportSetter(self.add_variables),
                                     'Metadata': ImportSetter(self.add_metadata)})

class ImportSetter(object):
    def __init__(self, setter):
        self._setter = setter
    def __call__(self, datacells, comment):
        self._setter(datacells[0], datacells[1:], comment)

class ResourceFileSettingTable(_SettingTable):
    def _get_setters(self):
        return utils.NormalizedDict({'Documentation': self.doc.set,
                                     'Document': self.doc.set,
                                     'Library': ImportSetter(self.add_library),
                                     'Resource': ImportSetter(self.add_resource),
                                     'Variables': ImportSetter(self.add_variables)})

class InitFileSettingTable(_SettingTable):
    def _get_setters(self):
        return utils.NormalizedDict({'Documentation': self.doc.set,
                                     'Document': self.doc.set,
                                     'Suite Setup': self.suite_setup.set,
                                     'Suite Precondition': self.suite_setup.set,
                                     'Suite Teardown': self.suite_teardown.set,
                                     'Suite Postcondition': self.suite_teardown.set,
                                     'Test Setup': self.test_setup.set,
                                     'Test Precondition': self.test_setup.set,
                                     'Test Teardown': self.test_teardown.set,
                                     'Test Postcondition': self.test_teardown.set,
                                     'Force Tags': self.force_tags.set,
                                     'Library': ImportSetter(self.add_library),
                                     'Resource': ImportSetter(self.add_resource),
                                     'Variables': ImportSetter(self.add_variables),
                                     'Metadata': ImportSetter(self.add_metadata)})

class VariableTable(_Table):

    def __init__(self, parent):
        _Table.__init__(self, parent)
        self.variables = []

    def add(self, name, value, comment=None):
        self.variables.append(Variable(name, value, comment))

    def __iter__(self):
        return iter(self.variables)

class TestCaseTable(_Table):

    def __init__(self, parent):
        _Table.__init__(self, parent)
        self.tests = []

    def add(self, name):
        self.tests.append(TestCase(self, name))
        return self.tests[-1]

    def __iter__(self):
        return iter(self.tests)

class KeywordTable(_Table):

    def __init__(self, parent):
        _Table.__init__(self, parent)
        self.keywords = []

    def add(self, name):
        self.keywords.append(UserKeyword(name))
        return self.keywords[-1]

    def __iter__(self):
        return iter(self.keywords)

class TestCaseTableNotAllowed(object):

    def __init__(self, where):
        self.message = 'Test case table not allowed in %s.' % where

    def __getattr__(self, name):
        raise DataError(self.message)

    def __nonzero__(self):
        return False

    def __iter__(self):
        return iter([])

class Variable(object):

    def __init__(self, name, value, comment=None):
        self.name = name.rstrip('= ')
        if name.startswith('$') and value == []:
            value = ''
        if isinstance(value, basestring):
            value = [value]  # Need to support scalar lists until RF 2.6
        self.value = value
        self.comment = comment

class _WithSteps(object):

    def add_step(self, content, comment=None):
        self.steps.append(Step(content, comment))
        return self.steps[-1]

class TestCase(_WithSteps, _WithSettings):

    def __init__(self, parent, name):
        self.parent = parent
        self.name = name
        self.doc = Documentation()
        self.tags = Tags()
        self.setup = Fixture()
        self.teardown = Fixture()
        self.timeout = Timeout()
        self.steps = []
        self._setters = self._get_setters()

    def _get_setters(self):
        return utils.NormalizedDict({'Documentation': self.doc.set,
                                     'Document': self.doc.set,
                                     'Setup': self.setup.set,
                                     'Precondition': self.setup.set,
                                     'Teardown': self.teardown.set,
                                     'Postcondition': self.teardown.set,
                                     'Tags': self.tags.set,
                                     'Timeout': self.timeout.set})

    def add_for_loop(self, data):
        self.steps.append(ForLoop(data))
        return self.steps[-1]

class UserKeyword(TestCase):

    def __init__(self, name):
        self.name = name
        self.doc = Documentation()
        self.args = Arguments()
        self.return_ = Return()
        self.timeout = Timeout()
        self.steps = []
        self._setters = self._get_setters()

    def _get_setters(self):
        return utils.NormalizedDict({'Documentation': self.doc.set,
                                     'Document': self.doc.set,
                                     'Arguments': self.args.set,
                                     'Return': self.return_.set,
                                     'Timeout': self.timeout.set})

class ForLoop(_WithSteps):

    def __init__(self, content):
        self.range, index = self._get_range_and_index(content)
        self.vars = content[:index]
        self.items = content[index+1:]
        self.steps = []

    def _get_range_and_index(self, content):
        for index, item in enumerate(content):
            if item.upper() in ['IN', 'IN RANGE']:
                return item.upper() == 'IN RANGE', index
        return False, len(content)

    def is_comment(self):
        return False

    def is_for_loop(self):
        return True

class Step(object):

    def __init__(self, content, comment=None):
        self.assign = self._get_assigned_vars(content)
        try:
            self.keyword = content[len(self.assign)]
        except IndexError:
            self.keyword = None
        self.args = content[len(self.assign)+1:]
        self.comment = comment

    def _get_assigned_vars(self, content):
        vars = []
        for item in content:
            item = item.rstrip('= ')
            if not is_var(item):
                break
            vars.append(item)
        return vars

    def is_comment(self):
        return not (self.assign or self.keyword or self.args)

    def is_for_loop(self):
        return False
<|endoftext|>


<commit_before>#  Copyright 2008-2009 Nokia Siemens Networks Oyj
#
#  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.

import os

from robot import utils
from robot.common import BaseTestSuite, BaseTestCase
from robot.parsing import TestCaseFile, TestDataDirectory
from robot.parsing.model import Step
from robot.errors import ExecutionFailed, DataError
from robot.variables import GLOBAL_VARIABLES
from robot.output import LOGGER

from fixture import Setup, Teardown, SuiteSetupListener, SuiteTearDownListener,\
    TestSetupListener, TestTeardownListener
from keywords import Keywords
from namespace import Namespace
from runerrors import SuiteRunErrors, TestRunErrors
from userkeyword import UserLibrary
from context import ExecutionContext
from defaultvalues import DefaultValues

def TestSuite(datasources, settings):
    datasources = [ utils.normpath(path) for path in datasources ]
    suite = _get_suite(datasources, settings['SuiteNames'])
    suite.set_options(settings)
    _check_suite_contains_tests(suite)
    return suite

def _get_suite(datasources, include_suites):
    if not datasources:
        raise DataError("No data sources given.")
    if len(datasources) > 1:
        return _get_multisource_suite(datasources, include_suites)
    return RunnableTestSuite(_parse_suite(datasources[0], include_suites))

def _parse_suite(path, include_suites):
    try:
        if os.path.isdir(path):
            return TestDataDirectory(source=path, include_suites=include_suites)
        return TestCaseFile(source=path)
    except DataError, err:
        raise DataError("Parsing '%s' failed: %s" % (path, unicode(err)))

def _get_multisource_suite(datasources, include_suites):
    suitedatas = []
    for datasource in datasources:
        try:
            suitedatas.append(_parse_suite(datasource, include_suites))
        except DataError, err:
            LOGGER.info(err)
    suite = RunnableMultiTestSuite(suitedatas)
    if suite.get_test_count() == 0:
        raise DataError("Data sources %s contain no test cases."
                        % utils.seq2str(datasources))
    return suite

def _check_suite_contains_tests(suite):
    suite.filter_empty_suites()
    if suite.get_test_count() == 0:
        raise DataError("Test suite '%s' contains no test cases." % (suite.source))

class RunnableTestSuite(BaseTestSuite):

    def __init__(self, data, parent=None, defaults=None):
        BaseTestSuite.__init__(self, data.name, data.source, parent)
        self.variables = GLOBAL_VARIABLES.copy()
        self.variables.set_from_variable_table(data.variable_table)
        self.source = data.source
        self.doc = data.setting_table.doc.value
        self.metadata = self._get_metadata(data.setting_table.metadata)
        self.imports = data.setting_table.imports
        self.user_keywords = UserLibrary(data.keyword_table.keywords)
        self.setup = Setup(data.setting_table.suite_setup.name,
                           data.setting_table.suite_setup.args)
        self.teardown = Teardown(data.setting_table.suite_teardown.name,
                                 data.setting_table.suite_teardown.args)
        defaults = DefaultValues(data.setting_table, defaults)
        for suite in data.children:
            RunnableTestSuite(suite, parent=self, defaults=defaults)
        for test in data.testcase_table:
            RunnableTestCase(test, parent=self, defaults=defaults)
        self._run_mode_exit_on_failure = False
        self._run_mode_dry_run = False

    def filter_empty_suites(self):
        for suite in self.suites[:]:
            suite.filter_empty_suites()
            if suite.get_test_count() == 0:
                self.suites.remove(suite)
                LOGGER.info("Running test suite '%s' failed: Test suite"
                            " contains no test cases." % (suite.source))

    def _get_metadata(self, metadata):
        meta = utils.NormalizedDict()
        for item in metadata:
            meta[item.name] = item.value
        return meta

    def run(self, output, parent=None, errors=None):
        context = self._start_run(output, parent, errors)
        self._run_setup(context)
        self._run_sub_suites(context)
        self._run_tests(context)
        self._report_status(context)
        self._run_teardown(context)
        self._end_run(context)

    def _start_run(self, output, parent, errors):
        if not errors:
            errors = SuiteRunErrors(self._run_mode_exit_on_failure)
        self.run_errors = errors
        self.run_errors.start_suite()
        self.status = 'RUNNING'
        self.starttime = utils.get_timestamp()
        parent_vars = parent.context.get_current_vars() if parent else None
        self.context = ExecutionContext(Namespace(self, parent_vars), output,
                                        self._run_mode_dry_run)
        self._set_variable_dependent_metadata(self.context)
        output.start_suite(self)
        return self.context

    def _set_variable_dependent_metadata(self, context):
        errors = []
        self.doc = context.replace_vars_from_setting('Documentation', self.doc,
                                                     errors)
        self.setup.replace_variables(context.get_current_vars(), errors)
        self.teardown.replace_variables(context.get_current_vars(), errors)
        for name, value in self.metadata.items():
            self.metadata[name] = context.replace_vars_from_setting(name, value,
                                                                    errors)
        if errors:
            self.run_errors.suite_init_err('Suite initialization failed:\n%s'
                                            % '\n'.join(errors))

    def _run_setup(self, context):
        if self.run_errors.is_suite_setup_allowed():
            self.setup.run(context, SuiteSetupListener(self))
            self.run_errors.setup_executed()

    def _run_teardown(self, context):
        if self.run_errors.is_suite_teardown_allowed():
            self.teardown.run(context, SuiteTearDownListener(self))

    def _run_sub_suites(self, context):
        for suite in self.suites:
            suite.run(context.output, self, self.run_errors)

    def _run_tests(self, context):
        executed_tests = []
        for test in self.tests:
            normname = utils.normalize(test.name)
            if normname in executed_tests:
                LOGGER.warn("Multiple test cases with name '%s' executed in "
                            "test suite '%s'"% (test.name, self.longname))
            executed_tests.append(normname)
            test.run(context, self.run_errors)
            context.set_prev_test_variables(test)

    def _report_status(self, context):
        self.set_status()
        self.message = self.run_errors.suite_error()
        context.report_suite_status(self.status, self.get_full_message())

    def _end_run(self, context):
        self.endtime = utils.get_timestamp()
        self.elapsedtime = utils.get_elapsed_time(self.starttime, self.endtime)
        context.copy_prev_test_vars_to_global()
        context.end_suite(self)
        self.run_errors.end_suite()

class RunnableMultiTestSuite(RunnableTestSuite):

    def __init__(self, suitedatas):
        BaseTestSuite.__init__(self, name='')
        self.variables = GLOBAL_VARIABLES.copy()
        self.doc = ''
        self.imports = []
        self.setup = Setup(None, None)
        self.teardown = Teardown(None, None)
        for suite in suitedatas:
            RunnableTestSuite(suite, parent=self)
        self._run_mode_exit_on_failure = False
        self._run_mode_dry_run = False

class RunnableTestCase(BaseTestCase):

    def __init__(self, tc_data, parent, defaults):
        BaseTestCase.__init__(self, tc_data.name, parent)
        self.doc = tc_data.doc.value
        self.setup = defaults.get_setup(tc_data.setup)
        self.teardown = defaults.get_teardown(tc_data.teardown)
        self.tags = defaults.get_tags(tc_data.tags)
        self.timeout = defaults.get_timeout(tc_data.timeout)
        self.template = defaults.get_template(tc_data.template)
        self.keywords = Keywords(tc_data.steps, self.template)

    def run(self, context, suite_errors):
        self._suite_errors = suite_errors
        self._start_run(context)
        if self.run_errors.is_allowed_to_run():
            self._run(context)
        else:
            self._not_allowed_to_run()
        self._end_run(context)

    def _start_run(self, context):
        self.run_errors = TestRunErrors(self._suite_errors)
        self.status = 'RUNNING'
        self.starttime = utils.get_timestamp()
        self.run_errors.init_err(self._init_test(context))
        context.start_test(self)

    def _init_test(self, context):
        errors = []
        self.doc = context.replace_vars_from_setting('Documentation', self.doc,
                                                     errors)
        self.setup.replace_variables(context.get_current_vars(), errors)
        self.teardown.replace_variables(context.get_current_vars(), errors)
        self.tags = utils.normalize_tags(context.replace_vars_from_setting('Tags',
                                                                           self.tags,
                                                                           errors))
        self.timeout.replace_variables(context.get_current_vars())
        if errors:
            return 'Test case initialization failed:\n%s' % '\n'.join(errors)
        if not self.name:
            return 'Test case name is required.'
        if not self.keywords:
            return 'Test case contains no keywords'
        return None

    def _run(self, context):
        self.timeout.start()
        self._run_setup(context)
        if not self.run_errors.setup_failed():
            try:
                self.keywords.run(context)
            except ExecutionFailed, err:
                self.run_errors.kw_err(unicode(err))
                self.keyword_failed(err)
        context.set_test_status_before_teardown(*self._report_status())
        self._run_teardown(context)
        self._report_status_after_teardown()

    def keyword_failed(self, err):
        self.timeout.set_keyword_timeout(err.timeout)
        self._suite_errors.test_failed(exit=err.exit)

    def _run_setup(self, context):
        self.setup.run(context, TestSetupListener(self))

    def _report_status(self):
        message = self.run_errors.get_message()
        if message:
            self.status = 'FAIL'
            self.message = message
        else:
            self.status = 'PASS'
        return self.message, self.status

    def _run_teardown(self, context):
        self.teardown.run(context, TestTeardownListener(self))

    def _report_status_after_teardown(self):
        if self.run_errors.teardown_failed():
            self.status = 'FAIL'
            self.message = self.run_errors.get_teardown_message(self.message)
        if self.status == 'PASS' and self.timeout.timed_out():
            self.status = 'FAIL'
            self.message = self.timeout.get_message()
        if self.status == 'FAIL':
            self._suite_errors.test_failed(critical=self.critical=='yes')

    def _not_allowed_to_run(self):
        self.status = 'FAIL'
        self.message = self.run_errors.parent_or_init_error()

    def _end_run(self, context):
        self.endtime = utils.get_timestamp()
        self.elapsedtime = utils.get_elapsed_time(self.starttime, self.endtime)
        context.end_test(self)
<commit_msg>cleanup<commit_after>#  Copyright 2008-2009 Nokia Siemens Networks Oyj
#
#  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.

import os

from robot import utils
from robot.common import BaseTestSuite, BaseTestCase
from robot.parsing import TestCaseFile, TestDataDirectory
from robot.errors import ExecutionFailed, DataError
from robot.variables import GLOBAL_VARIABLES
from robot.output import LOGGER

from fixture import (Setup, Teardown, SuiteSetupListener, SuiteTearDownListener,
                     TestSetupListener, TestTeardownListener)
from keywords import Keywords
from namespace import Namespace
from runerrors import SuiteRunErrors, TestRunErrors
from userkeyword import UserLibrary
from context import ExecutionContext
from defaultvalues import DefaultValues

def TestSuite(datasources, settings):
    datasources = [ utils.normpath(path) for path in datasources ]
    suite = _get_suite(datasources, settings['SuiteNames'])
    suite.set_options(settings)
    _check_suite_contains_tests(suite)
    return suite

def _get_suite(datasources, include_suites):
    if not datasources:
        raise DataError("No data sources given.")
    if len(datasources) > 1:
        return _get_multisource_suite(datasources, include_suites)
    return RunnableTestSuite(_parse_suite(datasources[0], include_suites))

def _parse_suite(path, include_suites):
    try:
        if os.path.isdir(path):
            return TestDataDirectory(source=path, include_suites=include_suites)
        return TestCaseFile(source=path)
    except DataError, err:
        raise DataError("Parsing '%s' failed: %s" % (path, unicode(err)))

def _get_multisource_suite(datasources, include_suites):
    suitedatas = []
    for datasource in datasources:
        try:
            suitedatas.append(_parse_suite(datasource, include_suites))
        except DataError, err:
            LOGGER.info(err)
    suite = RunnableMultiTestSuite(suitedatas)
    if suite.get_test_count() == 0:
        raise DataError("Data sources %s contain no test cases."
                        % utils.seq2str(datasources))
    return suite

def _check_suite_contains_tests(suite):
    suite.filter_empty_suites()
    if suite.get_test_count() == 0:
        raise DataError("Test suite '%s' contains no test cases." % (suite.source))

class RunnableTestSuite(BaseTestSuite):

    def __init__(self, data, parent=None, defaults=None):
        BaseTestSuite.__init__(self, data.name, data.source, parent)
        self.variables = GLOBAL_VARIABLES.copy()
        self.variables.set_from_variable_table(data.variable_table)
        self.source = data.source
        self.doc = data.setting_table.doc.value
        self.metadata = self._get_metadata(data.setting_table.metadata)
        self.imports = data.setting_table.imports
        self.user_keywords = UserLibrary(data.keyword_table.keywords)
        self.setup = Setup(data.setting_table.suite_setup.name,
                           data.setting_table.suite_setup.args)
        self.teardown = Teardown(data.setting_table.suite_teardown.name,
                                 data.setting_table.suite_teardown.args)
        defaults = DefaultValues(data.setting_table, defaults)
        for suite in data.children:
            RunnableTestSuite(suite, parent=self, defaults=defaults)
        for test in data.testcase_table:
            RunnableTestCase(test, parent=self, defaults=defaults)
        self._run_mode_exit_on_failure = False
        self._run_mode_dry_run = False

    def filter_empty_suites(self):
        for suite in self.suites[:]:
            suite.filter_empty_suites()
            if suite.get_test_count() == 0:
                self.suites.remove(suite)
                LOGGER.info("Running test suite '%s' failed: Test suite"
                            " contains no test cases." % (suite.source))

    def _get_metadata(self, metadata):
        meta = utils.NormalizedDict()
        for item in metadata:
            meta[item.name] = item.value
        return meta

    def run(self, output, parent=None, errors=None):
        context = self._start_run(output, parent, errors)
        self._run_setup(context)
        self._run_sub_suites(context)
        self._run_tests(context)
        self._report_status(context)
        self._run_teardown(context)
        self._end_run(context)

    def _start_run(self, output, parent, errors):
        if not errors:
            errors = SuiteRunErrors(self._run_mode_exit_on_failure)
        self.run_errors = errors
        self.run_errors.start_suite()
        self.status = 'RUNNING'
        self.starttime = utils.get_timestamp()
        parent_vars = parent.context.get_current_vars() if parent else None
        self.context = ExecutionContext(Namespace(self, parent_vars), output,
                                        self._run_mode_dry_run)
        self._set_variable_dependent_metadata(self.context)
        output.start_suite(self)
        return self.context

    def _set_variable_dependent_metadata(self, context):
        errors = []
        self.doc = context.replace_vars_from_setting('Documentation', self.doc,
                                                     errors)
        self.setup.replace_variables(context.get_current_vars(), errors)
        self.teardown.replace_variables(context.get_current_vars(), errors)
        for name, value in self.metadata.items():
            self.metadata[name] = context.replace_vars_from_setting(name, value,
                                                                    errors)
        if errors:
            self.run_errors.suite_init_err('Suite initialization failed:\n%s'
                                            % '\n'.join(errors))

    def _run_setup(self, context):
        if self.run_errors.is_suite_setup_allowed():
            self.setup.run(context, SuiteSetupListener(self))
            self.run_errors.setup_executed()

    def _run_teardown(self, context):
        if self.run_errors.is_suite_teardown_allowed():
            self.teardown.run(context, SuiteTearDownListener(self))

    def _run_sub_suites(self, context):
        for suite in self.suites:
            suite.run(context.output, self, self.run_errors)

    def _run_tests(self, context):
        executed_tests = []
        for test in self.tests:
            normname = utils.normalize(test.name)
            if normname in executed_tests:
                LOGGER.warn("Multiple test cases with name '%s' executed in "
                            "test suite '%s'"% (test.name, self.longname))
            executed_tests.append(normname)
            test.run(context, self.run_errors)
            context.set_prev_test_variables(test)

    def _report_status(self, context):
        self.set_status()
        self.message = self.run_errors.suite_error()
        context.report_suite_status(self.status, self.get_full_message())

    def _end_run(self, context):
        self.endtime = utils.get_timestamp()
        self.elapsedtime = utils.get_elapsed_time(self.starttime, self.endtime)
        context.copy_prev_test_vars_to_global()
        context.end_suite(self)
        self.run_errors.end_suite()

class RunnableMultiTestSuite(RunnableTestSuite):

    def __init__(self, suitedatas):
        BaseTestSuite.__init__(self, name='')
        self.variables = GLOBAL_VARIABLES.copy()
        self.doc = ''
        self.imports = []
        self.setup = Setup(None, None)
        self.teardown = Teardown(None, None)
        for suite in suitedatas:
            RunnableTestSuite(suite, parent=self)
        self._run_mode_exit_on_failure = False
        self._run_mode_dry_run = False

class RunnableTestCase(BaseTestCase):

    def __init__(self, tc_data, parent, defaults):
        BaseTestCase.__init__(self, tc_data.name, parent)
        self.doc = tc_data.doc.value
        self.setup = defaults.get_setup(tc_data.setup)
        self.teardown = defaults.get_teardown(tc_data.teardown)
        self.tags = defaults.get_tags(tc_data.tags)
        self.timeout = defaults.get_timeout(tc_data.timeout)
        self.template = defaults.get_template(tc_data.template)
        self.keywords = Keywords(tc_data.steps, self.template)

    def run(self, context, suite_errors):
        self._suite_errors = suite_errors
        self._start_run(context)
        if self.run_errors.is_allowed_to_run():
            self._run(context)
        else:
            self._not_allowed_to_run()
        self._end_run(context)

    def _start_run(self, context):
        self.run_errors = TestRunErrors(self._suite_errors)
        self.status = 'RUNNING'
        self.starttime = utils.get_timestamp()
        self.run_errors.init_err(self._init_test(context))
        context.start_test(self)

    def _init_test(self, context):
        errors = []
        self.doc = context.replace_vars_from_setting('Documentation', self.doc,
                                                     errors)
        self.setup.replace_variables(context.get_current_vars(), errors)
        self.teardown.replace_variables(context.get_current_vars(), errors)
        self.tags = utils.normalize_tags(context.replace_vars_from_setting('Tags',
                                                                           self.tags,
                                                                           errors))
        self.timeout.replace_variables(context.get_current_vars())
        if errors:
            return 'Test case initialization failed:\n%s' % '\n'.join(errors)
        if not self.name:
            return 'Test case name is required.'
        if not self.keywords:
            return 'Test case contains no keywords'
        return None

    def _run(self, context):
        self.timeout.start()
        self._run_setup(context)
        if not self.run_errors.setup_failed():
            try:
                self.keywords.run(context)
            except ExecutionFailed, err:
                self.run_errors.kw_err(unicode(err))
                self.keyword_failed(err)
        context.set_test_status_before_teardown(*self._report_status())
        self._run_teardown(context)
        self._report_status_after_teardown()

    def keyword_failed(self, err):
        self.timeout.set_keyword_timeout(err.timeout)
        self._suite_errors.test_failed(exit=err.exit)

    def _run_setup(self, context):
        self.setup.run(context, TestSetupListener(self))

    def _report_status(self):
        message = self.run_errors.get_message()
        if message:
            self.status = 'FAIL'
            self.message = message
        else:
            self.status = 'PASS'
        return self.message, self.status

    def _run_teardown(self, context):
        self.teardown.run(context, TestTeardownListener(self))

    def _report_status_after_teardown(self):
        if self.run_errors.teardown_failed():
            self.status = 'FAIL'
            self.message = self.run_errors.get_teardown_message(self.message)
        if self.status == 'PASS' and self.timeout.timed_out():
            self.status = 'FAIL'
            self.message = self.timeout.get_message()
        if self.status == 'FAIL':
            self._suite_errors.test_failed(critical=self.critical=='yes')

    def _not_allowed_to_run(self):
        self.status = 'FAIL'
        self.message = self.run_errors.parent_or_init_error()

    def _end_run(self, context):
        self.endtime = utils.get_timestamp()
        self.elapsedtime = utils.get_elapsed_time(self.starttime, self.endtime)
        context.end_test(self)
<|endoftext|>