axbuild 0.4.16

An OS build lib toolkit used by arceos
Documentation
use std::path::{Path, PathBuf};

use anyhow::bail;

use super::{
    ARCEOS_AXTEST_GROUP, ARCEOS_C_TEST_GROUP, ARCEOS_RUST_TEST_BUILD_GROUP, ARCEOS_RUST_TEST_GROUP,
    ARCEOS_RUST_TEST_PACKAGE, ARCEOS_TEST_SUITE_OS,
    args::ArgsTestQemu,
    assets::{
        arceos_rust_test_dir, build_config_path, qemu_config_path, read_manifest_package_name,
    },
    rust_qemu::rust_qemu_features_for_run,
    types::{ArceosRustQemuCase, QemuTestFlow},
};
use crate::{
    arceos::ArceOS,
    test::{case::TestQemuCase, qemu as qemu_test, suite as test_suite},
};

pub(super) fn discover_rust_qemu_cases(
    arceos: &ArceOS,
    arch: &str,
    target: &str,
    selected_case: Option<&str>,
    allow_missing_selected_case: bool,
) -> anyhow::Result<Vec<ArceosRustQemuCase>> {
    let root = arceos_rust_test_dir(arceos);
    rust_qemu_features_for_run(selected_case, allow_missing_selected_case)?
        .into_iter()
        .map(|feature| load_arceos_test_suit_qemu_case(&root, arch, target, feature))
        .collect()
}

pub(super) fn discover_qemu_cases_in_dir(
    dir: &Path,
    arch: &str,
    target: &str,
    selected_case: Option<&str>,
    group: &str,
) -> anyhow::Result<Vec<ArceosRustQemuCase>> {
    qemu_test::discover_qemu_cases(dir, arch, target, selected_case, "ArceOS", group)?
        .into_iter()
        .map(load_rust_qemu_case)
        .collect::<anyhow::Result<Vec<_>>>()
}

pub(super) fn discover_qemu_cases_in_dir_allow_empty(
    dir: &Path,
    arch: &str,
    target: &str,
    selected_case: Option<&str>,
    group: &str,
) -> anyhow::Result<Vec<ArceosRustQemuCase>> {
    qemu_test::discover_qemu_cases_allow_empty(dir, arch, target, selected_case, "ArceOS", group)?
        .into_iter()
        .map(load_rust_qemu_case)
        .collect::<anyhow::Result<Vec<_>>>()
}

fn load_rust_qemu_case(case: qemu_test::DiscoveredQemuCase) -> anyhow::Result<ArceosRustQemuCase> {
    let package = read_manifest_package_name(&case.case_dir.join("Cargo.toml"))?;
    let host_http_server = qemu_test::load_qemu_case_host_http_server(&case.qemu_config_path)?;
    Ok(ArceosRustQemuCase {
        case: TestQemuCase {
            name: case.name,
            display_name: case.display_name,
            case_dir: case.case_dir,
            qemu_config_path: case.qemu_config_path,
            test_commands: Vec::new(),
            host_symbolize_success_regex: Vec::new(),
            host_http_server,
            subcases: Vec::new(),
            grouped_subcase_filter: None,
        },
        build_group: case.build_group,
        build_config_path: case.build_config_path,
        package,
        feature: None,
    })
}

pub(super) fn load_arceos_test_suit_qemu_case(
    root: &Path,
    arch: &str,
    target: &str,
    feature: &str,
) -> anyhow::Result<ArceosRustQemuCase> {
    let build_config_path = arceos_test_suit_build_config_path(root, target)?;
    let qemu_config_path = arceos_test_suit_qemu_config_path(root, arch)?;
    let host_http_server = qemu_test::load_qemu_case_host_http_server(&qemu_config_path)?;
    Ok(ArceosRustQemuCase {
        case: TestQemuCase {
            name: feature.to_string(),
            display_name: feature.to_string(),
            case_dir: root.to_path_buf(),
            qemu_config_path,
            test_commands: Vec::new(),
            host_symbolize_success_regex: Vec::new(),
            host_http_server,
            subcases: Vec::new(),
            grouped_subcase_filter: None,
        },
        build_group: ARCEOS_RUST_TEST_BUILD_GROUP.to_string(),
        build_config_path,
        package: ARCEOS_RUST_TEST_PACKAGE.to_string(),
        feature: Some(feature.to_string()),
    })
}

