extern crate inc;
use inc::{cli, core::*};
use rand::random;
use std::{fs, panic};
#[cfg(test)]
extern crate quickcheck;
#[cfg(test)]
#[macro_use(quickcheck)]
extern crate quickcheck_macros;
const TEST_FOLDER: &str = "/tmp/inc";
mod integers {
use super::*;
#[test]
fn unit() {
for t in &[0, 1, -1, 10, -10, 2736, -2736, 536_870_911, -536_870_912] {
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);
}
}
}
}
mod cond {
use super::*;
#[test]
fn simple() {
let tests = [
("(if #t 12 13)", "12"),
("(if #f 12 13)", "13"),
("(if 0 12 13)", "12"),
("(if () 43 ())", "43"),
("(if #t (if 12 13 4) 17)", "13"),
("(if #f 12 (if #f 13 4))", "4"),
("(if #\\X (if 1 2 3) (if 4 5 6))", "2"),
("(if (not (boolean? #t)) 15 (boolean? #f))", "#t"),
("(if (if (char? #\\a) (boolean? #\\b) (fixnum? #\\c)) 119 -23)", "-23"),
("(if (if (if (not 1) (not 2) (not 3)) 4 5) 6 7)", "6"),
("(if (not (if (if (not 1) (not 2) (not 3)) 4 5)) 6 7)", "7"),
("(not (if (not (if (if (not 1) (not 2) (not 3)) 4 5)) 6 7))", "#f"),
("(if (char? 12) 13 14)", "14"),
("(if (char? #\\a) 13 14)", "13"),
("(inc (if (dec 1) (dec 13) 14))", "13"),
("(if (= 12 13) 12 13)", "13"),
("(if (= 12 12) 13 14)", "13"),
("(if (< 12 13) 12 13)", "12"),
("(if (< 12 12) 13 14)", "14"),
("(if (< 13 12) 13 14)", "14"),
("(if (<= 12 13) 12 13)", "12"),
("(if (<= 12 12) 12 13)", "12"),
("(if (<= 13 12) 13 14)", "14"),
("(if (> 12 13) 12 13)", "13"),
("(if (> 12 12) 12 13)", "13"),
("(if (> 13 12) 13 14)", "13"),
("(if (>= 12 13) 12 13)", "13"),
("(if (>= 12 12) 12 13)", "12"),
("(if (>= 13 12) 13 14)", "13"),
];
test_many(&tests)
}
}
mod heap {
use super::*;
#[test]
fn pairs() {
let tests = [
("(pair? (cons 1 2))", "#t"),
("(pair? (cons 1 2))", "#t"),
("(pair? 12)", "#f"),
("(pair? #t)", "#f"),
("(pair? #f)", "#f"),
("(pair? ())", "#f"),
("(fixnum? (cons 12 43))", "#f"),
("(boolean? (cons 12 43))", "#f"),
("(null? (cons 12 43))", "#f"),
("(not (cons 12 43))", "#f"),
("(if (cons 12 43) 32 43)", "32"),
("(car (cons 1 23))", "1"),
("(cdr (cons 43 123))", "123"),
("(car (car (cons (cons 12 3) (cons #t #f))))", "12"),
("(cdr (car (cons (cons 12 3) (cons #t #f))))", "3"),
("(car (cdr (cons (cons 12 3) (cons #t #f))))", "#t"),
("(cdr (cdr (cons (cons 12 3) (cons #t #f))))", "#f"),
(
"(let
((x (let
((y (+ 1 2)))
(* y y))))
(cons x (+ x x)))",
"(9 . 18)",
),
("(let ((t (cons 1 2))) (let ((t t)) (let ((t t)) (let ((t t)) t))))", "(1 . 2)"),
("(let ((t (let ((t (let ((t (let ((t (cons 1 2))) t))) t))) t))) t)", "(1 . 2)"),
(
"(let ((x ()))
(let ((x (cons x x)))
(let ((x (cons x x)))
(let ((x (cons x x)))
(cons x x)))))",
"((((()) ()) (()) ()) ((()) ()) (()) ())",
),
];
test_many(&tests)
}
mod strings {
use super::*;
#[test]
fn simple() {
let tests = [
("\"Hello world !!\"", "\"Hello world !!\""),
("(boolean? \"hello\")", "#f"),
("(null? \"hello\")", "#f"),
("(pair? \"hello\")", "#f"),
("(string? \"hello\")", "#t"),
("(string? #f)", "#f"),
("(string? #t)", "#f"),
("(string? ())", "#f"),
("(string? (cons 1 2))", "#f"),
("(string? 1287)", "#f"),
("(make-string 0)", "\"\""),
("(null? (make-string 4))", "#f"),
("(string? (make-string 0))", "#t"),
("(string? (make-string 4))", "#t"),
];
test_many(&tests)
}
#[test]
fn args() {
test1("(if (zero? 1) \"yes\" \"nope\")", "\"nope\"")
}
#[test]
fn runtime() {
test1("(string-length \"Hello world !!\")", "14");
test1("(string-length \"\")", "0");
test1("(string-length \"🐈\")", "4")
}
}
mod symbols {
use super::*;
#[test]
fn simple() {
let tests = [
("'one", "'one"),
("(boolean? 'yo)", "#f"),
("(null? 'duh)", "#f"),
("(string? 'blah)", "#f"),
("(symbol? 'blah)", "#t"),
];
test_many(&tests)
}
#[test]
fn args() {
test1("(if (zero? 1) 'yes 'nope)", "'nope")
}
#[test]
fn runtime() {
test1("(symbol=? 'one 'two)", "#f");
test1("(symbol=? 'woo 'woo)", "#t")
}
}
mod vector {
use super::*;
#[test]
fn simple() {
test1("(vector 1 5 'one 'two \"DAMN\")", "[1 5 'one 'two \"DAMN\"]");
}
}
}
mod functions {
use super::*;
#[test]
fn no_arg() {
test1("(let ((f (lambda () 5))) 7)", "7");
test1("(let ((f (lambda () 5))) (let ((x 12)) x))", "12")
}
#[test]
fn unary() {
test1("(let ((f (lambda (x) (+ x 12)))) (f 13))", "25");
test1("(let ((f (lambda () 5))) (let ((x (f))) x))", "5");
}
#[test]
fn use_results() {
test1("(let ((f (lambda () 5))) (+ (f) 6))", "11");
test1("(let ((f (lambda () 5))) (- 20 (f)))", "15");
test1("(let ((f (lambda () 5))) (+ (f) (f)))", "10")
}
#[test]
fn two() {
test1(
"(let ((f (lambda () (+ 5 7)))
(g (lambda () 13)))
(+ (f) (g)))",
"25",
)
}
#[test]
fn repeat() {
test1("(let ((f (lambda (x) (+ x 12)))) (f 13))", "25");
test1("(let ((f (lambda (x) (+ x 12)))) (f (f 10)))", "34");
test1("(let ((f (lambda (x) (+ x 12)))) (f (f (f 0))))", "36")
}
#[test]
fn recursive() {
test1(
"(let ((e (lambda (x) (if (zero? x) #t (o (dec x)))))
(o (lambda (x) (if (zero? x) #f (e (dec x))))))
(e 25))",
"#f",
);
test1(
"(let ((f (lambda (x)
(if (zero? x)
1
(* x (f (dec x))))))) (f 5))",
"120",
)
}
#[test]
fn nested() {
test1("(let ((f (lambda (x) (g x x))) (g (lambda (x y) (+ x y)))) (f 12))", "24");
}
#[test]
fn complex_args() {
test1(
"(let ((f (lambda (x y) (+ x y)))
(g (lambda (x) (+ x 10))))
(f (g (g 5)) (g 0)))",
"35",
);
}
#[test]
#[ignore]
fn closure() {
test1("(let ((x 42)) (let ((f (lambda () x))) (f)))", "12")
}
}
mod io {
use super::*;
use std::fs::read_to_string;
#[test]
fn fd() {
let k = r#"
(let ((open-output-file (lambda (fname)
(let ((fd (rt-open-write fname)))
(vector 'port fname fd)))))
(open-output-file "/tmp/inc/io.txt"))"#;
test1(k, r#"['port "/tmp/inc/io.txt" 4]"#);
}
#[test]
fn write() {
let k = r#"
(let ((open-output-file (lambda (fname)
(let ((fd (rt-open-write fname)))
(vector 'port fname fd))))
(f (open-output-file "/tmp/inc/io.txt")))
(writeln "hello world" f))"#;
test1(k, "()");
assert_eq!("hello world\n", read_to_string("/tmp/inc/io.txt").unwrap())
}
}
fn config(base_folder: &str, program: String) -> Config {
let output = format!("{}/inc", base_folder);
Config { program, output }
}
fn test_many(tests: &[(&str, &str)]) {
for (inp, out) in tests.iter() {
test1(inp, out);
}
}
fn test1(input: &str, output: &str) {
let base_folder = format!("{}/{:x?}", TEST_FOLDER, random::<u32>());
fs::create_dir_all(&base_folder).unwrap();
fs::write(format!("{}/test.lisp", base_folder), input).unwrap();
let config = config(&base_folder, input.to_string());
let result = cli::run(&config, cli::Action::Run);
match result {
Ok(Some(result)) => {
assert_eq!(result, output, "Failed: {} != {}", input, output);
}
Ok(None) => panic!("Test produced no output"),
Err(e) => panic!("{}", e),
}
fs::remove_dir_all(&base_folder).unwrap_or_default()
}