hypothalamus 0.5.0

An optimizing Brainfuck AOT compiler with an LLVM IR backend
Documentation
use assert_cmd::Command;
use predicates::prelude::*;
use predicates::str::contains;
use std::fs;

#[test]
fn help_is_generic_about_targets() {
    Command::cargo_bin("hypothalamus")
        .expect("binary should build")
        .arg("--help")
        .assert()
        .success()
        .stdout(contains("Hypothalamus - Brainfuck AOT compiler"))
        .stdout(contains("--target <TARGET>"))
        .stdout(contains("--list-targets"))
        .stdout(contains("--gba-gcc").not());
}

#[test]
fn version_prints_package_version() {
    Command::cargo_bin("hypothalamus")
        .expect("binary should build")
        .arg("--version")
        .assert()
        .success()
        .stdout(contains(format!(
            "hypothalamus {}",
            env!("CARGO_PKG_VERSION")
        )));
}

#[test]
fn list_targets_includes_current_presets() {
    Command::cargo_bin("hypothalamus")
        .expect("binary should build")
        .arg("--list-targets")
        .assert()
        .success()
        .stdout(contains("native"))
        .stdout(contains("x86_64-none"))
        .stdout(contains("i386-none"))
        .stdout(contains("nds-arm9"))
        .stdout(contains("gba"));
}

#[test]
fn emits_llvm_ir_to_stdout() {
    Command::cargo_bin("hypothalamus")
        .expect("binary should build")
        .args(["--emit", "llvm-ir", "-o", "-", "examples/hello.bf"])
        .assert()
        .success()
        .stdout(contains("; Generated by hypothalamus."))
        .stdout(contains("define i32 @main()"))
        .stdout(contains("@putchar"));
}

#[test]
fn writes_llvm_ir_to_output_file() {
    let temp_dir = tempfile::tempdir().expect("create temp dir");
    let output = temp_dir.path().join("hello.ll");

    Command::cargo_bin("hypothalamus")
        .expect("binary should build")
        .args(["--emit", "llvm-ir", "examples/hello.bf", "-o"])
        .arg(&output)
        .assert()
        .success();

    let ir = fs::read_to_string(output).expect("read generated LLVM IR");
    assert!(ir.contains("; Generated by hypothalamus."));
    assert!(ir.contains("define i32 @main()"));
}

#[test]
fn rejects_stdout_output_for_non_llvm_ir_modes() {
    for args in [
        ["--emit", "obj", "-o", "-", "examples/hello.bf"],
        ["--emit", "asm", "-o", "-", "examples/hello.bf"],
        ["--target", "gba", "-o", "-", "examples/hello.bf"],
    ] {
        Command::cargo_bin("hypothalamus")
            .expect("binary should build")
            .args(args)
            .assert()
            .code(1)
            .stderr(contains("--emit llvm-ir"));
    }
}

#[test]
fn emits_custom_freestanding_symbols_to_stdout() {
    Command::cargo_bin("hypothalamus")
        .expect("binary should build")
        .args([
            "--freestanding",
            "--entry",
            "kernel_bf_main",
            "--putchar-symbol",
            "serial_write_byte",
            "--getchar-symbol",
            "serial_read_byte",
            "--emit",
            "llvm-ir",
            "-o",
            "-",
            "examples/hello.bf",
        ])
        .assert()
        .success()
        .stdout(contains("define void @kernel_bf_main()"))
        .stdout(contains("declare void @serial_write_byte(i8)"))
        .stdout(contains("declare i32 @serial_read_byte()"))
        .stdout(contains("define i32 @main()").not());
}

#[test]
fn nds_arm9_target_emits_freestanding_llvm_ir() {
    Command::cargo_bin("hypothalamus")
        .expect("binary should build")
        .args([
            "--target",
            "nds-arm9",
            "--emit",
            "llvm-ir",
            "-o",
            "-",
            "examples/hello.bf",
        ])
        .assert()
        .success()
        .stdout(contains("target triple = \"armv5te-none-eabi\""))
        .stdout(contains("define void @bf_main()"))
        .stdout(contains("declare void @bf_putchar(i8)"))
        .stdout(contains("define i32 @main()").not());
}