灵符lingfu

ling-fu

↓ 0 downloads

灵符 — The Ling project manager. lingfu new/build/run/normalize and more.

lingfu install ling-fu
LicenseApache-2.0 OR MIT
AuthorsLing Lin <taellinglin@gmail.com>, Sanny Lin <SannyLing53@gmail.com>
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / Files (28)

tests/normalize_cli.rs
//! lingfu normalizer end-to-end tests.
//!
//! Drives the real `lingfu` binary: writes an English Ling program into a temp
//! directory, runs `lingfu normalize <lang>`, and asserts the translated
//! keywords and builtins appear. This guards the multilingual guarantee — a
//! program normalized to any supported language must use that language's tokens.

use std::fs;
use std::path::PathBuf;
use std::process::Command;

const LINGFU: &str = env!("CARGO_BIN_EXE_lingfu");

const PROGRAM: &str = "bind start = do {\n  print(sin(0.0))\n  print(sqrt(16.0))\n  print(max(3.0, 7.0))\n  fill(0, 0, 0)\n  present()\n}\n";

/// Normalize `PROGRAM` to `lang` in an isolated temp dir and return the result.
fn normalize_to(lang: &str) -> String {
    let dir = std::env::temp_dir().join(format!("lingfu_norm_{lang}_{}", std::process::id()));
    let _ = fs::remove_dir_all(&dir);
    fs::create_dir_all(&dir).expect("create temp dir");
    let file = dir.join("prog.ling");
    fs::write(&file, PROGRAM).expect("write program");

    let status = Command::new(LINGFU)
        .arg("normalize")
        .arg(lang)
        .arg("--content-only")
        .current_dir(&dir)
        .status()
        .expect("run lingfu");
    assert!(
        status.success(),
        "lingfu normalize {lang} exited with failure"
    );

    let out = fs::read_to_string(&file).expect("read normalized program");
    let _ = fs::remove_dir_all(&dir);
    out
}

#[test]
fn normalize_to_chinese() {
    let out = normalize_to("zh");
    assert!(out.contains('令'), "bind → 令\n{out}");
    assert!(out.contains("正弦"), "sin → 正弦\n{out}");
    assert!(out.contains("平方根"), "sqrt → 平方根\n{out}");
    assert!(out.contains("最大"), "max → 最大\n{out}");
}

#[test]
fn normalize_to_japanese() {
    let out = normalize_to("ja");
    assert!(out.contains("束縛"), "bind → 束縛\n{out}");
    assert!(out.contains("サイン"), "sin → サイン\n{out}");
    assert!(out.contains("平方根"), "sqrt → 平方根\n{out}");
}

#[test]
fn normalize_to_korean() {
    let out = normalize_to("ko");
    assert!(out.contains("바인드"), "bind → 바인드\n{out}");
    assert!(out.contains("사인"), "sin → 사인\n{out}");
    assert!(out.contains("제곱근"), "sqrt → 제곱근\n{out}");
}

#[test]
fn normalize_to_thai() {
    let out = normalize_to("th");
    assert!(out.contains("ผูก"), "bind → ผูก\n{out}");
    assert!(out.contains("ไซน์"), "sin → ไซน์\n{out}");
    assert!(out.contains("รากที่สอง"), "sqrt → รากที่สอง\n{out}");
}

#[test]
fn normalize_input_builtins() {
    // Gamepad/joystick builtins must normalize to each language's *runtime*
    // aliases (the forms src/runtime/mod.rs actually dispatches on).
    let prog = "bind start = do {\n  pad_poll()\n  print(pad_button(0, \"a\"))\n  print(pad_lx(0))\n  pad_rumble(0, 1.0, 1.0)\n}\n";
    let cases: [(&str, [&str; 4]); 4] = [
        ("zh", ["手柄轮询", "手柄按键", "手柄左X", "手柄震动"]),
        (
            "ja",
            ["パッド更新", "パッドボタン", "パッド左X", "パッド振動"],
        ),
        ("ko", ["패드폴링", "패드버튼", "패드왼X", "패드진동"]),
        ("th", ["อัปเดตแพด", "ปุ่มแพด", "แพดซ้ายX", "แพดสั่น"]),
    ];
    for (lang, expect) in cases {
        let dir = std::env::temp_dir().join(format!("lingfu_pad_{lang}_{}", std::process::id()));
        let _ = fs::remove_dir_all(&dir);
        fs::create_dir_all(&dir).unwrap();
        let file = dir.join("prog.ling");
        fs::write(&file, prog).unwrap();
        let status = Command::new(LINGFU)
            .arg("normalize")
            .arg(lang)
            .arg("--content-only")
            .current_dir(&dir)
            .status()
            .expect("run lingfu");
        assert!(status.success(), "normalize {lang} failed");
        let out = fs::read_to_string(&file).unwrap();
        let _ = fs::remove_dir_all(&dir);
        for token in expect {
            assert!(out.contains(token), "{lang}: expected `{token}`\n{out}");
        }
    }
}

#[test]
fn normalize_to_english_uses_bind_not_let() {
    // Round-trip a Chinese program back to English; the canonical bind keyword
    // must be `bind` (never `let`, which the runtime does not accept).
    let dir = std::env::temp_dir().join(format!("lingfu_en_{}", std::process::id()));
    let _ = fs::remove_dir_all(&dir);
    fs::create_dir_all(&dir).unwrap();
    let file: PathBuf = dir.join("prog.ling");
    fs::write(&file, "令 start = 执 { 印(正弦(0.0)) }\n").unwrap();
    let status = Command::new(LINGFU)
        .arg("normalize")
        .arg("en")
        .arg("--content-only")
        .current_dir(&dir)
        .status()
        .expect("run lingfu");
    assert!(status.success());
    let out = fs::read_to_string(&file).unwrap();
    let _ = fs::remove_dir_all(&dir);
    assert!(out.contains("bind"), "令 → bind\n{out}");
    assert!(!out.contains("let "), "must not emit `let`\n{out}");
}

← Back to index