1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 #![crate_name = "compiletest"]
16 extern crate env_logger;
17 extern crate filetime;
25 extern crate lazy_static;
27 extern crate serde_derive;
28 extern crate serde_json;
32 use common::CompareMode;
33 use common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS};
34 use common::{Config, TestPaths};
35 use common::{DebugInfoBoth, DebugInfoGdb, DebugInfoLldb, Mode, Pretty};
36 use filetime::FileTime;
39 use std::ffi::OsString;
41 use std::io::{self, ErrorKind};
42 use std::path::{Path, PathBuf};
43 use std::process::Command;
44 use test::ColorConfig;
47 use self::header::{EarlyProps, Ignore};
61 let config = parse_config(env::args().collect());
63 if config.valgrind_path.is_none() && config.force_valgrind {
64 panic!("Can't find Valgrind to run Valgrind tests");
71 pub fn parse_config(args: Vec<String>) -> Config {
72 let mut opts = Options::new();
76 "path to host shared libraries",
81 "path to target shared libraries",
87 "path to rustc to use for compiling",
93 "path to rustdoc to use for compiling",
99 "path to python to use for doc tests",
105 "path to python to use for doc tests",
111 "path to Valgrind executable for Valgrind tests",
117 "fail if Valgrind tests cannot be run under Valgrind",
122 "path to LLVM's FileCheck binary",
125 .reqopt("", "src-base", "directory to scan for test files", "PATH")
129 "directory to deposit test outputs",
135 "the target-stage identifier",
141 "which sort of compile tests to run",
142 "(compile-fail|run-fail|run-pass|\
143 run-pass-valgrind|pretty|debug-info|incremental|mir-opt)",
145 .optflag("", "ignored", "run tests marked as ignored")
146 .optflag("", "exact", "filters match exactly")
150 "supervisor program to run tests under \
151 (eg. emulator, valgrind)",
157 "flags to pass to rustc for host",
163 "flags to pass to rustc for target",
166 .optflag("", "verbose", "run tests verbosely, showing all output")
170 "overwrite stderr/stdout files instead of complaining about a mismatch",
175 "print one character per test instead of one line",
177 .optopt("", "color", "coloring: auto, always, never", "WHEN")
178 .optopt("", "logfile", "file to log test execution to", "FILE")
179 .optopt("", "target", "the target to build for", "TARGET")
180 .optopt("", "host", "the host to build for", "HOST")
184 "path to GDB to use for GDB debuginfo tests",
190 "the version of LLDB used",
196 "the version of LLVM used",
199 .optflag("", "system-llvm", "is LLVM the system LLVM")
202 "android-cross-path",
203 "Android NDK standalone path",
206 .optopt("", "adb-path", "path to the android debugger", "PATH")
210 "path to tests for the android debugger",
216 "directory containing LLDB's python module",
219 .reqopt("", "cc", "path to a C compiler", "PATH")
220 .reqopt("", "cxx", "path to a C++ compiler", "PATH")
221 .reqopt("", "cflags", "flags for the C compiler", "FLAGS")
222 .optopt("", "ar", "path to an archiver", "PATH")
223 .optopt("", "linker", "path to a linker", "PATH")
227 "list of LLVM components built in",
230 .reqopt("", "llvm-cxxflags", "C++ flags for LLVM", "FLAGS")
231 .optopt("", "nodejs", "the name of nodejs", "PATH")
234 "remote-test-client",
235 "path to the remote test client",
241 "mode describing what file the actual ui output will be compared to",
244 .optflag("h", "help", "show this message");
246 let (argv0, args_) = args.split_first().unwrap();
247 if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
248 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
249 println!("{}", opts.usage(&message));
254 let matches = &match opts.parse(args_) {
256 Err(f) => panic!("{:?}", f),
259 if matches.opt_present("h") || matches.opt_present("help") {
260 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
261 println!("{}", opts.usage(&message));
266 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
267 match m.opt_str(nm) {
268 Some(s) => PathBuf::from(&s),
269 None => panic!("no option (=path) found for {}", nm),
273 fn make_absolute(path: PathBuf) -> PathBuf {
274 if path.is_relative() {
275 env::current_dir().unwrap().join(path)
281 let target = opt_str2(matches.opt_str("target"));
282 let android_cross_path = opt_path(matches, "android-cross-path");
283 let (gdb, gdb_version, gdb_native_rust) = analyze_gdb(matches.opt_str("gdb"), &target,
284 &android_cross_path);
285 let (lldb_version, lldb_native_rust) = extract_lldb_version(matches.opt_str("lldb-version"));
287 let color = match matches.opt_str("color").as_ref().map(|x| &**x) {
288 Some("auto") | None => ColorConfig::AutoColor,
289 Some("always") => ColorConfig::AlwaysColor,
290 Some("never") => ColorConfig::NeverColor,
292 "argument for --color must be auto, always, or never, but found `{}`",
297 let src_base = opt_path(matches, "src-base");
298 let run_ignored = matches.opt_present("ignored");
300 bless: matches.opt_present("bless"),
301 compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
302 run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
303 rustc_path: opt_path(matches, "rustc-path"),
304 rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from),
305 lldb_python: matches.opt_str("lldb-python").unwrap(),
306 docck_python: matches.opt_str("docck-python").unwrap(),
307 valgrind_path: matches.opt_str("valgrind-path"),
308 force_valgrind: matches.opt_present("force-valgrind"),
309 llvm_filecheck: matches.opt_str("llvm-filecheck").map(|s| PathBuf::from(&s)),
311 build_base: opt_path(matches, "build-base"),
312 stage_id: matches.opt_str("stage-id").unwrap(),
317 .expect("invalid mode"),
319 filter: matches.free.first().cloned(),
320 filter_exact: matches.opt_present("exact"),
321 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
322 runtool: matches.opt_str("runtool"),
323 host_rustcflags: matches.opt_str("host-rustcflags"),
324 target_rustcflags: matches.opt_str("target-rustcflags"),
326 host: opt_str2(matches.opt_str("host")),
332 llvm_version: matches.opt_str("llvm-version"),
333 system_llvm: matches.opt_present("system-llvm"),
334 android_cross_path: android_cross_path,
335 adb_path: opt_str2(matches.opt_str("adb-path")),
336 adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
337 adb_device_status: opt_str2(matches.opt_str("target")).contains("android")
338 && "(none)" != opt_str2(matches.opt_str("adb-test-dir"))
339 && !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
340 lldb_python_dir: matches.opt_str("lldb-python-dir"),
341 verbose: matches.opt_present("verbose"),
342 quiet: matches.opt_present("quiet"),
344 remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from),
345 compare_mode: matches.opt_str("compare-mode").map(CompareMode::parse),
347 cc: matches.opt_str("cc").unwrap(),
348 cxx: matches.opt_str("cxx").unwrap(),
349 cflags: matches.opt_str("cflags").unwrap(),
350 ar: matches.opt_str("ar").unwrap_or("ar".into()),
351 linker: matches.opt_str("linker"),
352 llvm_components: matches.opt_str("llvm-components").unwrap(),
353 llvm_cxxflags: matches.opt_str("llvm-cxxflags").unwrap(),
354 nodejs: matches.opt_str("nodejs"),
358 pub fn log_config(config: &Config) {
360 logv(c, "configuration:".to_string());
363 format!("compile_lib_path: {:?}", config.compile_lib_path),
365 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
366 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
367 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path));
368 logv(c, format!("src_base: {:?}", config.src_base.display()));
369 logv(c, format!("build_base: {:?}", config.build_base.display()));
370 logv(c, format!("stage_id: {}", config.stage_id));
371 logv(c, format!("mode: {}", config.mode));
372 logv(c, format!("run_ignored: {}", config.run_ignored));
377 opt_str(&config.filter.as_ref().map(|re| re.to_owned()))
380 logv(c, format!("filter_exact: {}", config.filter_exact));
381 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
384 format!("host-rustcflags: {}", opt_str(&config.host_rustcflags)),
388 format!("target-rustcflags: {}", opt_str(&config.target_rustcflags)),
390 logv(c, format!("target: {}", config.target));
391 logv(c, format!("host: {}", config.host));
395 "android-cross-path: {:?}",
396 config.android_cross_path.display()
399 logv(c, format!("adb_path: {:?}", config.adb_path));
400 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
403 format!("adb_device_status: {}", config.adb_device_status),
405 logv(c, format!("ar: {}", config.ar));
406 logv(c, format!("linker: {:?}", config.linker));
407 logv(c, format!("verbose: {}", config.verbose));
408 logv(c, format!("quiet: {}", config.quiet));
409 logv(c, "\n".to_string());
412 pub fn opt_str(maybestr: &Option<String>) -> &str {
419 pub fn opt_str2(maybestr: Option<String>) -> String {
421 None => "(none)".to_owned(),
426 pub fn run_tests(config: &Config) {
427 if config.target.contains("android") {
428 if config.mode == DebugInfoGdb || config.mode == DebugInfoBoth {
430 "{} debug-info test uses tcp 5039 port.\
435 // android debug-info test uses remote debugger so, we test 1 thread
436 // at once as they're all sharing the same TCP port to communicate
439 // we should figure out how to lift this restriction! (run them all
440 // on different ports allocated dynamically).
441 env::set_var("RUST_TEST_THREADS", "1");
446 // Note that we don't need to emit the gdb warning when
447 // DebugInfoBoth, so it is ok to list that here.
448 DebugInfoBoth | DebugInfoLldb => {
449 if let Some(lldb_version) = config.lldb_version.as_ref() {
450 if is_blacklisted_lldb_version(&lldb_version[..]) {
452 "WARNING: The used version of LLDB ({}) has a \
453 known issue that breaks debuginfo tests. See \
454 issue #32520 for more information. Skipping all \
462 // Some older versions of LLDB seem to have problems with multiple
463 // instances running in parallel, so only run one test thread at a
465 env::set_var("RUST_TEST_THREADS", "1");
469 if config.remote_test_client.is_some() && !config.target.contains("android") {
471 "WARNING: debuginfo tests are not available when \
477 _ => { /* proceed */ }
480 // FIXME(#33435) Avoid spurious failures in codegen-units/partitioning tests.
481 if let Mode::CodegenUnits = config.mode {
482 let _ = fs::remove_dir_all("tmp/partitioning-tests");
485 let opts = test_opts(config);
486 let tests = make_tests(config);
487 // sadly osx needs some file descriptor limits raised for running tests in
488 // parallel (especially when we have lots and lots of child processes).
489 // For context, see #8904
491 raise_fd_limit::raise_fd_limit();
493 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
494 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
495 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
497 // Let tests know which target they're running as
498 env::set_var("TARGET", &config.target);
500 let res = test::run_tests_console(&opts, tests.into_iter().collect());
503 Ok(false) => panic!("Some tests failed"),
505 println!("I/O failure during tests: {:?}", e);
510 pub fn test_opts(config: &Config) -> test::TestOpts {
512 filter: config.filter.clone(),
513 filter_exact: config.filter_exact,
514 run_ignored: config.run_ignored,
515 format: if config.quiet {
516 test::OutputFormat::Terse
518 test::OutputFormat::Pretty
520 logfile: config.logfile.clone(),
522 bench_benchmarks: true,
523 nocapture: match env::var("RUST_TEST_NOCAPTURE") {
524 Ok(val) => &val != "0",
531 options: test::Options::new(),
535 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
536 debug!("making tests from {:?}", config.src_base.display());
537 let mut tests = Vec::new();
538 collect_tests_from_dir(
548 fn collect_tests_from_dir(
552 relative_dir_path: &Path,
553 tests: &mut Vec<test::TestDescAndFn>,
554 ) -> io::Result<()> {
555 // Ignore directories that contain a file
556 // `compiletest-ignore-dir`.
557 for file in fs::read_dir(dir)? {
559 let name = file.file_name();
560 if name == *"compiletest-ignore-dir" {
563 if name == *"Makefile" && config.mode == Mode::RunMake {
564 let paths = TestPaths {
565 file: dir.to_path_buf(),
566 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
568 tests.extend(make_test(config, &paths));
573 // If we find a test foo/bar.rs, we have to build the
574 // output directory `$build/foo` so we can write
575 // `$build/foo/bar` into it. We do this *now* in this
576 // sequential loop because otherwise, if we do it in the
577 // tests themselves, they race for the privilege of
578 // creating the directories and sometimes fail randomly.
579 let build_dir = output_relative_path(config, relative_dir_path);
580 fs::create_dir_all(&build_dir).unwrap();
582 // Add each `.rs` file as a test, and recurse further on any
583 // subdirectories we find, except for `aux` directories.
584 let dirs = fs::read_dir(dir)?;
587 let file_path = file.path();
588 let file_name = file.file_name();
589 if is_test(&file_name) {
590 debug!("found test file: {:?}", file_path.display());
591 let paths = TestPaths {
593 relative_dir: relative_dir_path.to_path_buf(),
595 tests.extend(make_test(config, &paths))
596 } else if file_path.is_dir() {
597 let relative_file_path = relative_dir_path.join(file.file_name());
598 if &file_name != "auxiliary" {
599 debug!("found directory: {:?}", file_path.display());
600 collect_tests_from_dir(config, base, &file_path, &relative_file_path, tests)?;
603 debug!("found other file/directory: {:?}", file_path.display());
609 pub fn is_test(file_name: &OsString) -> bool {
610 let file_name = file_name.to_str().unwrap();
612 if !file_name.ends_with(".rs") {
616 // `.`, `#`, and `~` are common temp-file prefixes.
617 let invalid_prefixes = &[".", "#", "~"];
618 !invalid_prefixes.iter().any(|p| file_name.starts_with(p))
621 pub fn make_test(config: &Config, testpaths: &TestPaths) -> Vec<test::TestDescAndFn> {
622 let early_props = if config.mode == Mode::RunMake {
623 // Allow `ignore` directives to be in the Makefile.
624 EarlyProps::from_file(config, &testpaths.file.join("Makefile"))
626 EarlyProps::from_file(config, &testpaths.file)
629 // The `should-fail` annotation doesn't apply to pretty tests,
630 // since we run the pretty printer across all tests by default.
631 // If desired, we could add a `should-fail-pretty` annotation.
632 let should_panic = match config.mode {
633 Pretty => test::ShouldPanic::No,
634 _ => if early_props.should_fail {
635 test::ShouldPanic::Yes
637 test::ShouldPanic::No
641 // Incremental tests are special, they inherently cannot be run in parallel.
642 // `runtest::run` will be responsible for iterating over revisions.
643 let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
646 early_props.revisions.iter().map(|r| Some(r)).collect()
651 // Debugging emscripten code doesn't make sense today
652 let ignore = early_props.ignore == Ignore::Ignore
657 revision.map(|s| s.as_str()),
659 || ((config.mode == DebugInfoBoth ||
660 config.mode == DebugInfoGdb || config.mode == DebugInfoLldb)
661 && config.target.contains("emscripten"))
662 || (config.mode == DebugInfoGdb && !early_props.ignore.can_run_gdb())
663 || (config.mode == DebugInfoLldb && !early_props.ignore.can_run_lldb());
664 test::TestDescAndFn {
665 desc: test::TestDesc {
666 name: make_test_name(config, testpaths, revision),
671 testfn: make_test_closure(config, early_props.ignore, testpaths, revision),
677 fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
678 output_base_dir(config, testpaths, revision).join("stamp")
683 testpaths: &TestPaths,
685 revision: Option<&str>,
687 let stamp_name = stamp(config, testpaths, revision);
689 let contents = match fs::read_to_string(&stamp_name) {
691 Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"),
692 Err(_) => return true,
694 let expected_hash = runtest::compute_stamp_hash(config);
695 if contents != expected_hash {
700 let rust_src_dir = config
701 .find_rust_src_root()
702 .expect("Could not find Rust source root");
703 let stamp = mtime(&stamp_name);
704 let mut inputs = vec![mtime(&testpaths.file), mtime(&config.rustc_path)];
705 for aux in props.aux.iter() {
706 inputs.push(mtime(&testpaths
713 // Relevant pretty printer files
714 let pretty_printer_files = [
715 "src/etc/debugger_pretty_printers_common.py",
716 "src/etc/gdb_load_rust_pretty_printers.py",
717 "src/etc/gdb_rust_pretty_printing.py",
718 "src/etc/lldb_batchmode.py",
719 "src/etc/lldb_rust_formatters.py",
721 for pretty_printer_file in &pretty_printer_files {
722 inputs.push(mtime(&rust_src_dir.join(pretty_printer_file)));
724 let mut entries = config.run_lib_path.read_dir().unwrap().collect::<Vec<_>>();
725 while let Some(entry) = entries.pop() {
726 let entry = entry.unwrap();
727 let path = entry.path();
728 if entry.metadata().unwrap().is_file() {
729 inputs.push(mtime(&path));
731 entries.extend(path.read_dir().unwrap());
734 if let Some(ref rustdoc_path) = config.rustdoc_path {
735 inputs.push(mtime(&rustdoc_path));
736 inputs.push(mtime(&rust_src_dir.join("src/etc/htmldocck.py")));
740 for extension in UI_EXTENSIONS {
741 let path = &expected_output_path(testpaths, revision, &config.compare_mode, extension);
742 inputs.push(mtime(path));
745 inputs.iter().any(|input| *input > stamp)
748 fn mtime(path: &Path) -> FileTime {
750 .map(|f| FileTime::from_last_modification_time(&f))
751 .unwrap_or_else(|_| FileTime::zero())
756 testpaths: &TestPaths,
757 revision: Option<&String>,
758 ) -> test::TestName {
759 // Convert a complete path to something like
761 // run-pass/foo/bar/baz.rs
762 let path = PathBuf::from(config.src_base.file_name().unwrap())
763 .join(&testpaths.relative_dir)
764 .join(&testpaths.file.file_name().unwrap());
765 let mode_suffix = match config.compare_mode {
766 Some(ref mode) => format!(" ({})", mode.to_str()),
767 None => String::new(),
769 test::DynTestName(format!(
774 revision.map_or("".to_string(), |rev| format!("#{}", rev))
778 fn make_test_closure(
781 testpaths: &TestPaths,
782 revision: Option<&String>,
784 let mut config = config.clone();
785 if config.mode == DebugInfoBoth {
786 // If both gdb and lldb were ignored, then the test as a whole
788 if !ignore.can_run_gdb() {
789 config.mode = DebugInfoLldb;
790 } else if !ignore.can_run_lldb() {
791 config.mode = DebugInfoGdb;
795 let testpaths = testpaths.clone();
796 let revision = revision.cloned();
797 test::DynTestFn(Box::new(move || {
798 runtest::run(config, &testpaths, revision.as_ref().map(|s| s.as_str()))
802 /// Returns true if the given target is an Android target for the
803 /// purposes of GDB testing.
804 fn is_android_gdb_target(target: &String) -> bool {
806 "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => true,
811 /// Returns (Path to GDB, GDB Version, GDB has Rust Support)
812 fn analyze_gdb(gdb: Option<String>, target: &String, android_cross_path: &PathBuf)
813 -> (Option<String>, Option<u32>, bool) {
815 const GDB_FALLBACK: &str = "gdb";
817 const GDB_FALLBACK: &str = "gdb.exe";
819 const MIN_GDB_WITH_RUST: u32 = 7011010;
821 let fallback_gdb = || {
822 if is_android_gdb_target(target) {
823 let mut gdb_path = match android_cross_path.to_str() {
824 Some(x) => x.to_owned(),
825 None => panic!("cannot find android cross path"),
827 gdb_path.push_str("/bin/gdb");
830 GDB_FALLBACK.to_owned()
834 let gdb = match gdb {
835 None => fallback_gdb(),
836 Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
837 Some(ref s) => s.to_owned(),
840 let mut version_line = None;
841 if let Ok(output) = Command::new(&gdb).arg("--version").output() {
842 if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
843 version_line = Some(first_line.to_string());
847 let version = match version_line {
848 Some(line) => extract_gdb_version(&line),
849 None => return (None, None, false),
852 let gdb_native_rust = version.map_or(false, |v| v >= MIN_GDB_WITH_RUST);
854 (Some(gdb), version, gdb_native_rust)
857 fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
858 let full_version_line = full_version_line.trim();
860 // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
861 // of the ? sections being optional
863 // We will parse up to 3 digits for minor and patch, ignoring the date
864 // We limit major to 1 digit, otherwise, on openSUSE, we parse the openSUSE version
866 // don't start parsing in the middle of a number
867 let mut prev_was_digit = false;
868 for (pos, c) in full_version_line.char_indices() {
869 if prev_was_digit || !c.is_digit(10) {
870 prev_was_digit = c.is_digit(10);
874 prev_was_digit = true;
876 let line = &full_version_line[pos..];
878 let next_split = match line.find(|c: char| !c.is_digit(10)) {
880 None => continue, // no minor version
883 if line.as_bytes()[next_split] != b'.' {
884 continue; // no minor version
887 let major = &line[..next_split];
888 let line = &line[next_split + 1..];
890 let (minor, patch) = match line.find(|c: char| !c.is_digit(10)) {
891 Some(idx) => if line.as_bytes()[idx] == b'.' {
892 let patch = &line[idx + 1..];
894 let patch_len = patch
895 .find(|c: char| !c.is_digit(10))
896 .unwrap_or_else(|| patch.len());
897 let patch = &patch[..patch_len];
898 let patch = if patch_len > 3 || patch_len == 0 {
904 (&line[..idx], patch)
908 None => (line, None),
911 if major.len() != 1 || minor.is_empty() {
915 let major: u32 = major.parse().unwrap();
916 let minor: u32 = minor.parse().unwrap();
917 let patch: u32 = patch.unwrap_or("0").parse().unwrap();
919 return Some(((major * 1000) + minor) * 1000 + patch);
925 /// Returns (LLDB version, LLDB is rust-enabled)
926 fn extract_lldb_version(full_version_line: Option<String>) -> (Option<String>, bool) {
927 // Extract the major LLDB version from the given version string.
928 // LLDB version strings are different for Apple and non-Apple platforms.
929 // The Apple variant looks like this:
931 // LLDB-179.5 (older versions)
932 // lldb-300.2.51 (new versions)
934 // We are only interested in the major version number, so this function
935 // will return `Some("179")` and `Some("300")` respectively.
937 // Upstream versions look like:
938 // lldb version 6.0.1
940 // There doesn't seem to be a way to correlate the Apple version
941 // with the upstream version, and since the tests were originally
942 // written against Apple versions, we make a fake Apple version by
943 // multiplying the first number by 100. This is a hack, but
944 // normally fine because the only non-Apple version we test is
947 if let Some(ref full_version_line) = full_version_line {
948 if !full_version_line.trim().is_empty() {
949 let full_version_line = full_version_line.trim();
951 for (pos, l) in full_version_line.char_indices() {
952 if l != 'l' && l != 'L' {
955 if pos + 5 >= full_version_line.len() {
958 let l = full_version_line[pos + 1..].chars().next().unwrap();
959 if l != 'l' && l != 'L' {
962 let d = full_version_line[pos + 2..].chars().next().unwrap();
963 if d != 'd' && d != 'D' {
966 let b = full_version_line[pos + 3..].chars().next().unwrap();
967 if b != 'b' && b != 'B' {
970 let dash = full_version_line[pos + 4..].chars().next().unwrap();
975 let vers = full_version_line[pos + 5..]
977 .take_while(|c| c.is_digit(10))
978 .collect::<String>();
979 if !vers.is_empty() {
980 return (Some(vers), full_version_line.contains("rust-enabled"));
984 if full_version_line.starts_with("lldb version ") {
985 let vers = full_version_line[13..]
987 .take_while(|c| c.is_digit(10))
988 .collect::<String>();
989 if !vers.is_empty() {
990 return (Some(vers + "00"), full_version_line.contains("rust-enabled"));
998 fn is_blacklisted_lldb_version(version: &str) -> bool {
1003 fn test_extract_gdb_version() {
1004 macro_rules! test { ($($expectation:tt: $input:tt,)*) => {{$(
1005 assert_eq!(extract_gdb_version($input), Some($expectation));
1009 7000001: "GNU gdb (GDB) CentOS (7.0.1-45.el5.centos)",
1011 7002000: "GNU gdb (GDB) Red Hat Enterprise Linux (7.2-90.el6)",
1013 7004000: "GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04",
1014 7004001: "GNU gdb (GDB) 7.4.1-debian",
1016 7006001: "GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7",
1018 7007001: "GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1",
1019 7007001: "GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1",
1020 7007001: "GNU gdb (GDB) Fedora 7.7.1-21.fc20",
1022 7008000: "GNU gdb (GDB; openSUSE 13.2) 7.8",
1023 7009001: "GNU gdb (GDB) Fedora 7.9.1-20.fc22",
1024 7010001: "GNU gdb (GDB) Fedora 7.10.1-31.fc23",
1026 7011000: "GNU gdb (Ubuntu 7.11-0ubuntu1) 7.11",
1027 7011001: "GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1",
1028 7011001: "GNU gdb (Debian 7.11.1-2) 7.11.1",
1029 7011001: "GNU gdb (GDB) Fedora 7.11.1-86.fc24",
1030 7011001: "GNU gdb (GDB; openSUSE Leap 42.1) 7.11.1",
1031 7011001: "GNU gdb (GDB; openSUSE Tumbleweed) 7.11.1",
1034 7011090: "GNU gdb (Ubuntu 7.11.90.20161005-0ubuntu1) 7.11.90.20161005-git",
1037 7012000: "GNU gdb (GDB) 7.12",
1038 7012000: "GNU gdb (GDB) 7.12.20161027-git",
1039 7012050: "GNU gdb (GDB) 7.12.50.20161027-git",