hisi-rf-link 0.1.0-alpha.26

Host-side linker, archive normalization, and ABI inventory for HiSilicon radio blobs
Documentation
use std::process::Command;

fn binary() -> std::path::PathBuf {
    let path = std::path::PathBuf::from(env!("CARGO_BIN_EXE_hisi-rf-link"));
    if path.is_absolute() {
        path
    } else {
        std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
            .join("../..")
            .join(path)
    }
}

#[test]
fn embedded_tools_expose_help() {
    let cache = tempfile::tempdir().unwrap();
    for command in [
        "patch-reloc",
        "verify-layout",
        "generate-rom-patch",
        "patch-from-oracle",
    ] {
        let output = Command::new(binary())
            .args([command, "--help"])
            .env("UV_CACHE_DIR", cache.path())
            .output()
            .expect("run embedded post-link tool");
        assert!(
            output.status.success(),
            "{command} --help failed:\n{}",
            String::from_utf8_lossy(&output.stderr)
        );
    }
}

#[test]
fn task_profile_exposes_help() {
    let output = Command::new(binary())
        .args(["task-profile", "--help"])
        .output()
        .expect("run task-profile --help");
    assert!(output.status.success());
    assert!(
        String::from_utf8(output.stdout)
            .unwrap()
            .contains("hash-bound scheduling profile")
    );
}

#[test]
fn inspect_summarizes_real_vendor_archive() {
    let archive = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("../../ws63-RF/lib/libwifi_alg_edca_opt.a");
    let output = Command::new(binary())
        .args(["inspect", "--summary"])
        .arg(archive)
        .output()
        .expect("inspect vendor archive");
    assert!(
        output.status.success(),
        "{}",
        String::from_utf8_lossy(&output.stderr)
    );
    let summary: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
    assert_eq!(summary["archives"], 1);
    assert!(summary["total"].as_u64().unwrap() > 0);
    assert!(summary["by_type"]["R_RISCV_48_LLUI"].as_u64().unwrap() > 0);
}

#[test]
fn normalize_is_deterministic_and_removes_vendor_relocations() {
    let archive = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("../../ws63-RF/lib/libwifi_alg_edca_opt.a");
    let directory = tempfile::tempdir().unwrap();
    let first = directory.path().join("first");
    let second = directory.path().join("second");
    let first_manifest = directory.path().join("first.json");
    let second_manifest = directory.path().join("second.json");

    for (output, manifest) in [(&first, &first_manifest), (&second, &second_manifest)] {
        let result = Command::new(binary())
            .args(["normalize", "--profile-revision", "test"])
            .arg("--out-dir")
            .arg(output)
            .arg("--manifest")
            .arg(manifest)
            .arg(&archive)
            .output()
            .expect("normalize vendor archive");
        assert!(
            result.status.success(),
            "{}",
            String::from_utf8_lossy(&result.stderr)
        );
    }

    let file_name = archive.file_name().unwrap();
    assert_eq!(
        std::fs::read(first.join(file_name)).unwrap(),
        std::fs::read(second.join(file_name)).unwrap()
    );
    assert_eq!(
        std::fs::read(&first_manifest).unwrap(),
        std::fs::read(&second_manifest).unwrap()
    );

    let verify = Command::new(binary())
        .args(["verify-normalized", "--manifest"])
        .arg(&first_manifest)
        .arg("--archive-dir")
        .arg(&first)
        .output()
        .expect("verify normalized archive");
    assert!(
        verify.status.success(),
        "{}",
        String::from_utf8_lossy(&verify.stderr)
    );

    let inspect = Command::new(binary())
        .args(["inspect", "--summary"])
        .arg(first.join(file_name))
        .output()
        .expect("inspect normalized archive");
    assert!(inspect.status.success());
    let summary: serde_json::Value = serde_json::from_slice(&inspect.stdout).unwrap();
    assert_eq!(summary["total"], 0);
}

#[test]
fn machine_profile_resolves_wifi_archives() {
    let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../ws63-RF");
    let output = Command::new(binary())
        .arg("archive-paths")
        .arg("wifi")
        .arg(root)
        .output()
        .expect("resolve archive profile");
    assert!(output.status.success());
    let paths = String::from_utf8(output.stdout).unwrap();
    assert_eq!(paths.lines().count(), 10);
    assert!(
        paths
            .lines()
            .next()
            .unwrap()
            .ends_with("libwifi_driver_hmac.a")
    );
}

#[test]
fn machine_profile_adds_mbedtls_crypto_archives_only_for_wpa3_personal() {
    let directory = tempfile::tempdir().unwrap();
    let sdk = directory.path().join("sdk");
    for relative in [
        "driver/security_unified/libdrv_security_unified.a",
        "hal/security_unified/libhal_security_unified.a",
        "libmbedtls_v3.6.0.a",
        "driver/security_unified/mbedtls_harden_adapt/libmbedtls_harden.a",
        "liteos/libs/libc.a",
        "liteos/libs/libm.a",
    ] {
        let path = sdk.join(relative);
        std::fs::create_dir_all(path.parent().unwrap()).unwrap();
        std::fs::write(path, []).unwrap();
    }
    let supplicant = directory.path().join("libwpa_supplicant.a");
    std::fs::write(&supplicant, []).unwrap();

    let resolve = |profile: &str| {
        let output = Command::new(binary())
            .args(["archive-paths", "wpa"])
            .arg(&sdk)
            .arg(&supplicant)
            .arg(profile)
            .output()
            .expect("resolve WPA archive profile");
        assert!(
            output.status.success(),
            "{}",
            String::from_utf8_lossy(&output.stderr)
        );
        String::from_utf8(output.stdout).unwrap()
    };

    let wpa2 = resolve("wpa2-personal");
    assert_eq!(wpa2.lines().count(), 5);
    assert!(!wpa2.contains("libmbedtls_v3.6.0.a"));
    assert!(!wpa2.contains("libmbedtls_harden.a"));

    let wpa3 = resolve("wpa3-personal");
    assert_eq!(wpa3.lines().count(), 7);
    assert!(wpa3.contains("libmbedtls_v3.6.0.a"));
    assert!(wpa3.contains("libmbedtls_harden.a"));
}