axbuild 0.4.17

An OS build lib toolkit used by arceos
Documentation
use super::{discovery::qemu_configs_in_dir, *};

pub(crate) fn normalize_qemu_test_commands<I, S>(
    qemu_config_path: &Path,
    commands: I,
    suite_name: &str,
) -> anyhow::Result<Vec<String>>
where
    I: IntoIterator<Item = S>,
    S: AsRef<str>,
{
    let mut test_commands = Vec::new();
    for command in commands {
        let command = command.as_ref().trim().to_string();
        if command.is_empty() {
            bail!(
                "{suite_name} grouped qemu case `{}` contains an empty test command",
                qemu_config_path.display()
            );
        }
        test_commands.push(command);
    }
    Ok(test_commands)
}

pub(crate) fn load_test_qemu_case_fields(
    display_name: String,
    name: String,
    case_dir: PathBuf,
    qemu_config_path: PathBuf,
    suite_name: &str,
    discover_subcases: bool,
) -> anyhow::Result<TestQemuCase> {
    let config = load_qemu_case_extra_config(&qemu_config_path)?;
    let test_commands =
        normalize_qemu_test_commands(&qemu_config_path, config.test_commands, suite_name)?;
    let subcases = if discover_subcases && !test_commands.is_empty() {
        let arch = qemu_config_path
            .file_stem()
            .and_then(|stem| stem.to_str())
            .and_then(|stem| stem.strip_prefix("qemu-"));
        discover_qemu_subcases(&case_dir, arch)?
    } else {
        Vec::new()
    };
    Ok(TestQemuCase {
        display_name,
        name,
        case_dir,
        qemu_config_path,
        test_commands,
        host_symbolize_success_regex: config.host_symbolize_success_regex,
        host_http_server: config.host_http_server,
        subcases,
        grouped_subcase_filter: None,
    })
}

pub(crate) fn load_qemu_case_extra_config(
    qemu_config_path: &Path,
) -> anyhow::Result<QemuCaseExtraConfig> {
    let content = fs::read_to_string(qemu_config_path)
        .with_context(|| format!("failed to read {}", qemu_config_path.display()))?;
    toml::from_str(&content)
        .with_context(|| format!("failed to parse {}", qemu_config_path.display()))
}

pub(crate) fn load_qemu_case_host_http_server(
    qemu_config_path: &Path,
) -> anyhow::Result<Option<HostHttpServerConfig>> {
    Ok(load_qemu_case_extra_config(qemu_config_path)?.host_http_server)
}

pub(super) fn discover_qemu_subcases(
    case_dir: &Path,
    arch: Option<&str>,
) -> anyhow::Result<Vec<TestQemuSubcase>> {
    let mut subcases = Vec::new();
    for entry in
        fs::read_dir(case_dir).with_context(|| format!("failed to read {}", case_dir.display()))?
    {
        let entry = entry?;
        let path = entry.path();
        if !path.is_dir() {
            continue;
        }

        if let Some(arch) = arch
            && let Some(configs) = qemu_configs_in_dir(&path)?
            && !configs.contains_key(arch)
        {
            continue;
        }

        let Ok(name) = entry.file_name().into_string() else {
            continue;
        };
        let kind = if path.join("c").is_dir() || path.join("CMakeLists.txt").is_file() {
            Some(TestQemuSubcaseKind::C)
        } else if path.join("rust").is_dir() {
            Some(TestQemuSubcaseKind::Rust)
        } else {
            None
        };

        if let Some(kind) = kind {
            subcases.push(TestQemuSubcase {
                name,
                case_dir: path,
                kind,
            });
        }
    }
    subcases.sort_by(|left, right| left.name.cmp(&right.name));
    Ok(subcases)
}

pub(crate) fn validate_grouped_qemu_commands(
    qemu: &QemuConfig,
    case: &TestQemuCase,
    suite_name: &str,
) -> anyhow::Result<()> {
    let shell_init_cmd_set = qemu
        .shell_init_cmd
        .as_deref()
        .map(str::trim)
        .is_some_and(|value| !value.is_empty());
    if shell_init_cmd_set && !case.test_commands.is_empty() {
        bail!(
            "{suite_name} grouped qemu case `{}` cannot define both `shell_init_cmd` and \
             `test_commands`",
            case.qemu_config_path.display()
        );
    }
    Ok(())
}