1*9c5db199SXin Li# Lint as: python2, python3 2*9c5db199SXin Li# Copyright 2019 The Chromium OS Authors. All rights reserved. 3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 4*9c5db199SXin Li# found in the LICENSE file. 5*9c5db199SXin Li 6*9c5db199SXin Liimport json 7*9c5db199SXin Liimport logging 8*9c5db199SXin Liimport os 9*9c5db199SXin Li 10*9c5db199SXin Liimport common 11*9c5db199SXin Lifrom autotest_lib.client.common_lib import error 12*9c5db199SXin Li 13*9c5db199SXin Li 14*9c5db199SXin Li# Path to the local checkout of the fw-testing-configs repo 15*9c5db199SXin Li_CONFIG_DIR = os.path.abspath(os.path.join( 16*9c5db199SXin Li os.path.dirname(os.path.realpath(__file__)), os.pardir, 17*9c5db199SXin Li 'fw-testing-configs')) 18*9c5db199SXin Li_CONSOLIDATED_JSON_BASENAME = 'CONSOLIDATED.json' 19*9c5db199SXin Li 20*9c5db199SXin Li 21*9c5db199SXin Lidef _consolidated_json_fp(): 22*9c5db199SXin Li """Get the absolute path to CONSOLIDATED.json.""" 23*9c5db199SXin Li return os.path.join(_CONFIG_DIR, _CONSOLIDATED_JSON_BASENAME) 24*9c5db199SXin Li 25*9c5db199SXin Li 26*9c5db199SXin Liclass Config(object): 27*9c5db199SXin Li """Configuration for FAFT tests. 28*9c5db199SXin Li 29*9c5db199SXin Li This object is meant to be the interface to all configuration required 30*9c5db199SXin Li by FAFT tests, including device specific overrides. 31*9c5db199SXin Li 32*9c5db199SXin Li It gets the values from the JSON files in _CONFIG_DIR. 33*9c5db199SXin Li Default values are declared in the DEFAULTS.json. 34*9c5db199SXin Li Platform-specific overrides come from <platform>.json. 35*9c5db199SXin Li If the platform has model-specific overrides, then those take precedence 36*9c5db199SXin Li over the platform's config. 37*9c5db199SXin Li If the platform inherits overrides from a parent platform, then the child 38*9c5db199SXin Li platform's overrides take precedence over the parent's. 39*9c5db199SXin Li 40*9c5db199SXin Li @ivar platform: string containing the board name being tested. 41*9c5db199SXin Li @ivar model: string containing the model name being tested 42*9c5db199SXin Li """ 43*9c5db199SXin Li 44*9c5db199SXin Li def __init__(self, platform, model=None): 45*9c5db199SXin Li """Initialize an object with FAFT settings. 46*9c5db199SXin Li Load JSON in order of importance (model, platform, parent/s, DEFAULTS). 47*9c5db199SXin Li 48*9c5db199SXin Li @param platform: The name of the platform being tested. 49*9c5db199SXin Li """ 50*9c5db199SXin Li self._precedence_list = [] 51*9c5db199SXin Li self._precedence_names = [] 52*9c5db199SXin Li with open(_consolidated_json_fp()) as f: 53*9c5db199SXin Li consolidated_json = json.load(f) 54*9c5db199SXin Li # Load the most specific JSON config possible by splitting `platform` 55*9c5db199SXin Li # at '_'/'-' and reversing ([::-1]). For example, veyron_minnie should 56*9c5db199SXin Li # load minnie.json. octopus_fleex should look for fleex.json. It 57*9c5db199SXin Li # doesn't exist, so instead it loads octopus.json. 58*9c5db199SXin Li platform = platform.lower().replace('-', '_') 59*9c5db199SXin Li for p in platform.rsplit('_')[::-1]: 60*9c5db199SXin Li logging.debug('Looking for %s config', p) 61*9c5db199SXin Li if p in consolidated_json: 62*9c5db199SXin Li logging.info('Found %s config', p) 63*9c5db199SXin Li self.platform = p 64*9c5db199SXin Li break 65*9c5db199SXin Li else: 66*9c5db199SXin Li self.platform = platform 67*9c5db199SXin Li if self.platform in consolidated_json: 68*9c5db199SXin Li platform_config = consolidated_json[self.platform] 69*9c5db199SXin Li seen_platforms = [self.platform] 70*9c5db199SXin Li self._add_cfg_to_precedence(self.platform, platform_config) 71*9c5db199SXin Li model_configs = platform_config.get('models', {}) 72*9c5db199SXin Li model_config = model_configs.get(str(model), None) 73*9c5db199SXin Li if model_config is not None: 74*9c5db199SXin Li self._add_cfg_to_precedence( 75*9c5db199SXin Li 'MODEL:%s' % model, model_config, prepend=True) 76*9c5db199SXin Li logging.debug('Using model override for %s', model) 77*9c5db199SXin Li parent_platform = self._precedence_list[-1].get('parent', None) 78*9c5db199SXin Li while parent_platform is not None: 79*9c5db199SXin Li if parent_platform in seen_platforms: 80*9c5db199SXin Li loop = ' -> '.join(seen_platforms + [parent_platform]) 81*9c5db199SXin Li raise error.TestError('fw-testing-configs for platform %s ' 82*9c5db199SXin Li 'contains an inheritance loop: %s' % ( 83*9c5db199SXin Li self.platform, loop)) 84*9c5db199SXin Li parent_config = consolidated_json[parent_platform] 85*9c5db199SXin Li seen_platforms.append(parent_platform) 86*9c5db199SXin Li self._add_cfg_to_precedence(parent_platform, parent_config) 87*9c5db199SXin Li parent_platform = self._precedence_list[-1].get('parent', None) 88*9c5db199SXin Li else: 89*9c5db199SXin Li logging.debug('Platform %s not found in %s. Using DEFAULTS.', 90*9c5db199SXin Li self.platform, consolidated_json) 91*9c5db199SXin Li default_config = consolidated_json['DEFAULTS'] 92*9c5db199SXin Li self._add_cfg_to_precedence('DEFAULTS', default_config) 93*9c5db199SXin Li 94*9c5db199SXin Li # Set attributes 95*9c5db199SXin Li all_attributes = self._precedence_list[-1].keys() 96*9c5db199SXin Li self.attributes = {} 97*9c5db199SXin Li self.attributes['platform'] = self.platform 98*9c5db199SXin Li for attribute in all_attributes: 99*9c5db199SXin Li if attribute.endswith('.DOC') or attribute == 'models': 100*9c5db199SXin Li continue 101*9c5db199SXin Li for config_dict in self._precedence_list: 102*9c5db199SXin Li if attribute in config_dict: 103*9c5db199SXin Li self.attributes[attribute] = config_dict[attribute] 104*9c5db199SXin Li break 105*9c5db199SXin Li 106*9c5db199SXin Li def _add_cfg_to_precedence(self, cfg_name, cfg, prepend=False): 107*9c5db199SXin Li """Add a configuration to self._precedence_list. 108*9c5db199SXin Li 109*9c5db199SXin Li @ivar cfg_name: The name of the config. 110*9c5db199SXin Li @ivar cfg: The config dict. 111*9c5db199SXin Li @ivar prepend: If true, add to the beginning of self._precedence_list. 112*9c5db199SXin Li Otherwise, add it to the end. 113*9c5db199SXin Li """ 114*9c5db199SXin Li position = 0 if prepend else len(self._precedence_list) 115*9c5db199SXin Li self._precedence_list.insert(position, cfg) 116*9c5db199SXin Li self._precedence_names.insert(position, cfg_name) 117*9c5db199SXin Li 118*9c5db199SXin Li def __getattr__(self, attr): 119*9c5db199SXin Li if attr in self.attributes: 120*9c5db199SXin Li return self.attributes[attr] 121*9c5db199SXin Li raise AttributeError('FAFT config has no attribute named %s' % attr) 122*9c5db199SXin Li 123*9c5db199SXin Li def __str__(self): 124*9c5db199SXin Li str_list = [] 125*9c5db199SXin Li str_list.append('----------[ FW Testing Config Variables ]----------') 126*9c5db199SXin Li str_list.append('--- Precedence list: %s ---' % self._precedence_names) 127*9c5db199SXin Li for attr in sorted(self.attributes): 128*9c5db199SXin Li str_list.append(' %s: %s' % (attr, self.attributes[attr])) 129*9c5db199SXin Li str_list.append('---------------------------------------------------') 130*9c5db199SXin Li return '\n'.join(str_list) 131