|  | # Copyright lowRISC contributors. | 
|  | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | import logging as log | 
|  | import pprint | 
|  | import sys | 
|  |  | 
|  | from utils import VERBOSE | 
|  |  | 
|  |  | 
|  | class Modes(): | 
|  | """ | 
|  | Abstraction for specifying collection of options called as 'modes'. This is | 
|  | the base class which is extended for run_modes, build_modes, tests and regressions. | 
|  | """ | 
|  | def self_str(self): | 
|  | ''' | 
|  | This is used to construct the string representation of the entire class object. | 
|  | ''' | 
|  | tname = "" | 
|  | if self.type != "": | 
|  | tname = self.type + "_" | 
|  | if self.mname != "": | 
|  | tname += self.mname | 
|  | if log.getLogger().isEnabledFor(VERBOSE): | 
|  | return "\n<---" + tname + ":\n" + pprint.pformat(self.__dict__) + \ | 
|  | "\n--->\n" | 
|  | else: | 
|  | return tname + ":" + self.name | 
|  |  | 
|  | def __str__(self): | 
|  | return self.self_str() | 
|  |  | 
|  | def __repr__(self): | 
|  | return self.self_str() | 
|  |  | 
|  | def __init__(self, mdict): | 
|  | keys = mdict.keys() | 
|  | attrs = self.__dict__.keys() | 
|  |  | 
|  | if 'name' not in keys: | 
|  | log.error("Key \"name\" missing in mode %s", mdict) | 
|  | sys.exit(1) | 
|  |  | 
|  | if not hasattr(self, "type"): | 
|  | log.fatal("Key \"type\" is missing or invalid") | 
|  | sys.exit(1) | 
|  |  | 
|  | if not hasattr(self, "mname"): | 
|  | self.mname = "" | 
|  |  | 
|  | for key in keys: | 
|  | if key not in attrs: | 
|  | log.error("Key %s in %s is invalid", key, mdict) | 
|  | sys.exit(1) | 
|  | setattr(self, key, mdict[key]) | 
|  |  | 
|  | def get_sub_modes(self): | 
|  | sub_modes = [] | 
|  | if hasattr(self, "en_" + self.type + "_modes"): | 
|  | sub_modes = getattr(self, "en_" + self.type + "_modes") | 
|  | return sub_modes | 
|  |  | 
|  | def set_sub_modes(self, sub_modes): | 
|  | setattr(self, "en_" + self.type + "_modes", sub_modes) | 
|  |  | 
|  | def merge_mode(self, mode): | 
|  | ''' | 
|  | Merge a new mode with self. | 
|  | Merge sub mode specified with 'en_*_modes with self. | 
|  | ''' | 
|  |  | 
|  | sub_modes = self.get_sub_modes() | 
|  | is_sub_mode = mode.name in sub_modes | 
|  |  | 
|  | if not mode.name == self.name and not is_sub_mode: | 
|  | return False | 
|  |  | 
|  | # Merge attributes in self with attributes in mode arg, since they are | 
|  | # the same mode but set in separate files, or a sub-mode. | 
|  | for attr, self_attr_val in self.__dict__.items(): | 
|  | mode_attr_val = getattr(mode, attr, None) | 
|  |  | 
|  | # If sub-mode, skip the name fields - they could differ. | 
|  | if is_sub_mode and attr in ['name', 'mname']: | 
|  | continue | 
|  |  | 
|  | # If mode's value is None, then nothing to do here. | 
|  | if mode_attr_val is None: | 
|  | continue | 
|  |  | 
|  | # If self value is None, then replace with mode's value. | 
|  | if self_attr_val is None: | 
|  | setattr(self, attr, mode_attr_val) | 
|  | continue | 
|  |  | 
|  | # If they are equal, then nothing to do here. | 
|  | if self_attr_val == mode_attr_val: | 
|  | continue | 
|  |  | 
|  | # Extend if they are both lists. | 
|  | if isinstance(self_attr_val, list): | 
|  | assert isinstance(mode_attr_val, list) | 
|  | self_attr_val.extend(mode_attr_val) | 
|  | continue | 
|  |  | 
|  | # If the current val is default, replace with new. | 
|  | scalar_types = {str: "", int: -1} | 
|  | default_val = scalar_types.get(type(self_attr_val)) | 
|  |  | 
|  | if type(self_attr_val) in scalar_types.keys( | 
|  | ) and self_attr_val == default_val: | 
|  | setattr(self, attr, mode_attr_val) | 
|  | continue | 
|  |  | 
|  | # Check if their types are compatible. | 
|  | if type(self_attr_val) != type(mode_attr_val): | 
|  | log.error( | 
|  | "Mode %s cannot be merged into %s due to a conflict " | 
|  | "(type mismatch): %s: {%s(%s), %s(%s)}", mode.name, | 
|  | self.name, attr, str(self_attr_val), | 
|  | str(type(self_attr_val)), str(mode_attr_val), | 
|  | str(type(mode_attr_val))) | 
|  | sys.exit(1) | 
|  |  | 
|  | # Check if they are different non-default values. | 
|  | if self_attr_val != default_val and mode_attr_val != default_val: | 
|  | log.error( | 
|  | "Mode %s cannot be merged into %s due to a conflict " | 
|  | "(unable to pick one from different values): " | 
|  | "%s: {%s, %s}", mode.name, self.name, attr, | 
|  | str(self_attr_val), str(mode_attr_val)) | 
|  | sys.exit(1) | 
|  |  | 
|  | # Check newly appended sub_modes, remove 'self' and duplicates | 
|  | sub_modes = self.get_sub_modes() | 
|  |  | 
|  | if sub_modes != []: | 
|  | new_sub_modes = [] | 
|  | for sub_mode in sub_modes: | 
|  | if self.name != sub_mode and sub_mode not in new_sub_modes: | 
|  | new_sub_modes.append(sub_mode) | 
|  | self.set_sub_modes(new_sub_modes) | 
|  | return True | 
|  |  | 
|  | @staticmethod | 
|  | def create_modes(ModeType, mdicts): | 
|  | ''' | 
|  | Create modes of type ModeType from a given list of raw dicts | 
|  | Process dependencies. | 
|  | Return a list of modes objects. | 
|  | ''' | 
|  | def merge_sub_modes(mode, parent, objs): | 
|  | # Check if there are modes available to merge | 
|  | sub_modes = mode.get_sub_modes() | 
|  | if sub_modes == []: | 
|  | return | 
|  |  | 
|  | # Set parent if it is None. If not, check cyclic dependency | 
|  | if parent is None: | 
|  | parent = mode | 
|  | else: | 
|  | if mode.name == parent.name: | 
|  | log.error("Cyclic dependency when processing mode \"%s\"", | 
|  | mode.name) | 
|  | sys.exit(1) | 
|  |  | 
|  | for sub_mode in sub_modes: | 
|  | # Find the sub_mode obj from str | 
|  | found = False | 
|  | for obj in objs: | 
|  | if sub_mode == obj.name: | 
|  | # First recursively merge the sub_modes | 
|  | merge_sub_modes(obj, parent, objs) | 
|  |  | 
|  | # Now merge the sub mode with mode | 
|  | mode.merge_mode(obj) | 
|  | found = True | 
|  | break | 
|  | if not found: | 
|  | log.error( | 
|  | "Sub mode \"%s\" added to mode \"%s\" was not found!", | 
|  | sub_mode, mode.name) | 
|  | sys.exit(1) | 
|  |  | 
|  | modes_objs = [] | 
|  | # create a default mode if available | 
|  | default_mode = ModeType.get_default_mode() | 
|  | if default_mode is not None: | 
|  | modes_objs.append(default_mode) | 
|  |  | 
|  | # Process list of raw dicts that represent the modes | 
|  | # Pass 1: Create unique set of modes by merging modes with the same name | 
|  | for mdict in mdicts: | 
|  | # Create a new item | 
|  | new_mode_merged = False | 
|  | new_mode = ModeType(mdict) | 
|  | for mode in modes_objs: | 
|  | # Merge new one with existing if available | 
|  | if mode.name == new_mode.name: | 
|  | mode.merge_mode(new_mode) | 
|  | new_mode_merged = True | 
|  | break | 
|  |  | 
|  | # Add the new mode to the list if not already appended | 
|  | if not new_mode_merged: | 
|  | modes_objs.append(new_mode) | 
|  | ModeType.item_names.append(new_mode.name) | 
|  |  | 
|  | # Pass 2: Recursively expand sub modes within parent modes | 
|  | for mode in modes_objs: | 
|  | merge_sub_modes(mode, None, modes_objs) | 
|  |  | 
|  | # Return the list of objects | 
|  | return modes_objs | 
|  |  | 
|  | @staticmethod | 
|  | def get_default_mode(ModeType): | 
|  | return None | 
|  |  | 
|  | @staticmethod | 
|  | def find_mode(mode_name, modes): | 
|  | ''' | 
|  | Given a mode_name in string, go through list of modes and return the mode with | 
|  | the string that matches. Thrown an error and return None if nothing was found. | 
|  | ''' | 
|  | for mode in modes: | 
|  | if mode_name == mode.name: | 
|  | return mode | 
|  | return None | 
|  |  | 
|  | @staticmethod | 
|  | def find_and_merge_modes(mode, mode_names, modes, merge_modes=True): | 
|  | ''' | 
|  | ''' | 
|  | found_mode_objs = [] | 
|  | for mode_name in mode_names: | 
|  | sub_mode = Modes.find_mode(mode_name, modes) | 
|  | if sub_mode is not None: | 
|  | found_mode_objs.append(sub_mode) | 
|  | if merge_modes is True: | 
|  | mode.merge_mode(sub_mode) | 
|  | else: | 
|  | log.error("Mode \"%s\" enabled within mode \"%s\" not found!", | 
|  | mode_name, mode.name) | 
|  | sys.exit(1) | 
|  | return found_mode_objs | 
|  |  | 
|  |  | 
|  | class BuildModes(Modes): | 
|  | """ | 
|  | Build modes. | 
|  | """ | 
|  |  | 
|  | # Maintain a list of build_modes str | 
|  | item_names = [] | 
|  |  | 
|  | def __init__(self, bdict): | 
|  | self.name = "" | 
|  | self.type = "build" | 
|  | if not hasattr(self, "mname"): | 
|  | self.mname = "mode" | 
|  | self.is_sim_mode = 0 | 
|  | self.pre_build_cmds = [] | 
|  | self.post_build_cmds = [] | 
|  | self.en_build_modes = [] | 
|  | self.build_opts = [] | 
|  | self.pre_run_cmds = [] | 
|  | self.post_run_cmds = [] | 
|  | self.run_opts = [] | 
|  | self.sw_images = [] | 
|  |  | 
|  | super().__init__(bdict) | 
|  | self.en_build_modes = list(set(self.en_build_modes)) | 
|  |  | 
|  | @staticmethod | 
|  | def get_default_mode(): | 
|  | return BuildModes({"name": "default"}) | 
|  |  | 
|  |  | 
|  | class RunModes(Modes): | 
|  | """ | 
|  | Run modes. | 
|  | """ | 
|  |  | 
|  | # Maintain a list of run_modes str | 
|  | item_names = [] | 
|  |  | 
|  | def __init__(self, rdict): | 
|  | self.name = "" | 
|  | self.type = "run" | 
|  | if not hasattr(self, "mname"): | 
|  | self.mname = "mode" | 
|  | self.reseed = None | 
|  | self.pre_run_cmds = [] | 
|  | self.post_run_cmds = [] | 
|  | self.en_run_modes = [] | 
|  | self.run_opts = [] | 
|  | self.uvm_test = "" | 
|  | self.uvm_test_seq = "" | 
|  | self.build_mode = "" | 
|  | self.sw_images = [] | 
|  | self.sw_build_device = "" | 
|  |  | 
|  | super().__init__(rdict) | 
|  | self.en_run_modes = list(set(self.en_run_modes)) | 
|  |  | 
|  | @staticmethod | 
|  | def get_default_mode(): | 
|  | return None | 
|  |  | 
|  |  | 
|  | class Tests(RunModes): | 
|  | """ | 
|  | Abstraction for tests. The RunModes abstraction can be reused here with a few | 
|  | modifications. | 
|  | """ | 
|  |  | 
|  | # Maintain a list of tests str | 
|  | item_names = [] | 
|  |  | 
|  | # TODO: This info should be passed via hjson | 
|  | defaults = { | 
|  | "reseed": None, | 
|  | "uvm_test": "", | 
|  | "uvm_test_seq": "", | 
|  | "build_mode": "", | 
|  | "sw_images": [], | 
|  | "sw_build_device": "", | 
|  | } | 
|  |  | 
|  | def __init__(self, tdict): | 
|  | if not hasattr(self, "mname"): | 
|  | self.mname = "test" | 
|  | super().__init__(tdict) | 
|  |  | 
|  | @staticmethod | 
|  | def create_tests(tdicts, sim_cfg): | 
|  | ''' | 
|  | Create Tests from a given list of raw dicts. | 
|  | TODO: enhance the raw dict to include file scoped defaults. | 
|  | Process enabled run modes and the set build mode. | 
|  | Return a list of test objects. | 
|  | ''' | 
|  | def get_pruned_en_run_modes(test_en_run_modes, global_en_run_modes): | 
|  | pruned_en_run_modes = [] | 
|  | for test_en_run_mode in test_en_run_modes: | 
|  | if test_en_run_mode not in global_en_run_modes: | 
|  | pruned_en_run_modes.append(test_en_run_mode) | 
|  | return pruned_en_run_modes | 
|  |  | 
|  | tests_objs = [] | 
|  | # Pass 1: Create unique set of tests by merging tests with the same name | 
|  | for tdict in tdicts: | 
|  | # Create a new item | 
|  | new_test_merged = False | 
|  | new_test = Tests(tdict) | 
|  | for test in tests_objs: | 
|  | # Merge new one with existing if available | 
|  | if test.name == new_test.name: | 
|  | test.merge_mode(new_test) | 
|  | new_test_merged = True | 
|  | break | 
|  |  | 
|  | # Add the new test to the list if not already appended | 
|  | if not new_test_merged: | 
|  | tests_objs.append(new_test) | 
|  | Tests.item_names.append(new_test.name) | 
|  |  | 
|  | # Pass 2: Process dependencies | 
|  | build_modes = [] | 
|  | if hasattr(sim_cfg, "build_modes"): | 
|  | build_modes = getattr(sim_cfg, "build_modes") | 
|  |  | 
|  | run_modes = [] | 
|  | if hasattr(sim_cfg, "run_modes"): | 
|  | run_modes = getattr(sim_cfg, "run_modes") | 
|  |  | 
|  | attrs = Tests.defaults | 
|  | for test_obj in tests_objs: | 
|  | # Unpack run_modes first | 
|  | en_run_modes = get_pruned_en_run_modes(test_obj.en_run_modes, | 
|  | sim_cfg.en_run_modes) | 
|  | Modes.find_and_merge_modes(test_obj, en_run_modes, run_modes) | 
|  |  | 
|  | # Find and set the missing attributes from sim_cfg | 
|  | # If not found in sim_cfg either, then throw a warning | 
|  | # TODO: These should be file-scoped | 
|  | for attr in attrs.keys(): | 
|  | # Check if attr value is default | 
|  | val = getattr(test_obj, attr) | 
|  | default_val = attrs[attr] | 
|  | if val == default_val: | 
|  | global_val = None | 
|  | # Check if we can find a default in sim_cfg | 
|  | if hasattr(sim_cfg, attr): | 
|  | global_val = getattr(sim_cfg, attr) | 
|  |  | 
|  | if global_val is not None and global_val != default_val: | 
|  | setattr(test_obj, attr, global_val) | 
|  |  | 
|  | # Unpack the build mode for this test | 
|  | build_mode_objs = Modes.find_and_merge_modes(test_obj, | 
|  | [test_obj.build_mode], | 
|  | build_modes, | 
|  | merge_modes=False) | 
|  | test_obj.build_mode = build_mode_objs[0] | 
|  |  | 
|  | # Error if set build mode is actually a sim mode | 
|  | if test_obj.build_mode.is_sim_mode is True: | 
|  | log.error( | 
|  | "Test \"%s\" uses build_mode %s which is actually a sim mode", | 
|  | test_obj.name, test_obj.build_mode.name) | 
|  | sys.exit(1) | 
|  |  | 
|  | # Merge build_mode's params with self | 
|  | test_obj.pre_run_cmds.extend(test_obj.build_mode.pre_run_cmds) | 
|  | test_obj.post_run_cmds.extend(test_obj.build_mode.post_run_cmds) | 
|  | test_obj.run_opts.extend(test_obj.build_mode.run_opts) | 
|  | test_obj.sw_images.extend(test_obj.build_mode.sw_images) | 
|  |  | 
|  | # Return the list of tests | 
|  | return tests_objs | 
|  |  | 
|  | @staticmethod | 
|  | def merge_global_opts(tests, global_pre_build_cmds, global_post_build_cmds, | 
|  | global_build_opts, global_pre_run_cmds, | 
|  | global_post_run_cmds, global_run_opts, global_sw_images): | 
|  | processed_build_modes = [] | 
|  | for test in tests: | 
|  | if test.build_mode.name not in processed_build_modes: | 
|  | test.build_mode.pre_build_cmds.extend(global_pre_build_cmds) | 
|  | test.build_mode.post_build_cmds.extend(global_post_build_cmds) | 
|  | test.build_mode.build_opts.extend(global_build_opts) | 
|  | processed_build_modes.append(test.build_mode.name) | 
|  | test.pre_run_cmds.extend(global_pre_run_cmds) | 
|  | test.post_run_cmds.extend(global_post_run_cmds) | 
|  | test.run_opts.extend(global_run_opts) | 
|  | test.sw_images.extend(global_sw_images) | 
|  |  | 
|  |  | 
|  | class Regressions(Modes): | 
|  | """ | 
|  | Abstraction for test sets / regression sets. | 
|  | """ | 
|  |  | 
|  | # Maintain a list of tests str | 
|  | item_names = [] | 
|  |  | 
|  | # TODO: define __repr__ and __str__ to print list of tests if VERBOSE | 
|  |  | 
|  | def __init__(self, regdict): | 
|  | self.name = "" | 
|  | self.type = "" | 
|  | if not hasattr(self, "mname"): | 
|  | self.mname = "regression" | 
|  |  | 
|  | # The `tests` member is typically a list, but it defaults to None. | 
|  | # There are 3 possible cases after all the HJson files are parsed, when | 
|  | # this particular regression is supplied to be run: | 
|  | # | 
|  | # 1. `tests` == None:   This is treated as "run ALL available tests". | 
|  | # 2. `tests` == []:     No available tests to run | 
|  | # 3. `len(tests)` > 0:  The provided set of tests are run. | 
|  | self.tests = None | 
|  | self.test_names = [] | 
|  |  | 
|  | self.reseed = None | 
|  | self.excl_tests = []  # TODO: add support for this | 
|  | self.en_sim_modes = [] | 
|  | self.en_run_modes = [] | 
|  | self.pre_build_cmds = [] | 
|  | self.post_build_cmds = [] | 
|  | self.pre_run_cmds = [] | 
|  | self.post_run_cmds = [] | 
|  | self.build_opts = [] | 
|  | self.run_opts = [] | 
|  | super().__init__(regdict) | 
|  |  | 
|  | @staticmethod | 
|  | def create_regressions(regdicts, sim_cfg, tests): | 
|  | ''' | 
|  | Create Test sets from a given list of raw dicts. | 
|  | Return a list of test set objects. | 
|  | ''' | 
|  |  | 
|  | regressions_objs = [] | 
|  | # Pass 1: Create unique set of test sets by merging test sets with the same name | 
|  | for regdict in regdicts: | 
|  | # Create a new item | 
|  | new_regression_merged = False | 
|  | new_regression = Regressions(regdict) | 
|  |  | 
|  | # Check for name conflicts with tests before merging | 
|  | if new_regression.name in Tests.item_names: | 
|  | log.error( | 
|  | "Test names and regression names are required to be unique. " | 
|  | "The regression \"%s\" bears the same name with an existing test. ", | 
|  | new_regression.name) | 
|  | sys.exit(1) | 
|  |  | 
|  | for regression in regressions_objs: | 
|  | # Merge new one with existing if available | 
|  | if regression.name == new_regression.name: | 
|  | regression.merge_mode(new_regression) | 
|  | new_regression_merged = True | 
|  | break | 
|  |  | 
|  | # Add the new test to the list if not already appended | 
|  | if not new_regression_merged: | 
|  | regressions_objs.append(new_regression) | 
|  | Regressions.item_names.append(new_regression.name) | 
|  |  | 
|  | # Pass 2: Process dependencies | 
|  | build_modes = [] | 
|  | if hasattr(sim_cfg, "build_modes"): | 
|  | build_modes = getattr(sim_cfg, "build_modes") | 
|  |  | 
|  | run_modes = [] | 
|  | if hasattr(sim_cfg, "run_modes"): | 
|  | run_modes = getattr(sim_cfg, "run_modes") | 
|  |  | 
|  | for regression_obj in regressions_objs: | 
|  | # Unpack the sim modes | 
|  | found_sim_mode_objs = Modes.find_and_merge_modes( | 
|  | regression_obj, regression_obj.en_sim_modes, build_modes, | 
|  | False) | 
|  |  | 
|  | for sim_mode_obj in found_sim_mode_objs: | 
|  | if sim_mode_obj.is_sim_mode == 0: | 
|  | log.error( | 
|  | "Enabled mode \"%s\" within the regression \"%s\" is not a sim mode", | 
|  | sim_mode_obj.name, regression_obj.name) | 
|  | sys.exit(1) | 
|  |  | 
|  | # Check if sim_mode_obj's sub-modes are a part of regressions's | 
|  | # sim modes- if yes, then it will cause duplication of cmds & | 
|  | # opts. Throw an error and exit. | 
|  | for sim_mode_obj_sub in sim_mode_obj.en_build_modes: | 
|  | if sim_mode_obj_sub in regression_obj.en_sim_modes: | 
|  | log.error( | 
|  | "Regression \"%s\" enables sim_modes \"%s\" and \"%s\". " | 
|  | "The former is already a sub_mode of the latter.", | 
|  | regression_obj.name, sim_mode_obj_sub, | 
|  | sim_mode_obj.name) | 
|  | sys.exit(1) | 
|  |  | 
|  | # Check if sim_mode_obj is also passed on the command line, in | 
|  | # which case, skip | 
|  | if sim_mode_obj.name in sim_cfg.en_build_modes: | 
|  | continue | 
|  |  | 
|  | # Merge the build and run cmds & opts from the sim modes | 
|  | regression_obj.pre_build_cmds.extend( | 
|  | sim_mode_obj.pre_build_cmds) | 
|  | regression_obj.post_build_cmds.extend( | 
|  | sim_mode_obj.post_build_cmds) | 
|  | regression_obj.build_opts.extend(sim_mode_obj.build_opts) | 
|  | regression_obj.pre_run_cmds.extend(sim_mode_obj.pre_run_cmds) | 
|  | regression_obj.post_run_cmds.extend(sim_mode_obj.post_run_cmds) | 
|  | regression_obj.run_opts.extend(sim_mode_obj.run_opts) | 
|  |  | 
|  | # Unpack the run_modes | 
|  | # TODO: If there are other params other than run_opts throw an | 
|  | # error and exit | 
|  | found_run_mode_objs = Modes.find_and_merge_modes( | 
|  | regression_obj, regression_obj.en_run_modes, run_modes, False) | 
|  |  | 
|  | # Only merge the pre_run_cmds, post_run_cmds & run_opts from the | 
|  | # run_modes enabled | 
|  | for run_mode_obj in found_run_mode_objs: | 
|  | # Check if run_mode_obj is also passed on the command line, in | 
|  | # which case, skip | 
|  | if run_mode_obj.name in sim_cfg.en_run_modes: | 
|  | continue | 
|  | regression_obj.pre_run_cmds.extend(run_mode_obj.pre_run_cmds) | 
|  | regression_obj.post_run_cmds.extend(run_mode_obj.post_run_cmds) | 
|  | regression_obj.run_opts.extend(run_mode_obj.run_opts) | 
|  |  | 
|  | # Unpack tests | 
|  | # If `tests` member resolves to None, then we add ALL available | 
|  | # tests for running the regression. | 
|  | if regression_obj.tests is None: | 
|  | log.log(VERBOSE, | 
|  | "Unpacking all tests in scope for regression \"%s\"", | 
|  | regression_obj.name) | 
|  | regression_obj.tests = sim_cfg.tests | 
|  | regression_obj.test_names = Tests.item_names | 
|  |  | 
|  | else: | 
|  | tests_objs = [] | 
|  | regression_obj.test_names = regression_obj.tests | 
|  | for test in regression_obj.tests: | 
|  | test_obj = Modes.find_mode(test, sim_cfg.tests) | 
|  | if test_obj is None: | 
|  | log.error( | 
|  | "Test \"%s\" added to regression \"%s\" not found!", | 
|  | test, regression_obj.name) | 
|  | continue | 
|  | tests_objs.append(test_obj) | 
|  | regression_obj.tests = tests_objs | 
|  |  | 
|  | # Return the list of tests | 
|  | return regressions_objs | 
|  |  | 
|  | def merge_regression_opts(self): | 
|  | processed_build_modes = [] | 
|  | for test in self.tests: | 
|  | if test.build_mode.name not in processed_build_modes: | 
|  | test.build_mode.pre_build_cmds.extend(self.pre_build_cmds) | 
|  | test.build_mode.post_build_cmds.extend(self.post_build_cmds) | 
|  | test.build_mode.build_opts.extend(self.build_opts) | 
|  | processed_build_modes.append(test.build_mode.name) | 
|  | test.pre_run_cmds.extend(self.pre_run_cmds) | 
|  | test.post_run_cmds.extend(self.post_run_cmds) | 
|  | test.run_opts.extend(self.run_opts) | 
|  |  | 
|  | # Override reseed if available. | 
|  | if self.reseed is not None: | 
|  | test.reseed = self.reseed |