xref: /aosp_15_r20/external/autotest/client/cros/video/device_capability.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Copyright 2017 The Chromium OS Authors. All rights reserved.
2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
3*9c5db199SXin Li# found in the LICENSE file.
4*9c5db199SXin Li
5*9c5db199SXin Liimport importlib
6*9c5db199SXin Liimport logging
7*9c5db199SXin Liimport os
8*9c5db199SXin Liimport re
9*9c5db199SXin Li
10*9c5db199SXin Liimport yaml
11*9c5db199SXin Li
12*9c5db199SXin Lifrom autotest_lib.client.common_lib import error
13*9c5db199SXin Li
14*9c5db199SXin Liclass DeviceCapability(object):
15*9c5db199SXin Li    """
16*9c5db199SXin Li    Generate capabilities status on DUT from yaml files in a given path.
17*9c5db199SXin Li    Answer from the capabilities whether some capability is satisfied on DUT.
18*9c5db199SXin Li    """
19*9c5db199SXin Li
20*9c5db199SXin Li    def __init__(self, settings_path='/usr/local/etc/autotest-capability'):
21*9c5db199SXin Li        """
22*9c5db199SXin Li        @param settings_path: string, the base directory for autotest
23*9c5db199SXin Li                              capability. There should be yaml files.
24*9c5db199SXin Li        """
25*9c5db199SXin Li        self.capabilities = self.__get_autotest_capability(settings_path)
26*9c5db199SXin Li        logging.info("Capabilities:\n%r", self.capabilities)
27*9c5db199SXin Li
28*9c5db199SXin Li
29*9c5db199SXin Li    def __get_autotest_capability(self, settings_path):
30*9c5db199SXin Li        """
31*9c5db199SXin Li        Generate and summarize capabilities from yaml files in
32*9c5db199SXin Li        settings_path with detectors.
33*9c5db199SXin Li
34*9c5db199SXin Li        @param settings_path: string, the base directory for autotest
35*9c5db199SXin Li                              capability. There should be yaml files.
36*9c5db199SXin Li        @returns dict:
37*9c5db199SXin Li            The capabilities on DUT.
38*9c5db199SXin Li            Its key is string denoting a capability. Its value is 'yes', 'no' or
39*9c5db199SXin Li            'disable.'
40*9c5db199SXin Li        """
41*9c5db199SXin Li
42*9c5db199SXin Li        def run_detector(name):
43*9c5db199SXin Li            """
44*9c5db199SXin Li            Run a detector in the detector directory. (i.e.
45*9c5db199SXin Li            autotest/files/client/cros/video/detectors)
46*9c5db199SXin Li            Return the result of the detector.
47*9c5db199SXin Li
48*9c5db199SXin Li            @param name: string, the name of running detector.
49*9c5db199SXin Li            @returns string, a result of detect() in the detector script.
50*9c5db199SXin Li            """
51*9c5db199SXin Li            if name not in detect_results:
52*9c5db199SXin Li                detector = importlib.import_module(
53*9c5db199SXin Li                    "autotest_lib.client.cros.video.detectors.%s"
54*9c5db199SXin Li                    % name)
55*9c5db199SXin Li                detect_results[name] = detector.detect()
56*9c5db199SXin Li                logging.info("Detector result (%s): %s",
57*9c5db199SXin Li                             name, detect_results[name])
58*9c5db199SXin Li            return detect_results[name]
59*9c5db199SXin Li
60*9c5db199SXin Li        managed_cap_fpath = os.path.join(settings_path,
61*9c5db199SXin Li                                         'managed-capabilities.yaml')
62*9c5db199SXin Li        if not os.path.exists(managed_cap_fpath):
63*9c5db199SXin Li            raise error.TestFail("%s is not installed" % managed_cap_fpath)
64*9c5db199SXin Li        managed_caps = yaml.safe_load(open(managed_cap_fpath))
65*9c5db199SXin Li
66*9c5db199SXin Li        cap_files = [f for f in os.listdir(settings_path)
67*9c5db199SXin Li                     if re.match(r'^[0-9]+-.*\.yaml$', f)]
68*9c5db199SXin Li        cap_files.sort(key=lambda f: int(f.split('-', 1)[0]))
69*9c5db199SXin Li
70*9c5db199SXin Li        detect_results = {}
71*9c5db199SXin Li        autotest_caps = dict.fromkeys(managed_caps, 'no')
72*9c5db199SXin Li        for fname in cap_files:
73*9c5db199SXin Li            logging.debug('Processing caps: %s', fname)
74*9c5db199SXin Li            fname = os.path.join(settings_path, fname)
75*9c5db199SXin Li            for rule in yaml.safe_load(open(fname)):
76*9c5db199SXin Li                # The type of rule is string or dict
77*9c5db199SXin Li                # If the type is a string, it is a capability (e.g. webcam).
78*9c5db199SXin Li                # If a specific condition (e.g. kepler, cpu type) is required,
79*9c5db199SXin Li                # rule would be dict, for example,
80*9c5db199SXin Li                # {'detector': 'intel_cpu',
81*9c5db199SXin Li                #  'match': ['intel_celeron_1007U'],
82*9c5db199SXin Li                #  'capabilities': ['no hw_h264_enc_1080_30'] }.
83*9c5db199SXin Li                logging.debug("%r", rule)
84*9c5db199SXin Li                caps = []
85*9c5db199SXin Li                if isinstance(rule, dict):
86*9c5db199SXin Li                    if run_detector(rule['detector']) in rule['match']:
87*9c5db199SXin Li                        caps = rule['capabilities']
88*9c5db199SXin Li                else:
89*9c5db199SXin Li                    caps = [rule]
90*9c5db199SXin Li
91*9c5db199SXin Li                for capability in caps:
92*9c5db199SXin Li                    m = re.match(r'(?:(disable|no)\s+)?([\w\-]+)$', capability)
93*9c5db199SXin Li                    prefix, capability = m.groups()
94*9c5db199SXin Li                    if capability in managed_caps:
95*9c5db199SXin Li                        autotest_caps[capability] = prefix or 'yes'
96*9c5db199SXin Li                    else:
97*9c5db199SXin Li                        raise error.TestFail(
98*9c5db199SXin Li                            "Unexpected capability: %s" % capability)
99*9c5db199SXin Li
100*9c5db199SXin Li        return autotest_caps
101*9c5db199SXin Li
102*9c5db199SXin Li
103*9c5db199SXin Li    def get_managed_caps(self):
104*9c5db199SXin Li        return self.capabilities.keys()
105*9c5db199SXin Li
106*9c5db199SXin Li
107*9c5db199SXin Li    def get_capability_results(self):
108*9c5db199SXin Li        return self.capabilities
109*9c5db199SXin Li
110*9c5db199SXin Li
111*9c5db199SXin Li    def get_capability(self, cap):
112*9c5db199SXin Li        """
113*9c5db199SXin Li        Decide if a device satisfies a required capability for an autotest.
114*9c5db199SXin Li
115*9c5db199SXin Li        @param cap: string, denoting one capability. It must be one in
116*9c5db199SXin Li                    settings_path + 'managed-capabilities.yaml.'
117*9c5db199SXin Li        @returns 'yes', 'no', or 'disable.'
118*9c5db199SXin Li        """
119*9c5db199SXin Li        try:
120*9c5db199SXin Li            return self.capabilities[cap]
121*9c5db199SXin Li        except KeyError:
122*9c5db199SXin Li            raise error.TestFail("Unexpected capability: %s" % cap)
123*9c5db199SXin Li
124*9c5db199SXin Li
125*9c5db199SXin Li    def ensure_capability(self, cap):
126*9c5db199SXin Li        """
127*9c5db199SXin Li        Raise TestNAError if a device doesn't satisfy cap.
128*9c5db199SXin Li        """
129*9c5db199SXin Li        if self.get_capability(cap) != 'yes':
130*9c5db199SXin Li            raise error.TestNAError("Missing Capability: %s" % cap)
131*9c5db199SXin Li
132*9c5db199SXin Li
133*9c5db199SXin Li    def have_capability(self, cap):
134*9c5db199SXin Li        """
135*9c5db199SXin Li        Return whether cap is available.
136*9c5db199SXin Li        """
137*9c5db199SXin Li        return self.get_capability(cap) == 'yes'
138