xref: /aosp_15_r20/external/mbedtls/tests/scripts/depends.py (revision 62c56f9862f102b96d72393aff6076c951fb8148)
1*62c56f98SSadaf Ebrahimi#!/usr/bin/env python3
2*62c56f98SSadaf Ebrahimi
3*62c56f98SSadaf Ebrahimi# Copyright The Mbed TLS Contributors
4*62c56f98SSadaf Ebrahimi# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
5*62c56f98SSadaf Ebrahimi
6*62c56f98SSadaf Ebrahimi"""
7*62c56f98SSadaf EbrahimiTest Mbed TLS with a subset of algorithms.
8*62c56f98SSadaf Ebrahimi
9*62c56f98SSadaf EbrahimiThis script can be divided into several steps:
10*62c56f98SSadaf Ebrahimi
11*62c56f98SSadaf EbrahimiFirst, include/mbedtls/mbedtls_config.h or a different config file passed
12*62c56f98SSadaf Ebrahimiin the arguments is parsed to extract any configuration options (using config.py).
13*62c56f98SSadaf Ebrahimi
14*62c56f98SSadaf EbrahimiThen, test domains (groups of jobs, tests) are built based on predefined data
15*62c56f98SSadaf Ebrahimicollected in the DomainData class. Here, each domain has five major traits:
16*62c56f98SSadaf Ebrahimi- domain name, can be used to run only specific tests via command-line;
17*62c56f98SSadaf Ebrahimi- configuration building method, described in detail below;
18*62c56f98SSadaf Ebrahimi- list of symbols passed to the configuration building method;
19*62c56f98SSadaf Ebrahimi- commands to be run on each job (only build, build and test, or any other custom);
20*62c56f98SSadaf Ebrahimi- optional list of symbols to be excluded from testing.
21*62c56f98SSadaf Ebrahimi
22*62c56f98SSadaf EbrahimiThe configuration building method can be one of the three following:
23*62c56f98SSadaf Ebrahimi
24*62c56f98SSadaf Ebrahimi- ComplementaryDomain - build a job for each passed symbol by disabling a single
25*62c56f98SSadaf Ebrahimi  symbol and its reverse dependencies (defined in REVERSE_DEPENDENCIES);
26*62c56f98SSadaf Ebrahimi
27*62c56f98SSadaf Ebrahimi- ExclusiveDomain - build a job where, for each passed symbol, only this particular
28*62c56f98SSadaf Ebrahimi  one is defined and other symbols from the list are unset. For each job look for
29*62c56f98SSadaf Ebrahimi  any non-standard symbols to set/unset in EXCLUSIVE_GROUPS. These are usually not
30*62c56f98SSadaf Ebrahimi  direct dependencies, but rather non-trivial results of other configs missing. Then
31*62c56f98SSadaf Ebrahimi  look for any unset symbols and handle their reverse dependencies.
32*62c56f98SSadaf Ebrahimi  Examples of EXCLUSIVE_GROUPS usage:
33*62c56f98SSadaf Ebrahimi  - MBEDTLS_SHA512_C job turns off all hashes except SHA512. MBEDTLS_SSL_COOKIE_C
34*62c56f98SSadaf Ebrahimi    requires either SHA256 or SHA384 to work, so it also has to be disabled.
35*62c56f98SSadaf Ebrahimi    This is not a dependency on SHA512_C, but a result of an exclusive domain
36*62c56f98SSadaf Ebrahimi    config building method. Relevant field:
37*62c56f98SSadaf Ebrahimi    'MBEDTLS_SHA512_C': ['-MBEDTLS_SSL_COOKIE_C'],
38*62c56f98SSadaf Ebrahimi
39*62c56f98SSadaf Ebrahimi- DualDomain - combination of the two above - both complementary and exclusive domain
40*62c56f98SSadaf Ebrahimi  job generation code will be run. Currently only used for hashes.
41*62c56f98SSadaf Ebrahimi
42*62c56f98SSadaf EbrahimiLastly, the collected jobs are executed and (optionally) tested, with
43*62c56f98SSadaf Ebrahimierror reporting and coloring as configured in options. Each test starts with
44*62c56f98SSadaf Ebrahimia full config without a couple of slowing down or unnecessary options
45*62c56f98SSadaf Ebrahimi(see set_reference_config), then the specific job config is derived.
46*62c56f98SSadaf Ebrahimi"""
47*62c56f98SSadaf Ebrahimiimport argparse
48*62c56f98SSadaf Ebrahimiimport os
49*62c56f98SSadaf Ebrahimiimport re
50*62c56f98SSadaf Ebrahimiimport shutil
51*62c56f98SSadaf Ebrahimiimport subprocess
52*62c56f98SSadaf Ebrahimiimport sys
53*62c56f98SSadaf Ebrahimiimport traceback
54*62c56f98SSadaf Ebrahimifrom typing import Union
55*62c56f98SSadaf Ebrahimi
56*62c56f98SSadaf Ebrahimi# Add the Mbed TLS Python library directory to the module search path
57*62c56f98SSadaf Ebrahimiimport scripts_path # pylint: disable=unused-import
58*62c56f98SSadaf Ebrahimiimport config
59*62c56f98SSadaf Ebrahimi
60*62c56f98SSadaf Ebrahimiclass Colors: # pylint: disable=too-few-public-methods
61*62c56f98SSadaf Ebrahimi    """Minimalistic support for colored output.
62*62c56f98SSadaf EbrahimiEach field of an object of this class is either None if colored output
63*62c56f98SSadaf Ebrahimiis not possible or not desired, or a pair of strings (start, stop) such
64*62c56f98SSadaf Ebrahimithat outputting start switches the text color to the desired color and
65*62c56f98SSadaf Ebrahimistop switches the text color back to the default."""
66*62c56f98SSadaf Ebrahimi    red = None
67*62c56f98SSadaf Ebrahimi    green = None
68*62c56f98SSadaf Ebrahimi    cyan = None
69*62c56f98SSadaf Ebrahimi    bold_red = None
70*62c56f98SSadaf Ebrahimi    bold_green = None
71*62c56f98SSadaf Ebrahimi    def __init__(self, options=None):
72*62c56f98SSadaf Ebrahimi        """Initialize color profile according to passed options."""
73*62c56f98SSadaf Ebrahimi        if not options or options.color in ['no', 'never']:
74*62c56f98SSadaf Ebrahimi            want_color = False
75*62c56f98SSadaf Ebrahimi        elif options.color in ['yes', 'always']:
76*62c56f98SSadaf Ebrahimi            want_color = True
77*62c56f98SSadaf Ebrahimi        else:
78*62c56f98SSadaf Ebrahimi            want_color = sys.stderr.isatty()
79*62c56f98SSadaf Ebrahimi        if want_color:
80*62c56f98SSadaf Ebrahimi            # Assume ANSI compatible terminal
81*62c56f98SSadaf Ebrahimi            normal = '\033[0m'
82*62c56f98SSadaf Ebrahimi            self.red = ('\033[31m', normal)
83*62c56f98SSadaf Ebrahimi            self.green = ('\033[32m', normal)
84*62c56f98SSadaf Ebrahimi            self.cyan = ('\033[36m', normal)
85*62c56f98SSadaf Ebrahimi            self.bold_red = ('\033[1;31m', normal)
86*62c56f98SSadaf Ebrahimi            self.bold_green = ('\033[1;32m', normal)
87*62c56f98SSadaf EbrahimiNO_COLORS = Colors(None)
88*62c56f98SSadaf Ebrahimi
89*62c56f98SSadaf Ebrahimidef log_line(text, prefix='depends.py:', suffix='', color=None):
90*62c56f98SSadaf Ebrahimi    """Print a status message."""
91*62c56f98SSadaf Ebrahimi    if color is not None:
92*62c56f98SSadaf Ebrahimi        prefix = color[0] + prefix
93*62c56f98SSadaf Ebrahimi        suffix = suffix + color[1]
94*62c56f98SSadaf Ebrahimi    sys.stderr.write(prefix + ' ' + text + suffix + '\n')
95*62c56f98SSadaf Ebrahimi    sys.stderr.flush()
96*62c56f98SSadaf Ebrahimi
97*62c56f98SSadaf Ebrahimidef log_command(cmd):
98*62c56f98SSadaf Ebrahimi    """Print a trace of the specified command.
99*62c56f98SSadaf Ebrahimicmd is a list of strings: a command name and its arguments."""
100*62c56f98SSadaf Ebrahimi    log_line(' '.join(cmd), prefix='+')
101*62c56f98SSadaf Ebrahimi
102*62c56f98SSadaf Ebrahimidef backup_config(options):
103*62c56f98SSadaf Ebrahimi    """Back up the library configuration file (mbedtls_config.h).
104*62c56f98SSadaf EbrahimiIf the backup file already exists, it is presumed to be the desired backup,
105*62c56f98SSadaf Ebrahimiso don't make another backup."""
106*62c56f98SSadaf Ebrahimi    if os.path.exists(options.config_backup):
107*62c56f98SSadaf Ebrahimi        options.own_backup = False
108*62c56f98SSadaf Ebrahimi    else:
109*62c56f98SSadaf Ebrahimi        options.own_backup = True
110*62c56f98SSadaf Ebrahimi        shutil.copy(options.config, options.config_backup)
111*62c56f98SSadaf Ebrahimi
112*62c56f98SSadaf Ebrahimidef restore_config(options):
113*62c56f98SSadaf Ebrahimi    """Restore the library configuration file (mbedtls_config.h).
114*62c56f98SSadaf EbrahimiRemove the backup file if it was saved earlier."""
115*62c56f98SSadaf Ebrahimi    if options.own_backup:
116*62c56f98SSadaf Ebrahimi        shutil.move(options.config_backup, options.config)
117*62c56f98SSadaf Ebrahimi    else:
118*62c56f98SSadaf Ebrahimi        shutil.copy(options.config_backup, options.config)
119*62c56f98SSadaf Ebrahimi
120*62c56f98SSadaf Ebrahimidef option_exists(conf, option):
121*62c56f98SSadaf Ebrahimi    return option in conf.settings
122*62c56f98SSadaf Ebrahimi
123*62c56f98SSadaf Ebrahimidef set_config_option_value(conf, option, colors, value: Union[bool, str]):
124*62c56f98SSadaf Ebrahimi    """Set/unset a configuration option, optionally specifying a value.
125*62c56f98SSadaf Ebrahimivalue can be either True/False (set/unset config option), or a string,
126*62c56f98SSadaf Ebrahimiwhich will make a symbol defined with a certain value."""
127*62c56f98SSadaf Ebrahimi    if not option_exists(conf, option):
128*62c56f98SSadaf Ebrahimi        log_line('Symbol {} was not found in {}'.format(option, conf.filename), color=colors.red)
129*62c56f98SSadaf Ebrahimi        return False
130*62c56f98SSadaf Ebrahimi
131*62c56f98SSadaf Ebrahimi    if value is False:
132*62c56f98SSadaf Ebrahimi        log_command(['config.py', 'unset', option])
133*62c56f98SSadaf Ebrahimi        conf.unset(option)
134*62c56f98SSadaf Ebrahimi    elif value is True:
135*62c56f98SSadaf Ebrahimi        log_command(['config.py', 'set', option])
136*62c56f98SSadaf Ebrahimi        conf.set(option)
137*62c56f98SSadaf Ebrahimi    else:
138*62c56f98SSadaf Ebrahimi        log_command(['config.py', 'set', option, value])
139*62c56f98SSadaf Ebrahimi        conf.set(option, value)
140*62c56f98SSadaf Ebrahimi    return True
141*62c56f98SSadaf Ebrahimi
142*62c56f98SSadaf Ebrahimidef set_reference_config(conf, options, colors):
143*62c56f98SSadaf Ebrahimi    """Change the library configuration file (mbedtls_config.h) to the reference state.
144*62c56f98SSadaf EbrahimiThe reference state is the one from which the tested configurations are
145*62c56f98SSadaf Ebrahimiderived."""
146*62c56f98SSadaf Ebrahimi    # Turn off options that are not relevant to the tests and slow them down.
147*62c56f98SSadaf Ebrahimi    log_command(['config.py', 'full'])
148*62c56f98SSadaf Ebrahimi    conf.adapt(config.full_adapter)
149*62c56f98SSadaf Ebrahimi    set_config_option_value(conf, 'MBEDTLS_TEST_HOOKS', colors, False)
150*62c56f98SSadaf Ebrahimi    set_config_option_value(conf, 'MBEDTLS_PSA_CRYPTO_CONFIG', colors, False)
151*62c56f98SSadaf Ebrahimi    if options.unset_use_psa:
152*62c56f98SSadaf Ebrahimi        set_config_option_value(conf, 'MBEDTLS_USE_PSA_CRYPTO', colors, False)
153*62c56f98SSadaf Ebrahimi
154*62c56f98SSadaf Ebrahimiclass Job:
155*62c56f98SSadaf Ebrahimi    """A job builds the library in a specific configuration and runs some tests."""
156*62c56f98SSadaf Ebrahimi    def __init__(self, name, config_settings, commands):
157*62c56f98SSadaf Ebrahimi        """Build a job object.
158*62c56f98SSadaf EbrahimiThe job uses the configuration described by config_settings. This is a
159*62c56f98SSadaf Ebrahimidictionary where the keys are preprocessor symbols and the values are
160*62c56f98SSadaf Ebrahimibooleans or strings. A boolean indicates whether or not to #define the
161*62c56f98SSadaf Ebrahimisymbol. With a string, the symbol is #define'd to that value.
162*62c56f98SSadaf EbrahimiAfter setting the configuration, the job runs the programs specified by
163*62c56f98SSadaf Ebrahimicommands. This is a list of lists of strings; each list of string is a
164*62c56f98SSadaf Ebrahimicommand name and its arguments and is passed to subprocess.call with
165*62c56f98SSadaf Ebrahimishell=False."""
166*62c56f98SSadaf Ebrahimi        self.name = name
167*62c56f98SSadaf Ebrahimi        self.config_settings = config_settings
168*62c56f98SSadaf Ebrahimi        self.commands = commands
169*62c56f98SSadaf Ebrahimi
170*62c56f98SSadaf Ebrahimi    def announce(self, colors, what):
171*62c56f98SSadaf Ebrahimi        '''Announce the start or completion of a job.
172*62c56f98SSadaf EbrahimiIf what is None, announce the start of the job.
173*62c56f98SSadaf EbrahimiIf what is True, announce that the job has passed.
174*62c56f98SSadaf EbrahimiIf what is False, announce that the job has failed.'''
175*62c56f98SSadaf Ebrahimi        if what is True:
176*62c56f98SSadaf Ebrahimi            log_line(self.name + ' PASSED', color=colors.green)
177*62c56f98SSadaf Ebrahimi        elif what is False:
178*62c56f98SSadaf Ebrahimi            log_line(self.name + ' FAILED', color=colors.red)
179*62c56f98SSadaf Ebrahimi        else:
180*62c56f98SSadaf Ebrahimi            log_line('starting ' + self.name, color=colors.cyan)
181*62c56f98SSadaf Ebrahimi
182*62c56f98SSadaf Ebrahimi    def configure(self, conf, options, colors):
183*62c56f98SSadaf Ebrahimi        '''Set library configuration options as required for the job.'''
184*62c56f98SSadaf Ebrahimi        set_reference_config(conf, options, colors)
185*62c56f98SSadaf Ebrahimi        for key, value in sorted(self.config_settings.items()):
186*62c56f98SSadaf Ebrahimi            ret = set_config_option_value(conf, key, colors, value)
187*62c56f98SSadaf Ebrahimi            if ret is False:
188*62c56f98SSadaf Ebrahimi                return False
189*62c56f98SSadaf Ebrahimi        return True
190*62c56f98SSadaf Ebrahimi
191*62c56f98SSadaf Ebrahimi    def test(self, options):
192*62c56f98SSadaf Ebrahimi        '''Run the job's build and test commands.
193*62c56f98SSadaf EbrahimiReturn True if all the commands succeed and False otherwise.
194*62c56f98SSadaf EbrahimiIf options.keep_going is false, stop as soon as one command fails. Otherwise
195*62c56f98SSadaf Ebrahimirun all the commands, except that if the first command fails, none of the
196*62c56f98SSadaf Ebrahimiother commands are run (typically, the first command is a build command
197*62c56f98SSadaf Ebrahimiand subsequent commands are tests that cannot run if the build failed).'''
198*62c56f98SSadaf Ebrahimi        built = False
199*62c56f98SSadaf Ebrahimi        success = True
200*62c56f98SSadaf Ebrahimi        for command in self.commands:
201*62c56f98SSadaf Ebrahimi            log_command(command)
202*62c56f98SSadaf Ebrahimi            ret = subprocess.call(command)
203*62c56f98SSadaf Ebrahimi            if ret != 0:
204*62c56f98SSadaf Ebrahimi                if command[0] not in ['make', options.make_command]:
205*62c56f98SSadaf Ebrahimi                    log_line('*** [{}] Error {}'.format(' '.join(command), ret))
206*62c56f98SSadaf Ebrahimi                if not options.keep_going or not built:
207*62c56f98SSadaf Ebrahimi                    return False
208*62c56f98SSadaf Ebrahimi                success = False
209*62c56f98SSadaf Ebrahimi            built = True
210*62c56f98SSadaf Ebrahimi        return success
211*62c56f98SSadaf Ebrahimi
212*62c56f98SSadaf Ebrahimi# If the configuration option A requires B, make sure that
213*62c56f98SSadaf Ebrahimi# B in REVERSE_DEPENDENCIES[A].
214*62c56f98SSadaf Ebrahimi# All the information here should be contained in check_config.h. This
215*62c56f98SSadaf Ebrahimi# file includes a copy because it changes rarely and it would be a pain
216*62c56f98SSadaf Ebrahimi# to extract automatically.
217*62c56f98SSadaf EbrahimiREVERSE_DEPENDENCIES = {
218*62c56f98SSadaf Ebrahimi    'MBEDTLS_AES_C': ['MBEDTLS_CTR_DRBG_C',
219*62c56f98SSadaf Ebrahimi                      'MBEDTLS_NIST_KW_C'],
220*62c56f98SSadaf Ebrahimi    'MBEDTLS_CHACHA20_C': ['MBEDTLS_CHACHAPOLY_C'],
221*62c56f98SSadaf Ebrahimi    'MBEDTLS_ECDSA_C': ['MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED',
222*62c56f98SSadaf Ebrahimi                        'MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED'],
223*62c56f98SSadaf Ebrahimi    'MBEDTLS_ECP_C': ['MBEDTLS_ECDSA_C',
224*62c56f98SSadaf Ebrahimi                      'MBEDTLS_ECDH_C',
225*62c56f98SSadaf Ebrahimi                      'MBEDTLS_ECJPAKE_C',
226*62c56f98SSadaf Ebrahimi                      'MBEDTLS_ECP_RESTARTABLE',
227*62c56f98SSadaf Ebrahimi                      'MBEDTLS_PK_PARSE_EC_EXTENDED',
228*62c56f98SSadaf Ebrahimi                      'MBEDTLS_PK_PARSE_EC_COMPRESSED',
229*62c56f98SSadaf Ebrahimi                      'MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED',
230*62c56f98SSadaf Ebrahimi                      'MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED',
231*62c56f98SSadaf Ebrahimi                      'MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED',
232*62c56f98SSadaf Ebrahimi                      'MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED',
233*62c56f98SSadaf Ebrahimi                      'MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED',
234*62c56f98SSadaf Ebrahimi                      'MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED',
235*62c56f98SSadaf Ebrahimi                      'MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED',
236*62c56f98SSadaf Ebrahimi                      'MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED'],
237*62c56f98SSadaf Ebrahimi    'MBEDTLS_ECP_DP_SECP256R1_ENABLED': ['MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED'],
238*62c56f98SSadaf Ebrahimi    'MBEDTLS_PKCS1_V21': ['MBEDTLS_X509_RSASSA_PSS_SUPPORT'],
239*62c56f98SSadaf Ebrahimi    'MBEDTLS_PKCS1_V15': ['MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED',
240*62c56f98SSadaf Ebrahimi                          'MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED',
241*62c56f98SSadaf Ebrahimi                          'MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED',
242*62c56f98SSadaf Ebrahimi                          'MBEDTLS_KEY_EXCHANGE_RSA_ENABLED'],
243*62c56f98SSadaf Ebrahimi    'MBEDTLS_RSA_C': ['MBEDTLS_X509_RSASSA_PSS_SUPPORT',
244*62c56f98SSadaf Ebrahimi                      'MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED',
245*62c56f98SSadaf Ebrahimi                      'MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED',
246*62c56f98SSadaf Ebrahimi                      'MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED',
247*62c56f98SSadaf Ebrahimi                      'MBEDTLS_KEY_EXCHANGE_RSA_ENABLED',
248*62c56f98SSadaf Ebrahimi                      'MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED'],
249*62c56f98SSadaf Ebrahimi    'MBEDTLS_SHA256_C': ['MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED',
250*62c56f98SSadaf Ebrahimi                         'MBEDTLS_ENTROPY_FORCE_SHA256',
251*62c56f98SSadaf Ebrahimi                         'MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT',
252*62c56f98SSadaf Ebrahimi                         'MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY',
253*62c56f98SSadaf Ebrahimi                         'MBEDTLS_LMS_C',
254*62c56f98SSadaf Ebrahimi                         'MBEDTLS_LMS_PRIVATE'],
255*62c56f98SSadaf Ebrahimi    'MBEDTLS_SHA512_C': ['MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT',
256*62c56f98SSadaf Ebrahimi                         'MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY'],
257*62c56f98SSadaf Ebrahimi    'MBEDTLS_SHA224_C': ['MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED',
258*62c56f98SSadaf Ebrahimi                         'MBEDTLS_ENTROPY_FORCE_SHA256',
259*62c56f98SSadaf Ebrahimi                         'MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT',
260*62c56f98SSadaf Ebrahimi                         'MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY'],
261*62c56f98SSadaf Ebrahimi    'MBEDTLS_X509_RSASSA_PSS_SUPPORT': []
262*62c56f98SSadaf Ebrahimi}
263*62c56f98SSadaf Ebrahimi
264*62c56f98SSadaf Ebrahimi# If an option is tested in an exclusive test, alter the following defines.
265*62c56f98SSadaf Ebrahimi# These are not necessarily dependencies, but just minimal required changes
266*62c56f98SSadaf Ebrahimi# if a given define is the only one enabled from an exclusive group.
267*62c56f98SSadaf EbrahimiEXCLUSIVE_GROUPS = {
268*62c56f98SSadaf Ebrahimi    'MBEDTLS_SHA512_C': ['-MBEDTLS_SSL_COOKIE_C',
269*62c56f98SSadaf Ebrahimi                         '-MBEDTLS_SSL_TLS_C'],
270*62c56f98SSadaf Ebrahimi    'MBEDTLS_ECP_DP_CURVE448_ENABLED': ['-MBEDTLS_ECDSA_C',
271*62c56f98SSadaf Ebrahimi                                        '-MBEDTLS_ECDSA_DETERMINISTIC',
272*62c56f98SSadaf Ebrahimi                                        '-MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED',
273*62c56f98SSadaf Ebrahimi                                        '-MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED',
274*62c56f98SSadaf Ebrahimi                                        '-MBEDTLS_ECJPAKE_C',
275*62c56f98SSadaf Ebrahimi                                        '-MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED'],
276*62c56f98SSadaf Ebrahimi    'MBEDTLS_ECP_DP_CURVE25519_ENABLED': ['-MBEDTLS_ECDSA_C',
277*62c56f98SSadaf Ebrahimi                                          '-MBEDTLS_ECDSA_DETERMINISTIC',
278*62c56f98SSadaf Ebrahimi                                          '-MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED',
279*62c56f98SSadaf Ebrahimi                                          '-MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED',
280*62c56f98SSadaf Ebrahimi                                          '-MBEDTLS_ECJPAKE_C',
281*62c56f98SSadaf Ebrahimi                                          '-MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED'],
282*62c56f98SSadaf Ebrahimi    'MBEDTLS_ARIA_C': ['-MBEDTLS_CMAC_C'],
283*62c56f98SSadaf Ebrahimi    'MBEDTLS_CAMELLIA_C': ['-MBEDTLS_CMAC_C'],
284*62c56f98SSadaf Ebrahimi    'MBEDTLS_CHACHA20_C': ['-MBEDTLS_CMAC_C', '-MBEDTLS_CCM_C', '-MBEDTLS_GCM_C'],
285*62c56f98SSadaf Ebrahimi    'MBEDTLS_DES_C': ['-MBEDTLS_CCM_C',
286*62c56f98SSadaf Ebrahimi                      '-MBEDTLS_GCM_C',
287*62c56f98SSadaf Ebrahimi                      '-MBEDTLS_SSL_TICKET_C',
288*62c56f98SSadaf Ebrahimi                      '-MBEDTLS_SSL_CONTEXT_SERIALIZATION'],
289*62c56f98SSadaf Ebrahimi}
290*62c56f98SSadaf Ebrahimidef handle_exclusive_groups(config_settings, symbol):
291*62c56f98SSadaf Ebrahimi    """For every symbol tested in an exclusive group check if there are other
292*62c56f98SSadaf Ebrahimidefines to be altered. """
293*62c56f98SSadaf Ebrahimi    for dep in EXCLUSIVE_GROUPS.get(symbol, []):
294*62c56f98SSadaf Ebrahimi        unset = dep.startswith('-')
295*62c56f98SSadaf Ebrahimi        dep = dep[1:]
296*62c56f98SSadaf Ebrahimi        config_settings[dep] = not unset
297*62c56f98SSadaf Ebrahimi
298*62c56f98SSadaf Ebrahimidef turn_off_dependencies(config_settings):
299*62c56f98SSadaf Ebrahimi    """For every option turned off config_settings, also turn off what depends on it.
300*62c56f98SSadaf EbrahimiAn option O is turned off if config_settings[O] is False."""
301*62c56f98SSadaf Ebrahimi    for key, value in sorted(config_settings.items()):
302*62c56f98SSadaf Ebrahimi        if value is not False:
303*62c56f98SSadaf Ebrahimi            continue
304*62c56f98SSadaf Ebrahimi        for dep in REVERSE_DEPENDENCIES.get(key, []):
305*62c56f98SSadaf Ebrahimi            config_settings[dep] = False
306*62c56f98SSadaf Ebrahimi
307*62c56f98SSadaf Ebrahimiclass BaseDomain: # pylint: disable=too-few-public-methods, unused-argument
308*62c56f98SSadaf Ebrahimi    """A base class for all domains."""
309*62c56f98SSadaf Ebrahimi    def __init__(self, symbols, commands, exclude):
310*62c56f98SSadaf Ebrahimi        """Initialize the jobs container"""
311*62c56f98SSadaf Ebrahimi        self.jobs = []
312*62c56f98SSadaf Ebrahimi
313*62c56f98SSadaf Ebrahimiclass ExclusiveDomain(BaseDomain): # pylint: disable=too-few-public-methods
314*62c56f98SSadaf Ebrahimi    """A domain consisting of a set of conceptually-equivalent settings.
315*62c56f98SSadaf EbrahimiEstablish a list of configuration symbols. For each symbol, run a test job
316*62c56f98SSadaf Ebrahimiwith this symbol set and the others unset."""
317*62c56f98SSadaf Ebrahimi    def __init__(self, symbols, commands, exclude=None):
318*62c56f98SSadaf Ebrahimi        """Build a domain for the specified list of configuration symbols.
319*62c56f98SSadaf EbrahimiThe domain contains a set of jobs that enable one of the elements
320*62c56f98SSadaf Ebrahimiof symbols and disable the others.
321*62c56f98SSadaf EbrahimiEach job runs the specified commands.
322*62c56f98SSadaf EbrahimiIf exclude is a regular expression, skip generated jobs whose description
323*62c56f98SSadaf Ebrahimiwould match this regular expression."""
324*62c56f98SSadaf Ebrahimi        super().__init__(symbols, commands, exclude)
325*62c56f98SSadaf Ebrahimi        base_config_settings = {}
326*62c56f98SSadaf Ebrahimi        for symbol in symbols:
327*62c56f98SSadaf Ebrahimi            base_config_settings[symbol] = False
328*62c56f98SSadaf Ebrahimi        for symbol in symbols:
329*62c56f98SSadaf Ebrahimi            description = symbol
330*62c56f98SSadaf Ebrahimi            if exclude and re.match(exclude, description):
331*62c56f98SSadaf Ebrahimi                continue
332*62c56f98SSadaf Ebrahimi            config_settings = base_config_settings.copy()
333*62c56f98SSadaf Ebrahimi            config_settings[symbol] = True
334*62c56f98SSadaf Ebrahimi            handle_exclusive_groups(config_settings, symbol)
335*62c56f98SSadaf Ebrahimi            turn_off_dependencies(config_settings)
336*62c56f98SSadaf Ebrahimi            job = Job(description, config_settings, commands)
337*62c56f98SSadaf Ebrahimi            self.jobs.append(job)
338*62c56f98SSadaf Ebrahimi
339*62c56f98SSadaf Ebrahimiclass ComplementaryDomain(BaseDomain): # pylint: disable=too-few-public-methods
340*62c56f98SSadaf Ebrahimi    """A domain consisting of a set of loosely-related settings.
341*62c56f98SSadaf EbrahimiEstablish a list of configuration symbols. For each symbol, run a test job
342*62c56f98SSadaf Ebrahimiwith this symbol unset.
343*62c56f98SSadaf EbrahimiIf exclude is a regular expression, skip generated jobs whose description
344*62c56f98SSadaf Ebrahimiwould match this regular expression."""
345*62c56f98SSadaf Ebrahimi    def __init__(self, symbols, commands, exclude=None):
346*62c56f98SSadaf Ebrahimi        """Build a domain for the specified list of configuration symbols.
347*62c56f98SSadaf EbrahimiEach job in the domain disables one of the specified symbols.
348*62c56f98SSadaf EbrahimiEach job runs the specified commands."""
349*62c56f98SSadaf Ebrahimi        super().__init__(symbols, commands, exclude)
350*62c56f98SSadaf Ebrahimi        for symbol in symbols:
351*62c56f98SSadaf Ebrahimi            description = '!' + symbol
352*62c56f98SSadaf Ebrahimi            if exclude and re.match(exclude, description):
353*62c56f98SSadaf Ebrahimi                continue
354*62c56f98SSadaf Ebrahimi            config_settings = {symbol: False}
355*62c56f98SSadaf Ebrahimi            turn_off_dependencies(config_settings)
356*62c56f98SSadaf Ebrahimi            job = Job(description, config_settings, commands)
357*62c56f98SSadaf Ebrahimi            self.jobs.append(job)
358*62c56f98SSadaf Ebrahimi
359*62c56f98SSadaf Ebrahimiclass DualDomain(ExclusiveDomain, ComplementaryDomain): # pylint: disable=too-few-public-methods
360*62c56f98SSadaf Ebrahimi    """A domain that contains both the ExclusiveDomain and BaseDomain tests.
361*62c56f98SSadaf EbrahimiBoth parent class __init__ calls are performed in any order and
362*62c56f98SSadaf Ebrahimieach call adds respective jobs. The job array initialization is done once in
363*62c56f98SSadaf EbrahimiBaseDomain, before the parent __init__ calls."""
364*62c56f98SSadaf Ebrahimi
365*62c56f98SSadaf Ebrahimiclass CipherInfo: # pylint: disable=too-few-public-methods
366*62c56f98SSadaf Ebrahimi    """Collect data about cipher.h."""
367*62c56f98SSadaf Ebrahimi    def __init__(self):
368*62c56f98SSadaf Ebrahimi        self.base_symbols = set()
369*62c56f98SSadaf Ebrahimi        with open('include/mbedtls/cipher.h', encoding="utf-8") as fh:
370*62c56f98SSadaf Ebrahimi            for line in fh:
371*62c56f98SSadaf Ebrahimi                m = re.match(r' *MBEDTLS_CIPHER_ID_(\w+),', line)
372*62c56f98SSadaf Ebrahimi                if m and m.group(1) not in ['NONE', 'NULL', '3DES']:
373*62c56f98SSadaf Ebrahimi                    self.base_symbols.add('MBEDTLS_' + m.group(1) + '_C')
374*62c56f98SSadaf Ebrahimi
375*62c56f98SSadaf Ebrahimiclass DomainData:
376*62c56f98SSadaf Ebrahimi    """A container for domains and jobs, used to structurize testing."""
377*62c56f98SSadaf Ebrahimi    def config_symbols_matching(self, regexp):
378*62c56f98SSadaf Ebrahimi        """List the mbedtls_config.h settings matching regexp."""
379*62c56f98SSadaf Ebrahimi        return [symbol for symbol in self.all_config_symbols
380*62c56f98SSadaf Ebrahimi                if re.match(regexp, symbol)]
381*62c56f98SSadaf Ebrahimi
382*62c56f98SSadaf Ebrahimi    def __init__(self, options, conf):
383*62c56f98SSadaf Ebrahimi        """Gather data about the library and establish a list of domains to test."""
384*62c56f98SSadaf Ebrahimi        build_command = [options.make_command, 'CFLAGS=-Werror']
385*62c56f98SSadaf Ebrahimi        build_and_test = [build_command, [options.make_command, 'test']]
386*62c56f98SSadaf Ebrahimi        self.all_config_symbols = set(conf.settings.keys())
387*62c56f98SSadaf Ebrahimi        # Find hash modules by name.
388*62c56f98SSadaf Ebrahimi        hash_symbols = self.config_symbols_matching(r'MBEDTLS_(MD|RIPEMD|SHA)[0-9]+_C\Z')
389*62c56f98SSadaf Ebrahimi        # Find elliptic curve enabling macros by name.
390*62c56f98SSadaf Ebrahimi        curve_symbols = self.config_symbols_matching(r'MBEDTLS_ECP_DP_\w+_ENABLED\Z')
391*62c56f98SSadaf Ebrahimi        # Find key exchange enabling macros by name.
392*62c56f98SSadaf Ebrahimi        key_exchange_symbols = self.config_symbols_matching(r'MBEDTLS_KEY_EXCHANGE_\w+_ENABLED\Z')
393*62c56f98SSadaf Ebrahimi        # Find cipher IDs (block permutations and stream ciphers --- chaining
394*62c56f98SSadaf Ebrahimi        # and padding modes are exercised separately) information by parsing
395*62c56f98SSadaf Ebrahimi        # cipher.h, as the information is not readily available in mbedtls_config.h.
396*62c56f98SSadaf Ebrahimi        cipher_info = CipherInfo()
397*62c56f98SSadaf Ebrahimi        # Find block cipher chaining and padding mode enabling macros by name.
398*62c56f98SSadaf Ebrahimi        cipher_chaining_symbols = self.config_symbols_matching(r'MBEDTLS_CIPHER_MODE_\w+\Z')
399*62c56f98SSadaf Ebrahimi        cipher_padding_symbols = self.config_symbols_matching(r'MBEDTLS_CIPHER_PADDING_\w+\Z')
400*62c56f98SSadaf Ebrahimi        self.domains = {
401*62c56f98SSadaf Ebrahimi            # Cipher IDs, chaining modes and padding modes. Run the test suites.
402*62c56f98SSadaf Ebrahimi            'cipher_id': ExclusiveDomain(cipher_info.base_symbols,
403*62c56f98SSadaf Ebrahimi                                         build_and_test),
404*62c56f98SSadaf Ebrahimi            'cipher_chaining': ExclusiveDomain(cipher_chaining_symbols,
405*62c56f98SSadaf Ebrahimi                                               build_and_test),
406*62c56f98SSadaf Ebrahimi            'cipher_padding': ExclusiveDomain(cipher_padding_symbols,
407*62c56f98SSadaf Ebrahimi                                              build_and_test),
408*62c56f98SSadaf Ebrahimi            # Elliptic curves. Run the test suites.
409*62c56f98SSadaf Ebrahimi            'curves': ExclusiveDomain(curve_symbols, build_and_test),
410*62c56f98SSadaf Ebrahimi            # Hash algorithms. Excluding exclusive domains of MD, RIPEMD, SHA1,
411*62c56f98SSadaf Ebrahimi            # SHA224 and SHA384 because MBEDTLS_ENTROPY_C is extensively used
412*62c56f98SSadaf Ebrahimi            # across various modules, but it depends on either SHA256 or SHA512.
413*62c56f98SSadaf Ebrahimi            # As a consequence an "exclusive" test of anything other than SHA256
414*62c56f98SSadaf Ebrahimi            # or SHA512 with MBEDTLS_ENTROPY_C enabled is not possible.
415*62c56f98SSadaf Ebrahimi            'hashes': DualDomain(hash_symbols, build_and_test,
416*62c56f98SSadaf Ebrahimi                                 exclude=r'MBEDTLS_(MD|RIPEMD|SHA1_)' \
417*62c56f98SSadaf Ebrahimi                                          '|MBEDTLS_SHA224_' \
418*62c56f98SSadaf Ebrahimi                                          '|MBEDTLS_SHA384_' \
419*62c56f98SSadaf Ebrahimi                                          '|MBEDTLS_SHA3_'),
420*62c56f98SSadaf Ebrahimi            # Key exchange types.
421*62c56f98SSadaf Ebrahimi            'kex': ExclusiveDomain(key_exchange_symbols, build_and_test),
422*62c56f98SSadaf Ebrahimi            'pkalgs': ComplementaryDomain(['MBEDTLS_ECDSA_C',
423*62c56f98SSadaf Ebrahimi                                           'MBEDTLS_ECP_C',
424*62c56f98SSadaf Ebrahimi                                           'MBEDTLS_PKCS1_V21',
425*62c56f98SSadaf Ebrahimi                                           'MBEDTLS_PKCS1_V15',
426*62c56f98SSadaf Ebrahimi                                           'MBEDTLS_RSA_C',
427*62c56f98SSadaf Ebrahimi                                           'MBEDTLS_X509_RSASSA_PSS_SUPPORT'],
428*62c56f98SSadaf Ebrahimi                                          build_and_test),
429*62c56f98SSadaf Ebrahimi        }
430*62c56f98SSadaf Ebrahimi        self.jobs = {}
431*62c56f98SSadaf Ebrahimi        for domain in self.domains.values():
432*62c56f98SSadaf Ebrahimi            for job in domain.jobs:
433*62c56f98SSadaf Ebrahimi                self.jobs[job.name] = job
434*62c56f98SSadaf Ebrahimi
435*62c56f98SSadaf Ebrahimi    def get_jobs(self, name):
436*62c56f98SSadaf Ebrahimi        """Return the list of jobs identified by the given name.
437*62c56f98SSadaf EbrahimiA name can either be the name of a domain or the name of one specific job."""
438*62c56f98SSadaf Ebrahimi        if name in self.domains:
439*62c56f98SSadaf Ebrahimi            return sorted(self.domains[name].jobs, key=lambda job: job.name)
440*62c56f98SSadaf Ebrahimi        else:
441*62c56f98SSadaf Ebrahimi            return [self.jobs[name]]
442*62c56f98SSadaf Ebrahimi
443*62c56f98SSadaf Ebrahimidef run(options, job, conf, colors=NO_COLORS):
444*62c56f98SSadaf Ebrahimi    """Run the specified job (a Job instance)."""
445*62c56f98SSadaf Ebrahimi    subprocess.check_call([options.make_command, 'clean'])
446*62c56f98SSadaf Ebrahimi    job.announce(colors, None)
447*62c56f98SSadaf Ebrahimi    if not job.configure(conf, options, colors):
448*62c56f98SSadaf Ebrahimi        job.announce(colors, False)
449*62c56f98SSadaf Ebrahimi        return False
450*62c56f98SSadaf Ebrahimi    conf.write()
451*62c56f98SSadaf Ebrahimi    success = job.test(options)
452*62c56f98SSadaf Ebrahimi    job.announce(colors, success)
453*62c56f98SSadaf Ebrahimi    return success
454*62c56f98SSadaf Ebrahimi
455*62c56f98SSadaf Ebrahimidef run_tests(options, domain_data, conf):
456*62c56f98SSadaf Ebrahimi    """Run the desired jobs.
457*62c56f98SSadaf Ebrahimidomain_data should be a DomainData instance that describes the available
458*62c56f98SSadaf Ebrahimidomains and jobs.
459*62c56f98SSadaf EbrahimiRun the jobs listed in options.tasks."""
460*62c56f98SSadaf Ebrahimi    if not hasattr(options, 'config_backup'):
461*62c56f98SSadaf Ebrahimi        options.config_backup = options.config + '.bak'
462*62c56f98SSadaf Ebrahimi    colors = Colors(options)
463*62c56f98SSadaf Ebrahimi    jobs = []
464*62c56f98SSadaf Ebrahimi    failures = []
465*62c56f98SSadaf Ebrahimi    successes = []
466*62c56f98SSadaf Ebrahimi    for name in options.tasks:
467*62c56f98SSadaf Ebrahimi        jobs += domain_data.get_jobs(name)
468*62c56f98SSadaf Ebrahimi    backup_config(options)
469*62c56f98SSadaf Ebrahimi    try:
470*62c56f98SSadaf Ebrahimi        for job in jobs:
471*62c56f98SSadaf Ebrahimi            success = run(options, job, conf, colors=colors)
472*62c56f98SSadaf Ebrahimi            if not success:
473*62c56f98SSadaf Ebrahimi                if options.keep_going:
474*62c56f98SSadaf Ebrahimi                    failures.append(job.name)
475*62c56f98SSadaf Ebrahimi                else:
476*62c56f98SSadaf Ebrahimi                    return False
477*62c56f98SSadaf Ebrahimi            else:
478*62c56f98SSadaf Ebrahimi                successes.append(job.name)
479*62c56f98SSadaf Ebrahimi        restore_config(options)
480*62c56f98SSadaf Ebrahimi    except:
481*62c56f98SSadaf Ebrahimi        # Restore the configuration, except in stop-on-error mode if there
482*62c56f98SSadaf Ebrahimi        # was an error, where we leave the failing configuration up for
483*62c56f98SSadaf Ebrahimi        # developer convenience.
484*62c56f98SSadaf Ebrahimi        if options.keep_going:
485*62c56f98SSadaf Ebrahimi            restore_config(options)
486*62c56f98SSadaf Ebrahimi        raise
487*62c56f98SSadaf Ebrahimi    if successes:
488*62c56f98SSadaf Ebrahimi        log_line('{} passed'.format(' '.join(successes)), color=colors.bold_green)
489*62c56f98SSadaf Ebrahimi    if failures:
490*62c56f98SSadaf Ebrahimi        log_line('{} FAILED'.format(' '.join(failures)), color=colors.bold_red)
491*62c56f98SSadaf Ebrahimi        return False
492*62c56f98SSadaf Ebrahimi    else:
493*62c56f98SSadaf Ebrahimi        return True
494*62c56f98SSadaf Ebrahimi
495*62c56f98SSadaf Ebrahimidef main():
496*62c56f98SSadaf Ebrahimi    try:
497*62c56f98SSadaf Ebrahimi        parser = argparse.ArgumentParser(
498*62c56f98SSadaf Ebrahimi            formatter_class=argparse.RawDescriptionHelpFormatter,
499*62c56f98SSadaf Ebrahimi            description=
500*62c56f98SSadaf Ebrahimi            "Test Mbed TLS with a subset of algorithms.\n\n"
501*62c56f98SSadaf Ebrahimi            "Example usage:\n"
502*62c56f98SSadaf Ebrahimi            r"./tests/scripts/depends.py \!MBEDTLS_SHA1_C MBEDTLS_SHA256_C""\n"
503*62c56f98SSadaf Ebrahimi            "./tests/scripts/depends.py MBEDTLS_AES_C hashes\n"
504*62c56f98SSadaf Ebrahimi            "./tests/scripts/depends.py cipher_id cipher_chaining\n")
505*62c56f98SSadaf Ebrahimi        parser.add_argument('--color', metavar='WHEN',
506*62c56f98SSadaf Ebrahimi                            help='Colorize the output (always/auto/never)',
507*62c56f98SSadaf Ebrahimi                            choices=['always', 'auto', 'never'], default='auto')
508*62c56f98SSadaf Ebrahimi        parser.add_argument('-c', '--config', metavar='FILE',
509*62c56f98SSadaf Ebrahimi                            help='Configuration file to modify',
510*62c56f98SSadaf Ebrahimi                            default='include/mbedtls/mbedtls_config.h')
511*62c56f98SSadaf Ebrahimi        parser.add_argument('-C', '--directory', metavar='DIR',
512*62c56f98SSadaf Ebrahimi                            help='Change to this directory before anything else',
513*62c56f98SSadaf Ebrahimi                            default='.')
514*62c56f98SSadaf Ebrahimi        parser.add_argument('-k', '--keep-going',
515*62c56f98SSadaf Ebrahimi                            help='Try all configurations even if some fail (default)',
516*62c56f98SSadaf Ebrahimi                            action='store_true', dest='keep_going', default=True)
517*62c56f98SSadaf Ebrahimi        parser.add_argument('-e', '--no-keep-going',
518*62c56f98SSadaf Ebrahimi                            help='Stop as soon as a configuration fails',
519*62c56f98SSadaf Ebrahimi                            action='store_false', dest='keep_going')
520*62c56f98SSadaf Ebrahimi        parser.add_argument('--list-jobs',
521*62c56f98SSadaf Ebrahimi                            help='List supported jobs and exit',
522*62c56f98SSadaf Ebrahimi                            action='append_const', dest='list', const='jobs')
523*62c56f98SSadaf Ebrahimi        parser.add_argument('--list-domains',
524*62c56f98SSadaf Ebrahimi                            help='List supported domains and exit',
525*62c56f98SSadaf Ebrahimi                            action='append_const', dest='list', const='domains')
526*62c56f98SSadaf Ebrahimi        parser.add_argument('--make-command', metavar='CMD',
527*62c56f98SSadaf Ebrahimi                            help='Command to run instead of make (e.g. gmake)',
528*62c56f98SSadaf Ebrahimi                            action='store', default='make')
529*62c56f98SSadaf Ebrahimi        parser.add_argument('--unset-use-psa',
530*62c56f98SSadaf Ebrahimi                            help='Unset MBEDTLS_USE_PSA_CRYPTO before any test',
531*62c56f98SSadaf Ebrahimi                            action='store_true', dest='unset_use_psa')
532*62c56f98SSadaf Ebrahimi        parser.add_argument('tasks', metavar='TASKS', nargs='*',
533*62c56f98SSadaf Ebrahimi                            help='The domain(s) or job(s) to test (default: all).',
534*62c56f98SSadaf Ebrahimi                            default=True)
535*62c56f98SSadaf Ebrahimi        options = parser.parse_args()
536*62c56f98SSadaf Ebrahimi        os.chdir(options.directory)
537*62c56f98SSadaf Ebrahimi        conf = config.ConfigFile(options.config)
538*62c56f98SSadaf Ebrahimi        domain_data = DomainData(options, conf)
539*62c56f98SSadaf Ebrahimi
540*62c56f98SSadaf Ebrahimi        if options.tasks is True:
541*62c56f98SSadaf Ebrahimi            options.tasks = sorted(domain_data.domains.keys())
542*62c56f98SSadaf Ebrahimi        if options.list:
543*62c56f98SSadaf Ebrahimi            for arg in options.list:
544*62c56f98SSadaf Ebrahimi                for domain_name in sorted(getattr(domain_data, arg).keys()):
545*62c56f98SSadaf Ebrahimi                    print(domain_name)
546*62c56f98SSadaf Ebrahimi            sys.exit(0)
547*62c56f98SSadaf Ebrahimi        else:
548*62c56f98SSadaf Ebrahimi            sys.exit(0 if run_tests(options, domain_data, conf) else 1)
549*62c56f98SSadaf Ebrahimi    except Exception: # pylint: disable=broad-except
550*62c56f98SSadaf Ebrahimi        traceback.print_exc()
551*62c56f98SSadaf Ebrahimi        sys.exit(3)
552*62c56f98SSadaf Ebrahimi
553*62c56f98SSadaf Ebrahimiif __name__ == '__main__':
554*62c56f98SSadaf Ebrahimi    main()
555