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