xref: /aosp_15_r20/external/autotest/autotest_lib/client/bin/setup_job.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# Lint as: python2, python3
2# Copyright 2007 Google Inc. Released under the GPL v2
3#
4# Eric Li <[email protected]>
5
6import logging, os, pickle, re, sys
7import common
8
9from autotest_lib.client.bin import job as client_job
10from autotest_lib.client.common_lib import base_job
11from autotest_lib.client.common_lib import error
12from autotest_lib.client.common_lib import logging_manager
13from autotest_lib.client.common_lib import packages
14
15
16class setup_job(client_job.job):
17    """
18    setup_job is a job which runs client test setup() method at server side.
19
20    This job is used to pre-setup client tests when development toolchain is not
21    available at client.
22    """
23
24    def __init__(self, options):
25        """
26        Since setup_job is a client job but run on a server, it takes no control
27        file as input. So client_job.__init__ is by-passed.
28
29        @param options: an object passed in from command line OptionParser.
30                        See all options defined on client/bin/autotest.
31        """
32        base_job.base_job.__init__(self, options=options)
33        self._cleanup_debugdir_files()
34        self._cleanup_results_dir()
35        self.machine_dict_list = [{'hostname' : options.hostname}]
36        # Client side tests should always run the same whether or not they are
37        # running in the lab.
38        self.in_lab = False
39        self.pkgmgr = packages.PackageManager(
40            self.autodir, run_function_dargs={'timeout':3600})
41
42
43def init_test(options, testdir):
44    """
45    Instantiate a client test object from a given test directory.
46
47    @param options Command line options passed in to instantiate a setup_job
48                   which associates with this test.
49    @param testdir The test directory.
50    @returns A test object or None if failed to instantiate.
51    """
52
53    locals_dict = locals().copy()
54    globals_dict = globals().copy()
55
56    locals_dict['testdir'] = testdir
57
58    job = setup_job(options=options)
59    locals_dict['job'] = job
60
61    test_name = os.path.split(testdir)[-1]
62    outputdir = os.path.join(job.resultdir, test_name)
63    try:
64        os.makedirs(outputdir)
65    except OSError:
66        pass
67    locals_dict['outputdir'] = outputdir
68
69    sys.path.insert(0, testdir)
70    client_test = None
71    try:
72        try:
73            import_stmt = 'import %s' % test_name
74            init_stmt = ('auto_test = %s.%s(job, testdir, outputdir)' %
75                         (test_name, test_name))
76            exec(import_stmt + '\n' + init_stmt, locals_dict, globals_dict)
77            client_test = globals_dict['auto_test']
78        except ImportError as e:
79            # skips error if test is control file without python test
80            if re.search(test_name, str(e)):
81                pass
82            # give the user a warning if there is an import error.
83            else:
84                logging.exception('%s import error: %s.  Skipping %s' %
85                              (test_name, e, test_name))
86        except Exception as e:
87            # Log other errors (e.g., syntax errors) and collect the test.
88            logging.exception("%s: %s", test_name, e)
89    finally:
90        sys.path.pop(0) # pop up testbindir
91    return client_test
92
93
94def load_all_client_tests(options):
95    """
96    Load and instantiate all client tests.
97
98    This function is inspired from runtest() on client/common_lib/test.py.
99
100    @param options: an object passed in from command line OptionParser.
101                    See all options defined on client/bin/autotest.
102
103    @return a tuple containing the list of all instantiated tests and
104            a list of tests that failed to instantiate.
105    """
106
107    local_namespace = locals().copy()
108    global_namespace = globals().copy()
109
110    all_tests = []
111    broken_tests = []
112    for test_base_dir in ['tests', 'site_tests']:
113        testdir = os.path.join(os.environ['AUTODIR'], test_base_dir)
114        for test_name in sorted(os.listdir(testdir)):
115            client_test = init_test(options, os.path.join(testdir, test_name))
116            if client_test:
117                all_tests.append(client_test)
118            else:
119                broken_tests.append(test_name)
120    return all_tests, broken_tests
121
122
123def setup_test(client_test):
124    """
125    Direct invoke test.setup() method.
126
127    @returns A boolean to represent success or not.
128    """
129
130    # TODO: check if its already build. .version? hash?
131    test_name = client_test.__class__.__name__
132    cwd = os.getcwd()
133    good_setup = False
134    try:
135        try:
136            outputdir = os.path.join(client_test.job.resultdir, test_name)
137            try:
138                os.makedirs(outputdir)
139                os.chdir(outputdir)
140            except OSError:
141                pass
142            logging.info('setup %s.' % test_name)
143            client_test.setup()
144
145            # Touch .version file under src to prevent further setup on client
146            # host. See client/common_lib/utils.py update_version()
147            if os.path.exists(client_test.srcdir):
148                versionfile = os.path.join(client_test.srcdir, '.version')
149                pickle.dump(client_test.version, open(versionfile, 'wb'))
150            good_setup = True
151        except Exception as err:
152            logging.error(err)
153            raise error.AutoservError('Failed to build client test %s on '
154                                      'server.' % test_name)
155    finally:
156        # back to original working dir
157        os.chdir(cwd)
158    return good_setup
159
160
161def setup_tests(options):
162    """
163    Load and instantiate all client tests.
164
165    This function is inspired from runtest() on client/common_lib/test.py.
166
167    @param options: an object passed in from command line OptionParser.
168                    See all options defined on client/bin/autotest.
169    """
170
171    assert options.client_test_setup, 'Specify prebuild client tests on the ' \
172                                      'command line.'
173
174    requested_tests = options.client_test_setup.split(',')
175    candidates, broken_tests = load_all_client_tests(options)
176
177    failed_tests = []
178    if 'all' in requested_tests:
179        need_to_setup = candidates
180        failed_tests += broken_tests
181    else:
182        need_to_setup = []
183        for candidate in candidates:
184            if candidate.__class__.__name__ in requested_tests:
185                need_to_setup.append(candidate)
186        for broken_test in broken_tests:
187            if broken_test in requested_tests:
188                failed_tests.append(broken_test)
189
190    if need_to_setup:
191        cwd = os.getcwd()
192        os.chdir(need_to_setup[0].job.clientdir)
193        os.system('tools/make_clean')
194        os.chdir(cwd)
195    elif not failed_tests:
196        logging.error('### No test setup candidates ###')
197        raise error.AutoservError('No test setup candidates.')
198
199    for client_test in need_to_setup:
200        good_setup = setup_test(client_test)
201        if not good_setup:
202            failed_tests.append(client_test.__class__.__name__)
203
204    logging.info('############################# SUMMARY '
205                 '#############################')
206
207    # Print out tests that failed
208    if failed_tests:
209        logging.info('Finished setup -- The following tests failed')
210        for failed_test in failed_tests:
211            logging.info(failed_test)
212    else:
213        logging.info('Finished setup -- All tests built successfully')
214    logging.info('######################### END SUMMARY '
215                 '##############################')
216    if failed_tests:
217        raise error.AutoservError('Finished setup with errors.')
218