<commit_before>{
  'includes': [
    '../third_party/webrtc/src/talk/build/common.gypi',
    '../build/config.gypi',
    '../nodejs.gypi',
    'addon.gypi',
  ],
  'targets': [
    {
      'target_name': 'webrtc',
      'sources': [
        'Global.cc',
        'Core.cc',
        'BackTrace.cc',
        'EventEmitter.cc',
        'Observers.cc',
        'Module.cc',
        'PeerConnection.cc',
        'DataChannel.cc',
        'GetSources.cc',
        'GetUserMedia.cc',
        'MediaStream.cc',
        'MediaStreamTrack.cc',
        'MediaConstraints.cc',
        'Stats.cc',
      ],
      'dependencies': [
        '<(webrtc_root)/webrtc.gyp:webrtc_all',
      ],
      'include_dirs': [
        '<(DEPTH)/third_party/jsoncpp/source/include',
        '<(DEPTH)/third_party/libsrtp/srtp',
        '<(DEPTH)/third_party/libyuv/include',
        "<!(node -e \"require('nan')\")",
      ],
      'conditions': [
        ['OS=="linux"', {
          'sources': [
            'Platform-linux.cc'
          ],
          'cflags': [
            '-Wno-deprecated-declarations',
            '-Wno-unused-variable',
            '-Wno-unknown-pragmas',
            '-Wno-unused-result',
          ],
          'cflags_cc': [
            '-std=gnu++11',
            '-Wno-overloaded-virtual',
          ],
          'ldflags': [
            '-Wl,--unresolved-symbols=ignore-in-object-files',
          ],
          'defines': [
            'USE_BACKTRACE',
          ],
        }],
        ['OS=="win"', {
          'sources': [
            'Platform-win32.cc'
          ],
          'msvs_disabled_warnings': [
            4267,
            4005,
            4201,
            4506,
          ],
        }],
        ['OS=="mac"', {
          'sources': [
            'Platform-osx.cc'
          ],
          'xcode_settings': {
            'OTHER_CFLAGS': [
              '-Wno-nonnull',
              '-Wno-deprecated-declarations',
              '-Wno-newline-eof',
              '-Wno-unknown-pragmas',
              '-Wno-unused-result',
            ],
          },
          'defines': [
            'USE_BACKTRACE',
          ],
        }],
      ],
    },
    {
      'target_name': 'All',
      'type': 'none',
      'dependencies': [
        'webrtc',
      ],
    },
  ],
}
<commit_msg>Add common.gypi<commit_after>{
  'includes': [
    '../third_party/webrtc/src/talk/build/common.gypi',
    '../third_party/webrtc/src/webrtc/build/common.gypi',
    '../build/config.gypi',
    '../nodejs.gypi',
    'addon.gypi',
  ],
  'targets': [
    {
      'target_name': 'webrtc',
      'sources': [
        'Global.cc',
        'Core.cc',
        'BackTrace.cc',
        'EventEmitter.cc',
        'Observers.cc',
        'Module.cc',
        'PeerConnection.cc',
        'DataChannel.cc',
        'GetSources.cc',
        'GetUserMedia.cc',
        'MediaStream.cc',
        'MediaStreamTrack.cc',
        'MediaConstraints.cc',
        'Stats.cc',
      ],
      'dependencies': [
        '<(webrtc_root)/webrtc.gyp:webrtc_all',
      ],
      'include_dirs': [
        '<(DEPTH)/third_party/jsoncpp/source/include',
        '<(DEPTH)/third_party/libsrtp/srtp',
        '<(DEPTH)/third_party/libyuv/include',
        "<!(node -e \"require('nan')\")",
      ],
      'conditions': [
        ['OS=="linux"', {
          'sources': [
            'Platform-linux.cc'
          ],
          'cflags': [
            '-Wno-deprecated-declarations',
            '-Wno-unused-variable',
            '-Wno-unknown-pragmas',
            '-Wno-unused-result',
          ],
          'cflags_cc': [
            '-std=gnu++11',
            '-Wno-overloaded-virtual',
          ],
          'ldflags': [
            '-Wl,--unresolved-symbols=ignore-in-object-files',
          ],
          'defines': [
            'USE_BACKTRACE',
          ],
        }],
        ['OS=="win"', {
          'sources': [
            'Platform-win32.cc'
          ],
          'msvs_disabled_warnings': [
            4267,
            4005,
            4201,
            4506,
          ],
        }],
        ['OS=="mac"', {
          'sources': [
            'Platform-osx.cc'
          ],
          'xcode_settings': {
            'OTHER_CFLAGS': [
              '-Wno-nonnull',
              '-Wno-deprecated-declarations',
              '-Wno-newline-eof',
              '-Wno-unknown-pragmas',
              '-Wno-unused-result',
            ],
          },
          'defines': [
            'USE_BACKTRACE',
          ],
        }],
      ],
    },
    {
      'target_name': 'All',
      'type': 'none',
      'dependencies': [
        'webrtc',
      ],
    },
  ],
}
<|endoftext|>


