#![cfg(unix)]
extern crate rexpect;
use std::process::Command;
use rexpect::session::{Options, PtySession, spawn_with_options};
use anyhow::{Context as _, Result};
struct REPL {
session: PtySession,
prompt: &'static str,
}
impl REPL {
fn new() -> REPL {
let repl = REPL::new_(false).unwrap_or_else(|err| panic!("{}", err));
repl
}
fn new_completion() -> REPL {
let repl = REPL::new_(true).unwrap_or_else(|err| panic!("{}", err));
repl
}
fn new_(auto_complete: bool) -> Result<REPL> {
let _ = env_logger::try_init();
let timeout: u64 = 5_000;
let prompt: &'static str = "REXPECT> ";
let mut command = Command::new("../target/debug/gluon");
command
.args(&["-i", "--color", "never", "--prompt", prompt])
.env("GLUON_PATH", "..");
if !auto_complete {
command.arg("--no-auto-complete");
}
let mut session = spawn_with_options(
command,
Options::new()
.timeout_ms(Some(timeout))
.strip_ansi_escape_codes(true),
)?;
session.exp_string(prompt)?;
Ok(REPL { session, prompt })
}
#[track_caller]
fn test(&mut self, send: &str, expect: Option<&str>) {
self.test_(send, expect)
.unwrap_or_else(|err| panic!("{:#}", err));
}
#[track_caller]
fn test_(&mut self, send: &str, expect: Option<&str>) -> Result<()> {
self.session.send_line(send)?;
self.session.exp_string(send).context("Send string")?;
if let Some(string) = expect {
self.session.exp_string(string).context("Expect string")?;
}
self.session
.exp_string(self.prompt)
.context("Expect prompt")?;
Ok(())
}
#[track_caller]
fn test_completion(&mut self, prefix: &str, expect: &str) {
self.session.send(prefix).unwrap();
self.session.send("\t").unwrap();
self.session.flush().unwrap();
self.session.exp_string(expect).unwrap();
}
fn quit(&mut self) {
self.quit_().unwrap_or_else(|err| panic!("{:#}", err));
}
fn quit_(&mut self) -> Result<()> {
let line: &'static str = ":q";
self.session.send_line(line)?;
self.session.exp_string(line)?;
self.session.exp_eof()?;
Ok(())
}
}
#[test]
fn prompt() {
let _repl = REPL::new();
}
#[test]
#[ignore]
fn quit() {
let mut repl = REPL::new();
repl.quit();
}
#[test]
fn hello_world() {
let mut repl = REPL::new();
repl.test("let io = import! std.io", None);
repl.test("io.println \"Hello world\"", Some("Hello world"));
}
#[test]
fn expression_types() {
let mut repl = REPL::new();
repl.test(":t 5", Some("Int"));
repl.test(":t 5 + 5", Some("Int"));
repl.test(":t \"gluon\"", Some("String"));
}
#[test]
fn names() {
let mut repl = REPL::new();
repl.test(
":i std.prelude.show",
Some("std.prelude.show: forall a . [std.show.Show a] -> a -> String"),
);
}
#[test]
fn comments() {
let mut repl = REPL::new();
repl.test("1 + 2 // Calls the + function on 1 and 2", Some("3"));
repl.test("1 + 2 /* Calls the + function on 1 and 2 */", Some("3"));
}
#[test]
fn if_expressions() {
let mut repl = REPL::new();
repl.test("if True then 1 else 0", Some("1"));
repl.test("if False then 1 else 0", Some("0"));
}
#[test]
fn records() {
let mut repl = REPL::new();
repl.test("let record = { pi = 3.14, add1 = (+) 1.0 }", None);
repl.test("record.pi", Some("3.14"));
repl.test("let record_2 = {x = 1 .. record }", None);
repl.test("record_2.x", Some("1"));
repl.test("record_2.pi", Some("3.14"));
}
#[test]
fn arrays() {
let mut repl = REPL::new();
repl.test("let array = import! std.array", None);
repl.test("array.len [1, 2, 3]", Some("3"));
}
#[test]
fn comment() {
let mut repl = REPL::new();
repl.test("// test", None);
}
#[test]
fn error_reports_correct_line() {
let mut repl = REPL::new();
repl.test("let { x } = {}", Some("let { x } = {}"));
}
#[test]
fn import() {
let mut repl = REPL::new();
repl.test("let { assert } = import! std.test", None);
}
#[test]
fn assert() {
let mut repl = REPL::new();
repl.test("let { assert } = import! std.test", None);
repl.test("assert False", None);
}
#[test]
fn completion() {
let mut repl = REPL::new_completion();
repl.test("let abc = 1", None);
repl.test_completion("a", "abc");
repl.session.send_line("let abc2 = 1").unwrap();
repl.session.send_line("let acc = 1").unwrap();
repl.test_completion("ab", "abc");
repl.test_completion("", "abc2");
}