extern crate inc;
use inc::*;
use std::{fs, process::Command};
use uuid::Uuid;
#[cfg(test)]
extern crate quickcheck;
#[cfg(test)]
#[macro_use(quickcheck)]
extern crate quickcheck_macros;
mod integers {
use super::*;
#[test]
fn unit() {
for t in &[0, 1, -1, 10, -10, 2736, -2736, 536870911, -536870912] {
test1(&t.to_string(), &t.to_string());
}
}
#[quickcheck]
fn quick(i: i64) -> () {
test1(&i.to_string(), &i.to_string())
}
}
mod immediate {
use super::*;
#[test]
fn unit() {
let tests = [
r"#f",
r"#t",
"()",
r"#\tab",
r"#\newline",
r"#\return",
r"#\space",
r"#\!",
r"#\#",
r"#\$",
r"#\%",
r"#\&",
r"#\'",
r"#\(",
r"#\)",
r"#\*",
r"#\+",
r"#\,",
r"#\-",
r"#\.",
r"#\/",
r"#\0",
r"#\9",
r"#\:",
r"#\;",
r"#\<",
r"#\=",
r"#\>",
r"#\?",
r"#\@",
r"#\A",
r"#\B",
r"#\Z",
r"#\(",
r"#\\",
r"#\]",
r"#\^",
r"#\_",
r"#\`",
r"#\a",
r"#\b",
r"#\z",
r"#\{",
r"#\|",
r"#\}",
r"#\~",
];
for t in tests.iter() {
test1(t, t);
}
}
}
mod unary {
use super::*;
#[test]
fn inc() {
let tests = vec![
("(inc 0)", "1"),
("(inc -1)", "0"),
("(inc 41)", "42"),
("(inc -100) ", "-99"),
("(inc (inc (inc (inc 1))))", "5"),
];
for (inp, out) in tests.iter() {
test1(inp, out);
}
}
#[test]
fn types() {
let tests = [
("(fixnum? 0)", "#t"),
("(fixnum? 1)", "#t"),
("(fixnum? -1)", "#t"),
("(fixnum? 536870911)", "#t"),
("(fixnum? -536870912)", "#t"),
("(fixnum? #t)", "#f"),
("(fixnum? #f)", "#f"),
("(fixnum? ())", "#f"),
("(fixnum? #\\Q)", "#f"),
("(char? #\\Q)", "#t"),
("(char? 0)", "#f"),
("(char? 1)", "#f"),
("(char? -1)", "#f"),
("(char? 536870911)", "#f"),
("(char? -536870912)", "#f"),
("(char? #t)", "#f"),
("(char? #f)", "#f"),
("(char? ())", "#f"),
("(not 1)", "#f"),
("(not #t)", "#f"),
("(not #f)", "#t"),
("(not ())", "#f"),
];
for (inp, out) in tests.iter() {
test1(inp, out);
}
}
#[test]
fn zero() {
let tests = vec![
(r"(zero? 0)", r"#t"),
(r"(zero? 1)", r"#f"),
(r"(zero? #t)", r"#f"),
(r"(zero? ())", r"#f"),
];
for (inp, out) in tests.iter() {
test1(inp, out);
}
}
#[test]
fn null() {
let tests = vec![
(r"(null? ())", r"#t"),
(r"(null? #\Q)", r"#f"),
(r"(null? #f)", r"#f"),
(r"(null? #t)", r"#f"),
(r"(null? -23873)", r"#f"),
(r"(null? 37287)", r"#f"),
(r"(null? #\a)", r"#f"),
];
for (inp, out) in tests.iter() {
test1(inp, out);
}
}
}
mod binary {
mod unit {
use super::super::*;
#[test]
fn plus() {
let tests = [("(+ 10 20)", "30"), ("(+ 40 (inc (inc 0)))", "42")];
for (inp, out) in tests.iter() {
test1(inp, out);
}
}
}
mod quick {
use super::super::*;
#[quickcheck]
fn plus(x: i64, y: i64) -> () {
test1(&format!("(+ {} {})", x, y), &(x + y).to_string())
}
#[quickcheck]
fn multiply(x: i64, y: i64) -> () {
test1(&format!("(* {} {})", x, y), &(x * y).to_string())
}
#[quickcheck]
fn divide(x: i64, y: i64) -> () {
if y != 0 {
test1(&format!("(/ {} {})", (x * y), y), &x.to_string())
}
}
#[quickcheck]
fn remainder(x: i64, y: i64) -> () {
if y != 0 {
test1(&format!("(% {} {})", x, y), &((x % y).to_string()))
}
}
}
}
mod bindings {
mod unit {
use super::super::*;
#[test]
fn simple() {
let tests = [
("(let ((x 5)) x)", "5"),
("(let ((x 5) (y 4)) (+ x y))", "9"),
("(let ((x (+ 1 2))) x)", "3"),
];
for (inp, out) in tests.iter() {
test1(inp, out);
}
}
#[test]
fn nested_scopes() {
let tests =
[("(let ((x (+ 1 2))) (let ((y (+ 3 4))) (+ x y)))", "10")];
for (inp, out) in tests.iter() {
test1(inp, out);
}
}
#[test]
fn shadow() {
let tests = [("(let ((x 1)) (let ((x 2)) #t) x)", "1")];
for (inp, out) in tests.iter() {
test1(inp, out);
}
}
}
}
fn config(program: String) -> Config {
let output = format!("/tmp/inc-{:?}", Uuid::new_v4());
Config { program, output }
}
fn build(mut config: &mut Config) -> bool {
inc::compile(&mut config).unwrap();
Command::new("clang")
.arg("-m64")
.arg("-g3")
.arg("-ggdb3")
.arg("-fomit-frame-pointer")
.arg("-fno-asynchronous-unwind-tables")
.arg("-O0")
.arg("runtime.c")
.arg(&config.asm())
.arg("-o")
.arg(&config.output)
.status()
.expect("Failed to compile binary")
.success()
}
fn test1(input: &str, output: &str) {
let mut config = config(input.to_string());
assert!(build(&mut config));
let proc = Command::new(&config.output)
.output()
.expect(&format!("Failed to run binary `{}`", &config.output));
assert!(
&proc.status.success(),
format!("Failed to run binary `{}`", &config.output)
);
assert_eq!(String::from_utf8(proc.stdout).unwrap().trim(), output);
fs::remove_file(&config.asm())
.expect("Failed to clear generated asm files");
fs::remove_file(&config.output).expect("Failed to clear executable");
fs::remove_dir_all(format!("{}.dSYM", &config.output)).unwrap_or_default()
}