<commit_before>"""
Utilities related to process/script execution.
"""

from rez.vendor.six import six
from rez.utils.yaml import dump_yaml
from rez.vendor.enum import Enum
from contextlib import contextmanager
import subprocess
import sys
import stat
import os

@contextmanager
def add_sys_paths(paths):
    """Add to sys.path, and revert on scope exit.
    """
    original_syspath = sys.path[:]
    sys.path.extend(paths)

    try:
        yield
    finally:
        sys.path = original_syspath

if six.PY2:
    class _PopenBase(subprocess.Popen):
        def __enter__(self):
            return self
        def __exit__(self, exc_type, value, traceback):
            self.wait()

else:  # py3
    _PopenBase = subprocess.Popen

class Popen(_PopenBase):
    """subprocess.Popen wrapper.

    Allows for Popen to be used as a context in both py2 and py3.
    """
    def __init__(self, args, **kwargs):

        # Avoids python bug described here: https://bugs.python.org/issue3905.
        # This can arise when apps (maya) install a non-standard stdin handler.
        #
        # In newer version of maya and katana, the sys.stdin object can also
        # become replaced by an object with no 'fileno' attribute, this is also
        # taken into account.
        #
        if "stdin" not in kwargs:
            try:
                file_no = sys.stdin.fileno()
            except AttributeError:
                file_no = sys.__stdin__.fileno()

            if file_no not in (0, 1, 2):
                kwargs["stdin"] = subprocess.PIPE

        # Add support for the new py3 "text" arg, which is equivalent to
        # "universal_newlines".
        # https://docs.python.org/3/library/subprocess.html#frequently-used-arguments
        #
        if "text" in kwargs:
            kwargs["universal_newlines"] = True
            del kwargs["text"]

        super(Popen, self).__init__(args, **kwargs)

class ExecutableScriptMode(Enum):
    """
    Which scripts to create with util.create_executable_script.
    """
    # Start with 1 to not collide with None checks

    # Requested script only. Usually extension-less.
    single = 1

    # Create .py script that will allow launching scripts on
    # windows without extension, but may require extension on
    # other systems.
    py = 2

    # Will create py script on windows and requested on
    # other platforms
    platform_specific = 3

    # Creates the requested script and an .py script so that scripts
    # can be launched without extension from windows and other
    # systems.
    both = 4

