xref: /aosp_15_r20/external/autotest/server/cros/faft/utils/config.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
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