from .state import *
from .runner import *
-from .test import *
+from .test_util import *
from .suite import *
from .loader import *
from .fixture import *
-from .config import *
-from main import main
+from .configuration import *
+from .main import main
#TODO Remove this awkward bootstrap
#FIXME
+++ /dev/null
-# Copyright (c) 2020 ARM Limited
-# All rights reserved
-#
-# The license below extends only to copyright in the software and shall
-# not be construed as granting a license to any other intellectual
-# property including but not limited to intellectual property relating
-# to a hardware implementation of the functionality of the software
-# licensed hereunder. You may use the software subject to the license
-# terms below provided that you ensure that this notice is replicated
-# unmodified and in its entirety in all distributions of the software,
-# modified or unmodified, in source code or in binary form.
-#
-# Copyright (c) 2017 Mark D. Hill and David A. Wood
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# Authors: Sean Wilson
-
-'''
-Global configuration module which exposes two types of configuration
-variables:
-
-1. config
-2. constants (Also attached to the config variable as an attribute)
-
-The main motivation for this module is to have a centralized location for
-defaults and configuration by command line and files for the test framework.
-
-A secondary goal is to reduce programming errors by providing common constant
-strings and values as python attributes to simplify detection of typos.
-A simple typo in a string can take a lot of debugging to uncover the issue,
-attribute errors are easier to notice and most autocompletion systems detect
-them.
-
-The config variable is initialzed by calling :func:`initialize_config`.
-Before this point only ``constants`` will be availaible. This is to ensure
-that library function writers never accidentally get stale config attributes.
-
-Program arguments/flag arguments are available from the config as attributes.
-If an attribute was not set by the command line or the optional config file,
-then it will fallback to the `_defaults` value, if still the value is not
-found an AttributeError will be raised.
-
-:func define_defaults:
- Provided by the config if the attribute is not found in the config or
- commandline. For instance, if we are using the list command fixtures might
- not be able to count on the build_dir being provided since we aren't going
- to build anything.
-
-:var constants:
- Values not directly exposed by the config, but are attached to the object
- for centralized access. I.E. you can reach them with
- :code:`config.constants.attribute`. These should be used for setting
- common string names used across the test framework.
- :code:`_defaults.build_dir = None` Once this module has been imported
- constants should not be modified and their base attributes are frozen.
-'''
-import abc
-import argparse
-import copy
-import os
-import re
-
-from ConfigParser import ConfigParser
-from pickle import HIGHEST_PROTOCOL as highest_pickle_protocol
-
-from helper import absdirpath, AttrDict, FrozenAttrDict
-
-class UninitialzedAttributeException(Exception):
- '''
- Signals that an attribute in the config file was not initialized.
- '''
- pass
-
-class UninitializedConfigException(Exception):
- '''
- Signals that the config was not initialized before trying to access an
- attribute.
- '''
- pass
-
-class TagRegex(object):
- def __init__(self, include, regex):
- self.include = include
- self.regex = re.compile(regex)
-
- def __str__(self):
- type_ = 'Include' if self.include else 'Remove'
- return '%10s: %s' % (type_, self.regex.pattern)
-
-class _Config(object):
- _initialized = False
-
- __shared_dict = {}
-
- constants = AttrDict()
- _defaults = AttrDict()
- _config = {}
-
- _cli_args = {}
- _post_processors = {}
-
- def __init__(self):
- # This object will act as if it were a singleton.
- self.__dict__ = self.__shared_dict
-
- def _init(self, parser):
- self._parse_commandline_args(parser)
- self._run_post_processors()
- self._initialized = True
-
- def _init_with_dicts(self, config, defaults):
- self._config = config
- self._defaults = defaults
- self._initialized = True
-
- def _add_post_processor(self, attr, post_processor):
- '''
- :param attr: Attribute to pass to and recieve from the
- :func:`post_processor`.
-
- :param post_processor: A callback functions called in a chain to
- perform additional setup for a config argument. Should return a
- tuple containing the new value for the config attr.
- '''
- if attr not in self._post_processors:
- self._post_processors[attr] = []
- self._post_processors[attr].append(post_processor)
-
- def _set(self, name, value):
- self._config[name] = value
-
- def _parse_commandline_args(self, parser):
- args = parser.parse_args()
-
- self._config_file_args = {}
-
- for attr in dir(args):
- # Ignore non-argument attributes.
- if not attr.startswith('_'):
- self._config_file_args[attr] = getattr(args, attr)
- self._config.update(self._config_file_args)
-
- def _run_post_processors(self):
- for attr, callbacks in self._post_processors.items():
- newval = self._lookup_val(attr)
- for callback in callbacks:
- newval = callback(newval)
- if newval is not None:
- newval = newval[0]
- self._set(attr, newval)
-
-
- def _lookup_val(self, attr):
- '''
- Get the attribute from the config or fallback to defaults.
-
- :returns: If the value is not stored return None. Otherwise a tuple
- containing the value.
- '''
- if attr in self._config:
- return (self._config[attr],)
- elif hasattr(self._defaults, attr):
- return (getattr(self._defaults, attr),)
-
- def __getattr__(self, attr):
- if attr in dir(super(_Config, self)):
- return getattr(super(_Config, self), attr)
- elif not self._initialized:
- raise UninitializedConfigException(
- 'Cannot directly access elements from the config before it is'
- ' initialized')
- else:
- val = self._lookup_val(attr)
- if val is not None:
- return val[0]
- else:
- raise UninitialzedAttributeException(
- '%s was not initialzed in the config.' % attr)
-
- def get_tags(self):
- d = {typ: set(self.__getattr__(typ))
- for typ in self.constants.supported_tags}
- if any(map(lambda vals: bool(vals), d.values())):
- return d
- else:
- return {}
-
-def define_defaults(defaults):
- '''
- Defaults are provided by the config if the attribute is not found in the
- config or commandline. For instance, if we are using the list command
- fixtures might not be able to count on the build_dir being provided since
- we aren't going to build anything.
- '''
- defaults.base_dir = os.path.abspath(os.path.join(absdirpath(__file__),
- os.pardir,
- os.pardir))
- defaults.result_path = os.path.join(os.getcwd(), '.testing-results')
- defaults.list_only_failed = False
- defaults.resource_url = 'http://dist.gem5.org/dist/v20'
-
-def define_constants(constants):
- '''
- 'constants' are values not directly exposed by the config, but are attached
- to the object for centralized access. These should be used for setting
- common string names used across the test framework. A simple typo in
- a string can take a lot of debugging to uncover the issue, attribute errors
- are easier to notice and most autocompletion systems detect them.
- '''
- constants.system_out_name = 'system-out'
- constants.system_err_name = 'system-err'
-
- constants.isa_tag_type = 'isa'
- constants.x86_tag = 'X86'
- constants.sparc_tag = 'SPARC'
- constants.riscv_tag = 'RISCV'
- constants.arm_tag = 'ARM'
- constants.mips_tag = 'MIPS'
- constants.power_tag = 'POWER'
- constants.null_tag = 'NULL'
-
- constants.variant_tag_type = 'variant'
- constants.opt_tag = 'opt'
- constants.debug_tag = 'debug'
- constants.fast_tag = 'fast'
-
- constants.length_tag_type = 'length'
- constants.quick_tag = 'quick'
- constants.long_tag = 'long'
-
- constants.host_isa_tag_type = 'host'
- constants.host_x86_64_tag = 'x86_64'
- constants.host_i386_tag = 'i386'
- constants.host_arm_tag = 'aarch64'
-
- constants.supported_tags = {
- constants.isa_tag_type : (
- constants.x86_tag,
- constants.sparc_tag,
- constants.riscv_tag,
- constants.arm_tag,
- constants.mips_tag,
- constants.power_tag,
- constants.null_tag,
- ),
- constants.variant_tag_type: (
- constants.opt_tag,
- constants.debug_tag,
- constants.fast_tag,
- ),
- constants.length_tag_type: (
- constants.quick_tag,
- constants.long_tag,
- ),
- constants.host_isa_tag_type: (
- constants.host_x86_64_tag,
- constants.host_i386_tag,
- constants.host_arm_tag,
- ),
- }
-
- # Binding target ISA with host ISA. This is useful for the
- # case where host ISA and target ISA need to coincide
- constants.target_host = {
- constants.arm_tag : (constants.host_arm_tag,),
- constants.x86_tag : (constants.host_x86_64_tag, constants.host_i386_tag),
- constants.sparc_tag : (constants.host_x86_64_tag, constants.host_i386_tag),
- constants.riscv_tag : (constants.host_x86_64_tag, constants.host_i386_tag),
- constants.mips_tag : (constants.host_x86_64_tag, constants.host_i386_tag),
- constants.power_tag : (constants.host_x86_64_tag, constants.host_i386_tag),
- constants.null_tag : (None,)
- }
-
- constants.supported_isas = constants.supported_tags['isa']
- constants.supported_variants = constants.supported_tags['variant']
- constants.supported_lengths = constants.supported_tags['length']
- constants.supported_hosts = constants.supported_tags['host']
-
- constants.tempdir_fixture_name = 'tempdir'
- constants.gem5_simulation_stderr = 'simerr'
- constants.gem5_simulation_stdout = 'simout'
- constants.gem5_simulation_stats = 'stats.txt'
- constants.gem5_simulation_config_ini = 'config.ini'
- constants.gem5_simulation_config_json = 'config.json'
- constants.gem5_returncode_fixture_name = 'gem5-returncode'
- constants.gem5_binary_fixture_name = 'gem5'
- constants.xml_filename = 'results.xml'
- constants.pickle_filename = 'results.pickle'
- constants.pickle_protocol = highest_pickle_protocol
-
- # The root directory which all test names will be based off of.
- constants.testing_base = absdirpath(os.path.join(absdirpath(__file__),
- os.pardir))
-
-def define_post_processors(config):
- '''
- post_processors are used to do final configuration of variables. This is
- useful if there is a dynamically set default, or some function that needs
- to be applied after parsing in order to set a configration value.
-
- Post processors must accept a single argument that will either be a tuple
- containing the already set config value or ``None`` if the config value
- has not been set to anything. They must return the modified value in the
- same format.
- '''
-
- def set_default_build_dir(build_dir):
- '''
- Post-processor to set the default build_dir based on the base_dir.
-
- .. seealso :func:`~_Config._add_post_processor`
- '''
- if not build_dir or build_dir[0] is None:
- base_dir = config._lookup_val('base_dir')[0]
- build_dir = (os.path.join(base_dir, 'build'),)
- return build_dir
-
- def fix_verbosity_hack(verbose):
- return (verbose[0].val,)
-
- def threads_as_int(threads):
- if threads is not None:
- return (int(threads[0]),)
-
- def test_threads_as_int(test_threads):
- if test_threads is not None:
- return (int(test_threads[0]),)
-
- def default_isa(isa):
- if not isa[0]:
- return [constants.supported_tags[constants.isa_tag_type]]
- else:
- return isa
-
- def default_variant(variant):
- if not variant[0]:
- # Default variant is only opt. No need to run tests with multiple
- # different compilation targets
- return [[constants.opt_tag]]
- else:
- return variant
-
- def default_length(length):
- if not length[0]:
- return [[constants.quick_tag]]
- else:
- return length
-
- def default_host(host):
- if not host[0]:
- try:
- import platform
- host_machine = platform.machine()
- if host_machine not in constants.supported_hosts:
- raise ValueError("Invalid host machine")
- return [[host_machine]]
- except:
- return [[constants.host_x86_64_tag]]
- else:
- return host
-
- def compile_tag_regex(positional_tags):
- if not positional_tags:
- return positional_tags
- else:
- new_positional_tags_list = []
- positional_tags = positional_tags[0]
-
- for flag, regex in positional_tags:
- if flag == 'exclude_tags':
- tag_regex = TagRegex(False, regex)
- elif flag == 'include_tags':
- tag_regex = TagRegex(True, regex)
- else:
- raise ValueError('Unsupported flag.')
- new_positional_tags_list.append(tag_regex)
-
- return (new_positional_tags_list,)
-
- config._add_post_processor('build_dir', set_default_build_dir)
- config._add_post_processor('verbose', fix_verbosity_hack)
- config._add_post_processor('isa', default_isa)
- config._add_post_processor('variant', default_variant)
- config._add_post_processor('length', default_length)
- config._add_post_processor('host', default_host)
- config._add_post_processor('threads', threads_as_int)
- config._add_post_processor('test_threads', test_threads_as_int)
- config._add_post_processor(StorePositionalTagsAction.position_kword,
- compile_tag_regex)
-class Argument(object):
- '''
- Class represents a cli argument/flag for a argparse parser.
-
- :attr name: The long name of this object that will be stored in the arg
- output by the final parser.
- '''
- def __init__(self, *flags, **kwargs):
- self.flags = flags
- self.kwargs = kwargs
-
- if len(flags) == 0:
- raise ValueError("Need at least one argument.")
- elif 'dest' in kwargs:
- self.name = kwargs['dest']
- elif len(flags) > 1 or flags[0].startswith('-'):
- for flag in flags:
- if not flag.startswith('-'):
- raise ValueError("invalid option string %s: must start"
- "with a character '-'" % flag)
-
- if flag.startswith('--'):
- if not hasattr(self, 'name'):
- self.name = flag.lstrip('-')
-
- if not hasattr(self, 'name'):
- self.name = flags[0].lstrip('-')
- self.name = self.name.replace('-', '_')
-
- def add_to(self, parser):
- '''Add this argument to the given parser.'''
- parser.add_argument(*self.flags, **self.kwargs)
-
- def copy(self):
- '''Copy this argument so you might modify any of its kwargs.'''
- return copy.deepcopy(self)
-
-
-class _StickyInt:
- '''
- A class that is used to cheat the verbosity count incrementer by
- pretending to be an int. This makes the int stay on the heap and eat other
- real numbers when they are added to it.
-
- We use this so we can allow the verbose flag to be provided before or after
- the subcommand. This likely has no utility outside of this use case.
- '''
- def __init__(self, val=0):
- self.val = val
- self.type = int
- def __add__(self, other):
- self.val += other
- return self
-
-common_args = NotImplemented
-
-class StorePositionAction(argparse.Action):
- '''Base class for classes wishing to create namespaces where
- arguments are stored in the order provided via the command line.
- '''
- position_kword = 'positional'
-
- def __call__(self, parser, namespace, values, option_string=None):
- if not self.position_kword in namespace:
- setattr(namespace, self.position_kword, [])
- previous = getattr(namespace, self.position_kword)
- previous.append((self.dest, values))
- setattr(namespace, self.position_kword, previous)
-
-class StorePositionalTagsAction(StorePositionAction):
- position_kword = 'tag_filters'
-
-def define_common_args(config):
- '''
- Common args are arguments which are likely to be simular between different
- subcommands, so they are available to all by placing their definitions
- here.
- '''
- global common_args
-
- # A list of common arguments/flags used across cli parsers.
- common_args = [
- Argument(
- 'directory',
- nargs='?',
- default=os.getcwd(),
- help='Directory to start searching for tests in'),
- Argument(
- '--exclude-tags',
- action=StorePositionalTagsAction,
- help='A tag comparison used to select tests.'),
- Argument(
- '--include-tags',
- action=StorePositionalTagsAction,
- help='A tag comparison used to select tests.'),
- Argument(
- '--isa',
- action='append',
- default=[],
- help="Only tests that are valid with one of these ISAs. "
- "Comma separated."),
- Argument(
- '--variant',
- action='append',
- default=[],
- help="Only tests that are valid with one of these binary variants"
- "(e.g., opt, debug). Comma separated."),
- Argument(
- '--length',
- action='append',
- default=[],
- help="Only tests that are one of these lengths. Comma separated."),
- Argument(
- '--host',
- action='append',
- default=[],
- help="Only tests that are meant to runnable on the selected host"),
- Argument(
- '--uid',
- action='store',
- default=None,
- help='UID of a specific test item to run.'),
- Argument(
- '--build-dir',
- action='store',
- help='Build directory for SCons'),
- Argument(
- '--base-dir',
- action='store',
- default=config._defaults.base_dir,
- help='Directory to change to in order to exec scons.'),
- Argument(
- '-j', '--threads',
- action='store',
- default=1,
- help='Number of threads to run SCons with.'),
- Argument(
- '-t', '--test-threads',
- action='store',
- default=1,
- help='Number of threads to spawn to run concurrent tests with.'),
- Argument(
- '-v',
- action='count',
- dest='verbose',
- default=_StickyInt(),
- help='Increase verbosity'),
- Argument(
- '--config-path',
- action='store',
- default=os.getcwd(),
- help='Path to read a testing.ini config in'
- ),
- Argument(
- '--skip-build',
- action='store_true',
- default=False,
- help='Skip the building component of SCons targets.'
- ),
- Argument(
- '--result-path',
- action='store',
- help='The path to store results in.'
- ),
- Argument(
- '--bin-path',
- action='store',
- default=None,
- help='Path where binaries are stored (downloaded if not present)'
- ),
- Argument(
- '--resource-url',
- action='store',
- default=config._defaults.resource_url,
- help='The URL where the resources reside.'
- ),
-
- ]
-
- # NOTE: There is a limitation which arises due to this format. If you have
- # multiple arguments with the same name only the final one in the list
- # will be saved.
- #
- # e.g. if you have a -v argument which increments verbosity level and
- # a separate --verbose flag which 'store's verbosity level. the final
- # one in the list will be saved.
- common_args = AttrDict({arg.name:arg for arg in common_args})
-
-
-class ArgParser(object):
- __metaclass__ = abc.ABCMeta
-
- def __init__(self, parser):
- # Copy public methods of the parser.
- for attr in dir(parser):
- if not attr.startswith('_'):
- setattr(self, attr, getattr(parser, attr))
- self.parser = parser
- self.add_argument = self.parser.add_argument
-
- # Argument will be added to all parsers and subparsers.
- common_args.verbose.add_to(parser)
-
-
-class CommandParser(ArgParser):
- '''
- Main parser which parses command strings and uses those to direct to
- a subparser.
- '''
- def __init__(self):
- parser = argparse.ArgumentParser()
- super(CommandParser, self).__init__(parser)
- self.subparser = self.add_subparsers(dest='command')
-
-
-class RunParser(ArgParser):
- '''
- Parser for the \'run\' command.
- '''
- def __init__(self, subparser):
- parser = subparser.add_parser(
- 'run',
- help='''Run Tests.'''
- )
-
- super(RunParser, self).__init__(parser)
-
- common_args.uid.add_to(parser)
- common_args.skip_build.add_to(parser)
- common_args.directory.add_to(parser)
- common_args.build_dir.add_to(parser)
- common_args.base_dir.add_to(parser)
- common_args.bin_path.add_to(parser)
- common_args.threads.add_to(parser)
- common_args.test_threads.add_to(parser)
- common_args.isa.add_to(parser)
- common_args.variant.add_to(parser)
- common_args.length.add_to(parser)
- common_args.host.add_to(parser)
- common_args.include_tags.add_to(parser)
- common_args.exclude_tags.add_to(parser)
-
-
-class ListParser(ArgParser):
- '''
- Parser for the \'list\' command.
- '''
- def __init__(self, subparser):
- parser = subparser.add_parser(
- 'list',
- help='''List and query test metadata.'''
- )
- super(ListParser, self).__init__(parser)
-
- Argument(
- '--suites',
- action='store_true',
- default=False,
- help='List all test suites.'
- ).add_to(parser)
- Argument(
- '--tests',
- action='store_true',
- default=False,
- help='List all test cases.'
- ).add_to(parser)
- Argument(
- '--fixtures',
- action='store_true',
- default=False,
- help='List all fixtures.'
- ).add_to(parser)
- Argument(
- '--all-tags',
- action='store_true',
- default=False,
- help='List all tags.'
- ).add_to(parser)
- Argument(
- '-q',
- dest='quiet',
- action='store_true',
- default=False,
- help='Quiet output (machine readable).'
- ).add_to(parser)
-
- common_args.directory.add_to(parser)
- common_args.bin_path.add_to(parser)
- common_args.isa.add_to(parser)
- common_args.variant.add_to(parser)
- common_args.length.add_to(parser)
- common_args.host.add_to(parser)
- common_args.include_tags.add_to(parser)
- common_args.exclude_tags.add_to(parser)
-
-
-class RerunParser(ArgParser):
- def __init__(self, subparser):
- parser = subparser.add_parser(
- 'rerun',
- help='''Rerun failed tests.'''
- )
- super(RerunParser, self).__init__(parser)
-
- common_args.skip_build.add_to(parser)
- common_args.directory.add_to(parser)
- common_args.build_dir.add_to(parser)
- common_args.base_dir.add_to(parser)
- common_args.bin_path.add_to(parser)
- common_args.threads.add_to(parser)
- common_args.test_threads.add_to(parser)
- common_args.isa.add_to(parser)
- common_args.variant.add_to(parser)
- common_args.length.add_to(parser)
- common_args.host.add_to(parser)
-
-config = _Config()
-define_constants(config.constants)
-
-# Constants are directly exposed and available once this module is created.
-# All constants MUST be defined before this point.
-config.constants = FrozenAttrDict(config.constants.__dict__)
-constants = config.constants
-
-'''
-This config object is the singleton config object available throughout the
-framework.
-'''
-def initialize_config():
- '''
- Parse the commandline arguments and setup the config varibles.
- '''
- global config
-
- # Setup constants and defaults
- define_defaults(config._defaults)
- define_post_processors(config)
- define_common_args(config)
-
- # Setup parser and subcommands
- baseparser = CommandParser()
- runparser = RunParser(baseparser.subparser)
- listparser = ListParser(baseparser.subparser)
- rerunparser = RerunParser(baseparser.subparser)
-
- # Initialize the config by parsing args and running callbacks.
- config._init(baseparser)
--- /dev/null
+# Copyright (c) 2020 ARM Limited
+# All rights reserved
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder. You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# Copyright (c) 2017 Mark D. Hill and David A. Wood
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Authors: Sean Wilson
+
+'''
+Global configuration module which exposes two types of configuration
+variables:
+
+1. config
+2. constants (Also attached to the config variable as an attribute)
+
+The main motivation for this module is to have a centralized location for
+defaults and configuration by command line and files for the test framework.
+
+A secondary goal is to reduce programming errors by providing common constant
+strings and values as python attributes to simplify detection of typos.
+A simple typo in a string can take a lot of debugging to uncover the issue,
+attribute errors are easier to notice and most autocompletion systems detect
+them.
+
+The config variable is initialzed by calling :func:`initialize_config`.
+Before this point only ``constants`` will be availaible. This is to ensure
+that library function writers never accidentally get stale config attributes.
+
+Program arguments/flag arguments are available from the config as attributes.
+If an attribute was not set by the command line or the optional config file,
+then it will fallback to the `_defaults` value, if still the value is not
+found an AttributeError will be raised.
+
+:func define_defaults:
+ Provided by the config if the attribute is not found in the config or
+ commandline. For instance, if we are using the list command fixtures might
+ not be able to count on the build_dir being provided since we aren't going
+ to build anything.
+
+:var constants:
+ Values not directly exposed by the config, but are attached to the object
+ for centralized access. I.E. you can reach them with
+ :code:`config.constants.attribute`. These should be used for setting
+ common string names used across the test framework.
+ :code:`_defaults.build_dir = None` Once this module has been imported
+ constants should not be modified and their base attributes are frozen.
+'''
+import abc
+import argparse
+import copy
+import os
+import re
+
+from six import add_metaclass
+from six.moves import configparser as ConfigParser
+from pickle import HIGHEST_PROTOCOL as highest_pickle_protocol
+
+from testlib.helper import absdirpath, AttrDict, FrozenAttrDict
+
+class UninitialzedAttributeException(Exception):
+ '''
+ Signals that an attribute in the config file was not initialized.
+ '''
+ pass
+
+class UninitializedConfigException(Exception):
+ '''
+ Signals that the config was not initialized before trying to access an
+ attribute.
+ '''
+ pass
+
+class TagRegex(object):
+ def __init__(self, include, regex):
+ self.include = include
+ self.regex = re.compile(regex)
+
+ def __str__(self):
+ type_ = 'Include' if self.include else 'Remove'
+ return '%10s: %s' % (type_, self.regex.pattern)
+
+class _Config(object):
+ _initialized = False
+
+ __shared_dict = {}
+
+ constants = AttrDict()
+ _defaults = AttrDict()
+ _config = {}
+
+ _cli_args = {}
+ _post_processors = {}
+
+ def __init__(self):
+ # This object will act as if it were a singleton.
+ self.__dict__ = self.__shared_dict
+
+ def _init(self, parser):
+ self._parse_commandline_args(parser)
+ self._run_post_processors()
+ self._initialized = True
+
+ def _init_with_dicts(self, config, defaults):
+ self._config = config
+ self._defaults = defaults
+ self._initialized = True
+
+ def _add_post_processor(self, attr, post_processor):
+ '''
+ :param attr: Attribute to pass to and recieve from the
+ :func:`post_processor`.
+
+ :param post_processor: A callback functions called in a chain to
+ perform additional setup for a config argument. Should return a
+ tuple containing the new value for the config attr.
+ '''
+ if attr not in self._post_processors:
+ self._post_processors[attr] = []
+ self._post_processors[attr].append(post_processor)
+
+ def _set(self, name, value):
+ self._config[name] = value
+
+ def _parse_commandline_args(self, parser):
+ args = parser.parse_args()
+
+ self._config_file_args = {}
+
+ for attr in dir(args):
+ # Ignore non-argument attributes.
+ if not attr.startswith('_'):
+ self._config_file_args[attr] = getattr(args, attr)
+ self._config.update(self._config_file_args)
+
+ def _run_post_processors(self):
+ for attr, callbacks in self._post_processors.items():
+ newval = self._lookup_val(attr)
+ for callback in callbacks:
+ newval = callback(newval)
+ if newval is not None:
+ newval = newval[0]
+ self._set(attr, newval)
+
+
+ def _lookup_val(self, attr):
+ '''
+ Get the attribute from the config or fallback to defaults.
+
+ :returns: If the value is not stored return None. Otherwise a tuple
+ containing the value.
+ '''
+ if attr in self._config:
+ return (self._config[attr],)
+ elif hasattr(self._defaults, attr):
+ return (getattr(self._defaults, attr),)
+
+ def __getattr__(self, attr):
+ if attr in dir(super(_Config, self)):
+ return getattr(super(_Config, self), attr)
+ elif not self._initialized:
+ raise UninitializedConfigException(
+ 'Cannot directly access elements from the config before it is'
+ ' initialized')
+ else:
+ val = self._lookup_val(attr)
+ if val is not None:
+ return val[0]
+ else:
+ raise UninitialzedAttributeException(
+ '%s was not initialzed in the config.' % attr)
+
+ def get_tags(self):
+ d = {typ: set(self.__getattr__(typ))
+ for typ in self.constants.supported_tags}
+ if any(map(lambda vals: bool(vals), d.values())):
+ return d
+ else:
+ return {}
+
+def define_defaults(defaults):
+ '''
+ Defaults are provided by the config if the attribute is not found in the
+ config or commandline. For instance, if we are using the list command
+ fixtures might not be able to count on the build_dir being provided since
+ we aren't going to build anything.
+ '''
+ defaults.base_dir = os.path.abspath(os.path.join(absdirpath(__file__),
+ os.pardir,
+ os.pardir))
+ defaults.result_path = os.path.join(os.getcwd(), '.testing-results')
+ defaults.list_only_failed = False
+ defaults.resource_url = 'http://dist.gem5.org/dist/v20'
+
+def define_constants(constants):
+ '''
+ 'constants' are values not directly exposed by the config, but are attached
+ to the object for centralized access. These should be used for setting
+ common string names used across the test framework. A simple typo in
+ a string can take a lot of debugging to uncover the issue, attribute errors
+ are easier to notice and most autocompletion systems detect them.
+ '''
+ constants.system_out_name = 'system-out'
+ constants.system_err_name = 'system-err'
+
+ constants.isa_tag_type = 'isa'
+ constants.x86_tag = 'X86'
+ constants.sparc_tag = 'SPARC'
+ constants.riscv_tag = 'RISCV'
+ constants.arm_tag = 'ARM'
+ constants.mips_tag = 'MIPS'
+ constants.power_tag = 'POWER'
+ constants.null_tag = 'NULL'
+
+ constants.variant_tag_type = 'variant'
+ constants.opt_tag = 'opt'
+ constants.debug_tag = 'debug'
+ constants.fast_tag = 'fast'
+
+ constants.length_tag_type = 'length'
+ constants.quick_tag = 'quick'
+ constants.long_tag = 'long'
+
+ constants.host_isa_tag_type = 'host'
+ constants.host_x86_64_tag = 'x86_64'
+ constants.host_i386_tag = 'i386'
+ constants.host_arm_tag = 'aarch64'
+
+ constants.supported_tags = {
+ constants.isa_tag_type : (
+ constants.x86_tag,
+ constants.sparc_tag,
+ constants.riscv_tag,
+ constants.arm_tag,
+ constants.mips_tag,
+ constants.power_tag,
+ constants.null_tag,
+ ),
+ constants.variant_tag_type: (
+ constants.opt_tag,
+ constants.debug_tag,
+ constants.fast_tag,
+ ),
+ constants.length_tag_type: (
+ constants.quick_tag,
+ constants.long_tag,
+ ),
+ constants.host_isa_tag_type: (
+ constants.host_x86_64_tag,
+ constants.host_i386_tag,
+ constants.host_arm_tag,
+ ),
+ }
+
+ # Binding target ISA with host ISA. This is useful for the
+ # case where host ISA and target ISA need to coincide
+ constants.target_host = {
+ constants.arm_tag : (constants.host_arm_tag,),
+ constants.x86_tag : (constants.host_x86_64_tag, constants.host_i386_tag),
+ constants.sparc_tag : (constants.host_x86_64_tag, constants.host_i386_tag),
+ constants.riscv_tag : (constants.host_x86_64_tag, constants.host_i386_tag),
+ constants.mips_tag : (constants.host_x86_64_tag, constants.host_i386_tag),
+ constants.power_tag : (constants.host_x86_64_tag, constants.host_i386_tag),
+ constants.null_tag : (None,)
+ }
+
+ constants.supported_isas = constants.supported_tags['isa']
+ constants.supported_variants = constants.supported_tags['variant']
+ constants.supported_lengths = constants.supported_tags['length']
+ constants.supported_hosts = constants.supported_tags['host']
+
+ constants.tempdir_fixture_name = 'tempdir'
+ constants.gem5_simulation_stderr = 'simerr'
+ constants.gem5_simulation_stdout = 'simout'
+ constants.gem5_simulation_stats = 'stats.txt'
+ constants.gem5_simulation_config_ini = 'config.ini'
+ constants.gem5_simulation_config_json = 'config.json'
+ constants.gem5_returncode_fixture_name = 'gem5-returncode'
+ constants.gem5_binary_fixture_name = 'gem5'
+ constants.xml_filename = 'results.xml'
+ constants.pickle_filename = 'results.pickle'
+ constants.pickle_protocol = highest_pickle_protocol
+
+ # The root directory which all test names will be based off of.
+ constants.testing_base = absdirpath(os.path.join(absdirpath(__file__),
+ os.pardir))
+
+def define_post_processors(config):
+ '''
+ post_processors are used to do final configuration of variables. This is
+ useful if there is a dynamically set default, or some function that needs
+ to be applied after parsing in order to set a configration value.
+
+ Post processors must accept a single argument that will either be a tuple
+ containing the already set config value or ``None`` if the config value
+ has not been set to anything. They must return the modified value in the
+ same format.
+ '''
+
+ def set_default_build_dir(build_dir):
+ '''
+ Post-processor to set the default build_dir based on the base_dir.
+
+ .. seealso :func:`~_Config._add_post_processor`
+ '''
+ if not build_dir or build_dir[0] is None:
+ base_dir = config._lookup_val('base_dir')[0]
+ build_dir = (os.path.join(base_dir, 'build'),)
+ return build_dir
+
+ def fix_verbosity_hack(verbose):
+ return (verbose[0].val,)
+
+ def threads_as_int(threads):
+ if threads is not None:
+ return (int(threads[0]),)
+
+ def test_threads_as_int(test_threads):
+ if test_threads is not None:
+ return (int(test_threads[0]),)
+
+ def default_isa(isa):
+ if not isa[0]:
+ return [constants.supported_tags[constants.isa_tag_type]]
+ else:
+ return isa
+
+ def default_variant(variant):
+ if not variant[0]:
+ # Default variant is only opt. No need to run tests with multiple
+ # different compilation targets
+ return [[constants.opt_tag]]
+ else:
+ return variant
+
+ def default_length(length):
+ if not length[0]:
+ return [[constants.quick_tag]]
+ else:
+ return length
+
+ def default_host(host):
+ if not host[0]:
+ try:
+ import platform
+ host_machine = platform.machine()
+ if host_machine not in constants.supported_hosts:
+ raise ValueError("Invalid host machine")
+ return [[host_machine]]
+ except:
+ return [[constants.host_x86_64_tag]]
+ else:
+ return host
+
+ def compile_tag_regex(positional_tags):
+ if not positional_tags:
+ return positional_tags
+ else:
+ new_positional_tags_list = []
+ positional_tags = positional_tags[0]
+
+ for flag, regex in positional_tags:
+ if flag == 'exclude_tags':
+ tag_regex = TagRegex(False, regex)
+ elif flag == 'include_tags':
+ tag_regex = TagRegex(True, regex)
+ else:
+ raise ValueError('Unsupported flag.')
+ new_positional_tags_list.append(tag_regex)
+
+ return (new_positional_tags_list,)
+
+ config._add_post_processor('build_dir', set_default_build_dir)
+ config._add_post_processor('verbose', fix_verbosity_hack)
+ config._add_post_processor('isa', default_isa)
+ config._add_post_processor('variant', default_variant)
+ config._add_post_processor('length', default_length)
+ config._add_post_processor('host', default_host)
+ config._add_post_processor('threads', threads_as_int)
+ config._add_post_processor('test_threads', test_threads_as_int)
+ config._add_post_processor(StorePositionalTagsAction.position_kword,
+ compile_tag_regex)
+class Argument(object):
+ '''
+ Class represents a cli argument/flag for a argparse parser.
+
+ :attr name: The long name of this object that will be stored in the arg
+ output by the final parser.
+ '''
+ def __init__(self, *flags, **kwargs):
+ self.flags = flags
+ self.kwargs = kwargs
+
+ if len(flags) == 0:
+ raise ValueError("Need at least one argument.")
+ elif 'dest' in kwargs:
+ self.name = kwargs['dest']
+ elif len(flags) > 1 or flags[0].startswith('-'):
+ for flag in flags:
+ if not flag.startswith('-'):
+ raise ValueError("invalid option string %s: must start"
+ "with a character '-'" % flag)
+
+ if flag.startswith('--'):
+ if not hasattr(self, 'name'):
+ self.name = flag.lstrip('-')
+
+ if not hasattr(self, 'name'):
+ self.name = flags[0].lstrip('-')
+ self.name = self.name.replace('-', '_')
+
+ def add_to(self, parser):
+ '''Add this argument to the given parser.'''
+ parser.add_argument(*self.flags, **self.kwargs)
+
+ def copy(self):
+ '''Copy this argument so you might modify any of its kwargs.'''
+ return copy.deepcopy(self)
+
+
+class _StickyInt:
+ '''
+ A class that is used to cheat the verbosity count incrementer by
+ pretending to be an int. This makes the int stay on the heap and eat other
+ real numbers when they are added to it.
+
+ We use this so we can allow the verbose flag to be provided before or after
+ the subcommand. This likely has no utility outside of this use case.
+ '''
+ def __init__(self, val=0):
+ self.val = val
+ self.type = int
+ def __add__(self, other):
+ self.val += other
+ return self
+
+common_args = NotImplemented
+
+class StorePositionAction(argparse.Action):
+ '''Base class for classes wishing to create namespaces where
+ arguments are stored in the order provided via the command line.
+ '''
+ position_kword = 'positional'
+
+ def __call__(self, parser, namespace, values, option_string=None):
+ if not self.position_kword in namespace:
+ setattr(namespace, self.position_kword, [])
+ previous = getattr(namespace, self.position_kword)
+ previous.append((self.dest, values))
+ setattr(namespace, self.position_kword, previous)
+
+class StorePositionalTagsAction(StorePositionAction):
+ position_kword = 'tag_filters'
+
+def define_common_args(config):
+ '''
+ Common args are arguments which are likely to be simular between different
+ subcommands, so they are available to all by placing their definitions
+ here.
+ '''
+ global common_args
+
+ # A list of common arguments/flags used across cli parsers.
+ common_args = [
+ Argument(
+ 'directory',
+ nargs='?',
+ default=os.getcwd(),
+ help='Directory to start searching for tests in'),
+ Argument(
+ '--exclude-tags',
+ action=StorePositionalTagsAction,
+ help='A tag comparison used to select tests.'),
+ Argument(
+ '--include-tags',
+ action=StorePositionalTagsAction,
+ help='A tag comparison used to select tests.'),
+ Argument(
+ '--isa',
+ action='append',
+ default=[],
+ help="Only tests that are valid with one of these ISAs. "
+ "Comma separated."),
+ Argument(
+ '--variant',
+ action='append',
+ default=[],
+ help="Only tests that are valid with one of these binary variants"
+ "(e.g., opt, debug). Comma separated."),
+ Argument(
+ '--length',
+ action='append',
+ default=[],
+ help="Only tests that are one of these lengths. Comma separated."),
+ Argument(
+ '--host',
+ action='append',
+ default=[],
+ help="Only tests that are meant to runnable on the selected host"),
+ Argument(
+ '--uid',
+ action='store',
+ default=None,
+ help='UID of a specific test item to run.'),
+ Argument(
+ '--build-dir',
+ action='store',
+ help='Build directory for SCons'),
+ Argument(
+ '--base-dir',
+ action='store',
+ default=config._defaults.base_dir,
+ help='Directory to change to in order to exec scons.'),
+ Argument(
+ '-j', '--threads',
+ action='store',
+ default=1,
+ help='Number of threads to run SCons with.'),
+ Argument(
+ '-t', '--test-threads',
+ action='store',
+ default=1,
+ help='Number of threads to spawn to run concurrent tests with.'),
+ Argument(
+ '-v',
+ action='count',
+ dest='verbose',
+ default=_StickyInt(),
+ help='Increase verbosity'),
+ Argument(
+ '--config-path',
+ action='store',
+ default=os.getcwd(),
+ help='Path to read a testing.ini config in'
+ ),
+ Argument(
+ '--skip-build',
+ action='store_true',
+ default=False,
+ help='Skip the building component of SCons targets.'
+ ),
+ Argument(
+ '--result-path',
+ action='store',
+ help='The path to store results in.'
+ ),
+ Argument(
+ '--bin-path',
+ action='store',
+ default=None,
+ help='Path where binaries are stored (downloaded if not present)'
+ ),
+ Argument(
+ '--resource-url',
+ action='store',
+ default=config._defaults.resource_url,
+ help='The URL where the resources reside.'
+ ),
+
+ ]
+
+ # NOTE: There is a limitation which arises due to this format. If you have
+ # multiple arguments with the same name only the final one in the list
+ # will be saved.
+ #
+ # e.g. if you have a -v argument which increments verbosity level and
+ # a separate --verbose flag which 'store's verbosity level. the final
+ # one in the list will be saved.
+ common_args = AttrDict({arg.name:arg for arg in common_args})
+
+@add_metaclass(abc.ABCMeta)
+class ArgParser(object):
+
+ def __init__(self, parser):
+ # Copy public methods of the parser.
+ for attr in dir(parser):
+ if not attr.startswith('_'):
+ setattr(self, attr, getattr(parser, attr))
+ self.parser = parser
+ self.add_argument = self.parser.add_argument
+
+ # Argument will be added to all parsers and subparsers.
+ common_args.verbose.add_to(parser)
+
+
+class CommandParser(ArgParser):
+ '''
+ Main parser which parses command strings and uses those to direct to
+ a subparser.
+ '''
+ def __init__(self):
+ parser = argparse.ArgumentParser()
+ super(CommandParser, self).__init__(parser)
+ self.subparser = self.add_subparsers(dest='command')
+
+
+class RunParser(ArgParser):
+ '''
+ Parser for the \'run\' command.
+ '''
+ def __init__(self, subparser):
+ parser = subparser.add_parser(
+ 'run',
+ help='''Run Tests.'''
+ )
+
+ super(RunParser, self).__init__(parser)
+
+ common_args.uid.add_to(parser)
+ common_args.skip_build.add_to(parser)
+ common_args.directory.add_to(parser)
+ common_args.build_dir.add_to(parser)
+ common_args.base_dir.add_to(parser)
+ common_args.bin_path.add_to(parser)
+ common_args.threads.add_to(parser)
+ common_args.test_threads.add_to(parser)
+ common_args.isa.add_to(parser)
+ common_args.variant.add_to(parser)
+ common_args.length.add_to(parser)
+ common_args.host.add_to(parser)
+ common_args.include_tags.add_to(parser)
+ common_args.exclude_tags.add_to(parser)
+
+
+class ListParser(ArgParser):
+ '''
+ Parser for the \'list\' command.
+ '''
+ def __init__(self, subparser):
+ parser = subparser.add_parser(
+ 'list',
+ help='''List and query test metadata.'''
+ )
+ super(ListParser, self).__init__(parser)
+
+ Argument(
+ '--suites',
+ action='store_true',
+ default=False,
+ help='List all test suites.'
+ ).add_to(parser)
+ Argument(
+ '--tests',
+ action='store_true',
+ default=False,
+ help='List all test cases.'
+ ).add_to(parser)
+ Argument(
+ '--fixtures',
+ action='store_true',
+ default=False,
+ help='List all fixtures.'
+ ).add_to(parser)
+ Argument(
+ '--all-tags',
+ action='store_true',
+ default=False,
+ help='List all tags.'
+ ).add_to(parser)
+ Argument(
+ '-q',
+ dest='quiet',
+ action='store_true',
+ default=False,
+ help='Quiet output (machine readable).'
+ ).add_to(parser)
+
+ common_args.directory.add_to(parser)
+ common_args.bin_path.add_to(parser)
+ common_args.isa.add_to(parser)
+ common_args.variant.add_to(parser)
+ common_args.length.add_to(parser)
+ common_args.host.add_to(parser)
+ common_args.include_tags.add_to(parser)
+ common_args.exclude_tags.add_to(parser)
+
+
+class RerunParser(ArgParser):
+ def __init__(self, subparser):
+ parser = subparser.add_parser(
+ 'rerun',
+ help='''Rerun failed tests.'''
+ )
+ super(RerunParser, self).__init__(parser)
+
+ common_args.skip_build.add_to(parser)
+ common_args.directory.add_to(parser)
+ common_args.build_dir.add_to(parser)
+ common_args.base_dir.add_to(parser)
+ common_args.bin_path.add_to(parser)
+ common_args.threads.add_to(parser)
+ common_args.test_threads.add_to(parser)
+ common_args.isa.add_to(parser)
+ common_args.variant.add_to(parser)
+ common_args.length.add_to(parser)
+ common_args.host.add_to(parser)
+
+config = _Config()
+define_constants(config.constants)
+
+# Constants are directly exposed and available once this module is created.
+# All constants MUST be defined before this point.
+config.constants = FrozenAttrDict(config.constants.__dict__)
+constants = config.constants
+
+'''
+This config object is the singleton config object available throughout the
+framework.
+'''
+def initialize_config():
+ '''
+ Parse the commandline arguments and setup the config varibles.
+ '''
+ global config
+
+ # Setup constants and defaults
+ define_defaults(config._defaults)
+ define_post_processors(config)
+ define_common_args(config)
+
+ # Setup parser and subcommands
+ baseparser = CommandParser()
+ runparser = RunParser(baseparser.subparser)
+ listparser = ListParser(baseparser.subparser)
+ rerunparser = RerunParser(baseparser.subparser)
+
+ # Initialize the config by parsing args and running callbacks.
+ config._init(baseparser)
import copy
import traceback
-import helper
-import log
+import testlib.helper as helper
+import testlib.log as log
class SkipException(Exception):
def __init__(self, fixture, testitem):
collector = helper.InstanceCollector()
def __new__(klass, *args, **kwargs):
- obj = super(Fixture, klass).__new__(klass, *args, **kwargs)
+ obj = super(Fixture, klass).__new__(klass)
Fixture.collector.collect(obj)
return obj
import multiprocessing
import os
-import Queue
import sys
import threading
import time
import traceback
-import helper
-import log
-import result
-import state
-import test
-import terminal
+import testlib.helper as helper
+import testlib.log as log
+import testlib.result as result
+import testlib.state as state
+import testlib.test_util as test
+import testlib.terminal as terminal
-from config import config, constants
+from six.moves import queue as Queue
+from testlib.configuration import config, constants
class _TestStreamManager(object):
import difflib
import errno
import os
-import Queue
import re
import shutil
import stat
import time
import traceback
+from six.moves import queue as Queue
+
#TODO Tear out duplicate logic from the sandbox IOManager
def log_call(logger, command, *popenargs, **kwargs):
'''
def log_output(log_callback, pipe, redirects=tuple()):
# Read iteractively, don't allow input to fill the pipe.
- for line in iter(pipe.readline, ''):
+ for line in iter(pipe.readline, b''):
+ line = line.decode("utf-8")
for r in redirects:
r.write(line)
log_callback(line.rstrip())
import os
import re
+import six
import sys
import traceback
-import config
-import log
-import suite as suite_mod
-import test as test_mod
-import fixture as fixture_mod
-import wrappers
-import uid
+import testlib.configuration as configuration
+import testlib.log as log
+import testlib.suite as suite_mod
+import testlib.test_util as test_mod
+import testlib.fixture as fixture_mod
+import testlib.wrappers as wrappers
+import testlib.uid as uid
class DuplicateTestItemException(Exception):
'''
if files:
directory = os.path.dirname(files[0])
for f in files:
- assert os.path.dirname(f) == directory
+ assert(os.path.dirname(f) == directory)
class Loader(object):
'''
self._loaded_a_file = True
for directory in self._discover_files(root):
+ directory = list(directory)
if directory:
_assert_files_in_same_dir(directory)
for f in directory:
def load_dir(self, directory):
for dir_ in self._discover_files(directory):
+ directory = list(directory)
_assert_files_in_same_dir(dir_)
for f in dir_:
self.load_file(f)
sys.path.insert(0, os.path.dirname(path))
cwd = os.getcwd()
os.chdir(os.path.dirname(path))
- config.config.file_under_load = path
+ configuration.config.file_under_load = path
new_tests = test_mod.TestCase.collector.create()
new_suites = suite_mod.TestSuite.collector.create()
new_fixtures = fixture_mod.Fixture.collector.create()
- def cleanup():
- config.config.file_under_load = None
- sys.path[:] = old_path
- os.chdir(cwd)
- test_mod.TestCase.collector.remove(new_tests)
- suite_mod.TestSuite.collector.remove(new_suites)
- fixture_mod.Fixture.collector.remove(new_fixtures)
-
try:
- execfile(path, newdict, newdict)
+ exec(open(path).read(), newdict, newdict)
except Exception as e:
log.test_log.debug(traceback.format_exc())
log.test_log.warn(
'Exception thrown while loading "%s"\n'
'Ignoring all tests in this file.'
% (path))
- cleanup()
+ # Clean up
+ configuration.config.file_under_load = None
+ sys.path[:] = old_path
+ os.chdir(cwd)
+ test_mod.TestCase.collector.remove(new_tests)
+ suite_mod.TestSuite.collector.remove(new_suites)
+ fixture_mod.Fixture.collector.remove(new_fixtures)
return
# Create a module test suite for those not contained in a suite.
self.suites.extend(loaded_suites)
self.suite_uids.update({suite.uid: suite
for suite in loaded_suites})
- cleanup()
+ # Clean up
+ configuration.config.file_under_load = None
+ sys.path[:] = old_path
+ os.chdir(cwd)
+ test_mod.TestCase.collector.remove(new_tests)
+ suite_mod.TestSuite.collector.remove(new_suites)
+ fixture_mod.Fixture.collector.remove(new_fixtures)
def _discover_files(self, root):
'''
This module supplies the global `test_log` object which all testing
results and messages are reported through.
'''
-import wrappers
+import testlib.wrappers as wrappers
+from six import add_metaclass
class LogLevel():
Fatal = 0
RecordTypeCounterMetaclass.counter += 1
+@add_metaclass(RecordTypeCounterMetaclass)
class Record(object):
'''
A generic object that is passed to the :class:`Log` and its handlers.
..note: Although not statically enforced, all items in the record should be
be pickleable. This enables logging accross multiple processes.
'''
- __metaclass__ = RecordTypeCounterMetaclass
def __init__(self, **data):
self.data = data
import os
import itertools
-import config
-import fixture as fixture_mod
-import handlers
-import loader as loader_mod
-import log
-import query
-import result
-import runner
-import terminal
-import uid
+import testlib.configuration as configuration
+import testlib.fixture as fixture_mod
+import testlib.handlers as handlers
+import testlib.loader as loader_mod
+import testlib.log as log
+import testlib.query as query
+import testlib.result as result
+import testlib.runner as runner
+import testlib.terminal as terminal
+import testlib.uid as uid
def entry_message():
log.test_log.message("Running the new gem5 testing script.")
class RunLogHandler():
def __init__(self):
term_handler = handlers.TerminalHandler(
- verbosity=config.config.verbose+log.LogLevel.Info
+ verbosity=configuration.config.verbose+log.LogLevel.Info
)
summary_handler = handlers.SummaryHandler()
self.mp_handler = handlers.MultiprocessingHandlerWrapper(
def schedule_finalized(self, test_schedule):
# Create the result handler object.
self.result_handler = handlers.ResultHandler(
- test_schedule, config.config.result_path)
+ test_schedule, configuration.config.result_path)
self.mp_handler.add_handler(self.result_handler)
def finish_testing(self):
return self.result_handler.unsuccessful()
def get_config_tags():
- return getattr(config.config,
- config.StorePositionalTagsAction.position_kword)
+ return getattr(configuration.config,
+ configuration.StorePositionalTagsAction.position_kword)
def filter_with_config_tags(loaded_library):
tags = get_config_tags()
final_tags = []
regex_fmt = '^%s$'
- cfg = config.config
+ cfg = configuration.config
def _append_inc_tag_filter(name):
if hasattr(cfg, name):
tag_opts = getattr(cfg, name)
for tag in tag_opts:
- final_tags.append(config.TagRegex(True, regex_fmt % tag))
+ final_tags.append(configuration.TagRegex(True, regex_fmt % tag))
def _append_rem_tag_filter(name):
if hasattr(cfg, name):
tag_opts = getattr(cfg, name)
for tag in cfg.constants.supported_tags[name]:
if tag not in tag_opts:
- final_tags.append(config.TagRegex(False, regex_fmt % tag))
+ final_tags.append(configuration.TagRegex(False, regex_fmt % tag))
# Append additional tags for the isa, length, and variant options.
# They apply last (they take priority)
testloader = loader_mod.Loader()
log.test_log.message(terminal.separator())
log.test_log.message('Loading Tests', bold=True)
- testloader.load_root(config.config.directory)
+ testloader.load_root(configuration.config.directory)
return testloader
def do_list():
term_handler = handlers.TerminalHandler(
- verbosity=config.config.verbose+log.LogLevel.Info,
- machine_only=config.config.quiet
+ verbosity=configuration.config.verbose+log.LogLevel.Info,
+ machine_only=configuration.config.quiet
)
log.test_log.log_obj.add_handler(term_handler)
qrunner = query.QueryRunner(test_schedule)
- if config.config.suites:
+ if configuration.config.suites:
qrunner.list_suites()
- elif config.config.tests:
+ elif configuration.config.tests:
qrunner.list_tests()
- elif config.config.all_tags:
+ elif configuration.config.all_tags:
qrunner.list_tags()
else:
qrunner.list_suites()
log.test_log.message('Running Tests from {} suites'
.format(len(test_schedule.suites)), bold=True)
log.test_log.message("Results will be stored in {}".format(
- config.config.result_path))
+ configuration.config.result_path))
log.test_log.message(terminal.separator())
# Build global fixtures and exectute scheduled test suites.
- if config.config.test_threads > 1:
+ if configuration.config.test_threads > 1:
library_runner = runner.LibraryParallelRunner(test_schedule)
- library_runner.set_threads(config.config.test_threads)
+ library_runner.set_threads(configuration.config.test_threads)
else:
library_runner = runner.LibraryRunner(test_schedule)
library_runner.run()
def do_run():
# Initialize early parts of the log.
with RunLogHandler() as log_handler:
- if config.config.uid:
- uid_ = uid.UID.from_uid(config.config.uid)
+ if configuration.config.uid:
+ uid_ = uid.UID.from_uid(configuration.config.uid)
if isinstance(uid_, uid.TestUID):
log.test_log.error('Unable to run a standalone test.\n'
'Gem5 expects test suites to be the smallest unit '
with RunLogHandler() as log_handler:
# Load previous results
results = result.InternalSavedResults.load(
- os.path.join(config.config.result_path,
- config.constants.pickle_filename))
+ os.path.join(configuration.config.result_path,
+ configuration.constants.pickle_filename))
rerun_suites = (suite.uid for suite in results if suite.unsuccessful)
Returns 0 on success and 1 otherwise so it can be used as a return code
for scripts.
'''
- config.initialize_config()
+ configuration.initialize_config()
# 'do' the given command.
- result = globals()['do_'+config.config.command]()
+ result = globals()['do_'+configuration.config.command]()
log.test_log.close()
return result
#
# Authors: Sean Wilson
-import terminal
-import log
+import testlib.terminal as terminal
+import testlib.log as log
# TODO Refactor print logic out of this so the objects
# created are separate from print logic.
import pickle
import xml.sax.saxutils
-from config import config
-import helper
-import state
-import log
+from testlib.configuration import config
+import testlib.helper as helper
+import testlib.state as state
+import testlib.log as log
def _create_uid_index(iterable):
index = {}
return self._metadata.result.value != state.Result.Passed
-class InternalTestResult(object, _CommonMetadataMixin):
+class InternalTestResult(_CommonMetadataMixin):
def __init__(self, obj, suite, directory):
self._metadata = obj.metadata
self.suite = suite
)
-class InternalSuiteResult(object, _CommonMetadataMixin):
+class InternalSuiteResult(_CommonMetadataMixin):
def __init__(self, obj, directory):
self._metadata = obj.metadata
self.directory = directory
return results
-class InternalLibraryResults(object, _CommonMetadataMixin):
+class InternalLibraryResults(_CommonMetadataMixin):
def __init__(self, obj, directory):
self.directory = directory
self._metadata = obj.metadata
if exc.errno != errno.EEXIST:
raise
- with open(path, 'w') as f:
+ with open(path, 'wb') as f:
pickle.dump(results, f, protocol)
@staticmethod
def load(path):
- with open(path, 'r') as f:
+ with open(path, 'rb') as f:
return pickle.load(f)
import threading
import traceback
-import helper
-import state
-import log
-import sandbox
+import testlib.helper as helper
+import testlib.state as state
+import testlib.log as log
+import testlib.sandbox as sandbox
-from state import Status, Result
-from fixture import SkipException
+from testlib.state import Status, Result
+from testlib.fixture import SkipException
def compute_aggregate_result(iterable):
'''
import threading
import traceback
-import log
+import testlib.log as log
pdb._Pdb = pdb.Pdb
class ForkedPdb(pdb._Pdb):
self.old_stdout = os.dup(sys.stdout.fileno())
os.dup2(self.stderr_wp, sys.stderr.fileno())
- sys.stderr = os.fdopen(self.stderr_wp, 'w', 0)
+ sys.stderr = os.fdopen(self.stderr_wp, 'w')
os.dup2(self.stdout_wp, sys.stdout.fileno())
- sys.stdout = os.fdopen(self.stdout_wp, 'w', 0)
+ sys.stdout = os.fdopen(self.stdout_wp, 'w')
def restore_pipes(self):
self.stderr_wp = os.dup(sys.stderr.fileno())
self.stdout_wp = os.dup(sys.stdout.fileno())
os.dup2(self.old_stderr, sys.stderr.fileno())
- sys.stderr = os.fdopen(self.old_stderr, 'w', 0)
+ sys.stderr = open(self.old_stderr, 'w')
os.dup2(self.old_stdout, sys.stdout.fileno())
- sys.stdout = os.fdopen(self.old_stdout, 'w', 0)
+ sys.stdout = open(self.old_stdout, 'w')
def start_loggers(self):
self.log_ouput()
# Authors: Sean Wilson
-import helper
-import runner as runner_mod
+import testlib.helper as helper
+import testlib.runner as runner_mod
class TestSuite(object):
'''
tags = set()
def __new__(klass, *args, **kwargs):
- obj = super(TestSuite, klass).__new__(klass, *args, **kwargs)
+ obj = super(TestSuite, klass).__new__(klass)
TestSuite.collector.collect(obj)
return obj
import fcntl
import termios
import struct
+import six
# Intended usage example:
#
def cap_string(s, *args):
cap = curses.tigetstr(s)
if cap:
- return curses.tparm(cap, *args)
+ return curses.tparm(cap, *args).decode("utf-8")
else:
return ''
except:
def __init__(self, cap_string):
for i, c in enumerate(color_names):
setattr(self, c, cap_string('setaf', i))
- for name, cap in capability_map.iteritems():
+ for name, cap in six.iteritems(capability_map):
setattr(self, name, cap_string(cap))
termcap = ColorStrings(cap_string)
.. seealso:: :func:`separator`
'''
# Use a bytearray so it's efficient to manipulate
- string = bytearray(separator(char, color=color))
+ string = bytearray(separator(char, color=color), 'utf-8')
# Check if we can fit inside with at least min_barrier.
gap = (len(string) - len(inside)) - min_barrier * 2
# We'll need to expand the string to fit us.
string.extend([ char for _ in range(-gap)])
# Emplace inside
- middle = ((len(string)-1)/2)
- start_idx = middle - len(inside)/2
- string[start_idx:len(inside)+start_idx] = inside
- return str(string)
+ middle = (len(string)-1)//2
+ start_idx = middle - len(inside)//2
+ string[start_idx:len(inside)+start_idx] = str.encode(inside)
+ return str(string.decode("utf-8"))
if __name__ == '__main__':
def test_termcap(obj):
for c_name in color_names:
c_str = getattr(obj, c_name)
- print c_str + c_name + obj.Normal
+ print(c_str + c_name + obj.Normal)
for attr_name in capability_names:
if attr_name == 'Normal':
continue
attr_str = getattr(obj, attr_name)
- print attr_str + c_str + attr_name + " " + c_name + obj.Normal
- print obj.Bold + obj.Underline + \
- c_name + "Bold Underline " + c_str + obj.Normal
+ print(attr_str + c_str + attr_name + " " + c_name + obj.Normal)
+ print(obj.Bold + obj.Underline + \
+ c_name + "Bold Underline " + c_str + obj.Normal)
- print "=== termcap enabled ==="
+ print("=== termcap enabled ===")
test_termcap(termcap)
- print termcap.Normal
- print "=== termcap disabled ==="
+ print(termcap.Normal)
+ print("=== termcap disabled ===")
test_termcap(no_termcap)
+++ /dev/null
-# Copyright (c) 2017 Mark D. Hill and David A. Wood
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met: redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer;
-# redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution;
-# neither the name of the copyright holders nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# Authors: Sean Wilson
-
-import functools
-
-import helper
-import runner as runner_mod
-
-class TestingException(Exception):
- '''Common ancestor for manual Testing Exceptions.'''
-class TestFailException(TestingException):
- '''Signals that a test has failed.'''
-class TestSkipException(TestingException):
- '''Signals that a test has been skipped.'''
-
-def fail(message):
- '''Cause the current test to fail with the given message.'''
- raise TestFailException(message)
-
-def skip(message):
- '''Cause the current test to skip with the given message.'''
- raise TestSkipException(message)
-
-class TestCase(object):
- '''
- Base class for all tests.
-
- ..note::
- The :func:`__new__` method enables collection of test cases, it must
- be called in order for test cases to be collected.
- '''
- fixtures = []
-
- # TODO, remove explicit dependency. Use the loader to set the
- # default runner
- runner = runner_mod.TestRunner
- collector = helper.InstanceCollector()
-
- def __new__(cls, *args, **kwargs):
- obj = super(TestCase, cls).__new__(cls, *args, **kwargs)
- TestCase.collector.collect(obj)
- return obj
-
- def __init__(self, name=None, fixtures=tuple(), **kwargs):
- self.fixtures = self.fixtures + list(fixtures)
- if name is None:
- name = self.__class__.__name__
- self.name = name
-
-class TestFunction(TestCase):
- '''
- TestCase implementation which uses a callable object as a test.
- '''
- def __init__(self, function, name=None, **kwargs):
- self.test_function = function
- if name is None:
- name = function.__name__
- TestCase.__init__(self, name=name, **kwargs)
-
- def test(self, *args, **kwargs):
- self.test_function(*args, **kwargs)
-
-# TODO Change the decorator to make this easier to create copy tests.
-# Good way to do so might be return by reference.
-def testfunction(function=None, name=None, fixtures=tuple()):
- '''
- A decorator used to wrap a function as a TestFunction.
- '''
- def testfunctiondecorator(function):
- '''Decorator used to mark a function as a test case.'''
- kwargs = {}
- if name is not None:
- kwargs['name'] = name
- if fixtures is not None:
- kwargs['fixtures'] = fixtures
- TestFunction(function, **kwargs)
- return function
- if function is not None:
- return testfunctiondecorator(function)
- else:
- return testfunctiondecorator
--- /dev/null
+# Copyright (c) 2017 Mark D. Hill and David A. Wood
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Authors: Sean Wilson
+
+import functools
+
+import testlib.helper as helper
+import testlib.runner as runner_mod
+
+class TestingException(Exception):
+ '''Common ancestor for manual Testing Exceptions.'''
+class TestFailException(TestingException):
+ '''Signals that a test has failed.'''
+class TestSkipException(TestingException):
+ '''Signals that a test has been skipped.'''
+
+def fail(message):
+ '''Cause the current test to fail with the given message.'''
+ raise TestFailException(message)
+
+def skip(message):
+ '''Cause the current test to skip with the given message.'''
+ raise TestSkipException(message)
+
+class TestCase(object):
+ '''
+ Base class for all tests.
+
+ ..note::
+ The :func:`__new__` method enables collection of test cases, it must
+ be called in order for test cases to be collected.
+ '''
+ fixtures = []
+
+ # TODO, remove explicit dependency. Use the loader to set the
+ # default runner
+ runner = runner_mod.TestRunner
+ collector = helper.InstanceCollector()
+
+ def __new__(cls, *args, **kwargs):
+ obj = super(TestCase, cls).__new__(cls)
+ TestCase.collector.collect(obj)
+ return obj
+
+ def __init__(self, name=None, fixtures=tuple(), **kwargs):
+ self.fixtures = self.fixtures + list(fixtures)
+ if name is None:
+ name = self.__class__.__name__
+ self.name = name
+
+class TestFunction(TestCase):
+ '''
+ TestCase implementation which uses a callable object as a test.
+ '''
+ def __init__(self, function, name=None, **kwargs):
+ self.test_function = function
+ if name is None:
+ name = function.__name__
+ TestCase.__init__(self, name=name, **kwargs)
+
+ def test(self, *args, **kwargs):
+ self.test_function(*args, **kwargs)
+
+# TODO Change the decorator to make this easier to create copy tests.
+# Good way to do so might be return by reference.
+def testfunction(function=None, name=None, fixtures=tuple()):
+ '''
+ A decorator used to wrap a function as a TestFunction.
+ '''
+ def testfunctiondecorator(function):
+ '''Decorator used to mark a function as a test case.'''
+ kwargs = {}
+ if name is not None:
+ kwargs['name'] = name
+ if fixtures is not None:
+ kwargs['fixtures'] = fixtures
+ TestFunction(function, **kwargs)
+ return function
+ if function is not None:
+ return testfunctiondecorator(function)
+ else:
+ return testfunctiondecorator
import os
import itertools
-import config
+import testlib.configuration as configuration
class UID(object):
sep = ':'
@staticmethod
def _shorten_path(path):
return os.path.relpath(path,
- os.path.commonprefix((config.constants.testing_base,
+ os.path.commonprefix((configuration.constants.testing_base,
path)))
@staticmethod
def _full_path(short_path):
- return os.path.join(config.constants.testing_base, short_path)
+ return os.path.join(configuration.constants.testing_base, short_path)
@classmethod
def uid_to_path(cls, uid):
'''
import itertools
-import log
-import uid
-from state import Status, Result
+import testlib.uid as uid
+from testlib.state import Status, Result
class TestCaseMetadata():
def __init__(self, name, uid, path, result, status, suite_uid):
# TODO Change log to provide status_update, result_update for all types.
def log_status(self, status):
+ import testlib.log as log
log.test_log.status_update(self, status)
def log_result(self, result):
+ import testlib.log as log
log.test_log.result_update(self, result)
def __iter__(self):
from common import Options
from common.Caches import *
from ruby import Ruby
+from six import add_metaclass
_have_kvm_support = 'BaseKvmCPU' in globals()
+@add_metaclass(ABCMeta)
class BaseSystem(object):
"""Base system builder.
the initialization process.
"""
- __metaclass__ = ABCMeta
-
def __init__(self, mem_mode='timing', mem_class=SimpleMemory,
cpu_class=TimingSimpleCPU, num_cpus=1, num_threads=1,
checker=False, mem_size=None, use_ruby=False):
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-import suite
-import fixture
+import testlib.suite
+import testlib.fixture
-from suite import *
-from fixture import *
+from .suite import *
+from .fixture import *
binary = joinpath(workload_binary.path, workload)
for cpu in valid_isas[isa]:
- gem5_verify_config(
+ gem5_verify_config(
name='cpu_test_{}_{}'.format(cpu,workload),
verifiers=verifiers,
config=joinpath(getcwd(), 'run.py'),
config_args=['--cpu={}'.format(cpu), binary],
valid_isas=(isa.upper(),),
fixtures=[workload_binary]
- )
+ )
import sys
import socket
import threading
-import urllib
-import urllib2
+
+from six.moves import urllib
from testlib.fixture import Fixture
-from testlib.config import config, constants
+from testlib.configuration import config, constants
from testlib.helper import log_call, cacheresult, joinpath, absdirpath
import testlib.log as log
from testlib.state import Result
except OSError as e:
if e.errno != errno.EEXIST:
raise
- urllib.urlretrieve(self.url, self.filename)
+ urllib.request.urlretrieve(self.url, self.filename)
def _getremotetime(self):
import datetime, time
import _strptime # Needed for python threading bug
- u = urllib2.urlopen(self.url, timeout=10)
+ u = urllib.request.urlopen(self.url, timeout=10)
+
return time.mktime(datetime.datetime.strptime( \
- u.info().getheaders("Last-Modified")[0],
+ u.info()["Last-Modified"],
"%a, %d %b %Y %X GMT").timetuple())
def _setup(self, testitem):
else:
try:
t = self._getremotetime()
- except (urllib2.URLError, socket.timeout):
+ except (urllib.error.URLError, socket.timeout):
# Problem checking the server, use the old files.
log.test_log.debug("Could not contact server. Binaries may be old.")
return
else:
try:
t = self._getremotetime()
- except (urllib2.URLError, socket.timeout):
+ except (urllib.error.URLError, socket.timeout):
# Problem checking the server, use the old files.
log.test_log.debug("Could not contact server. "
"Binaries may be old.")
Test file for simple memory test
TODO: Add stats checking
'''
+import six
+
from testlib import *
gem5_verify_config(
for name, params in simple_mem_params:
- args = ['--' + key + '=' + val for key,val in params.iteritems()]
+ args = ['--' + key + '=' + val for key,val in six.iteritems(params)]
gem5_verify_config(
name='simple_mem_' + name,
import subprocess
import sys
-from testlib.test import TestFunction
+from testlib.test_util import TestFunction
from testlib.suite import TestSuite
from testlib.helper import log_call
-from testlib.config import constants, config
-from fixture import TempdirFixture, Gem5Fixture, VariableFixture
-import verifier
+from testlib.configuration import constants, config
+from .fixture import TempdirFixture, Gem5Fixture, VariableFixture
+
+from . import verifier
def gem5_verify_config(name,
config,
command.append(config)
# Config_args should set up the program args.
command.extend(config_args)
- returncode.value = log_call(params.log, command, stderr=sys.stderr)
+ returncode.value = log_call(params.log, command, stdout=sys.stdout,
+ stderr=sys.stderr)
return test_run_gem5
'''
import re
-from testlib import test
-from testlib.config import constants
+from testlib import test_util as test
+from testlib.configuration import constants
from testlib.helper import joinpath, diff_out_file
class Verifier(object):
-#!/usr/bin/env python2.7
+#!/usr/bin/env python
'''
The main source for testlib. Ties together the default test runners and
loaders.
import sys
import os
+os.environ["PYTHONUNBUFFERED"] = "1"
+
base_dir = os.path.dirname(os.path.abspath(__file__))
ext_path = os.path.join(base_dir, os.pardir, 'ext')
sys.path.insert(0, ext_path)
import testlib.main as testlib
-import testlib.config as config
+import testlib.configuration as config
import testlib.helper as helper
config.basedir = helper.absdirpath(__file__)
-#!/usr/bin/env python2.7
+#!/usr/bin/env python
#
# Copyright (c) 2016 ARM Limited
# All rights reserved
-#!/usr/bin/env python2.7
+#!/usr/bin/env python
#
# Copyright (c) 2016 ARM Limited
# All rights reserved
-#!/usr/bin/env python2.7
+#!/usr/bin/env python
#
# Copyright (c) 2016 ARM Limited
# All rights reserved
from abc import ABCMeta, abstractmethod
import inspect
import pickle
+from six import add_metaclass
import string
import sys
def __nonzero__(self):
return all([ r for r in self.results ])
+@add_metaclass(ABCMeta)
class ResultFormatter(object):
- __metaclass__ = ABCMeta
def __init__(self, fout=sys.stdout, verbose=False):
self.verbose = verbose
-#!/usr/bin/env python2.7
+#!/usr/bin/env python
#
# Copyright (c) 2016-2017 ARM Limited
# All rights reserved
from abc import ABCMeta, abstractmethod
import os
from collections import namedtuple
-from .units import *
-from .helpers import FileIgnoreList
-from .results import TestResult
+
+from six import add_metaclass
+
+import sys
+sys.path.append(os.path.dirname(__file__))
+
+from units import *
+from helpers import FileIgnoreList
+from results import TestResult
import shutil
_test_base = os.path.join(os.path.dirname(__file__), "..")
all_categories = ("quick", "long")
all_modes = ("fs", "se")
+@add_metaclass(ABCMeta)
class Test(object):
"""Test case base class.
"""
- __metaclass__ = ABCMeta
-
def __init__(self, name):
self.test_name = name
-#!/usr/bin/env python2.7
+#!/usr/bin/env python
#
# Copyright (c) 2016 ARM Limited
# All rights reserved
import functools
import os
import re
+from six import add_metaclass
import subprocess
import sys
import traceback
-from .results import UnitResult
-from .helpers import *
+sys.path.append(os.path.dirname(__file__))
+from results import UnitResult
+from helpers import *
_test_base = os.path.join(os.path.dirname(__file__), "..")
+@add_metaclass(ABCMeta)
class TestUnit(object):
"""Base class for all test units.
"""
- __metaclass__ = ABCMeta
-
def __init__(self, name, ref_dir, test_dir, skip=False):
self.name = name
self.ref_dir = ref_dir
-#!/usr/bin/env python2.7
+#!/usr/bin/env python
#
# Copyright (c) 2016 ARM Limited
# All rights reserved