# TODO: Maybe also allow distlib.ScriptMaker instead of the .py + PATHEXT.
def create_executable_script(filepath, body, program=None, py_script_mode=None):
    """
    Create an executable script. In case a py_script_mode has been set to create
    a .py script the shell is expected to have the PATHEXT environment
    variable to include ".PY" in order to properly launch the command without
    the .py extension.

    Args:
        filepath (str): File to create.
        body (str or callable): Contents of the script. If a callable, its code
            is used as the script body.
        program (str): Name of program to launch the script. Default is 'python'
        py_script_mode(ExecutableScriptMode): What kind of script to create.
            Defaults to rezconfig.create_executable_script_mode.
    Returns:
        List of filepaths of created scripts. This may differ from the supplied
        filepath depending on the py_script_mode

    """
    from rez.config import config
    from rez.utils.platform_ import platform_
    program = program or "python"
    py_script_mode = py_script_mode or config.create_executable_script_mode

    if callable(body):
        from rez.utils.sourcecode import SourceCode
        code = SourceCode(func=body)
        body = code.source

    if not body.endswith('\n'):
        body += '\n'

    # Windows does not support shebang, but it will run with
    # default python, or in case of later python versions 'py' that should
    # try to use sensible python interpreters depending on the shebang line.
    # Compare PEP-397.
    # In order for execution to work in windows we need to create a .py
    # file and set the PATHEXT to include .py (as done by the shell plugins)
    # So depending on the py_script_mode we might need to create more then
    # one script

    script_filepaths = [filepath]
    if program == "python":
        script_filepaths = _get_python_script_files(filepath, py_script_mode,
                                                    platform_.name)

    for current_filepath in script_filepaths:
        with open(current_filepath, 'w') as f:
            # TODO: make cross platform
            f.write("#!/usr/bin/env %s\n" % program)
            f.write(body)

        # TODO: Although Windows supports os.chmod you can only set the readonly
        # flag. Setting the file readonly breaks the unit tests that expect to
        # clean up the files once the test has run.  Temporarily we don't bother
        # setting the permissions, but this will need to change.
        if os.name == "posix":
            os.chmod(current_filepath, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH
                     | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

    return script_filepaths

def _get_python_script_files(filepath, py_script_mode, platform):
    """
    Evaluates the py_script_mode for the requested filepath on the given
    platform.

    Args:
        filepath: requested filepath
        py_script_mode (ExecutableScriptMode):
        platform (str): Platform to evaluate the script files for

    Returns:
        list of str: filepaths of scripts to create based on inputs

    """
    script_filepaths = []
    base_filepath, extension = os.path.splitext(filepath)
    has_py_ext = extension == ".py"
    is_windows = platform == "windows"

    if py_script_mode == ExecutableScriptMode.single or \
            py_script_mode == ExecutableScriptMode.both or \
            (py_script_mode == ExecutableScriptMode.py and has_py_ext) or \
            (py_script_mode == ExecutableScriptMode.platform_specific and
             not is_windows) or \
            (py_script_mode == ExecutableScriptMode.platform_specific and
             is_windows and has_py_ext):
        script_filepaths.append(filepath)

    if not has_py_ext and \
            ((py_script_mode == ExecutableScriptMode.both) or
             (py_script_mode == ExecutableScriptMode.py) or
             (py_script_mode == ExecutableScriptMode.platform_specific and
              is_windows)):
        script_filepaths.append(base_filepath + ".py")

    return script_filepaths

def create_forwarding_script(filepath, module, func_name, *nargs, **kwargs):
    """Create a 'forwarding' script.

    A forwarding script is one that executes some arbitrary Rez function. This
    is used internally by Rez to dynamically create a script that uses Rez,
    even though the parent environment may not be configured to do so.
    """
    doc = dict(
        module=module,
        func_name=func_name)

    if nargs:
        doc["nargs"] = nargs
    if kwargs:
        doc["kwargs"] = kwargs

    body = dump_yaml(doc)
    create_executable_script(filepath, body, "_rez_fwd")
<commit_msg>temporary fix for Popen()<commit_after>"""
Utilities related to process/script execution.
"""

from rez.vendor.six import six
from rez.utils.yaml import dump_yaml
from rez.vendor.enum import Enum
from contextlib import contextmanager
import subprocess
import sys
import stat
import os

@contextmanager
def add_sys_paths(paths):
    """Add to sys.path, and revert on scope exit.
    """
    original_syspath = sys.path[:]
    sys.path.extend(paths)

    try:
        yield
    finally:
        sys.path = original_syspath

if six.PY2:
    class _PopenBase(subprocess.Popen):
        def __enter__(self):
            return self
        def __exit__(self, exc_type, value, traceback):
            self.wait()

else:  # py3
    _PopenBase = subprocess.Popen

class Popen(_PopenBase):
    """subprocess.Popen wrapper.

    Allows for Popen to be used as a context in both py2 and py3.
    """
    def __init__(self, args, **kwargs):

        # Avoids python bug described here: https://bugs.python.org/issue3905.
        # This can arise when apps (maya) install a non-standard stdin handler.
        #
        # In newer version of maya and katana, the sys.stdin object can also
        # become replaced by an object with no 'fileno' attribute, this is also
        # taken into account.
        #
        if "stdin" not in kwargs:
            try:
                file_no = sys.stdin.fileno()
            except AttributeError:
                file_no = sys.__stdin__.fileno()

            if file_no not in (0, 1, 2):
                kwargs["stdin"] = subprocess.PIPE

        # Add support for the new py3 "text" arg, which is equivalent to
        # "universal_newlines".
        # https://docs.python.org/3/library/subprocess.html#frequently-used-arguments
        #
        self._universal_newlines = False
        if any([
            kwargs.get('text', False),
            kwargs.get('universal_newlines', False),
        ]):
            kwargs.pop('text', None)
            kwargs.pop('universal_newlines', None)
            self._universal_newlines = True

        super(Popen, self).__init__(args, **kwargs)

    def communicate(self, *args, **kwargs):
        r""" Wraps :py:meth:`subprocess.Popen.communicate` to workaround windows decode-error.

        Args:
            *args/**kwargs: passed to :py:meth:`subprocess.Popen.communicate`

        Returns:
            tuple: 2-tuple of ``(stdout, stderr)``
        """
        # in python-(3.6.5...3.7.4) on windows, `(univeral_newlines/text)=True` fails to decode
        # some characters (ex: b'\x8d'). Temporary Workaround.
        #
        # see: https://github.com/nerdvegas/rez/issues/776
        (stdout, stderr) = super(Popen, self).communicate(*args, **kwargs)
        if self._universal_newlines:
            stdout = self._convert_console_output_to_native_str(stdout)
            stderr = self._convert_console_output_to_native_str(stderr)

            stdout = self._convert_universal_newlines(stdout)
            stderr = self._convert_universal_newlines(stderr)

        return (stdout, stderr)

    def _convert_console_output_to_native_str(self, text):
        """ Converts bytestring/unicode to native string.

        Args:
            text (Nonetype, str, unicode, bytes):
                text to conver to native str. None is ignored.

        Returns:
            str:      unicode or bytes. whichever is native to your python version.
            Nonetype: if provided `text` is None.
        """
        # ignore if (stdout/stderr)!=subprocess.PIPE
        if text is None:
            return text

        if isinstance(text, str):
            return text

        # python3 does not have type 'unicode'
        if sys.version_info[0] >= 3:
            unicode = str

        # python2 uses bytestrings as str
        # python3 uses unicode as str
        if sys.version_info[0] < 3:
            if isinstance(text, unicode):
                return text.encode('utf-8')
        else:
            if isinstance(text, bytes):
                return text.decode('utf-8')

        text_type = str(type(text))
        raise NotImplementedError('Unexpected text type: {}'.format(text_type))

    def _convert_universal_newlines(self, text):
        """ Replaces `\r\n` and `\r` with `\n` .

        Args:
            text (str): text to make newlines universal in.

        Returns:
            str: text with universal_newlines.
        """
        if text is None:
            return text

        # approximating text=True/universal_newlines=True
        # https://docs.python.org/3/library/subprocess.html?#frequently-used-arguments
        # https://docs.python.org/3/library/io.html#io.TextIOWrapper
        text = text.replace('\r\n', '\n')
        text = text.replace('\r', '\n')
        return text

class ExecutableScriptMode(Enum):
    """
    Which scripts to create with util.create_executable_script.
    """
    # Start with 1 to not collide with None checks

    # Requested script only. Usually extension-less.
    single = 1

    # Create .py script that will allow launching scripts on
    # windows without extension, but may require extension on
    # other systems.
    py = 2

    # Will create py script on windows and requested on
    # other platforms
    platform_specific = 3

    # Creates the requested script and an .py script so that scripts
    # can be launched without extension from windows and other
    # systems.
    both = 4

# TODO: Maybe also allow distlib.ScriptMaker instead of the .py + PATHEXT.
def create_executable_script(filepath, body, program=None, py_script_mode=None):
    """
    Create an executable script. In case a py_script_mode has been set to create
    a .py script the shell is expected to have the PATHEXT environment
    variable to include ".PY" in order to properly launch the command without
    the .py extension.

    Args:
        filepath (str): File to create.
        body (str or callable): Contents of the script. If a callable, its code
            is used as the script body.
        program (str): Name of program to launch the script. Default is 'python'
        py_script_mode(ExecutableScriptMode): What kind of script to create.
            Defaults to rezconfig.create_executable_script_mode.
    Returns:
        List of filepaths of created scripts. This may differ from the supplied
        filepath depending on the py_script_mode

    """
    from rez.config import config
    from rez.utils.platform_ import platform_
    program = program or "python"
    py_script_mode = py_script_mode or config.create_executable_script_mode

    if callable(body):
        from rez.utils.sourcecode import SourceCode
        code = SourceCode(func=body)
        body = code.source

    if not body.endswith('\n'):
        body += '\n'

    # Windows does not support shebang, but it will run with
    # default python, or in case of later python versions 'py' that should
    # try to use sensible python interpreters depending on the shebang line.
    # Compare PEP-397.
    # In order for execution to work in windows we need to create a .py
    # file and set the PATHEXT to include .py (as done by the shell plugins)
    # So depending on the py_script_mode we might need to create more then
    # one script

    script_filepaths = [filepath]
    if program == "python":
        script_filepaths = _get_python_script_files(filepath, py_script_mode,
                                                    platform_.name)

    for current_filepath in script_filepaths:
        with open(current_filepath, 'w') as f:
            # TODO: make cross platform
            f.write("#!/usr/bin/env %s\n" % program)
            f.write(body)

        # TODO: Although Windows supports os.chmod you can only set the readonly
        # flag. Setting the file readonly breaks the unit tests that expect to
        # clean up the files once the test has run.  Temporarily we don't bother
        # setting the permissions, but this will need to change.
        if os.name == "posix":
            os.chmod(current_filepath, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH
                     | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

    return script_filepaths

def _get_python_script_files(filepath, py_script_mode, platform):
    """
    Evaluates the py_script_mode for the requested filepath on the given
    platform.

    Args:
        filepath: requested filepath
        py_script_mode (ExecutableScriptMode):
        platform (str): Platform to evaluate the script files for

    Returns:
        list of str: filepaths of scripts to create based on inputs

    """
    script_filepaths = []
    base_filepath, extension = os.path.splitext(filepath)
    has_py_ext = extension == ".py"
    is_windows = platform == "windows"

    if py_script_mode == ExecutableScriptMode.single or \
            py_script_mode == ExecutableScriptMode.both or \
            (py_script_mode == ExecutableScriptMode.py and has_py_ext) or \
            (py_script_mode == ExecutableScriptMode.platform_specific and
             not is_windows) or \
            (py_script_mode == ExecutableScriptMode.platform_specific and
             is_windows and has_py_ext):
        script_filepaths.append(filepath)

    if not has_py_ext and \
            ((py_script_mode == ExecutableScriptMode.both) or
             (py_script_mode == ExecutableScriptMode.py) or
             (py_script_mode == ExecutableScriptMode.platform_specific and
              is_windows)):
        script_filepaths.append(base_filepath + ".py")

    return script_filepaths

def create_forwarding_script(filepath, module, func_name, *nargs, **kwargs):
    """Create a 'forwarding' script.

    A forwarding script is one that executes some arbitrary Rez function. This
    is used internally by Rez to dynamically create a script that uses Rez,
    even though the parent environment may not be configured to do so.
    """
    doc = dict(
        module=module,
        func_name=func_name)

    if nargs:
        doc["nargs"] = nargs
    if kwargs:
        doc["kwargs"] = kwargs

    body = dump_yaml(doc)
    create_executable_script(filepath, body, "_rez_fwd")
<|endoftext|>


<commit_before>#  Copyright 2008-2011 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

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

    def __init__(self, options={}, log=True):
        self._opts = {}
        self._cli_opts = self._cli_opts.copy()
        self._cli_opts.update(self._extra_cli_opts)
        self._process_cli_opts(options, 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():
            try:
                value = opts[cli_name]
                if value in [None, []]:
                    raise KeyError
            except KeyError:
                value = default
            self[name] = self._process_value(name, value, log)

    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 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 in ['RemoveKeywords', 'LogLevel']:
            return value.upper()
        if name in ['SplitOutputs', 'Summary', 'SummaryTitle']:
            return self._removed_in_26(name, log)
        return value

    # TODO: Remove --splitoutputs, --summary, and --summarytitle in 2.7
    def _removed_in_26(self, name, log):
        start, instead = {
            'SplitOutputs': ('Splitting outputs is',
                             'The --splitlog option can be used instead. '),
            'Summary':      ('Summary reports are', ''),
            'SummaryTitle': ('Summary titles are', '')
        }[name]
        option, default = self._cli_opts[name]
        if log:
            LOGGER.warn('%s not supported in Robot Framework 2.6 or newer. %s'
                        'The --%s option will be removed altogether in '
                        'version 2.7.' % (start, instead, option))
        return default

    def __getitem__(self, name):
        if name not in self._cli_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', '&'), ('&', ' & '), ('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', []),
                       'WarnOnSkipped' : ('warnonskippedfiles', False),
                       'Variables'     : ('variable', []),
                       'VariableFiles' : ('variablefile', []),
                       'Listeners'     : ('listener', []),
                       '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 ['Output', 'RemoveKeywords']:
            settings._opts[name] = 'NONE'
        for name in ['Name', 'Doc']:
            settings._opts[name] = None
        settings._opts['LogLevel'] = 'TRACE'
        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'),
                       'RemoveKeywords' : ('removekeywords', 'NONE'),
                       'StartTime'      : ('starttime', 'N/A'),
                       'EndTime'        : ('endtime', 'N/A')}

    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'],
            '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': self['LogTitle'],
            'reportURL': self._url_from_path(self.log, self.report),
            'splitLogBase': os.path.basename(os.path.splitext(self.log)[0])
        }

    @property
    def report_config(self):
        if not self.report:
            return {}
        return {
            'title': self['ReportTitle'],
            '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>settings: made default output directory path absolute<commit_after>#  Copyright 2008-2011 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

class _BaseSettings(object):
    _cli_opts = {'Name'             : ('name', None),
                 'Doc'              : ('doc', None),
                 'Metadata'         : ('metadata', []),
                 'TestNames'        : ('test', []),
                 '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'),
                 'Summary'          : ('summary', 'NONE'),
                 'XUnitFile'        : ('xunitfile', 'NONE'),
                 'SplitLog'         : ('splitlog', False),
                 'SplitOutputs'     : ('splitoutputs', -1),
                 'TimestampOutputs' : ('timestampoutputs', False),
                 'LogTitle'         : ('logtitle', None),
                 'ReportTitle'      : ('reporttitle', None),
                 'SummaryTitle'     : ('summarytitle', None),
                 'ReportBackground' : ('reportbackground',
                                       ('#99FF66', '#99FF66', '#FF3333')),
                 'SuiteStatLevel'   : ('suitestatlevel', -1),
                 'TagStatInclude'   : ('tagstatinclude', []),
                 'TagStatExclude'   : ('tagstatexclude', []),
                 'TagStatCombine'   : ('tagstatcombine', []),
                 'TagDoc'           : ('tagdoc', []),
                 'TagStatLink'      : ('tagstatlink', []),
                 'NoStatusRC'       : ('nostatusrc', False),
                 'RunEmptySuite'    : ('runemptysuite', False),
                 'MonitorWidth'     : ('monitorwidth', 78),
                 'MonitorColors'    : ('monitorcolors', 'AUTO'),
                 'StdOut'           : ('stdout', None),
                 'StdErr'           : ('stderr', None)}
    _output_opts = ['Output', 'Log', 'Report', 'DebugFile', 'XUnitFile']

    def __init__(self, options={}, log=True):
        self._opts = {}
        self._cli_opts = self._cli_opts.copy()
        self._cli_opts.update(self._extra_cli_opts)
        self._process_cli_opts(options, 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():
            try:
                value = opts[cli_name]
                if value in [None, []]:
                    raise KeyError
            except KeyError:
                value = default
            self[name] = self._process_value(name, value, log)

    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 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 in ['RemoveKeywords', 'LogLevel']:
            return value.upper()
        if name in ['SplitOutputs', 'Summary', 'SummaryTitle']:
            return self._removed_in_26(name, log)
        return value

    # TODO: Remove --splitoutputs, --summary, and --summarytitle in 2.7
    def _removed_in_26(self, name, log):
        start, instead = {
            'SplitOutputs': ('Splitting outputs is',
                             'The --splitlog option can be used instead. '),
            'Summary':      ('Summary reports are', ''),
            'SummaryTitle': ('Summary titles are', '')
        }[name]
        option, default = self._cli_opts[name]
        if log:
            LOGGER.warn('%s not supported in Robot Framework 2.6 or newer. %s'
                        'The --%s option will be removed altogether in '
                        'version 2.7.' % (start, instead, option))
        return default

    def __getitem__(self, name):
        if name not in self._cli_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', '&'), ('&', ' & '), ('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', []),
                       'WarnOnSkipped' : ('warnonskippedfiles', False),
                       'Variables'     : ('variable', []),
                       'VariableFiles' : ('variablefile', []),
                       'Listeners'     : ('listener', []),
                       '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 ['Output', 'RemoveKeywords']:
            settings._opts[name] = 'NONE'
        for name in ['Name', 'Doc']:
            settings._opts[name] = None
        settings._opts['LogLevel'] = 'TRACE'
        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'),
                       'RemoveKeywords' : ('removekeywords', 'NONE'),
                       'StartTime'      : ('starttime', 'N/A'),
                       'EndTime'        : ('endtime', 'N/A')}

    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'],
            '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': self['LogTitle'],
            'reportURL': self._url_from_path(self.log, self.report),
            'splitLogBase': os.path.basename(os.path.splitext(self.log)[0])
        }

    @property
    def report_config(self):
        if not self.report:
            return {}
        return {
            'title': self['ReportTitle'],
            '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):

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

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

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

    def report_invalid_syntax(self, message, level='ERROR'):
        # TODO: Use the real table name here when headers are available
        table = type(self).__name__.replace('Table', '')
        self.parent.report_invalid_syntax(table, 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)

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 and not self.keyword and not self.args
<commit_msg>Fixed comment with for loop and added attribute is_for_loop to make client code cleaner.<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

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

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

    def report_invalid_syntax(self, message, level='ERROR'):
        # TODO: Use the real table name here when headers are available
        table = type(self).__name__.replace('Table', '')
        self.parent.report_invalid_syntax(table, 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|>