1#!/usr/local/bin/python3.8
2
3# Copyright 2002-2005 Dave Abrahams.
4# Copyright 2002-2006 Vladimir Prus.
5# Distributed under the Boost Software License, Version 1.0.
6#    (See accompanying file LICENSE_1_0.txt or copy at
7#         http://www.boost.org/LICENSE_1_0.txt)
8
9from __future__ import print_function
10
11import BoostBuild
12
13import os
14import os.path
15import sys
16
17xml = "--xml" in sys.argv
18toolset = BoostBuild.get_toolset()
19
20
21# Clear environment for testing.
22#
23for s in ("BOOST_ROOT", "BOOST_BUILD_PATH", "JAM_TOOLSET", "BCCROOT",
24    "MSVCDir", "MSVC", "MSVCNT", "MINGW", "watcom"):
25    try:
26        del os.environ[s]
27    except:
28        pass
29
30BoostBuild.set_defer_annotations(1)
31
32
33def run_tests(critical_tests, other_tests):
34    """
35      Runs first the critical_tests and then the other_tests.
36
37      Writes the name of the first failed test to test_results.txt. Critical
38    tests are run in the specified order, other tests are run starting with the
39    one that failed first on the last test run.
40
41    """
42    last_failed = last_failed_test()
43    other_tests = reorder_tests(other_tests, last_failed)
44    all_tests = critical_tests + other_tests
45
46    invocation_dir = os.getcwd()
47    max_test_name_len = 10
48    for x in all_tests:
49        if len(x) > max_test_name_len:
50            max_test_name_len = len(x)
51
52    pass_count = 0
53    failures_count = 0
54
55    for test in all_tests:
56        if not xml:
57            s = "%%-%ds :" % max_test_name_len % test
58            print(s, end='')
59
60        passed = 0
61        try:
62            __import__(test)
63            passed = 1
64        except KeyboardInterrupt:
65            """This allows us to abort the testing manually using Ctrl-C."""
66            raise
67        except SystemExit:
68            """This is the regular way our test scripts are supposed to report
69            test failures."""
70        except:
71            exc_type, exc_value, exc_tb = sys.exc_info()
72            try:
73                BoostBuild.annotation("failure - unhandled exception", "%s - "
74                    "%s" % (exc_type.__name__, exc_value))
75                BoostBuild.annotate_stack_trace(exc_tb)
76            finally:
77                #   Explicitly clear a hard-to-garbage-collect traceback
78                # related reference cycle as per documented sys.exc_info()
79                # usage suggestion.
80                del exc_tb
81
82        if passed:
83            pass_count += 1
84        else:
85            failures_count += 1
86            if failures_count == 1:
87                f = open(os.path.join(invocation_dir, "test_results.txt"), "w")
88                try:
89                    f.write(test)
90                finally:
91                    f.close()
92
93        #   Restore the current directory, which might have been changed by the
94        # test.
95        os.chdir(invocation_dir)
96
97        if not xml:
98            if passed:
99                print("PASSED")
100            else:
101                print("FAILED")
102                BoostBuild.flush_annotations()
103        else:
104            rs = "succeed"
105            if not passed:
106                rs = "fail"
107            print('''
108<test-log library="build" test-name="%s" test-type="run" toolset="%s" test-program="%s" target-directory="%s">
109<run result="%s">''' % (test, toolset, "tools/build/v2/test/" + test + ".py",
110                "boost/bin.v2/boost.build.tests/" + toolset + "/" + test, rs))
111            if not passed:
112                BoostBuild.flush_annotations(1)
113            print('''
114</run>
115</test-log>
116''')
117        sys.stdout.flush()  # Makes testing under emacs more entertaining.
118        BoostBuild.clear_annotations()
119
120    # Erase the file on success.
121    if failures_count == 0:
122        open("test_results.txt", "w").close()
123
124    if not xml:
125        print('''
126        === Test summary ===
127        PASS: %d
128        FAIL: %d
129        ''' % (pass_count, failures_count))
130
131    # exit with failure with failures
132    if failures_count > 0:
133        sys.exit(1)
134
135def last_failed_test():
136    "Returns the name of the last failed test or None."
137    try:
138        f = open("test_results.txt")
139        try:
140            return f.read().strip()
141        finally:
142            f.close()
143    except Exception:
144        return None
145
146
147def reorder_tests(tests, first_test):
148    try:
149        n = tests.index(first_test)
150        return [first_test] + tests[:n] + tests[n + 1:]
151    except ValueError:
152        return tests
153
154
155critical_tests = ["unit_tests", "module_actions", "startup_v2", "core_d12",
156    "core_typecheck", "core_delete_module", "core_language", "core_arguments",
157    "core_varnames", "core_import_module"]
158
159# We want to collect debug information about the test site before running any
160# of the tests, but only when not running the tests interactively. Then the
161# user can easily run this always-failing test directly to see what it would
162# have returned and there is no need to have it spoil a possible 'all tests
163# passed' result.
164if xml:
165    critical_tests.insert(0, "collect_debug_info")
166
167tests = ["absolute_sources",
168         "alias",
169         "alternatives",
170         "always",
171         "bad_dirname",
172         "build_dir",
173         "build_file",
174         "build_hooks",
175         "build_no",
176         "builtin_echo",
177         "builtin_exit",
178         "builtin_glob",
179         "builtin_split_by_characters",
180         "bzip2",
181         "c_file",
182         "chain",
183         "clean",
184         "command_line_properties",
185         "composite",
186         "conditionals",
187         "conditionals2",
188         "conditionals3",
189         "conditionals_multiple",
190         "configuration",
191         "configure",
192         "copy_time",
193         "core_action_output",
194         "core_action_status",
195         "core_actions_quietly",
196         "core_at_file",
197         "core_bindrule",
198         "core_syntax_error_exit_status",
199         "core_fail_expected",
200         "core_jamshell",
201         "core_multifile_actions",
202         "core_nt_cmd_line",
203         "core_option_d2",
204         "core_option_l",
205         "core_option_n",
206         "core_parallel_actions",
207         "core_parallel_multifile_actions_1",
208         "core_parallel_multifile_actions_2",
209         "core_scanner",
210         "core_source_line_tracking",
211         "core_update_now",
212         "core_variables_in_actions",
213         "custom_generator",
214         "debugger",
215         "debugger-mi",
216         "default_build",
217         "default_features",
218# This test is known to be broken itself.
219#         "default_toolset",
220         "dependency_property",
221         "dependency_test",
222         "disambiguation",
223         "dll_path",
224         "double_loading",
225         "duplicate",
226         "example_libraries",
227         "example_make",
228         "exit_status",
229         "expansion",
230         "explicit",
231         "feature_cxxflags",
232         "feature_implicit_dependency",
233         "feature_relevant",
234         "feature_suppress_import_lib",
235         "file_types",
236         "flags",
237         "generator_selection",
238         "generators_test",
239         "implicit_dependency",
240         "indirect_conditional",
241         "inherit_toolset",
242         "inherited_dependency",
243         "inline",
244         "libjpeg",
245         "liblzma",
246         "libzstd",
247         "lib_source_property",
248         "lib_zlib",
249         "library_chain",
250         "library_property",
251         "link",
252         "load_order",
253         "loop",
254         "make_rule",
255         "message",
256         "ndebug",
257         "no_type",
258         "notfile",
259         "ordered_include",
260         "out_of_tree",
261         "package",
262         "param",
263         "path_features",
264         "prebuilt",
265         "print",
266         "project_dependencies",
267         "project_glob",
268         "project_id",
269         "project_root_constants",
270         "project_root_rule",
271         "project_test3",
272         "project_test4",
273         "property_expansion",
274         "rebuilds",
275         "relative_sources",
276         "remove_requirement",
277         "rescan_header",
278         "resolution",
279         "rootless",
280         "scanner_causing_rebuilds",
281         "searched_lib",
282         "skipping",
283         "sort_rule",
284         "source_locations",
285         "source_order",
286         "space_in_path",
287         "stage",
288         "standalone",
289         "static_and_shared_library",
290         "suffix",
291         "tag",
292         "test_rc",
293         "testing",
294         "timedata",
295         "toolset_clang_darwin",
296         "toolset_clang_linux",
297         "toolset_clang_vxworks",
298         "toolset_darwin",
299         "toolset_defaults",
300         "toolset_gcc",
301         "toolset_intel_darwin",
302         "toolset_requirements",
303         "unit_test",
304         "unused",
305         "use_requirements",
306         "using",
307         "wrapper",
308         "wrong_project",
309         ]
310
311if os.name == "posix":
312    tests.append("symlink")
313    # On Windows, library order is not important, so skip this test. Besides,
314    # it fails ;-). Further, the test relies on the fact that on Linux, one can
315    # build a shared library with unresolved symbols. This is not true on
316    # Windows, even with cygwin gcc.
317
318#   Disable this test until we figure how to address failures due to --as-needed being default now.
319#    if "CYGWIN" not in os.uname()[0]:
320#        tests.append("library_order")
321
322if toolset.startswith("gcc") and os.name != "nt":
323    # On Windows it's allowed to have a static runtime with gcc. But this test
324    # assumes otherwise. Hence enable it only when not on Windows.
325    tests.append("gcc_runtime")
326
327# PCH test seems broken in strange ways. Disable it.
328# if toolset.startswith("gcc") or toolset.startswith("msvc"):
329#     tests.append("pch")
330
331# Disable on OSX as it doesn't seem to work for unknown reasons.
332if sys.platform != 'darwin':
333    tests.append("builtin_glob_archive")
334
335if "--extras" in sys.argv:
336    tests.append("boostbook")
337    tests.append("qt4")
338    tests.append("qt5")
339    tests.append("example_qt4")
340    # Requires ./whatever.py to work, so is not guaranteed to work everywhere.
341    tests.append("example_customization")
342    # Requires gettext tools.
343    tests.append("example_gettext")
344elif not xml:
345    print("Note: skipping extra tests")
346
347run_tests(critical_tests, tests)
348