From: Nikos Nikoleris Date: Tue, 18 Jun 2019 10:50:24 +0000 (+0100) Subject: tests: Refactor the Gem5Fixture to derive from UniqueFixture X-Git-Tag: v19.0.0.0~678 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=8282b7408b68187155e8109ba4dd267f5bac2c94;p=gem5.git tests: Refactor the Gem5Fixture to derive from UniqueFixture Gem5Fixture is used to define a fixture for building the gem5 binary. Most tests are expected to define their own Gem5Fixture, however, as some might depend on the same binary (e.g., ./build/ARM/gem5.opt), they will try to re-define a fixture for the same target. This patchset changes Gem5Fixture to derive from UniqueFixture. In addition, this patchset changes the way global fixtures are discovered to work with the new Gem5Fixture class. Instead of enumerating them when test definitions are loaded, we do so after the tests have been filtered according to specified tags (e.g., include opt variant, exclude fast, debug variants). Change-Id: Ie868a7e18ef6c3271f3c8a658229657cd43997cb Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/19251 Tested-by: kokoro Reviewed-by: Giacomo Travaglini Maintainer: Giacomo Travaglini --- diff --git a/ext/testlib/fixture.py b/ext/testlib/fixture.py index be8924474..7af6cb289 100644 --- a/ext/testlib/fixture.py +++ b/ext/testlib/fixture.py @@ -32,8 +32,6 @@ import traceback import helper import log -global_fixtures = [] - class SkipException(Exception): def __init__(self, fixture, testitem): self.fixture = fixture @@ -71,21 +69,11 @@ class Fixture(object): if name is None: name = self.__class__.__name__ self.name = name + self._is_global = False def skip(self, testitem): raise SkipException(self.name, testitem.metadata) - def schedule_finalized(self, schedule): - ''' - This method is called once the schedule of for tests is known. - To enable tests to use the same fixture defintion for each execution - fixtures must return a copy of themselves in this method. - - :returns: a copy of this fixture which will be setup/torndown - when the test item this object is tied to is about to execute. - ''' - return self.copy() - def init(self, *args, **kwargs): pass @@ -95,9 +83,6 @@ class Fixture(object): def teardown(self, testitem): pass - def copy(self): - return copy.deepcopy(self) - def skip_cleanup(self): ''' If this method is called, then we should make sure that nothing is @@ -105,11 +90,8 @@ class Fixture(object): ''' pass + def set_global(self): + self._is_global = True -def globalfixture(fixture): - ''' - Store the given fixture as a global fixture. Its setup() method - will be called before the first test is executed. - ''' - global_fixtures.append(fixture) - return fixture + def is_global(self): + return self._is_global diff --git a/ext/testlib/loader.py b/ext/testlib/loader.py index e788c33a9..8f8f60e70 100644 --- a/ext/testlib/loader.py +++ b/ext/testlib/loader.py @@ -147,7 +147,7 @@ class Loader(object): @property def schedule(self): - return wrappers.LoadedLibrary(self.suites, fixture_mod.global_fixtures) + return wrappers.LoadedLibrary(self.suites) def load_schedule_for_suites(self, *uids): files = {uid.UID.uid_to_path(id_) for id_ in uids} @@ -155,8 +155,7 @@ class Loader(object): self.load_file(file_) return wrappers.LoadedLibrary( - [self.suite_uids[id_] for id_ in uids], - fixture_mod.global_fixtures) + [self.suite_uids[id_] for id_ in uids]) def _verify_no_duplicate_suites(self, new_suites): new_suite_uids = self.suite_uids.copy() diff --git a/ext/testlib/main.py b/ext/testlib/main.py index ac795473d..cbba0005f 100644 --- a/ext/testlib/main.py +++ b/ext/testlib/main.py @@ -254,20 +254,6 @@ def run_schedule(test_schedule, log_handler): log_handler.schedule_finalized(test_schedule) - # Iterate through all fixtures notifying them of the test schedule. - for suite in test_schedule: - copied_fixtures = [] - for fixture in suite.fixtures: - copied_fixtures.append(fixture.schedule_finalized(test_schedule)) - suite.fixtures = copied_fixtures - - for test in suite: - copied_fixtures = [] - for fixture in test.fixtures: - copied_fixtures.append(fixture.schedule_finalized( - test_schedule)) - test.fixtures = copied_fixtures - log.test_log.message(terminal.separator()) log.test_log.message('Running Tests from {} suites' .format(len(test_schedule.suites)), bold=True) diff --git a/ext/testlib/wrappers.py b/ext/testlib/wrappers.py index 4e96f3629..4bd22a468 100644 --- a/ext/testlib/wrappers.py +++ b/ext/testlib/wrappers.py @@ -1,3 +1,15 @@ +# Copyright (c) 2019 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. # @@ -179,9 +191,8 @@ class LoadedLibrary(LoadedTestable): Wraps a collection of all loaded test suites and provides utility functions for accessing fixtures. ''' - def __init__(self, suites, global_fixtures): + def __init__(self, suites): LoadedTestable.__init__(self, suites) - self.global_fixtures = global_fixtures def _generate_metadata(self): return LibraryMetadata( **{ @@ -196,19 +207,13 @@ class LoadedLibrary(LoadedTestable): ''' return iter(self.obj) - def all_fixture_tuples(self): - return itertools.chain( - self.global_fixtures, - *(suite.fixtures for suite in self.obj)) - def all_fixtures(self): ''' :returns: an interator overall all global, suite, and test fixtures ''' return itertools.chain(itertools.chain( - self.global_fixtures, - *(suite.fixtures for suite in self.obj)), + *(suite.fixtures for suite in self.obj)), *(self.test_fixtures(suite) for suite in self.obj) ) @@ -221,7 +226,11 @@ class LoadedLibrary(LoadedTestable): @property def fixtures(self): - return self.global_fixtures + global_fixtures = [] + for fixture in self.all_fixtures(): + if fixture.is_global(): + global_fixtures.append(fixture) + return global_fixtures @property def uid(self): diff --git a/tests/gem5/fixture.py b/tests/gem5/fixture.py index b04a334b0..e84b89f69 100644 --- a/tests/gem5/fixture.py +++ b/tests/gem5/fixture.py @@ -43,7 +43,7 @@ import tempfile import shutil import threading -from testlib.fixture import Fixture, globalfixture +from testlib.fixture import Fixture from testlib.config import config, constants from testlib.helper import log_call, cacheresult, joinpath, absdirpath import testlib.log as log @@ -112,7 +112,7 @@ class UniqueFixture(Fixture): self._setup(testitem) -class SConsFixture(Fixture): +class SConsFixture(UniqueFixture): ''' Fixture will wait until all SCons targets are collected and tests are about to be ran, then will invocate a single instance of SCons for all @@ -121,21 +121,18 @@ class SConsFixture(Fixture): :param directory: The directory which scons will -C (cd) into before executing. If None is provided, will choose the config base_dir. ''' - def __init__(self, directory=None, target_class=None, options=[]): - self.directory = directory if directory else config.base_dir - self.target_class = target_class if target_class else SConsTarget - self.threads = config.threads - self.targets = set() - self.options = options - super(SConsFixture, self).__init__() - def setup(self, testitem): + def __new__(cls, target): + obj = super(SConsFixture, cls).__new__(cls, target) + return obj + + def _setup(self, testitem): if config.skip_build: return command = [ 'scons', '-C', self.directory, - '-j', str(self.threads), + '-j', str(config.threads), '--ignore-style' ] @@ -159,70 +156,27 @@ class SConsFixture(Fixture): command.extend(self.options) log_call(log.test_log, command) - -class SConsTarget(Fixture): - # The singleton scons fixture we'll use for all targets. - default_scons_invocation = None - - def __init__(self, target, build_dir=None, invocation=None): - ''' - Represents a target to be built by an 'invocation' of scons. - - :param target: The target known to scons. - - :param build_dir: The 'build' directory path which will be prepended - to the target name. - - :param invocation: Represents an invocation of scons which we will - automatically attach this target to. If None provided, uses the - main 'scons' invocation. - ''' - - if build_dir is None: - build_dir = config.build_dir - self.target = os.path.join(build_dir, target) - super(SConsTarget, self).__init__(name=target) - - if invocation is None: - if self.default_scons_invocation is None: - SConsTarget.default_scons_invocation = SConsFixture() - globalfixture(SConsTarget.default_scons_invocation) - - invocation = self.default_scons_invocation - self.invocation = invocation - - def schedule_finalized(self, schedule): - self.invocation.targets.add(self.target) - return Fixture.schedule_finalized(self, schedule) - -class Gem5Fixture(SConsTarget): - other_invocations = {} # stores scons invocations other than the default - - def __init__(self, isa, variant, protocol=None): +class Gem5Fixture(SConsFixture): + def __new__(cls, isa, variant, protocol=None): + target_dir = joinpath(config.build_dir, isa.upper()) if protocol: - # When specifying an non-default protocol, we have to make a - # separate scons invocation with specific parameters. However, if - # more than one tests needs the same target, we need to make sure - # that we don't call scons too many times. - target_dir = isa.upper()+'-'+protocol - target = joinpath(target_dir, 'gem5.%s' % variant) - if target_dir in self.other_invocations.keys(): - invocation = self.other_invocations[target_dir] - else: - options = ['PROTOCOL='+protocol, '--default='+isa.upper()] - invocation = SConsFixture(options=options) - globalfixture(invocation) - Gem5Fixture.other_invocations[target_dir] = invocation - else: - target = joinpath(isa.upper(), 'gem5.%s' % variant) - invocation = None # use default - super(Gem5Fixture, self).__init__(target, invocation=invocation) + target_dir += '_' + protocol + target = joinpath(target_dir, 'gem5.%s' % variant) + obj = super(Gem5Fixture, cls).__new__(cls, target) + return obj + def _init(self, isa, variant, protocol=None): self.name = constants.gem5_binary_fixture_name + + self.targets = [self.target] self.path = self.target - self.isa = isa - self.variant = variant + self.directory = config.base_dir + self.options = [] + if protocol: + self.options = [ '--default=' + isa.upper(), + 'PROTOCOL=' + protocol ] + self.set_global() class MakeFixture(Fixture): def __init__(self, directory, *args, **kwargs):