• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python2
2# Copyright 2019 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6from __future__ import print_function
7
8import argparse
9import contextlib
10import copy
11import logging
12import os
13import re
14import shutil
15import stat
16import subprocess
17import tempfile
18import textwrap
19import zipfile
20# Use 'sudo pip install jinja2' to install.
21from jinja2 import Template
22
23
24# TODO(ihf): Assign better TIME to control files. Scheduling uses this to run
25# LENGTHY first, then LONG, MEDIUM etc. But we need LENGTHY for the collect
26# job, downgrade all others. Make sure this still works in CQ/smoke suite.
27_CONTROLFILE_TEMPLATE = Template(
28    textwrap.dedent("""\
29    # Copyright {{year}} The Chromium OS Authors. All rights reserved.
30    # Use of this source code is governed by a BSD-style license that can be
31    # found in the LICENSE file.
32
33    # This file has been automatically generated. Do not edit!
34    {%- if servo_support_needed %}
35
36    from autotest_lib.server import utils
37
38    {%- endif %}
39
40    AUTHOR = 'ARC++ Team'
41    NAME = '{{name}}'
42    ATTRIBUTES = '{{attributes}}'
43    DEPENDENCIES = '{{dependencies}}'
44    JOB_RETRIES = {{job_retries}}
45    TEST_TYPE = 'server'
46    TIME = '{{test_length}}'
47    MAX_RESULT_SIZE_KB = {{max_result_size_kb}}
48    {%- if sync_count and sync_count > 1 %}
49    SYNC_COUNT = {{sync_count}}
50    {%- endif %}
51    {%- if priority %}
52    PRIORITY = {{priority}}
53    {%- endif %}
54    DOC = '{{DOC}}'
55    {%- if servo_support_needed %}
56
57    # For local debugging, if your test setup doesn't have servo, REMOVE these
58    # two lines.
59    args_dict = utils.args_to_dict(args)
60    servo_args = hosts.CrosHost.get_servo_arguments(args_dict)
61
62    {%- endif %}
63    {% if sync_count and sync_count > 1 %}
64    from autotest_lib.server import utils as server_utils
65    def {{test_func_name}}(ntuples):
66        host_list = [hosts.create_host(machine) for machine in ntuples]
67    {% else %}
68    def {{test_func_name}}(machine):
69        {%- if servo_support_needed %}
70        # REMOVE 'servo_args=servo_args' arg for local debugging if your test
71        # setup doesn't have servo.
72        try:
73            host_list = [hosts.create_host(machine, servo_args=servo_args)]
74        except:
75            # Just ignore any servo setup flakiness.
76            host_list = [hosts.create_host(machine)]
77        {%- else %}
78        host_list = [hosts.create_host(machine)]
79        {%- endif %}
80    {%- endif %}
81        job.run_test(
82            '{{base_name}}',
83    {%- if camera_facing %}
84            camera_facing='{{camera_facing}}',
85            cmdline_args=args,
86    {%- endif %}
87            hosts=host_list,
88            iterations=1,
89    {%- if max_retries != None %}
90            max_retry={{max_retries}},
91    {%- endif %}
92    {%- if enable_default_apps %}
93            enable_default_apps=True,
94    {%- endif %}
95    {%- if needs_push_media %}
96            needs_push_media={{needs_push_media}},
97    {%- endif %}
98            tag='{{tag}}',
99            test_name='{{name}}',
100    {%- if authkey %}
101            authkey='{{authkey}}',
102    {%- endif %}
103            run_template={{run_template}},
104            retry_template={{retry_template}},
105            target_module={% if target_module %}'{{target_module}}'{% else %}None{%endif%},
106            target_plan={% if target_plan %}'{{target_plan}}'{% else %}None{% endif %},
107    {%- if abi %}
108            bundle='{{abi}}',
109    {%- endif %}
110    {%- if extra_artifacts %}
111            extra_artifacts={{extra_artifacts}},
112    {%- endif %}
113    {%- if extra_artifacts_host %}
114            extra_artifacts_host={{extra_artifacts_host}},
115    {%- endif %}
116    {%- if uri %}
117            uri='{{uri}}',
118    {%- endif %}
119    {%- for arg in extra_args %}
120            {{arg}},
121    {%- endfor %}
122    {%- if servo_support_needed %}
123            hard_reboot_on_failure=True,
124    {%- endif %}
125    {%- if camera_facing %}
126            load_waivers=False,
127    {%- endif %}
128            timeout={{timeout}})
129
130    {% if sync_count and sync_count > 1 -%}
131    ntuples, failures = server_utils.form_ntuples_from_machines(machines,
132                                                                SYNC_COUNT)
133    # Use log=False in parallel_simple to avoid an exception in setting up
134    # the incremental parser when SYNC_COUNT > 1.
135    parallel_simple({{test_func_name}}, ntuples, log=False)
136    {% else -%}
137    parallel_simple({{test_func_name}}, machines)
138    {% endif %}
139"""))
140
141CONFIG = None
142
143_COLLECT = 'tradefed-run-collect-tests-only-internal'
144_PUBLIC_COLLECT = 'tradefed-run-collect-tests-only'
145
146_TEST_LENGTH = {1: 'FAST', 2: 'SHORT', 3: 'MEDIUM', 4: 'LONG', 5: 'LENGTHY'}
147
148_ALL = 'all'
149
150
151def get_tradefed_build(line):
152    """Gets the build of Android CTS from tradefed.
153
154    @param line Tradefed identification output on startup. Example:
155                Android Compatibility Test Suite 7.0 (3423912)
156    @return Tradefed CTS build. Example: 2813453.
157    """
158    # Sample string:
159    # - Android Compatibility Test Suite 7.0 (3423912)
160    # - Android Compatibility Test Suite for Instant Apps 1.0 (4898911)
161    # - Android Google Mobile Services (GMS) Test Suite 6.0_r1 (4756896)
162    m = re.search(r' \((.*)\)', line)
163    if m:
164        return m.group(1)
165    logging.warning('Could not identify build in line "%s".', line)
166    return '<unknown>'
167
168
169def get_tradefed_revision(line):
170    """Gets the revision of Android CTS from tradefed.
171
172    @param line Tradefed identification output on startup.
173                Example:
174                 Android Compatibility Test Suite 6.0_r6 (2813453)
175                 Android Compatibility Test Suite for Instant Apps 1.0 (4898911)
176    @return Tradefed CTS revision. Example: 6.0_r6.
177    """
178    tradefed_identifier_list = [
179            r'Android Google Mobile Services \(GMS\) Test Suite (.*) \(',
180            r'Android Compatibility Test Suite(?: for Instant Apps)? (.*) \(',
181            r'Android Vendor Test Suite (.*) \(',
182            r'Android Security Test Suite (.*) \('
183    ]
184
185    for identifier in tradefed_identifier_list:
186        m = re.search(identifier, line)
187        if m:
188            return m.group(1)
189
190    logging.warning('Could not identify revision in line "%s".', line)
191    return None
192
193
194def get_bundle_abi(filename):
195    """Makes an educated guess about the ABI.
196
197    In this case we chose to guess by filename, but we could also parse the
198    xml files in the module. (Maybe this needs to be done in the future.)
199    """
200    if CONFIG.get('DYNAMIC_TEST_FETCH'):
201        return None
202    if filename.endswith('arm.zip'):
203        return 'arm'
204    if filename.endswith('arm64.zip'):
205        return 'arm64'
206    if filename.endswith('x86.zip'):
207        return 'x86'
208
209    assert(CONFIG['TRADEFED_CTS_COMMAND'] =='gts'), 'Only GTS has empty ABI'
210    return ''
211
212
213def get_extension(module, abi, revision, is_public=False, led_provision=None, camera_facing=None):
214    """Defines a unique string.
215
216    Notice we chose module revision first, then abi, as the module revision
217    changes regularly. This ordering makes it simpler to add/remove modules.
218    @param module: CTS module which will be tested in the control file. If 'all'
219                   is specified, the control file will runs all the tests.
220    @param public: boolean variable to specify whether or not the bundle is from
221                   public source or not.
222    @param led_provision: string or None indicate whether the camerabox has led
223                          light or not.
224    @param camera_facing: string or None indicate whether it's camerabox tests
225                          for specific camera facing or not.
226    @return string: unique string for specific tests. If public=True then the
227                    string is "<abi>.<module>", otherwise, the unique string is
228                    "<revision>.<abi>.<module>". Note that if abi is empty, the
229                    abi part is omitted.
230    """
231    ext_parts = []
232    if not CONFIG.get('DYNAMIC_TEST_FETCH') and not is_public:
233        ext_parts = [revision]
234    if not CONFIG.get('DYNAMIC_TEST_FETCH') and abi:
235        ext_parts += [abi]
236    ext_parts += [module]
237    if led_provision:
238        ext_parts += [led_provision]
239    if camera_facing:
240        ext_parts += ['camerabox', camera_facing]
241    return '.'.join(ext_parts)
242
243
244def get_doc(modules, abi, is_public):
245    """Defines the control file DOC string."""
246    if modules.intersection(get_collect_modules(is_public)):
247        module_text = 'all'
248    else:
249        # Generate per-module DOC
250        module_text = 'module ' + ', '.join(sorted(list(modules)))
251
252    abi_text = (' using %s ABI' % abi) if abi else ''
253
254    doc = ('Run %s of the %s%s in the ARC++ container.'
255           % (module_text, CONFIG['DOC_TITLE'], abi_text))
256    return doc
257
258
259def servo_support_needed(modules, is_public=True):
260    """Determines if servo support is needed for a module."""
261    return not is_public and all(module in CONFIG['NEEDS_POWER_CYCLE']
262                                 for module in modules)
263
264
265def get_controlfile_name(module,
266                         abi,
267                         revision,
268                         is_public=False,
269                         led_provision=None,
270                         camera_facing=None):
271    """Defines the control file name.
272
273    @param module: CTS module which will be tested in the control file. If 'all'
274                   is specified, the control file will runs all the tests.
275    @param public: boolean variable to specify whether or not the bundle is from
276                   public source or not.
277    @param camera_facing: string or None indicate whether it's camerabox tests
278                          for specific camera facing or not.
279    @param led_provision: string or None indicate whether the camerabox has led
280                          light or not.
281    @return string: control file for specific tests. If public=True or
282                    module=all, then the name will be "control.<abi>.<module>",
283                    otherwise, the name will be
284                    "control.<revision>.<abi>.<module>".
285    """
286    return 'control.%s' % get_extension(module, abi, revision, is_public, led_provision,
287                                        camera_facing)
288
289
290def get_sync_count(_modules, _abi, _is_public):
291    return 1
292
293
294def get_suites(modules, abi, is_public, camera_facing=None):
295    """Defines the suites associated with a module.
296
297    @param module: CTS module which will be tested in the control file. If 'all'
298                   is specified, the control file will runs all the tests.
299    # TODO(ihf): Make this work with the "all" and "collect" generation,
300    # which currently bypass this function.
301    """
302    if is_public:
303        # On moblab everything runs in the same suite.
304        return [CONFIG['MOBLAB_SUITE_NAME']]
305
306    suites = set(CONFIG['INTERNAL_SUITE_NAMES'])
307
308    for module in modules:
309        if module in get_collect_modules(is_public):
310            # We collect all tests both in arc-gts and arc-gts-qual as both have
311            # a chance to be complete (and used for submission).
312            suites |= set(CONFIG['QUAL_SUITE_NAMES'])
313        if module in CONFIG['EXTRA_ATTRIBUTES']:
314            # Special cases come with their own suite definitions.
315            suites |= set(CONFIG['EXTRA_ATTRIBUTES'][module])
316        if module in CONFIG['SMOKE'] and (abi == 'arm' or abi == ''):
317            # Handle VMTest by adding a few jobs to suite:smoke.
318            suites.add('suite:smoke')
319        if module in CONFIG['HARDWARE_DEPENDENT_MODULES']:
320            # CTS modules to be run on all unibuild models.
321            suites.add('suite:arc-cts-unibuild-hw')
322        if abi == 'x86':
323            # Handle a special builder for running all of CTS in a betty VM.
324            # TODO(ihf): figure out if this builder is still alive/needed.
325            vm_suite = None
326            for suite in CONFIG['VMTEST_INFO_SUITES']:
327                if not vm_suite:
328                    vm_suite = suite
329                if module in CONFIG['VMTEST_INFO_SUITES'][suite]:
330                    vm_suite = suite
331            if vm_suite is not None:
332                suites.add('suite:%s' % vm_suite)
333        # One or two modules hould be in suite:bvt-arc to cover CQ/PFQ. A few
334        # spare/fast modules can run in suite:bvt-perbuild in case we need a
335        # replacement for the module in suite:bvt-arc (integration test for
336        # cheets_CTS only, not a correctness test for CTS content).
337        if module in CONFIG['BVT_ARC'] and (abi == 'arm' or abi == ''):
338            suites.add('suite:bvt-arc')
339        elif module in CONFIG['BVT_PERBUILD'] and (abi == 'arm' or abi == ''):
340            suites.add('suite:bvt-perbuild')
341
342    if camera_facing != None:
343        suites.add('suite:arc-cts-camera')
344
345    return sorted(list(suites))
346
347
348def get_dependencies(modules, abi, is_public, led_provision, camera_facing):
349    """Defines lab dependencies needed to schedule a module.
350
351    @param module: CTS module which will be tested in the control file. If 'all'
352                   is specified, the control file will runs all the tests.
353    @param abi: string that specifies the application binary interface of the
354                current test.
355    @param is_public: boolean variable to specify whether or not the bundle is
356                      from public source or not.
357    @param led_provision: specify if led is provisioned in the camerabox setup. 'noled' when
358                          there is no led light in the box and 'led' otherwise.
359    @param camera_facing: specify requirement of camerabox setup with target
360                          test camera facing. Set to None if it's not camerabox
361                          related test.
362    """
363    dependencies = ['arc']
364    if abi in CONFIG['LAB_DEPENDENCY']:
365        dependencies += CONFIG['LAB_DEPENDENCY'][abi]
366
367    if led_provision is not None:
368        dependencies.append('camerabox_light:'+led_provision)
369
370    if camera_facing is not None:
371        dependencies.append('camerabox_facing:'+camera_facing)
372
373    for module in modules:
374        if is_public and module in CONFIG['PUBLIC_DEPENDENCIES']:
375            dependencies.extend(CONFIG['PUBLIC_DEPENDENCIES'][module])
376
377    return ', '.join(dependencies)
378
379
380def get_job_retries(modules, is_public, suites):
381    """Define the number of job retries associated with a module.
382
383    @param module: CTS module which will be tested in the control file. If a
384                   special module is specified, the control file will runs all
385                   the tests without retry.
386    @param is_public: true if the control file is for moblab (public) use.
387    @param suites: the list of suites that the control file belongs to.
388    """
389    # TODO(haddowk): remove this when cts p has stabalized.
390    if is_public:
391        return CONFIG['CTS_JOB_RETRIES_IN_PUBLIC']
392    # Presubmit check forces to set 2 or more retries for CQ tests.
393    if 'suite:bvt-arc' in suites:
394        return 2
395    retries = 1  # 0 is NO job retries, 1 is one retry etc.
396    for module in modules:
397        # We don't want job retries for module collection or special cases.
398        if (module in get_collect_modules(is_public) or module == _ALL or
399            ('CtsDeqpTestCases' in CONFIG['EXTRA_MODULES'] and
400             module in CONFIG['EXTRA_MODULES']['CtsDeqpTestCases']['SUBMODULES']
401             )):
402            retries = 0
403    return retries
404
405
406def get_max_retries(modules, abi, suites, is_public):
407    """Partners experiance issues where some modules are flaky and require more
408
409       retries.  Calculate the retry number per module on moblab.
410    @param module: CTS module which will be tested in the control file.
411    """
412    retry = -1
413    if is_public:
414        if _ALL in CONFIG['PUBLIC_MODULE_RETRY_COUNT']:
415            retry = CONFIG['PUBLIC_MODULE_RETRY_COUNT'][_ALL]
416
417        # In moblab at partners we may need many more retries than in lab.
418        for module in modules:
419            if module in CONFIG['PUBLIC_MODULE_RETRY_COUNT']:
420                retry = max(retry, CONFIG['PUBLIC_MODULE_RETRY_COUNT'][module])
421    else:
422        # See if we have any special values for the module, chose the largest.
423        for module in modules:
424            if module in CONFIG['CTS_MAX_RETRIES']:
425                retry = max(retry, CONFIG['CTS_MAX_RETRIES'][module])
426
427    # Ugly overrides.
428    # In bvt we don't want to hold the CQ/PFQ too long.
429    if 'suite:bvt-arc' in suites:
430        retry = 3
431    # Not strict as CQ for bvt-perbuild. Let per-module config take priority.
432    if retry == -1 and 'suite:bvt-perbuild' in suites:
433        retry = 3
434    # During qualification we want at least 9 retries, possibly more.
435    # TODO(kinaba&yoshiki): do not abuse suite names
436    if CONFIG.get('QUAL_SUITE_NAMES') and \
437            set(CONFIG['QUAL_SUITE_NAMES']) & set(suites):
438        retry = max(retry, CONFIG['CTS_QUAL_RETRIES'])
439    # Collection should never have a retry. This needs to be last.
440    if modules.intersection(get_collect_modules(is_public)):
441        retry = 0
442
443    if retry >= 0:
444        return retry
445    # Default case omits the retries in the control file, so tradefed_test.py
446    # can chose its own value.
447    return None
448
449
450def get_max_result_size_kb(modules, is_public):
451    """Returns the maximum expected result size in kB for autotest.
452
453    @param modules: List of CTS modules to be tested by the control file.
454    """
455    for module in modules:
456        if (module in get_collect_modules(is_public) or
457            module == 'CtsDeqpTestCases'):
458            # CTS tests and dump logs for android-cts.
459            return CONFIG['LARGE_MAX_RESULT_SIZE']
460    # Individual module normal produces less results than all modules.
461    return CONFIG['NORMAL_MAX_RESULT_SIZE']
462
463
464def get_extra_args(modules, is_public):
465    """Generate a list of extra arguments to pass to the test.
466
467    Some params are specific to a particular module, particular mode or
468    combination of both, generate a list of arguments to pass into the template.
469
470    @param modules: List of CTS modules to be tested by the control file.
471    """
472    extra_args = set()
473    preconditions = []
474    login_preconditions = []
475    prerequisites = []
476    for module in modules:
477        # Remove this once JDK9 is the base JDK for lab.
478        if CONFIG.get('USE_JDK9', False):
479            extra_args.add('use_jdk9=True')
480        if is_public:
481            extra_args.add('warn_on_test_retry=False')
482            extra_args.add('retry_manual_tests=True')
483            preconditions.extend(CONFIG['PUBLIC_PRECONDITION'].get(module, []))
484        else:
485            preconditions.extend(CONFIG['PRECONDITION'].get(module, []))
486            login_preconditions.extend(
487                CONFIG['LOGIN_PRECONDITION'].get(module, []))
488            prerequisites.extend(CONFIG['PREREQUISITES'].get(module,[]))
489
490    # Notice: we are just squishing the preconditions for all modules together
491    # with duplicated command removed. This may not always be correct.
492    # In such a case one should split the bookmarks in a way that the modules
493    # with conflicting preconditions end up in separate control files.
494    def deduped(lst):
495        """Keep only the first occurrence of each element."""
496        return [e for i, e in enumerate(lst) if e not in lst[0:i]]
497
498    if preconditions:
499        # To properly escape the public preconditions we need to format the list
500        # manually using join.
501        extra_args.add('precondition_commands=[%s]' % ', '.join(
502            deduped(preconditions)))
503    if login_preconditions:
504        extra_args.add('login_precondition_commands=[%s]' % ', '.join(
505            deduped(login_preconditions)))
506    if prerequisites:
507        extra_args.add("prerequisites=['%s']" % "', '".join(
508            deduped(prerequisites)))
509    return sorted(list(extra_args))
510
511
512def get_test_length(modules):
513    """ Calculate the test length based on the module name.
514
515    To better optimize DUT's connected to moblab, it is better to run the
516    longest tests and tests that require limited resources.  For these modules
517    override from the default test length.
518
519    @param module: CTS module which will be tested in the control file. If 'all'
520                   is specified, the control file will runs all the tests.
521
522    @return string: one of the specified test lengths:
523                    ['FAST', 'SHORT', 'MEDIUM', 'LONG', 'LENGTHY']
524    """
525    length = 3  # 'MEDIUM'
526    for module in modules:
527        if module in CONFIG['OVERRIDE_TEST_LENGTH']:
528            length = max(length, CONFIG['OVERRIDE_TEST_LENGTH'][module])
529    return _TEST_LENGTH[length]
530
531
532def get_test_priority(modules, is_public):
533    """ Calculate the test priority based on the module name.
534
535    On moblab run all long running tests and tests that have some unique
536    characteristic at a higher priority (50).
537
538    This optimizes the total run time of the suite assuring the shortest
539    time between suite kick off and 100% complete.
540
541    @param module: CTS module which will be tested in the control file.
542
543    @return int: 0 if priority not to be overridden, or priority number otherwise.
544    """
545    if not is_public:
546        return 0
547
548    priority = 0
549    overide_test_priority_dict = CONFIG.get('PUBLIC_OVERRIDE_TEST_PRIORITY', {})
550    for module in modules:
551        if module in overide_test_priority_dict:
552            priority = max(priority, overide_test_priority_dict[module])
553        elif (module in CONFIG['OVERRIDE_TEST_LENGTH'] or
554                module in CONFIG['PUBLIC_DEPENDENCIES'] or
555                module in CONFIG['PUBLIC_PRECONDITION'] or
556                module.split('.')[0] in CONFIG['OVERRIDE_TEST_LENGTH']):
557            priority = max(priority, 50)
558    return priority
559
560
561def get_authkey(is_public):
562    if is_public or not CONFIG['AUTHKEY']:
563        return None
564    return CONFIG['AUTHKEY']
565
566
567def _format_collect_cmd(is_public, abi_to_run, retry):
568    """Returns a list specifying tokens for tradefed to list all tests."""
569    if retry:
570        return None
571    cmd = ['run', 'commandAndExit', 'collect-tests-only']
572    if CONFIG['TRADEFED_DISABLE_REBOOT_ON_COLLECTION']:
573        cmd += ['--disable-reboot']
574    for m in CONFIG['MEDIA_MODULES']:
575        cmd.append('--module-arg')
576        cmd.append('%s:skip-media-download:true' % m)
577    if (not is_public and
578            not CONFIG.get('NEEDS_DYNAMIC_CONFIG_ON_COLLECTION', True)):
579        cmd.append('--dynamic-config-url=')
580    if abi_to_run:
581        cmd += ['--abi', abi_to_run]
582    return cmd
583
584
585def _get_special_command_line(modules, _is_public):
586    """This function allows us to split a module like Deqp into segments."""
587    cmd = []
588    for module in sorted(modules):
589        cmd += CONFIG['EXTRA_COMMANDLINE'].get(module, [])
590    return cmd
591
592
593def _format_modules_cmd(is_public,
594                        abi_to_run,
595                        modules=None,
596                        retry=False,
597                        whole_module_set=None):
598    """Returns list of command tokens for tradefed."""
599    if retry:
600        assert(CONFIG['TRADEFED_RETRY_COMMAND'] == 'cts' or
601               CONFIG['TRADEFED_RETRY_COMMAND'] == 'retry')
602
603        cmd = ['run', 'commandAndExit', CONFIG['TRADEFED_RETRY_COMMAND'],
604               '--retry', '{session_id}']
605    else:
606        # For runs create a logcat file for each individual failure.
607        cmd = ['run', 'commandAndExit', CONFIG['TRADEFED_CTS_COMMAND']]
608
609        special_cmd = _get_special_command_line(modules, is_public)
610        if special_cmd:
611            cmd.extend(special_cmd)
612        elif _ALL in modules:
613            pass
614        elif len(modules) == 1:
615            cmd += ['--module', list(modules)[0]]
616        else:
617            if whole_module_set is None:
618                assert (CONFIG['TRADEFED_CTS_COMMAND'] != 'cts-instant'), \
619                       'cts-instant cannot include multiple modules'
620                # We run each module with its own --include-filter option.
621                # https://source.android.com/compatibility/cts/run
622                for module in sorted(modules):
623                    cmd += ['--include-filter', module]
624            else:
625                # CTS-Instant does not support --include-filter due to
626                # its implementation detail. Instead, exclude the complement.
627                for module in whole_module_set - set(modules):
628                    cmd += ['--exclude-filter', module]
629
630        # For runs create a logcat file for each individual failure.
631        # Not needed on moblab, nobody is going to look at them.
632        if (not modules.intersection(CONFIG['DISABLE_LOGCAT_ON_FAILURE']) and
633            not is_public and
634            CONFIG['TRADEFED_CTS_COMMAND'] != 'gts'):
635            cmd.append('--logcat-on-failure')
636
637        if CONFIG['TRADEFED_IGNORE_BUSINESS_LOGIC_FAILURE']:
638            cmd.append('--ignore-business-logic-failure')
639
640    if CONFIG['TRADEFED_DISABLE_REBOOT']:
641        cmd.append('--disable-reboot')
642    if (CONFIG['TRADEFED_MAY_SKIP_DEVICE_INFO'] and
643        not (modules.intersection(CONFIG['BVT_ARC'] + CONFIG['SMOKE'] +
644             CONFIG['NEEDS_DEVICE_INFO']))):
645        cmd.append('--skip-device-info')
646    if abi_to_run:
647        cmd += ['--abi', abi_to_run]
648    # If NEEDS_DYNAMIC_CONFIG is set, disable the feature except on the modules
649    # that explicitly set as needed.
650    if (not is_public and CONFIG.get('NEEDS_DYNAMIC_CONFIG') and
651            not modules.intersection(CONFIG['NEEDS_DYNAMIC_CONFIG'])):
652        cmd.append('--dynamic-config-url=')
653
654    return cmd
655
656
657def get_run_template(modules,
658                     is_public,
659                     retry=False,
660                     abi_to_run=None,
661                     whole_module_set=None):
662    """Command to run the modules specified by a control file."""
663    no_intersection = not modules.intersection(get_collect_modules(is_public))
664    collect_present = (_COLLECT in modules or _PUBLIC_COLLECT in modules)
665    all_present = _ALL in modules
666    if no_intersection or (all_present and not collect_present):
667        return _format_modules_cmd(is_public,
668                                   abi_to_run,
669                                   modules,
670                                   retry=retry,
671                                   whole_module_set=whole_module_set)
672    elif collect_present:
673        return _format_collect_cmd(is_public, abi_to_run, retry=retry)
674    return None
675
676def get_retry_template(modules, is_public):
677    """Command to retry the failed modules as specified by a control file."""
678    return get_run_template(modules, is_public, retry=True)
679
680
681def get_extra_modules_dict(is_public, abi):
682    if not is_public:
683        return CONFIG['EXTRA_MODULES']
684
685    extra_modules = copy.deepcopy(CONFIG['PUBLIC_EXTRA_MODULES'])
686    if abi in CONFIG['EXTRA_SUBMODULE_OVERRIDE']:
687        for _, submodules in extra_modules.items():
688            for old, news in CONFIG['EXTRA_SUBMODULE_OVERRIDE'][abi].items():
689                submodules.remove(old)
690                submodules.extend(news)
691    return {
692        module: {
693            'SUBMODULES': submodules,
694            'SUITES': [CONFIG['MOBLAB_SUITE_NAME']],
695        } for module, submodules in extra_modules.items()
696    }
697
698
699def get_extra_artifacts(modules):
700    artifacts = []
701    for module in modules:
702        if module in CONFIG['EXTRA_ARTIFACTS']:
703            artifacts += CONFIG['EXTRA_ARTIFACTS'][module]
704    return artifacts
705
706
707def get_extra_artifacts_host(modules):
708    if not 'EXTRA_ARTIFACTS_HOST' in CONFIG:
709        return
710
711    artifacts = []
712    for module in modules:
713        if module in CONFIG['EXTRA_ARTIFACTS_HOST']:
714            artifacts += CONFIG['EXTRA_ARTIFACTS_HOST'][module]
715    return artifacts
716
717
718def calculate_timeout(modules, suites):
719    """Calculation for timeout of tradefed run.
720
721    Timeout is at least one hour, except if part of BVT_ARC.
722    Notice these do get adjusted dynamically by number of ABIs on the DUT.
723    """
724    if 'suite:bvt-arc' in suites:
725        return int(3600 * CONFIG['BVT_TIMEOUT'])
726    if CONFIG.get('QUAL_SUITE_NAMES') and \
727            CONFIG.get('QUAL_TIMEOUT') and \
728            ((set(CONFIG['QUAL_SUITE_NAMES']) & set(suites)) and \
729            not (_COLLECT in modules or _PUBLIC_COLLECT in modules)):
730        return int(3600 * CONFIG['QUAL_TIMEOUT'])
731
732    timeout = 0
733    # First module gets 1h (standard), all other half hour extra (heuristic).
734    default_timeout = int(3600 * CONFIG['CTS_TIMEOUT_DEFAULT'])
735    delta = default_timeout
736    for module in modules:
737        if module in CONFIG['CTS_TIMEOUT']:
738            # Modules that run very long are encoded here.
739            timeout += int(3600 * CONFIG['CTS_TIMEOUT'][module])
740        elif module.startswith('CtsDeqpTestCases.dEQP-VK.'):
741            # TODO: Optimize this temporary hack by reducing this value or
742            # setting appropriate values for each test if possible.
743            timeout = max(timeout, int(3600 * 12))
744        elif 'Jvmti' in module:
745            # We have too many of these modules and they run fast.
746            timeout += 300
747        else:
748            timeout += delta
749            delta = default_timeout // 2
750    return timeout
751
752
753def needs_push_media(modules):
754    """Oracle to determine if to push several GB of media files to DUT."""
755    if modules.intersection(set(CONFIG['NEEDS_PUSH_MEDIA'])):
756        return True
757    return False
758
759
760def enable_default_apps(modules):
761    """Oracle to determine if to enable default apps (eg. Files.app)."""
762    if modules.intersection(set(CONFIG['ENABLE_DEFAULT_APPS'])):
763        return True
764    return False
765
766
767def get_controlfile_content(combined,
768                            modules,
769                            abi,
770                            revision,
771                            build,
772                            uri,
773                            suites=None,
774                            is_public=False,
775                            is_latest=False,
776                            led_provision=None,
777                            camera_facing=None,
778                            whole_module_set=None):
779    """Returns the text inside of a control file.
780
781    @param combined: name to use for this combination of modules.
782    @param modules: set of CTS modules which will be tested in the control
783                   file. If 'all' is specified, the control file will run
784                   all the tests.
785    """
786    # We tag results with full revision now to get result directories containing
787    # the revision. This fits stainless/ better.
788    tag = '%s' % get_extension(combined, abi, revision, is_public, led_provision,
789                               camera_facing)
790    # For test_that the NAME should be the same as for the control file name.
791    # We could try some trickery here to get shorter extensions for a default
792    # suite/ARM. But with the monthly uprevs this will quickly get confusing.
793    name = '%s.%s' % (CONFIG['TEST_NAME'], tag)
794    if not suites:
795        suites = get_suites(modules, abi, is_public, camera_facing)
796    attributes = ', '.join(suites)
797    uri = 'LATEST' if is_latest else (None if is_public else uri)
798    target_module = None
799    if (combined not in get_collect_modules(is_public) and combined != _ALL):
800        target_module = combined
801    for target, config in get_extra_modules_dict(is_public, abi).items():
802        if combined in config['SUBMODULES']:
803            target_module = target
804    return _CONTROLFILE_TEMPLATE.render(
805            year=CONFIG['COPYRIGHT_YEAR'],
806            name=name,
807            base_name=CONFIG['TEST_NAME'],
808            test_func_name=CONFIG['CONTROLFILE_TEST_FUNCTION_NAME'],
809            attributes=attributes,
810            dependencies=get_dependencies(modules, abi, is_public,
811                                          led_provision, camera_facing),
812            extra_artifacts=get_extra_artifacts(modules),
813            extra_artifacts_host=get_extra_artifacts_host(modules),
814            job_retries=get_job_retries(modules, is_public, suites),
815            max_result_size_kb=get_max_result_size_kb(modules, is_public),
816            revision=revision,
817            build=build,
818            abi=abi,
819            needs_push_media=needs_push_media(modules),
820            enable_default_apps=enable_default_apps(modules),
821            tag=tag,
822            uri=uri,
823            DOC=get_doc(modules, abi, is_public),
824            servo_support_needed=servo_support_needed(modules, is_public),
825            max_retries=get_max_retries(modules, abi, suites, is_public),
826            timeout=calculate_timeout(modules, suites),
827            run_template=get_run_template(modules,
828                                          is_public,
829                                          abi_to_run=CONFIG.get(
830                                                  'REPRESENTATIVE_ABI',
831                                                  {}).get(abi, None),
832                                          whole_module_set=whole_module_set),
833            retry_template=get_retry_template(modules, is_public),
834            target_module=target_module,
835            target_plan=None,
836            test_length=get_test_length(modules),
837            priority=get_test_priority(modules, is_public),
838            extra_args=get_extra_args(modules, is_public),
839            authkey=get_authkey(is_public),
840            sync_count=get_sync_count(modules, abi, is_public),
841            camera_facing=camera_facing)
842
843
844def get_tradefed_data(path, is_public, abi):
845    """Queries tradefed to provide us with a list of modules.
846
847    Notice that the parsing gets broken at times with major new CTS drops.
848    """
849    tradefed = os.path.join(path, CONFIG['TRADEFED_EXECUTABLE_PATH'])
850    # Forgive me for I have sinned. Same as: chmod +x tradefed.
851    os.chmod(tradefed, os.stat(tradefed).st_mode | stat.S_IEXEC)
852    cmd_list = [tradefed, 'list', 'modules']
853    logging.info('Calling tradefed for list of modules.')
854    with open(os.devnull, 'w') as devnull:
855        # tradefed terminates itself if stdin is not a tty.
856        tradefed_output = subprocess.check_output(cmd_list, stdin=devnull)
857
858    # TODO(ihf): Get a tradefed command which terminates then refactor.
859    p = subprocess.Popen(cmd_list, stdout=subprocess.PIPE)
860    modules = set()
861    build = '<unknown>'
862    line = ''
863    revision = None
864    is_in_intaractive_mode = True
865    # The process does not terminate, but we know the last test is vm-tests-tf.
866    while True:
867        line = p.stdout.readline().strip()
868        # Android Compatibility Test Suite 7.0 (3423912)
869        if (line.startswith('Android Compatibility Test Suite ')
870                    or line.startswith('Android Google ')
871                    or line.startswith('Android Vendor Test Suite')
872                    or line.startswith('Android Security Test Suite')):
873            logging.info('Unpacking: %s.', line)
874            build = get_tradefed_build(line)
875            revision = get_tradefed_revision(line)
876        elif line.startswith('Non-interactive mode: '):
877            is_in_intaractive_mode = False
878        elif line.startswith('arm') or line.startswith('x86'):
879            # Newer CTS shows ABI-module pairs like "arm64-v8a CtsNetTestCases"
880            line = line.split()[1]
881            if line not in CONFIG.get('EXCLUDE_MODULES', []):
882                modules.add(line)
883        elif line.startswith('Cts'):
884            modules.add(line)
885        elif line.startswith('Gts'):
886            # Older GTS plainly lists the module names
887            modules.add(line)
888        elif line.startswith('cts-'):
889            modules.add(line)
890        elif line.startswith('signed-Cts'):
891            modules.add(line)
892        elif line.startswith('vm-tests-tf'):
893            modules.add(line)
894            break  # TODO(ihf): Fix using this as EOS.
895        elif not line:
896            exit_code = p.poll()
897            if exit_code is not None:
898                # The process has automatically exited.
899                if is_in_intaractive_mode or exit_code != 0:
900                    # The process exited unexpectedly in interactive mode,
901                    # or exited with error in non-interactive mode.
902                    logging.warning(
903                        'The process has exited unexpectedly (exit code: %d)',
904                        exit_code)
905                    modules = set()
906                break
907        elif line.isspace() or line.startswith('Use "help"'):
908            pass
909        else:
910            logging.warning('Ignoring "%s"', line)
911    if p.poll() is None:
912        # Kill the process if alive.
913        p.kill()
914    p.wait()
915
916    if not modules:
917        raise Exception("no modules found.")
918    return list(modules), build, revision
919
920
921def download(uri, destination):
922    """Download |uri| to local |destination|.
923
924       |destination| must be a file path (not a directory path)."""
925    if uri.startswith('http://') or uri.startswith('https://'):
926        subprocess.check_call(['wget', uri, '-O', destination])
927    elif uri.startswith('gs://'):
928        subprocess.check_call(['gsutil', 'cp', uri, destination])
929    else:
930        raise Exception
931
932
933@contextlib.contextmanager
934def pushd(d):
935    """Defines pushd."""
936    current = os.getcwd()
937    os.chdir(d)
938    try:
939        yield
940    finally:
941        os.chdir(current)
942
943
944def unzip(filename, destination):
945    """Unzips a zip file to the destination directory."""
946    with pushd(destination):
947        # We are trusting Android to have a valid zip file for us.
948        with zipfile.ZipFile(filename) as zf:
949            zf.extractall()
950
951
952def get_collect_modules(is_public):
953    if is_public:
954        return set([_PUBLIC_COLLECT])
955    return set([_COLLECT])
956
957
958@contextlib.contextmanager
959def TemporaryDirectory(prefix):
960    """Poor man's python 3.2 import."""
961    tmp = tempfile.mkdtemp(prefix=prefix)
962    try:
963        yield tmp
964    finally:
965        shutil.rmtree(tmp)
966
967
968def get_word_pattern(m, l=1):
969    """Return the first few words of the CamelCase module name.
970
971    Break after l+1 CamelCase word.
972    Example: CtsDebugTestCases -> CtsDebug.
973    """
974    s = re.findall('^[a-z-]+|[A-Z]*[^A-Z0-9]*', m)[0:l + 1]
975    # Ignore Test or TestCases at the end as they don't add anything.
976    if len(s) > l:
977        if s[l].startswith('Test') or s[l].startswith('['):
978            return ''.join(s[0:l])
979        if s[l - 1] == 'Test' and s[l].startswith('Cases'):
980            return ''.join(s[0:l - 1])
981    return ''.join(s[0:l + 1])
982
983
984def combine_modules_by_common_word(modules):
985    """Returns a dictionary of (combined name, set of module) pairs.
986
987    This gives a mild compaction of control files (from about 320 to 135).
988    Example:
989    'CtsVoice' -> ['CtsVoiceInteractionTestCases', 'CtsVoiceSettingsTestCases']
990    """
991    d = dict()
992    # On first pass group modules with common first word together.
993    for module in modules:
994        pattern = get_word_pattern(module)
995        v = d.get(pattern, [])
996        v.append(module)
997        v.sort()
998        d[pattern] = v
999    # Second pass extend names to maximum common prefix. This keeps control file
1000    # names identical if they contain only one module and less ambiguous if they
1001    # contain multiple modules.
1002    combined = dict()
1003    for key in sorted(d):
1004        # Instead if a one syllable prefix use longest common prefix of modules.
1005        prefix = os.path.commonprefix(d[key])
1006        # Beautification: strip Tests/TestCases from end of prefix, but only if
1007        # there is more than one module in the control file. This avoids
1008        # slightly strange combination of having CtsDpiTestCases1/2 inside of
1009        # CtsDpiTestCases (now just CtsDpi to make it clearer there are several
1010        # modules in this control file).
1011        if len(d[key]) > 1:
1012            prefix = re.sub('TestCases$', '', prefix)
1013            prefix = re.sub('Tests$', '', prefix)
1014        # Beautification: CtsMedia files run very long and are unstable. Give
1015        # each module its own control file, even though this heuristic would
1016        # lump them together.
1017        if prefix.startswith('CtsMedia'):
1018            # Separate each CtsMedia* modules, but group extra modules with
1019            # optional parametrization (ex: secondary_user, instant) together.
1020            prev = ' '
1021            for media in sorted(d[key]):
1022                if media.startswith(prev):
1023                    combined[prev].add(media)
1024                else:
1025                    prev = media
1026                    combined[media] = set([media])
1027
1028        else:
1029            combined[prefix] = set(d[key])
1030    print('Reduced number of control files from %d to %d.' % (len(modules),
1031                                                              len(combined)))
1032    return combined
1033
1034
1035def combine_modules_by_bookmark(modules):
1036    """Return a manually curated list of name, module pairs.
1037
1038    Ideally we split "all" into a dictionary of maybe 10-20 equal runtime parts.
1039    (Say 2-5 hours each.) But it is ok to run problematic modules alone.
1040    """
1041    d = dict()
1042    # Figure out sets of modules between bookmarks. Not optimum time complexity.
1043    for bookmark in CONFIG['QUAL_BOOKMARKS']:
1044        if modules:
1045            for module in sorted(modules):
1046                if module < bookmark:
1047                    v = d.get(bookmark, set())
1048                    v.add(module)
1049                    d[bookmark] = v
1050            # Remove processed modules.
1051            if bookmark in d:
1052                modules = modules - d[bookmark]
1053    # Clean up names.
1054    combined = dict()
1055    for key in sorted(d):
1056        v = sorted(d[key])
1057        # New name is first element '_-_' last element.
1058        # Notice there is a bug in $ADB_VENDOR_KEYS path name preventing
1059        # arbitrary characters.
1060        prefix = v[0] + '_-_' + v[-1]
1061        combined[prefix] = set(v)
1062    return combined
1063
1064
1065def write_controlfile(name,
1066                      modules,
1067                      abi,
1068                      revision,
1069                      build,
1070                      uri,
1071                      suites,
1072                      is_public,
1073                      is_latest=False,
1074                      whole_module_set=None):
1075    """Write a single control file."""
1076    filename = get_controlfile_name(name, abi, revision, is_public)
1077    content = get_controlfile_content(name,
1078                                      modules,
1079                                      abi,
1080                                      revision,
1081                                      build,
1082                                      uri,
1083                                      suites,
1084                                      is_public,
1085                                      is_latest,
1086                                      whole_module_set=whole_module_set)
1087    with open(filename, 'w') as f:
1088        f.write(content)
1089
1090
1091def write_moblab_controlfiles(modules, abi, revision, build, uri, is_public):
1092    """Write all control files for moblab.
1093
1094    Nothing gets combined.
1095
1096    Moblab uses one module per job. In some cases like Deqp which can run super
1097    long it even creates several jobs per module. Moblab can do this as it has
1098    less relative overhead spinning up jobs than the lab.
1099    """
1100    for module in modules:
1101        # No need to generate control files with extra suffix, since --module
1102        # option will cover variants with optional parameters.
1103        if "[" in module:
1104            continue
1105        write_controlfile(module, set([module]), abi, revision, build, uri,
1106                          [CONFIG['MOBLAB_SUITE_NAME']], is_public)
1107
1108
1109def write_regression_controlfiles(modules, abi, revision, build, uri,
1110                                  is_public, is_latest):
1111    """Write all control files for stainless/ToT regression lab coverage.
1112
1113    Regression coverage on tot currently relies heavily on watching stainless
1114    dashboard and sponge. So instead of running everything in a single run
1115    we split CTS into many jobs. It used to be one job per module, but that
1116    became too much in P (more than 300 per ABI). Instead we combine modules
1117    with similar names and run these in the same job (alphabetically).
1118    """
1119    combined = combine_modules_by_common_word(set(modules))
1120    for key in combined:
1121        write_controlfile(key, combined[key], abi, revision, build, uri, None,
1122                          is_public, is_latest)
1123
1124
1125def write_qualification_controlfiles(modules, abi, revision, build, uri,
1126                                     is_public):
1127    """Write all control files to run "all" tests for qualification.
1128
1129    Qualification was performed on N by running all tests using tradefed
1130    sharding (specifying SYNC_COUNT=2) in the control files. In skylab
1131    this is currently not implemented, so we fall back to autotest sharding
1132    all CTS tests into 10-20 hand chosen shards.
1133    """
1134    combined = combine_modules_by_bookmark(set(modules))
1135    for key in combined:
1136        write_controlfile('all.' + key, combined[key], abi, revision, build,
1137                          uri, CONFIG.get('QUAL_SUITE_NAMES'), is_public)
1138
1139
1140def write_qualification_and_regression_controlfile(modules, abi, revision,
1141                                                   build, uri, is_public):
1142    """Write a control file to run "all" tests for qualification and regression.
1143    """
1144    # For cts-instant, qualication control files are expected to cover
1145    # regressions as well. Hence the 'suite:arc-cts' is added.
1146    suites = ['suite:arc-cts', 'suite:arc-cts-qual']
1147    module_set = set(modules)
1148    combined = combine_modules_by_bookmark(module_set)
1149    for key in combined:
1150        write_controlfile('all.' + key,
1151                          combined[key],
1152                          abi,
1153                          revision,
1154                          build,
1155                          uri,
1156                          suites,
1157                          is_public,
1158                          whole_module_set=module_set)
1159
1160
1161def write_collect_controlfiles(_modules, abi, revision, build, uri, is_public,
1162                               is_latest):
1163    """Write all control files for test collection used as reference to
1164
1165    compute completeness (missing tests) on the CTS dashboard.
1166    """
1167    if is_public:
1168        suites = [CONFIG['MOBLAB_SUITE_NAME']]
1169    else:
1170        suites = CONFIG['INTERNAL_SUITE_NAMES'] \
1171               + CONFIG.get('QUAL_SUITE_NAMES', [])
1172    for module in get_collect_modules(is_public):
1173        write_controlfile(module, set([module]), abi, revision, build, uri,
1174                          suites, is_public, is_latest)
1175
1176
1177def write_extra_controlfiles(_modules, abi, revision, build, uri, is_public,
1178                             is_latest):
1179    """Write all extra control files as specified in config.
1180
1181    This is used by moblab to load balance large modules like Deqp, as well as
1182    making custom modules such as WM presubmit. A similar approach was also used
1183    during bringup of grunt to split media tests.
1184    """
1185    for module, config in get_extra_modules_dict(is_public, abi).items():
1186        for submodule in config['SUBMODULES']:
1187            write_controlfile(submodule, set([submodule]), abi, revision,
1188                              build, uri, config['SUITES'], is_public,
1189                              is_latest)
1190
1191
1192def write_extra_camera_controlfiles(abi, revision, build, uri, is_public):
1193    """Control files for CtsCameraTestCases.camerabox.*"""
1194    module = 'CtsCameraTestCases'
1195    for facing in ['back', 'front']:
1196        for led_provision in ['led', 'noled']:
1197            name = get_controlfile_name(module, abi,
1198                                        revision, is_public, led_provision, facing)
1199            content = get_controlfile_content(module,
1200                                              set([module]),
1201                                              abi,
1202                                              revision,
1203                                              build,
1204                                              uri,
1205                                              None,
1206                                              is_public,
1207                                              led_provision=led_provision,
1208                                              camera_facing=facing)
1209            with open(name, 'w') as f:
1210                f.write(content)
1211
1212
1213def run(uris, is_public, is_latest, cache_dir):
1214    """Downloads each bundle in |uris| and generates control files for each
1215
1216    module as reported to us by tradefed.
1217    """
1218    for uri in uris:
1219        abi = get_bundle_abi(uri)
1220        # Get tradefed data by downloading & unzipping the files
1221        with TemporaryDirectory(prefix='cts-android_') as tmp:
1222            if cache_dir is not None:
1223                assert(os.path.isdir(cache_dir))
1224                bundle = os.path.join(cache_dir, os.path.basename(uri))
1225                if not os.path.exists(bundle):
1226                    logging.info('Downloading to %s.', cache_dir)
1227                    download(uri, bundle)
1228            else:
1229                bundle = os.path.join(tmp, os.path.basename(uri))
1230                logging.info('Downloading to %s.', tmp)
1231                download(uri, bundle)
1232            logging.info('Extracting %s.', bundle)
1233            unzip(bundle, tmp)
1234            modules, build, revision = get_tradefed_data(tmp, is_public, abi)
1235            if not revision:
1236                raise Exception('Could not determine revision.')
1237
1238            logging.info('Writing all control files.')
1239            if is_public:
1240                write_moblab_controlfiles(modules, abi, revision, build, uri,
1241                                          is_public)
1242            else:
1243                if CONFIG['CONTROLFILE_WRITE_SIMPLE_QUAL_AND_REGRESS']:
1244                    write_qualification_and_regression_controlfile(
1245                            modules, abi, revision, build, uri, is_public)
1246                else:
1247                    write_regression_controlfiles(modules, abi, revision,
1248                                                  build, uri, is_public,
1249                                                  is_latest)
1250                    write_qualification_controlfiles(modules, abi, revision,
1251                                                     build, uri, is_public)
1252
1253                if CONFIG['CONTROLFILE_WRITE_CAMERA']:
1254                    write_extra_camera_controlfiles(abi, revision, build, uri,
1255                                                    is_public)
1256
1257            if CONFIG.get('CONTROLFILE_WRITE_COLLECT', True):
1258                write_collect_controlfiles(modules, abi, revision, build, uri,
1259                                           is_public, is_latest)
1260
1261            if CONFIG['CONTROLFILE_WRITE_EXTRA']:
1262                write_extra_controlfiles(None, abi, revision, build, uri,
1263                                         is_public, is_latest)
1264
1265
1266def main(config):
1267    """ Entry method of generator """
1268
1269    global CONFIG
1270    CONFIG = config
1271
1272    logging.basicConfig(level=logging.INFO)
1273    parser = argparse.ArgumentParser(
1274        description='Create control files for a CTS bundle on GS.',
1275        formatter_class=argparse.RawTextHelpFormatter)
1276    parser.add_argument(
1277            'uris',
1278            nargs='+',
1279            help='List of Google Storage URIs to CTS bundles. Example:\n'
1280            'gs://chromeos-arc-images/cts/bundle/P/'
1281            'android-cts-9.0_r9-linux_x86-x86.zip')
1282    parser.add_argument(
1283            '--is_public',
1284            dest='is_public',
1285            default=False,
1286            action='store_true',
1287            help='Generate the public control files for CTS, default generate'
1288            ' the internal control files')
1289    parser.add_argument(
1290            '--is_latest',
1291            dest='is_latest',
1292            default=False,
1293            action='store_true',
1294            help='Generate the control files for CTS from the latest CTS bundle'
1295            ' stored in the internal storage')
1296    parser.add_argument(
1297            '--cache_dir',
1298            dest='cache_dir',
1299            default=None,
1300            action='store',
1301            help='Cache directory for downloaded bundle file. Uses the cached '
1302            'bundle file if exists, or caches a downloaded file to this '
1303            'directory if not.')
1304    args = parser.parse_args()
1305    run(args.uris, args.is_public, args.is_latest, args.cache_dir)
1306