pub(super) fn arceos_test_suit_build_config_path(
    root: &Path,
    target: &str,
) -> anyhow::Result<PathBuf> {
    build_config_path(root, target, "ArceOS rust test suite")
}

pub(super) fn arceos_test_suit_qemu_config_path(
    root: &Path,
    arch: &str,
) -> anyhow::Result<PathBuf> {
    qemu_config_path(root, arch, "ArceOS rust test suite")
}

pub(super) fn selected_qemu_test_groups(
    workspace_root: &Path,
    args: &ArgsTestQemu,
) -> anyhow::Result<Vec<QemuTestFlow>> {
    if args.only_c {
        return Ok(vec![QemuTestFlow::C]);
    }
    if args.only_rust {
        return Ok(vec![QemuTestFlow::Rust]);
    }

    match args.test_group.as_deref() {
        None => {
            let mut flows = vec![QemuTestFlow::Rust, QemuTestFlow::C];
            for group in test_suite::discover_group_names(workspace_root, ARCEOS_TEST_SUITE_OS)? {
                if group != ARCEOS_RUST_TEST_GROUP
                    && group != ARCEOS_C_TEST_GROUP
                    && group != ARCEOS_AXTEST_GROUP
                {
                    flows.push(QemuTestFlow::Generic(group));
                }
            }
            Ok(flows)
        }
        Some(ARCEOS_RUST_TEST_GROUP) => Ok(vec![QemuTestFlow::Rust]),
        Some(ARCEOS_C_TEST_GROUP) => Ok(vec![QemuTestFlow::C]),
        Some(ARCEOS_AXTEST_GROUP) => Ok(vec![QemuTestFlow::Axtest]),
        Some(group) => {
            let dir = test_suite::group_dir(workspace_root, ARCEOS_TEST_SUITE_OS, group);
            if dir.is_dir() {
                Ok(vec![QemuTestFlow::Generic(group.to_string())])
            } else {
                bail!(
                    "unsupported ArceOS qemu test group `{group}`; supported groups are: {}",
                    test_suite::supported_group_names(workspace_root, ARCEOS_TEST_SUITE_OS)
                        .unwrap_or_else(|_| {
                            format!("{ARCEOS_RUST_TEST_GROUP}, {ARCEOS_C_TEST_GROUP}")
                        })
                )
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use tempfile::tempdir;

    use super::*;

    fn qemu_args(only_rust: bool, only_c: bool, package: Vec<String>) -> ArgsTestQemu {
        ArgsTestQemu {
            arch: None,
            target: Some("x86_64-unknown-none".to_string()),
            test_group: None,
            test_case: None,
            list: false,
            package,
            only_rust,
            only_c,
            no_symbolize: false,
            keep_qemu_log: false,
        }
    }

    #[test]
    fn selected_qemu_test_groups_default_runs_rust_then_c() {
        let dir = tempdir().unwrap();
        let flows =
            selected_qemu_test_groups(dir.path(), &qemu_args(false, false, Vec::new())).unwrap();

        assert_eq!(flows, &[QemuTestFlow::Rust, QemuTestFlow::C]);
    }

    #[test]
    fn selected_qemu_test_groups_only_rust_skips_c() {
        let dir = tempdir().unwrap();
        let flows =
            selected_qemu_test_groups(dir.path(), &qemu_args(true, false, Vec::new())).unwrap();

        assert_eq!(flows, &[QemuTestFlow::Rust]);
    }

    #[test]
    fn selected_qemu_test_groups_only_c_skips_rust() {
        let dir = tempdir().unwrap();
        let flows =
            selected_qemu_test_groups(dir.path(), &qemu_args(false, true, Vec::new())).unwrap();

        assert_eq!(flows, &[QemuTestFlow::C]);
    }

    #[test]
    fn selected_qemu_test_groups_package_filter_no_longer_changes_groups() {
        let dir = tempdir().unwrap();
        let flows = selected_qemu_test_groups(
            dir.path(),
            &qemu_args(false, false, vec!["arceos-test-suit".to_string()]),
        )
        .unwrap();

        assert_eq!(flows, &[QemuTestFlow::Rust, QemuTestFlow::C]);
    }
}