use super::report::{ar_capabilities_as_json, cxx_capabilities_as_json};
use super::*;
#[test]
fn parses_clang_first_line() {
let id = parse_cxx_version_output(
"clang version 17.0.6\nTarget: x86_64-unknown-linux-gnu\nThread model: posix\n",
);
assert_eq!(id.kind, CompilerKind::Clang);
let v = id.version.expect("version parsed");
assert_eq!(v.major, 17);
assert_eq!(v.minor, Some(0));
assert_eq!(v.patch, Some(6));
assert_eq!(id.target.as_deref(), Some("x86_64-unknown-linux-gnu"));
}
#[test]
fn parses_apple_clang() {
let id = parse_cxx_version_output(
"Apple clang version 14.0.3 (clang-1403.0.22.14.1)\nTarget: arm64-apple-darwin22.5.0\nThread model: posix\n",
);
assert_eq!(id.kind, CompilerKind::AppleClang);
let v = id.version.unwrap();
assert_eq!((v.major, v.minor, v.patch), (14, Some(0), Some(3)));
}
#[test]
fn parses_gcc_with_distro_prefix() {
let id = parse_cxx_version_output(
"g++ (Ubuntu 11.4.0-1ubuntu1) 11.4.0\nCopyright (C) 2021 Free Software Foundation, Inc.\n",
);
assert_eq!(id.kind, CompilerKind::Gcc);
let v = id.version.unwrap();
assert_eq!((v.major, v.minor, v.patch), (11, Some(4), Some(0)));
}
#[test]
fn parses_msvc_first_line() {
let id = parse_cxx_version_output(
"Microsoft (R) C/C++ Optimizing Compiler Version 19.39.33523 for x64\n",
);
assert_eq!(id.kind, CompilerKind::Msvc);
let v = id.version.unwrap();
assert_eq!(v.major, 19);
}
#[test]
fn unknown_when_unrecognized() {
let id = parse_cxx_version_output("My funky compiler 0.0\n");
assert_eq!(id.kind, CompilerKind::Unknown);
assert!(id.version.is_none());
}
#[test]
fn empty_output_is_unknown() {
let id = parse_cxx_version_output("");
assert_eq!(id.kind, CompilerKind::Unknown);
assert!(id.raw_version_line.is_empty());
}
#[test]
fn parses_gnu_ar() {
let id = parse_ar_version_output(
"GNU ar (GNU Binutils for Debian) 2.40\nCopyright (C) 2023 Free Software Foundation, Inc.\n",
);
assert_eq!(id.kind, ArchiverKind::Ar);
let v = id.version.unwrap();
assert_eq!(v.major, 2);
}
#[test]
fn parses_llvm_ar_version() {
let id = parse_ar_version_output(
"LLVM (http://llvm.org/):\n LLVM version 17.0.6\n Optimized build.\n",
);
assert_eq!(id.kind, ArchiverKind::LlvmAr);
let v = id.version.unwrap();
assert_eq!(v.major, 17);
}
#[test]
fn detects_lib_exe_as_unsupported() {
let id = parse_ar_version_output(
"Microsoft (R) Library Manager Version 14.39.33523.0\nCopyright (C) Microsoft Corporation.\n",
);
assert_eq!(id.kind, ArchiverKind::Lib);
}
#[test]
fn unknown_archiver_classification() {
let id = parse_ar_version_output("just-some-archiver 0.1\n");
assert_eq!(id.kind, ArchiverKind::Unknown);
assert!(id.version.is_none());
}
#[test]
fn clang_capabilities_include_gcc_style_and_cxx17() {
let id = CompilerIdentity {
kind: CompilerKind::Clang,
version: CompilerVersion::parse("17.0.6"),
target: None,
raw_version_line: "clang version 17.0.6".into(),
};
let caps = derive_cxx_capabilities(&id);
assert!(caps.gcc_style_flags.supported);
assert!(caps.depfile_mmd_mf.supported);
assert!(caps.std_flag.supported);
assert!(caps.cxx_standard_17.supported);
}
#[test]
fn gcc_pre_5_does_not_claim_cxx17() {
let id = CompilerIdentity {
kind: CompilerKind::Gcc,
version: CompilerVersion::parse("4.8.5"),
target: None,
raw_version_line: "g++ 4.8.5".into(),
};
let caps = derive_cxx_capabilities(&id);
assert!(caps.gcc_style_flags.supported);
assert!(!caps.cxx_standard_17.supported);
}
#[test]
fn msvc_capabilities_reject_gcc_style() {
let id = CompilerIdentity {
kind: CompilerKind::Msvc,
version: CompilerVersion::parse("19.39.0"),
target: None,
raw_version_line: "Microsoft Optimizing Compiler".into(),
};
let caps = derive_cxx_capabilities(&id);
assert!(!caps.gcc_style_flags.supported);
assert_eq!(caps.gcc_style_flags.source, CapabilitySource::Unsupported);
assert!(caps.msvc_style_flags.supported);
}
#[test]
fn unknown_compiler_capabilities_are_conservative() {
let id = CompilerIdentity::unknown("strange compiler");
let caps = derive_cxx_capabilities(&id);
assert!(!caps.gcc_style_flags.supported);
assert_eq!(
caps.gcc_style_flags.source,
CapabilitySource::AssumedDefault
);
assert!(!caps.depfile_mmd_mf.supported);
}
#[test]
fn ar_capabilities_recognize_gnu_ar() {
let id = ArchiverIdentity {
kind: ArchiverKind::Ar,
version: CompilerVersion::parse("2.40"),
raw_version_line: "GNU ar".into(),
};
let caps = derive_ar_capabilities(&id);
assert!(caps.ar_crs.supported);
assert!(caps.static_library_output.supported);
}
#[test]
fn msvc_lib_archives_without_ar_crs() {
let id = ArchiverIdentity {
kind: ArchiverKind::Lib,
version: None,
raw_version_line: "Microsoft Library Manager".into(),
};
let caps = derive_ar_capabilities(&id);
assert!(!caps.ar_crs.supported);
assert_eq!(caps.ar_crs.source, CapabilitySource::Unsupported);
assert!(caps.static_library_output.supported);
}
#[test]
fn validate_accepts_msvc_cxx() {
let id = CompilerIdentity {
kind: CompilerKind::Msvc,
version: None,
target: None,
raw_version_line: "MSVC".into(),
};
let caps = derive_cxx_capabilities(&id);
assert!(caps.msvc_style_flags.supported);
assert!(validate_cxx_for_backend("cl.exe", &id, &caps).is_ok());
}
fn msvc_identity(version: &str) -> CompilerIdentity {
CompilerIdentity {
kind: CompilerKind::Msvc,
version: CompilerVersion::parse(version),
target: None,
raw_version_line: format!("Microsoft Optimizing Compiler {version}"),
}
}
#[test]
fn msvc_std_capabilities_are_version_gated() {
let modern = derive_cxx_capabilities(&msvc_identity("19.39.33523"));
assert!(modern.cxx_standard_17.supported);
assert_eq!(modern.cxx_standard_17.source, CapabilitySource::Version);
assert!(modern.c_standard_11.supported);
assert_eq!(modern.c_standard_11.source, CapabilitySource::Version);
let mid = derive_cxx_capabilities(&msvc_identity("19.20.0"));
assert!(mid.cxx_standard_17.supported);
assert!(!mid.c_standard_11.supported);
assert_eq!(mid.c_standard_11.source, CapabilitySource::Version);
let old = derive_cxx_capabilities(&msvc_identity("19.00.24210"));
assert!(!old.cxx_standard_17.supported);
assert!(!old.c_standard_11.supported);
}
#[test]
fn msvc_unparsed_version_assumes_modern_support() {
let caps = derive_cxx_capabilities(&CompilerIdentity {
kind: CompilerKind::Msvc,
version: None,
target: None,
raw_version_line: "Microsoft Optimizing Compiler".into(),
});
assert!(caps.cxx_standard_17.supported);
assert_eq!(
caps.cxx_standard_17.source,
CapabilitySource::AssumedDefault
);
assert!(caps.c_standard_11.supported);
assert_eq!(caps.c_standard_11.source, CapabilitySource::AssumedDefault);
}
#[test]
fn gnu_c_standard_11_is_unconditional() {
for id in [
CompilerIdentity {
kind: CompilerKind::Gcc,
version: CompilerVersion::parse("4.8.5"),
target: None,
raw_version_line: "g++ 4.8.5".into(),
},
CompilerIdentity {
kind: CompilerKind::Clang,
version: CompilerVersion::parse("3.4"),
target: None,
raw_version_line: "clang version 3.4".into(),
},
] {
assert!(derive_cxx_capabilities(&id).c_standard_11.supported);
}
}
#[test]
fn validate_rejects_msvc_too_old_for_std_flags() {
let old = msvc_identity("19.00.24210");
let caps = derive_cxx_capabilities(&old);
assert!(matches!(
validate_cxx_for_backend("cl.exe", &old, &caps),
Err(ToolDetectionError::CxxLacksStdCxx17 { .. })
));
assert!(matches!(
validate_cc_for_backend("cl.exe", &old, &caps),
Err(ToolDetectionError::CLacksStdC11 { .. })
));
}
#[test]
fn clang_cl_has_msvc_dialect_with_clang_diagnostics() {
let id = CompilerIdentity {
kind: CompilerKind::ClangCl,
version: CompilerVersion::parse("17.0.6"),
target: None,
raw_version_line: "clang version 17.0.6".into(),
};
assert!(id.kind.speaks_msvc_dialect());
assert!(id.kind.is_clang_like());
assert!(!id.kind.supports_gcc_style_command_line());
let caps = derive_cxx_capabilities(&id);
assert!(caps.msvc_style_flags.supported);
assert!(!caps.gcc_style_flags.supported);
assert!(!caps.depfile_mmd_mf.supported);
assert!(caps.cxx_standard_17.supported);
assert!(caps.c_standard_11.supported);
assert!(validate_cxx_for_backend("clang-cl", &id, &caps).is_ok());
assert!(validate_cc_for_backend("clang-cl", &id, &caps).is_ok());
}
#[test]
fn validate_accepts_modern_and_unversioned_msvc_c() {
for id in [
msvc_identity("19.39.33523"),
CompilerIdentity {
kind: CompilerKind::Msvc,
version: None,
target: None,
raw_version_line: "MSVC".into(),
},
] {
let caps = derive_cxx_capabilities(&id);
assert!(validate_cc_for_backend("cl.exe", &id, &caps).is_ok());
}
}
#[test]
fn validate_rejects_unknown_cxx() {
let id = CompilerIdentity::unknown("???");
let caps = derive_cxx_capabilities(&id);
let err = validate_cxx_for_backend("custom-cxx", &id, &caps).unwrap_err();
assert!(matches!(
err,
ToolDetectionError::UnknownCxxRequiresGccStyle { .. }
));
}
#[test]
fn validate_accepts_clang() {
let id = CompilerIdentity {
kind: CompilerKind::Clang,
version: CompilerVersion::parse("17.0.6"),
target: None,
raw_version_line: "clang version 17.0.6".into(),
};
let caps = derive_cxx_capabilities(&id);
assert!(validate_cxx_for_backend("clang++", &id, &caps).is_ok());
}
#[test]
fn validate_rejects_gcc_too_old_for_cxx17() {
let id = CompilerIdentity {
kind: CompilerKind::Gcc,
version: CompilerVersion::parse("4.8.5"),
target: None,
raw_version_line: "g++ 4.8".into(),
};
let caps = derive_cxx_capabilities(&id);
let err = validate_cxx_for_backend("g++", &id, &caps).unwrap_err();
assert!(matches!(err, ToolDetectionError::CxxLacksStdCxx17 { .. }));
}
#[test]
fn validate_cc_accepts_pure_c_clang_without_cxx17_capability() {
let id = CompilerIdentity {
kind: CompilerKind::Clang,
version: CompilerVersion::parse("17.0.6"),
target: None,
raw_version_line: "clang version 17.0.6".into(),
};
let mut caps = derive_cxx_capabilities(&id);
caps.cxx_standard_17 = Capability {
supported: false,
source: CapabilitySource::Unsupported,
};
assert!(validate_cc_for_backend("cc", &id, &caps).is_ok());
assert!(matches!(
validate_cxx_for_backend("cc", &id, &caps).unwrap_err(),
ToolDetectionError::CxxLacksStdCxx17 { .. }
));
}
#[test]
fn validate_cc_accepts_msvc() {
let id = CompilerIdentity {
kind: CompilerKind::Msvc,
version: None,
target: None,
raw_version_line: "MSVC".into(),
};
let caps = derive_cxx_capabilities(&id);
assert!(validate_cc_for_backend("cl.exe", &id, &caps).is_ok());
}
#[test]
fn validate_cc_rejects_unknown_compiler_without_gcc_style() {
let id = CompilerIdentity::unknown("???");
let caps = derive_cxx_capabilities(&id);
let err = validate_cc_for_backend("custom-cc", &id, &caps).unwrap_err();
assert!(matches!(
err,
ToolDetectionError::UnknownCRequiresGccStyle { .. }
));
}
#[test]
fn validate_cc_rejects_gcc_without_depfile_support() {
let id = CompilerIdentity {
kind: CompilerKind::Gcc,
version: CompilerVersion::parse("9.4.0"),
target: None,
raw_version_line: "gcc 9.4".into(),
};
let mut caps = derive_cxx_capabilities(&id);
caps.depfile_mmd_mf = Capability {
supported: false,
source: CapabilitySource::Unsupported,
};
let err = validate_cc_for_backend("cc", &id, &caps).unwrap_err();
assert!(matches!(err, ToolDetectionError::CLacksDepfile { .. }));
}
#[test]
fn validate_accepts_msvc_archiver() {
let id = ArchiverIdentity {
kind: ArchiverKind::Lib,
version: None,
raw_version_line: "Microsoft Library Manager".into(),
};
let caps = derive_ar_capabilities(&id);
assert!(validate_ar_for_backend("lib.exe", &id, &caps).is_ok());
}
#[test]
fn version_display_truncates_unset_components() {
let v = CompilerVersion::parse("11").unwrap();
assert_eq!(v.to_display_string(), "11");
let v = CompilerVersion::parse("11.4").unwrap();
assert_eq!(v.to_display_string(), "11.4");
let v = CompilerVersion::parse("11.4.0").unwrap();
assert_eq!(v.to_display_string(), "11.4.0");
}
fn pretty(value: &serde_json::Value) -> String {
serde_json::to_string_pretty(value).unwrap()
}
fn cxx_identity_and_capabilities_json(version_output: &str) -> String {
let id = parse_cxx_version_output(version_output);
let caps = derive_cxx_capabilities(&id);
pretty(&serde_json::json!({
"identity": id.as_json(),
"capabilities": cxx_capabilities_as_json(&caps),
}))
}
fn ar_identity_and_capabilities_json(version_output: &str) -> String {
let id = parse_ar_version_output(version_output);
let caps = derive_ar_capabilities(&id);
pretty(&serde_json::json!({
"identity": id.as_json(),
"capabilities": ar_capabilities_as_json(&caps),
}))
}
#[test]
fn snapshot_clang_identity_and_capabilities() {
let actual = cxx_identity_and_capabilities_json(
"clang version 17.0.6\nTarget: x86_64-unknown-linux-gnu\nThread model: posix\n",
);
let expected = r#"{
"identity": {
"kind": "clang",
"version": "17.0.6",
"target": "x86_64-unknown-linux-gnu",
"raw_version_line": "clang version 17.0.6"
},
"capabilities": {
"c_standard_11": {
"supported": true,
"source": "version"
},
"cxx_standard_17": {
"supported": true,
"source": "version"
},
"depfile_mmd_mf": {
"supported": true,
"source": "version"
},
"gcc_style_flags": {
"supported": true,
"source": "version"
},
"msvc_style_flags": {
"supported": false,
"source": "assumed-default"
},
"std_flag": {
"supported": true,
"source": "version"
}
}
}"#;
assert_eq!(actual, expected);
}
#[test]
fn snapshot_apple_clang_identity_and_capabilities() {
let actual = cxx_identity_and_capabilities_json(
"Apple clang version 14.0.3 (clang-1403.0.22.14.1)\nTarget: arm64-apple-darwin22.5.0\nThread model: posix\n",
);
let expected = r#"{
"identity": {
"kind": "apple-clang",
"version": "14.0.3",
"target": "arm64-apple-darwin22.5.0",
"raw_version_line": "Apple clang version 14.0.3 (clang-1403.0.22.14.1)"
},
"capabilities": {
"c_standard_11": {
"supported": true,
"source": "version"
},
"cxx_standard_17": {
"supported": true,
"source": "version"
},
"depfile_mmd_mf": {
"supported": true,
"source": "version"
},
"gcc_style_flags": {
"supported": true,
"source": "version"
},
"msvc_style_flags": {
"supported": false,
"source": "assumed-default"
},
"std_flag": {
"supported": true,
"source": "version"
}
}
}"#;
assert_eq!(actual, expected);
}
#[test]
fn snapshot_gcc_identity_and_capabilities() {
let actual = cxx_identity_and_capabilities_json(
"g++ (Ubuntu 11.4.0-1ubuntu1) 11.4.0\nCopyright (C) 2021 Free Software Foundation, Inc.\n",
);
let expected = r#"{
"identity": {
"kind": "gcc",
"version": "11.4.0",
"raw_version_line": "g++ (Ubuntu 11.4.0-1ubuntu1) 11.4.0"
},
"capabilities": {
"c_standard_11": {
"supported": true,
"source": "version"
},
"cxx_standard_17": {
"supported": true,
"source": "version"
},
"depfile_mmd_mf": {
"supported": true,
"source": "version"
},
"gcc_style_flags": {
"supported": true,
"source": "version"
},
"msvc_style_flags": {
"supported": false,
"source": "assumed-default"
},
"std_flag": {
"supported": true,
"source": "version"
}
}
}"#;
assert_eq!(actual, expected);
}
#[test]
fn snapshot_msvc_identity_and_capabilities() {
let actual = cxx_identity_and_capabilities_json(
"Microsoft (R) C/C++ Optimizing Compiler Version 19.39.33523 for x64\n",
);
let expected = r#"{
"identity": {
"kind": "msvc",
"version": "19.39.33523",
"raw_version_line": "Microsoft (R) C/C++ Optimizing Compiler Version 19.39.33523 for x64"
},
"capabilities": {
"c_standard_11": {
"supported": true,
"source": "version"
},
"cxx_standard_17": {
"supported": true,
"source": "version"
},
"depfile_mmd_mf": {
"supported": false,
"source": "unsupported"
},
"gcc_style_flags": {
"supported": false,
"source": "unsupported"
},
"msvc_style_flags": {
"supported": true,
"source": "version"
},
"std_flag": {
"supported": false,
"source": "unsupported"
}
}
}"#;
assert_eq!(actual, expected);
}
#[test]
fn snapshot_unknown_compiler_capabilities_are_conservative() {
let actual = cxx_identity_and_capabilities_json("My funky compiler 0.0\n");
let expected = r#"{
"identity": {
"kind": "unknown",
"raw_version_line": "My funky compiler 0.0"
},
"capabilities": {
"c_standard_11": {
"supported": false,
"source": "assumed-default"
},
"cxx_standard_17": {
"supported": false,
"source": "assumed-default"
},
"depfile_mmd_mf": {
"supported": false,
"source": "assumed-default"
},
"gcc_style_flags": {
"supported": false,
"source": "assumed-default"
},
"msvc_style_flags": {
"supported": false,
"source": "assumed-default"
},
"std_flag": {
"supported": false,
"source": "assumed-default"
}
}
}"#;
assert_eq!(actual, expected);
}
#[test]
fn snapshot_gnu_ar_identity_and_capabilities() {
let actual = ar_identity_and_capabilities_json(
"GNU ar (GNU Binutils for Debian) 2.40\nCopyright (C) 2023 Free Software Foundation, Inc.\n",
);
let expected = r#"{
"identity": {
"kind": "ar",
"version": "2.40",
"raw_version_line": "GNU ar (GNU Binutils for Debian) 2.40"
},
"capabilities": {
"ar_crs": {
"supported": true,
"source": "version"
},
"static_library_output": {
"supported": true,
"source": "version"
}
}
}"#;
assert_eq!(actual, expected);
}
#[test]
fn snapshot_msvc_lib_archiver_produces_static_library_without_ar_crs() {
let actual = ar_identity_and_capabilities_json(
"Microsoft (R) Library Manager Version 14.39.33523.0\nCopyright (C) Microsoft Corporation.\n",
);
let expected = r#"{
"identity": {
"kind": "lib",
"version": "14.39.33523",
"raw_version_line": "Microsoft (R) Library Manager Version 14.39.33523.0"
},
"capabilities": {
"ar_crs": {
"supported": false,
"source": "unsupported"
},
"static_library_output": {
"supported": true,
"source": "version"
}
}
}"#;
assert_eq!(actual, expected);
}
#[test]
fn snapshot_full_detection_report_for_clang_plus_gnu_ar() {
let cxx_id =
parse_cxx_version_output("clang version 17.0.6\nTarget: x86_64-unknown-linux-gnu\n");
let cxx_caps = derive_cxx_capabilities(&cxx_id);
let ar_id = parse_ar_version_output("GNU ar (GNU Binutils) 2.40\n");
let ar_caps = derive_ar_capabilities(&ar_id);
let report = ToolchainDetectionReport {
cxx: ToolDetection {
path: camino::Utf8PathBuf::from("/opt/llvm/bin/clang++"),
identity: cxx_id,
capabilities: cxx_caps,
},
cc: None,
ar: ToolDetection {
path: camino::Utf8PathBuf::from("/usr/bin/ar"),
identity: ar_id,
capabilities: ar_caps,
},
};
let actual = pretty(&report.as_json());
let expected = r#"{
"cxx": {
"path": "/opt/llvm/bin/clang++",
"identity": {
"kind": "clang",
"version": "17.0.6",
"target": "x86_64-unknown-linux-gnu",
"raw_version_line": "clang version 17.0.6"
},
"capabilities": {
"c_standard_11": {
"supported": true,
"source": "version"
},
"cxx_standard_17": {
"supported": true,
"source": "version"
},
"depfile_mmd_mf": {
"supported": true,
"source": "version"
},
"gcc_style_flags": {
"supported": true,
"source": "version"
},
"msvc_style_flags": {
"supported": false,
"source": "assumed-default"
},
"std_flag": {
"supported": true,
"source": "version"
}
}
},
"ar": {
"path": "/usr/bin/ar",
"identity": {
"kind": "ar",
"version": "2.40",
"raw_version_line": "GNU ar (GNU Binutils) 2.40"
},
"capabilities": {
"ar_crs": {
"supported": true,
"source": "version"
},
"static_library_output": {
"supported": true,
"source": "version"
}
}
}
}"#;
assert_eq!(actual, expected);
}