axbuild 0.4.15

An OS build lib toolkit used by arceos
Documentation
use anyhow::bail;

use super::{
    ARCEOS_C_TEST_GROUP, ARCEOS_RUST_TEST_GROUP, ARCEOS_TEST_SUITE_OS,
    assets::{
        arceos_c_test_dir, arceos_rust_test_dir, arceos_test_group_dir, arceos_test_suit_qemu_archs,
    },
    c_qemu::{
        arceos_c_test_suit_build_config_path, arceos_c_test_suit_qemu_config_path,
        c_qemu_features_for_list,
    },
    discovery::{
        arceos_test_suit_build_config_path, arceos_test_suit_qemu_config_path,
        discover_qemu_cases_in_dir,
    },
    rust_qemu::rust_qemu_features_for_list,
};
use crate::{
    arceos::ArceOS,
    test::{qemu as qemu_test, suite as test_suite},
};

pub(super) fn list_rust_qemu_cases(
    arceos: &ArceOS,
    target: Option<(&str, &str)>,
    selected_case: Option<&str>,
    allow_missing_selected_case: bool,
) -> anyhow::Result<Option<String>> {
    let cases = rust_qemu_case_names(arceos, target, selected_case, allow_missing_selected_case)?;
    if cases.is_empty() {
        return Ok(None);
    }
    Ok(Some(qemu_test::render_case_tree(
        ARCEOS_RUST_TEST_GROUP,
        cases,
    )))
}

pub(super) fn list_c_qemu_cases(
    arceos: &ArceOS,
    target: Option<(&str, &str)>,
    selected_case: Option<&str>,
) -> anyhow::Result<Option<String>> {
    let cases = c_qemu_case_names(arceos, target, selected_case)?;
    if cases.is_empty() {
        return Ok(None);
    }
    Ok(Some(qemu_test::render_case_tree(
        ARCEOS_C_TEST_GROUP,
        cases,
    )))
}

pub(super) fn list_generic_qemu_cases(
    arceos: &ArceOS,
    target: Option<(&str, &str)>,
    group: &str,
    selected_case: Option<&str>,
) -> anyhow::Result<Option<String>> {
    let dir = arceos_test_group_dir(arceos.app.workspace_root(), group);
    let cases: Vec<String> = match target {
        Some((arch, target)) => {
            discover_qemu_cases_in_dir(&dir, arch, target, selected_case, group)?
                .into_iter()
                .map(|case| case.case.name)
                .collect()
        }
        None => qemu_test::discover_all_qemu_cases(&dir, selected_case, "ArceOS", group)
            .map_err(anyhow::Error::new)?,
    };
    if cases.is_empty() {
        return Ok(None);
    }
    Ok(Some(qemu_test::render_case_tree(group, cases)))
}

pub(super) fn all_qemu_case_groups(
    arceos: &ArceOS,
    selected_case: Option<&str>,
) -> anyhow::Result<Vec<(String, Vec<qemu_test::ListedQemuCase>)>> {
    let mut groups = Vec::new();
    for group in
        test_suite::discover_group_names(arceos.app.workspace_root(), ARCEOS_TEST_SUITE_OS)?
    {
        let cases: Option<Vec<qemu_test::ListedQemuCase>> = match group.as_str() {
            ARCEOS_RUST_TEST_GROUP => rust_qemu_listed_cases(arceos, selected_case)
                .ok()
                .filter(|cases| !cases.is_empty()),
            ARCEOS_C_TEST_GROUP => c_qemu_listed_cases(arceos, selected_case)
                .ok()
                .filter(|v| !v.is_empty()),
            _ => {
                let dir = arceos_test_group_dir(arceos.app.workspace_root(), &group);
                match qemu_test::discover_all_qemu_cases_with_archs(
                    &dir,
                    selected_case,
                    "ArceOS",
                    &group,
                ) {
                    Ok(cases) if !cases.is_empty() => Some(cases),
                    Ok(_) => None,
                    Err(err) if qemu_list_error_is_ignorable(err.kind()) => None,
                    Err(err) => return Err(anyhow::Error::new(err)),
                }
            }
        };
        if let Some(cases) = cases {
            groups.push((group, cases));
        }
    }
    if groups.is_empty()
        && let Some(case) = selected_case
    {
        bail!("unknown ArceOS qemu test case `{case}`");
    }
    Ok(groups)
}

fn qemu_list_error_is_ignorable(kind: qemu_test::ListQemuCasesErrorKind) -> bool {
    matches!(
        kind,
        qemu_test::ListQemuCasesErrorKind::EmptyGroup
            | qemu_test::ListQemuCasesErrorKind::UnknownSelectedCase
    )
}

fn rust_qemu_listed_cases(
    arceos: &ArceOS,
    selected_case: Option<&str>,
) -> anyhow::Result<Vec<qemu_test::ListedQemuCase>> {
    let root = arceos_rust_test_dir(arceos);
    let archs = arceos_test_suit_qemu_archs(&root)?;
    if archs.is_empty() {
        bail!("no ArceOS rust qemu configs found under {}", root.display());
    }
    Ok(rust_qemu_features_for_list(selected_case, false)?
        .into_iter()
        .map(|feature| qemu_test::ListedQemuCase {
            name: feature.to_string(),
            archs: archs.clone(),
        })
        .collect())
}

fn rust_qemu_case_names(
    arceos: &ArceOS,
    target: Option<(&str, &str)>,
    selected_case: Option<&str>,
    allow_missing_selected_case: bool,
) -> anyhow::Result<Vec<String>> {
    match target {
        Some((arch, target)) => {
            let root = arceos_rust_test_dir(arceos);
            arceos_test_suit_build_config_path(&root, target)?;
            arceos_test_suit_qemu_config_path(&root, arch)?;
            Ok(
                rust_qemu_features_for_list(selected_case, allow_missing_selected_case)?
                    .into_iter()
                    .map(str::to_string)
                    .collect(),
            )
        }
        None => Ok(
            rust_qemu_features_for_list(selected_case, allow_missing_selected_case)?
                .into_iter()
                .map(str::to_string)
                .collect(),
        ),
    }
}

fn c_qemu_case_names(
    arceos: &ArceOS,
    target: Option<(&str, &str)>,
    selected_case: Option<&str>,
) -> anyhow::Result<Vec<String>> {
    if let Some((arch, target)) = target {
        let root = arceos_c_test_dir(arceos);
        arceos_c_test_suit_build_config_path(&root, target)?;
        arceos_c_test_suit_qemu_config_path(&root, arch)?;
    }

    Ok(c_qemu_features_for_list(selected_case)?
        .into_iter()
        .map(str::to_string)
        .collect())
}

fn c_qemu_listed_cases(
    arceos: &ArceOS,
    selected_case: Option<&str>,
) -> qemu_test::ListQemuCasesResult<Vec<qemu_test::ListedQemuCase>> {
    let root = arceos_c_test_dir(arceos);
    let archs = arceos_test_suit_qemu_archs(&root).map_err(qemu_test::ListQemuCasesError::from)?;
    if archs.is_empty() {
        return Ok(Vec::new());
    }
    let Ok(features) = c_qemu_features_for_list(selected_case) else {
        return Ok(Vec::new());
    };
    Ok(features
        .into_iter()
        .map(|feature| qemu_test::ListedQemuCase {
            name: feature.to_string(),
            archs: archs.clone(),
        })
        .collect())
}