1from __future__ import print_function
2from __future__ import absolute_import
3
4# System modules
5import argparse
6import sys
7import multiprocessing
8import os
9import textwrap
10
11# Third-party modules
12
13# LLDB modules
14from . import configuration
15
16
17class ArgParseNamespace(object):
18    pass
19
20
21def parse_args(parser, argv):
22    """ Returns an argument object. LLDB_TEST_ARGUMENTS environment variable can
23        be used to pass additional arguments.
24    """
25    args = ArgParseNamespace()
26
27    if ('LLDB_TEST_ARGUMENTS' in os.environ):
28        print(
29            "Arguments passed through environment: '%s'" %
30            os.environ['LLDB_TEST_ARGUMENTS'])
31        args = parser.parse_args([sys.argv[0]].__add__(
32            os.environ['LLDB_TEST_ARGUMENTS'].split()), namespace=args)
33
34    return parser.parse_args(args=argv, namespace=args)
35
36
37def default_thread_count():
38    # Check if specified in the environment
39    num_threads_str = os.environ.get("LLDB_TEST_THREADS")
40    if num_threads_str:
41        return int(num_threads_str)
42    else:
43        return multiprocessing.cpu_count()
44
45
46def create_parser():
47    parser = argparse.ArgumentParser(
48        description='description',
49        prefix_chars='+-',
50        add_help=False)
51    group = None
52
53    # Helper function for boolean options (group will point to the current
54    # group when executing X)
55    X = lambda optstr, helpstr, **kwargs: group.add_argument(
56        optstr, help=helpstr, action='store_true', **kwargs)
57
58    group = parser.add_argument_group('Help')
59    group.add_argument(
60        '-h',
61        '--help',
62        dest='h',
63        action='store_true',
64        help="Print this help message and exit.  Add '-v' for more detailed help.")
65
66    # C and Python toolchain options
67    group = parser.add_argument_group('Toolchain options')
68    group.add_argument(
69        '-A',
70        '--arch',
71        metavar='arch',
72        action='append',
73        dest='archs',
74        help=textwrap.dedent('''Specify the architecture(s) to test. This option can be specified more than once'''))
75    group.add_argument('-C', '--compiler', metavar='compiler', dest='compilers', action='append', help=textwrap.dedent(
76        '''Specify the compiler(s) used to build the inferior executables. The compiler path can be an executable basename or a full path to a compiler executable. This option can be specified multiple times.'''))
77    if sys.platform == 'darwin':
78        group.add_argument('--apple-sdk', metavar='apple_sdk', dest='apple_sdk', help=textwrap.dedent(
79            '''Specify the name of the Apple SDK (macosx, macosx.internal, iphoneos, iphoneos.internal, or path to SDK) and use the appropriate tools from that SDK's toolchain.'''))
80    # FIXME? This won't work for different extra flags according to each arch.
81    group.add_argument(
82        '-E',
83        metavar='extra-flags',
84        help=textwrap.dedent('''Specify the extra flags to be passed to the toolchain when building the inferior programs to be debugged
85                                                           suggestions: do not lump the "-A arch1 -A arch2" together such that the -E option applies to only one of the architectures'''))
86
87    # Test filtering options
88    group = parser.add_argument_group('Test filtering options')
89    group.add_argument(
90        '-f',
91        metavar='filterspec',
92        action='append',
93        help='Specify a filter, which consists of the test class name, a dot, followed by the test method, to only admit such test into the test suite')  # FIXME: Example?
94    X('-l', "Don't skip long running tests")
95    group.add_argument(
96        '-p',
97        metavar='pattern',
98        help='Specify a regexp filename pattern for inclusion in the test suite')
99    group.add_argument(
100        '-G',
101        '--category',
102        metavar='category',
103        action='append',
104        dest='categoriesList',
105        help=textwrap.dedent('''Specify categories of test cases of interest. Can be specified more than once.'''))
106    group.add_argument(
107        '--skip-category',
108        metavar='category',
109        action='append',
110        dest='skipCategories',
111        help=textwrap.dedent('''Specify categories of test cases to skip. Takes precedence over -G. Can be specified more than once.'''))
112
113    # Configuration options
114    group = parser.add_argument_group('Configuration options')
115    group.add_argument(
116        '--framework',
117        metavar='framework-path',
118        help='The path to LLDB.framework')
119    group.add_argument(
120        '--executable',
121        metavar='executable-path',
122        help='The path to the lldb executable')
123    group.add_argument(
124        '-s',
125        metavar='name',
126        help='Specify the name of the dir created to store the session files of tests with errored or failed status. If not specified, the test driver uses the timestamp as the session dir name')
127    group.add_argument(
128        '-S',
129        '--session-file-format',
130        default=configuration.session_file_format,
131        metavar='format',
132        help='Specify session file name format.  See configuration.py for a description.')
133    group.add_argument(
134        '-y',
135        type=int,
136        metavar='count',
137        help="Specify the iteration count used to collect our benchmarks. An example is the number of times to do 'thread step-over' to measure stepping speed.")
138    group.add_argument(
139        '-#',
140        type=int,
141        metavar='sharp',
142        dest='sharp',
143        help='Repeat the test suite for a specified number of times')
144    group.add_argument('--channel', metavar='channel', dest='channels', action='append', help=textwrap.dedent(
145        "Specify the log channels (and optional categories) e.g. 'lldb all' or 'gdb-remote packets' if no categories are specified, 'default' is used"))
146    group.add_argument(
147        '--log-success',
148        dest='log_success',
149        action='store_true',
150        help="Leave logs/traces even for successful test runs (useful for creating reference log files during debugging.)")
151
152    # Configuration options
153    group = parser.add_argument_group('Remote platform options')
154    group.add_argument(
155        '--platform-name',
156        dest='lldb_platform_name',
157        metavar='platform-name',
158        help='The name of a remote platform to use')
159    group.add_argument(
160        '--platform-url',
161        dest='lldb_platform_url',
162        metavar='platform-url',
163        help='A LLDB platform URL to use when connecting to a remote platform to run the test suite')
164    group.add_argument(
165        '--platform-working-dir',
166        dest='lldb_platform_working_dir',
167        metavar='platform-working-dir',
168        help='The directory to use on the remote platform.')
169
170    # Test-suite behaviour
171    group = parser.add_argument_group('Runtime behaviour options')
172    X('-d', 'Suspend the process after launch to wait indefinitely for a debugger to attach')
173    X('-q', "Don't print extra output from this script.")
174    X('-t', 'Turn on tracing of lldb command and other detailed test executions')
175    group.add_argument(
176        '-u',
177        dest='unset_env_varnames',
178        metavar='variable',
179        action='append',
180        help='Specify an environment variable to unset before running the test cases. e.g., -u DYLD_INSERT_LIBRARIES -u MallocScribble')
181    group.add_argument(
182        '--env',
183        dest='set_env_vars',
184        metavar='variable',
185        action='append',
186        help='Specify an environment variable to set to the given value before running the test cases e.g.: --env CXXFLAGS=-O3 --env DYLD_INSERT_LIBRARIES')
187    X('-v', 'Do verbose mode of unittest framework (print out each test case invocation)')
188    group.add_argument(
189        '--enable-crash-dialog',
190        dest='disable_crash_dialog',
191        action='store_false',
192        help='(Windows only) When LLDB crashes, display the Windows crash dialog.')
193    group.set_defaults(disable_crash_dialog=True)
194
195    group = parser.add_argument_group('Parallel execution options')
196    group.add_argument(
197        '--inferior',
198        action='store_true',
199        help=('specify this invocation is a multiprocess inferior, '
200              'used internally'))
201    group.add_argument(
202        '--no-multiprocess',
203        action='store_true',
204        help='skip running the multiprocess test runner')
205    group.add_argument(
206        '--threads',
207        type=int,
208        dest='num_threads',
209        default=default_thread_count(),
210        help=('The number of threads/processes to use when running tests '
211              'separately, defaults to the number of CPU cores available'))
212    group.add_argument(
213        '--test-subdir',
214        action='store',
215        help='Specify a test subdirectory to use relative to the test root dir'
216    )
217    group.add_argument(
218        '--test-runner-name',
219        action='store',
220        help=('Specify a test runner strategy.  Valid values: multiprocessing,'
221              ' multiprocessing-pool, serial, threading, threading-pool')
222    )
223
224    # Test results support.
225    group = parser.add_argument_group('Test results options')
226    group.add_argument(
227        '--curses',
228        action='store_true',
229        help='Shortcut for specifying test results using the curses formatter')
230    group.add_argument(
231        '--results-file',
232        action='store',
233        help=('Specifies the file where test results will be written '
234              'according to the results-formatter class used'))
235    group.add_argument(
236        '--results-port',
237        action='store',
238        type=int,
239        help=('Specifies the localhost port to which the results '
240              'formatted output should be sent'))
241    group.add_argument(
242        '--results-formatter',
243        action='store',
244        help=('Specifies the full package/module/class name used to translate '
245              'test events into some kind of meaningful report, written to '
246              'the designated output results file-like object'))
247    group.add_argument(
248        '--results-formatter-option',
249        '-O',
250        action='append',
251        dest='results_formatter_options',
252        help=('Specify an option to pass to the formatter. '
253              'Use --results-formatter-option="--option1=val1" '
254              'syntax.  Note the "=" is critical, don\'t include whitespace.'))
255    group.add_argument(
256        '--event-add-entries',
257        action='store',
258        help=('Specify comma-separated KEY=VAL entries to add key and value '
259              'pairs to all test events generated by this test run.  VAL may '
260              'be specified as VAL:TYPE, where TYPE may be int to convert '
261              'the value to an int'))
262
263    # Re-run related arguments
264    group = parser.add_argument_group('Test Re-run Options')
265    group.add_argument(
266        '--rerun-all-issues',
267        action='store_true',
268        help=('Re-run all issues that occurred during the test run '
269              'irrespective of the test method\'s marking as flakey. '
270              'Default behavior is to apply re-runs only to flakey '
271              'tests that generate issues.'))
272    group.add_argument(
273        '--rerun-max-file-threshold',
274        action='store',
275        type=int,
276        default=50,
277        help=('Maximum number of files requiring a rerun beyond '
278              'which the rerun will not occur.  This is meant to '
279              'stop a catastrophically failing test suite from forcing '
280              'all tests to be rerun in the single-worker phase.'))
281
282    # Remove the reference to our helper function
283    del X
284
285    group = parser.add_argument_group('Test directories')
286    group.add_argument(
287        'args',
288        metavar='test-dir',
289        nargs='*',
290        help='Specify a list of directory names to search for test modules named after Test*.py (test discovery). If empty, search from the current working directory instead.')
291
292    return parser
293