| #!/usr/bin/python |
| |
| # Copyright 2002-2005 Dave Abrahams. |
| # Copyright 2002-2006 Vladimir Prus. |
| # Distributed under the Boost Software License, Version 1.0. |
| # (See accompanying file LICENSE_1_0.txt or copy at |
| # http://www.boost.org/LICENSE_1_0.txt) |
| |
| import BoostBuild |
| |
| import os |
| import os.path |
| import sys |
| |
| xml = "--xml" in sys.argv |
| toolset = BoostBuild.get_toolset() |
| |
| |
| # Clear environment for testing. |
| # |
| for s in ("BOOST_ROOT", "BOOST_BUILD_PATH", "JAM_TOOLSET", "BCCROOT", |
| "MSVCDir", "MSVC", "MSVCNT", "MINGW", "watcom"): |
| try: |
| del os.environ[s] |
| except: |
| pass |
| |
| BoostBuild.set_defer_annotations(1) |
| |
| |
| def run_tests(critical_tests, other_tests): |
| """ |
| Runs first the critical_tests and then the other_tests. |
| |
| Writes the name of the first failed test to test_results.txt. Critical |
| tests are run in the specified order, other tests are run starting with the |
| one that failed first on the last test run. |
| |
| """ |
| last_failed = last_failed_test() |
| other_tests = reorder_tests(other_tests, last_failed) |
| all_tests = critical_tests + other_tests |
| |
| invocation_dir = os.getcwd() |
| max_test_name_len = 10 |
| for x in all_tests: |
| if len(x) > max_test_name_len: |
| max_test_name_len = len(x) |
| |
| pass_count = 0 |
| failures_count = 0 |
| |
| for test in all_tests: |
| if not xml: |
| print("%%-%ds :" % max_test_name_len % test), |
| |
| passed = 0 |
| try: |
| __import__(test) |
| passed = 1 |
| except KeyboardInterrupt: |
| """This allows us to abort the testing manually using Ctrl-C.""" |
| raise |
| except SystemExit: |
| """This is the regular way our test scripts are supposed to report |
| test failures.""" |
| except: |
| exc_type, exc_value, exc_tb = sys.exc_info() |
| try: |
| BoostBuild.annotation("failure - unhandled exception", "%s - " |
| "%s" % (exc_type.__name__, exc_value)) |
| BoostBuild.annotate_stack_trace(exc_tb) |
| finally: |
| # Explicitly clear a hard-to-garbage-collect traceback |
| # related reference cycle as per documented sys.exc_info() |
| # usage suggestion. |
| del exc_tb |
| |
| if passed: |
| pass_count += 1 |
| else: |
| failures_count += 1 |
| if failures_count == 1: |
| f = open(os.path.join(invocation_dir, "test_results.txt"), "w") |
| try: |
| f.write(test) |
| finally: |
| f.close() |
| |
| # Restore the current directory, which might have been changed by the |
| # test. |
| os.chdir(invocation_dir) |
| |
| if not xml: |
| if passed: |
| print("PASSED") |
| else: |
| print("FAILED") |
| else: |
| rs = "succeed" |
| if not passed: |
| rs = "fail" |
| print """ |
| <test-log library="build" test-name="%s" test-type="run" toolset="%s" test-program="%s" target-directory="%s"> |
| <run result="%s">""" % (test, toolset, "tools/build/v2/test/" + test + ".py", |
| "boost/bin.v2/boost.build.tests/" + toolset + "/" + test, rs) |
| if not passed: |
| BoostBuild.flush_annotations(1) |
| print """ |
| </run> |
| </test-log> |
| """ |
| sys.stdout.flush() # Makes testing under emacs more entertaining. |
| BoostBuild.clear_annotations() |
| |
| # Erase the file on success. |
| if failures_count == 0: |
| open("test_results.txt", "w").close() |
| |
| if not xml: |
| print """ |
| === Test summary === |
| PASS: %d |
| FAIL: %d |
| """ % (pass_count, failures_count) |
| |
| |
| def last_failed_test(): |
| "Returns the name of the last failed test or None." |
| try: |
| f = open("test_results.txt") |
| try: |
| return f.read().strip() |
| finally: |
| f.close() |
| except Exception: |
| return None |
| |
| |
| def reorder_tests(tests, first_test): |
| try: |
| n = tests.index(first_test) |
| return [first_test] + tests[:n] + tests[n + 1:] |
| except ValueError: |
| return tests |
| |
| |
| critical_tests = ["unit_tests", "module_actions", "startup_v2", "core_d12", |
| "core_typecheck", "core_delete_module", "core_language", "core_arguments", |
| "core_varnames", "core_import_module"] |
| |
| # We want to collect debug information about the test site before running any |
| # of the tests, but only when not running the tests interactively. Then the |
| # user can easily run this always-failing test directly to see what it would |
| # have returned and there is no need to have it spoil a possible 'all tests |
| # passed' result. |
| if xml: |
| critical_tests.insert(0, "collect_debug_info") |
| |
| tests = ["absolute_sources", |
| "alias", |
| "alternatives", |
| "bad_dirname", |
| "build_dir", |
| "build_file", |
| "build_no", |
| "builtin_echo", |
| "builtin_exit", |
| "builtin_glob", |
| "builtin_split_by_characters", |
| "c_file", |
| "chain", |
| "clean", |
| "composite", |
| "conditionals", |
| "conditionals2", |
| "conditionals3", |
| "conditionals_multiple", |
| "configuration", |
| "copy_time", |
| "core_action_output", |
| "core_action_status", |
| "core_actions_quietly", |
| "core_at_file", |
| "core_bindrule", |
| "core_jamshell", |
| "core_multifile_actions", |
| "core_nt_cmd_line", |
| "core_option_d2", |
| "core_option_l", |
| "core_option_n", |
| "core_parallel_actions", |
| "core_parallel_multifile_actions_1", |
| "core_parallel_multifile_actions_2", |
| "core_source_line_tracking", |
| "core_update_now", |
| "core_variables_in_actions", |
| "custom_generator", |
| "default_build", |
| "default_features", |
| # This test is known to be broken itself. |
| # "default_toolset", |
| "dependency_property", |
| "dependency_test", |
| "direct_request_test", |
| "disambiguation", |
| "dll_path", |
| "double_loading", |
| "duplicate", |
| "example_libraries", |
| "example_make", |
| "exit_status", |
| "expansion", |
| "explicit", |
| "feature_cxxflags", |
| "free_features_request", |
| "generator_selection", |
| "generators_test", |
| "implicit_dependency", |
| "indirect_conditional", |
| "inherit_toolset", |
| "inherited_dependency", |
| "inline", |
| "lib_source_property", |
| "library_chain", |
| "library_property", |
| "link", |
| "load_order", |
| "loop", |
| "make_rule", |
| "message", |
| "ndebug", |
| "no_type", |
| "notfile", |
| "ordered_include", |
| "out_of_tree", |
| "path_features", |
| "prebuilt", |
| "print", |
| "project_dependencies", |
| "project_glob", |
| "project_id", |
| "project_root_constants", |
| "project_root_rule", |
| "project_test3", |
| "project_test4", |
| "property_expansion", |
| "rebuilds", |
| "regression", |
| "relative_sources", |
| "remove_requirement", |
| "rescan_header", |
| "resolution", |
| "scanner_causing_rebuilds", |
| "searched_lib", |
| "skipping", |
| "sort_rule", |
| "source_locations", |
| "source_order", |
| "space_in_path", |
| "stage", |
| "standalone", |
| "static_and_shared_library", |
| "suffix", |
| "tag", |
| "test_result_dumping", |
| "test_rc", |
| "testing_support", |
| "timedata", |
| "toolset_requirements", |
| "unit_test", |
| "unused", |
| "use_requirements", |
| "using", |
| "wrapper", |
| "wrong_project", |
| "zlib" |
| ] |
| |
| if os.name == "posix": |
| tests.append("symlink") |
| # On Windows, library order is not important, so skip this test. Besides, |
| # it fails ;-). Further, the test relies on the fact that on Linux, one can |
| # build a shared library with unresolved symbols. This is not true on |
| # Windows, even with cygwin gcc. |
| if "CYGWIN" not in os.uname()[0]: |
| tests.append("library_order") |
| |
| if toolset.startswith("gcc"): |
| tests.append("gcc_runtime") |
| |
| if toolset.startswith("gcc") or toolset.startswith("msvc"): |
| tests.append("pch") |
| |
| if "--extras" in sys.argv: |
| tests.append("boostbook") |
| tests.append("qt4") |
| tests.append("qt5") |
| tests.append("example_qt4") |
| # Requires ./whatever.py to work, so is not guaranted to work everywhere. |
| tests.append("example_customization") |
| # Requires gettext tools. |
| tests.append("example_gettext") |
| elif not xml: |
| print("Note: skipping extra tests") |
| |
| run_tests(critical_tests, tests) |