feat: enhance automatic test skipping logic for multilib builds

This commit is contained in:
2026-03-15 22:05:10 -05:00
parent 0d768e0941
commit db0dcb2ff5
8 changed files with 132 additions and 19 deletions
+8 -2
View File
@@ -177,8 +177,14 @@ pub fn build(
}
}
if flags.skip_tests {
crate::log_info!("Skipping tests: disabled by build.flags.skip_tests");
if spec.should_skip_automatic_tests() {
if flags.skip_tests {
crate::log_info!("Skipping tests: disabled by build.flags.skip_tests");
} else {
crate::log_info!(
"Skipping tests: automatic tests are disabled for multilib builds"
);
}
} else {
let test_dirs = resolve_make_dirs(
&build_dir,
+8 -2
View File
@@ -127,8 +127,14 @@ pub fn build(
anyhow::bail!("cmake build failed");
}
if flags.skip_tests {
crate::log_info!("Skipping tests: disabled by build.flags.skip_tests");
if spec.should_skip_automatic_tests() {
if flags.skip_tests {
crate::log_info!("Skipping tests: disabled by build.flags.skip_tests");
} else {
crate::log_info!(
"Skipping tests: automatic tests are disabled for multilib builds"
);
}
} else {
let test_targets = phase_targets(&flags.make_test_target, &flags.make_test_targets);
if !cmake_uses_default_ctest(flags) {
+8 -2
View File
@@ -86,8 +86,14 @@ pub fn build(
anyhow::bail!("ninja build failed");
}
if flags.skip_tests {
crate::log_info!("Skipping tests: disabled by build.flags.skip_tests");
if spec.should_skip_automatic_tests() {
if flags.skip_tests {
crate::log_info!("Skipping tests: disabled by build.flags.skip_tests");
} else {
crate::log_info!(
"Skipping tests: automatic tests are disabled for multilib builds"
);
}
} else {
let test_suites = meson_test_suites(flags);
if test_suites.is_empty() {
+8 -2
View File
@@ -116,8 +116,14 @@ pub fn build(
}
}
if flags.skip_tests {
crate::log_info!("Skipping tests: disabled by build.flags.skip_tests");
if spec.should_skip_automatic_tests() {
if flags.skip_tests {
crate::log_info!("Skipping tests: disabled by build.flags.skip_tests");
} else {
crate::log_info!(
"Skipping tests: automatic tests are disabled for multilib builds"
);
}
} else {
let test_dirs = autotools::resolve_make_dirs(
&actual_src,
+59 -6
View File
@@ -927,12 +927,19 @@ fn build_type_runs_automatic_tests(spec: &package::PackageSpec) -> bool {
)
}
fn automatic_tests_disabled_for_outputs(
pkg_spec: &package::PackageSpec,
requested_outputs: deps::RequestedOutputs,
) -> bool {
pkg_spec.should_skip_automatic_tests() || requested_outputs.includes_lib32()
}
fn maybe_disable_tests_for_missing_deps(
pkg_spec: &mut package::PackageSpec,
db_path: &Path,
requested_outputs: deps::RequestedOutputs,
) -> Result<()> {
if pkg_spec.build.flags.skip_tests
if automatic_tests_disabled_for_outputs(pkg_spec, requested_outputs)
|| !build_type_runs_automatic_tests(pkg_spec)
|| deps::declared_test_deps(pkg_spec, requested_outputs).is_empty()
{
@@ -956,7 +963,7 @@ fn maybe_prompt_to_skip_tests_for_missing_requested_deps(
missing_test: &[String],
reason: &str,
) -> Result<bool> {
if pkg_spec.build.flags.skip_tests
if pkg_spec.should_skip_automatic_tests()
|| !build_type_runs_automatic_tests(pkg_spec)
|| missing_test.is_empty()
{
@@ -993,7 +1000,7 @@ fn should_install_test_deps(
requested_outputs: deps::RequestedOutputs,
) -> bool {
install_test_deps
&& !pkg_spec.build.flags.skip_tests
&& !automatic_tests_disabled_for_outputs(pkg_spec, requested_outputs)
&& !deps::declared_test_deps(pkg_spec, requested_outputs).is_empty()
}
@@ -4200,7 +4207,9 @@ fn run_direct_install_request(
anyhow::bail!("Missing test dependencies: {}", unavailable_test.join(", "));
}
if !pkg_spec.build.flags.skip_tests && !dep_spec_paths.is_empty() {
if !automatic_tests_disabled_for_outputs(&pkg_spec, requested_outputs)
&& !dep_spec_paths.is_empty()
{
ui::warn(format!(
"Missing test dependencies: {}",
missing_test.join(", ")
@@ -4714,7 +4723,12 @@ pub fn run(cli: Cli) -> Result<()> {
Err(err) => return Err(err),
};
if cleanup_deps && !pkg_spec.build.flags.skip_tests {
if cleanup_deps
&& !automatic_tests_disabled_for_outputs(
&pkg_spec,
requested_outputs,
)
{
auto_installed_deps.record_plan(
&dep_plan,
&missing_test,
@@ -4722,7 +4736,9 @@ pub fn run(cli: Cli) -> Result<()> {
);
}
if !pkg_spec.build.flags.skip_tests && !dep_plan.steps.is_empty() {
if !automatic_tests_disabled_for_outputs(&pkg_spec, requested_outputs)
&& !dep_plan.steps.is_empty()
{
ui::warn(format!(
"Missing test dependencies: {}",
missing_test.join(", ")
@@ -7385,6 +7401,43 @@ optional = []
Ok(())
}
#[test]
fn requested_test_deps_prompt_is_ignored_for_multilib_builds() -> Result<()> {
let _guard = ASSUME_YES_TEST_LOCK.lock().expect("assume-yes test lock");
let mut spec = test_package_spec(package::BuildType::Meson, None, &[]);
spec.build.flags.build_32 = true;
spec.dependencies.test = vec!["pytest".into()];
ui::set_assume_yes(true);
let prompted = maybe_prompt_to_skip_tests_for_missing_requested_deps(
&mut spec,
&["pytest".into()],
"Requested test dependencies are missing",
)?;
ui::set_assume_yes(false);
assert!(!prompted);
assert!(!spec.build.flags.skip_tests);
Ok(())
}
#[test]
fn should_not_install_test_deps_for_cli_lib32_only_builds() {
let mut spec = test_package_spec(package::BuildType::Meson, None, &[]);
spec.dependencies.lib32 = Some(package::DependencyGroup {
build: Vec::new(),
runtime: Vec::new(),
test: vec!["lib32-pytest".into()],
optional: Vec::new(),
});
assert!(!should_install_test_deps(
&spec,
true,
deps::RequestedOutputs::Lib32Only
));
}
#[test]
fn rootfs_is_system_root_detects_live_rootfs() {
assert!(rootfs_is_system_root(Path::new("/")));
+14 -2
View File
@@ -40,6 +40,12 @@ pub(crate) enum RequestedOutputs {
Lib32Only,
}
impl RequestedOutputs {
pub(crate) fn includes_lib32(self) -> bool {
matches!(self, Self::PrimaryAndLib32 | Self::Lib32Only)
}
}
/// Parse a dependency string into name, version, and operator
fn parse_dep(dep: &str) -> ParsedDep<'_> {
// Try operators in order of specificity (>= before >, etc.)
@@ -154,6 +160,10 @@ fn build_type_runs_automatic_tests(spec: &PackageSpec) -> bool {
)
}
fn automatic_tests_disabled_for_outputs(spec: &PackageSpec, outputs: RequestedOutputs) -> bool {
spec.should_skip_automatic_tests() || outputs.includes_lib32()
}
/// Check whether a dependency expression is satisfied by the installed package DB.
pub fn is_dep_satisfied_in_db(dep: &str, db_path: &Path) -> Result<bool> {
if !db_path.exists() {
@@ -358,7 +368,8 @@ pub(crate) fn print_dep_status_for_outputs(
"Test dependencies",
&primary.test,
&missing_test,
!spec.build.flags.skip_tests && build_type_runs_automatic_tests(spec),
!automatic_tests_disabled_for_outputs(spec, outputs)
&& build_type_runs_automatic_tests(spec),
);
if !primary.optional.is_empty() {
ui::info(format!(
@@ -395,7 +406,8 @@ pub(crate) fn print_dep_status_for_outputs(
"Lib32 test dependencies",
&lib32.test,
&missing_test,
!spec.build.flags.skip_tests && build_type_runs_automatic_tests(spec),
!automatic_tests_disabled_for_outputs(spec, outputs)
&& build_type_runs_automatic_tests(spec),
);
if !lib32.optional.is_empty() {
ui::info(format!(
+23
View File
@@ -266,6 +266,14 @@ impl PackageSpec {
self.build.flags.lib32_only
}
/// Return true when builder-managed automatic tests should be skipped.
///
/// Automatic test phases are disabled when `build.flags.skip_tests` is set and for
/// multilib builds, because the generated lib32 output is built in a separate 32-bit pass.
pub fn should_skip_automatic_tests(&self) -> bool {
self.build.flags.skip_tests || self.builds_lib32_output()
}
/// Return the effective dependency set used by the generated lib32 companion package.
pub fn lib32_dependencies(&self) -> Dependencies {
let mut deps = self
@@ -3086,6 +3094,19 @@ type = "autotools"
assert_eq!(spec.build.flags.post_install_lib32, vec!["echo lib32"]);
}
#[test]
fn multilib_builds_skip_automatic_tests() {
let mut spec = mk_spec("foo", "1.0");
assert!(!spec.should_skip_automatic_tests());
spec.build.flags.build_32 = true;
assert!(spec.should_skip_automatic_tests());
spec.build.flags.build_32 = false;
spec.build.flags.skip_tests = true;
assert!(spec.should_skip_automatic_tests());
}
#[test]
fn parse_post_configure_from_spec() {
let tmp = tempfile::tempdir().unwrap();
@@ -3982,6 +4003,8 @@ pub struct BuildFlags {
)]
pub no_compress_man: bool,
/// Skip automatic build-system test execution (e.g. Autotools `make check`/`make test`).
///
/// Automatic tests are also skipped for multilib (`build_32` / `lib32_only`) builds.
#[serde(default, alias = "skip-tests")]
pub skip_tests: bool,
/// Run an additional lib32 build pass and emit a `lib32-*` package.
+4 -3
View File
@@ -624,6 +624,7 @@ fn source_deps_for_install(
let mut deps_all = Vec::new();
let lib32_only = lib32_only || spec.builds_only_lib32_output();
let include_lib32 = lib32_only || spec.builds_lib32_output();
let skip_automatic_tests = spec.should_skip_automatic_tests() || lib32_only;
let local_provides = spec.local_dependency_provides_for_selection(!lib32_only, include_lib32);
if !lib32_only && !spec.is_metapackage() {
for dep in &spec.dependencies.build {
@@ -658,7 +659,7 @@ fn source_deps_for_install(
}
}
}
if include_test_deps && !spec.build.flags.skip_tests {
if include_test_deps && !skip_automatic_tests {
if !lib32_only {
for dep in &spec.dependencies.test {
push_unique(&mut deps_all, dep.clone());
@@ -961,7 +962,7 @@ mod tests {
let deps = source_deps_for_install(&spec, true, true);
assert!(deps.contains(&"gcc-multilib".to_string()));
assert!(deps.contains(&"lib32-zlib".to_string()));
assert!(deps.contains(&"lib32-bats".to_string()));
assert!(!deps.contains(&"lib32-bats".to_string()));
assert!(!deps.contains(&"make".to_string()));
assert!(!deps.contains(&"zlib".to_string()));
assert!(!deps.contains(&"bats".to_string()));
@@ -981,7 +982,7 @@ mod tests {
let deps = source_deps_for_install(&spec, true, false);
assert!(deps.contains(&"gcc-multilib".to_string()));
assert!(deps.contains(&"lib32-zlib".to_string()));
assert!(deps.contains(&"lib32-bats".to_string()));
assert!(!deps.contains(&"lib32-bats".to_string()));
assert!(!deps.contains(&"make".to_string()));
assert!(!deps.contains(&"zlib".to_string()));
assert!(!deps.contains(&"bats".to_string()));