extern crate evcxr;
use evcxr::{Error, EvalContext, EvalContextOutputs};
use std::collections::HashMap;
use std::io;
use std::sync::mpsc;
macro_rules! eval {
($ctxt:expr, $($t:tt)*) => {$ctxt.eval(stringify!($($t)*)).unwrap().content_by_mime_type}
}
fn new_context_and_outputs() -> (EvalContext, EvalContextOutputs) {
if false {
evcxr::runtime_hook();
let mut command = std::process::Command::new(&std::env::current_exe().unwrap());
command
.arg("--test-threads")
.arg("1")
.arg("--nocapture")
.arg("--quiet");
EvalContext::with_subprocess_command(command).unwrap()
} else {
let testing_runtime_path = std::env::current_exe()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.join("testing_runtime");
EvalContext::with_subprocess_command(std::process::Command::new(&testing_runtime_path))
.unwrap()
}
}
fn send_output<T: io::Write + Send + 'static>(channel: mpsc::Receiver<String>, mut output: T) {
std::thread::spawn(move || {
while let Ok(line) = channel.recv() {
if writeln!(output, "{}", line).is_err() {
break;
}
}
});
}
fn new_context() -> EvalContext {
let (context, outputs) = new_context_and_outputs();
send_output(outputs.stderr, io::stderr());
context
}
#[test]
fn single_statement() {
let mut e = new_context();
eval!(e, assert_eq!(40i32 + 2, 42));
}
#[test]
fn save_and_restore_variables() {
let mut e = new_context();
eval!(e, let mut a = 34; let b = 8;);
eval!(e, a = a + b;);
eval!(e, assert_eq!(a, 42););
assert_eq!(eval!(e, a), text_plain("42"));
match e.eval("b = 2;") {
Err(Error::CompilationErrors(errors)) => {
if errors.len() != 1 {
println!("{:#?}", errors);
}
assert_eq!(errors.len(), 1);
assert_eq!(errors[0].code(), Some("E0384"));
}
_ => unreachable!(),
}
}
#[test]
fn printing() {
let (mut e, outputs) = new_context_and_outputs();
eval!(e,
println!("This is stdout");
eprintln!("This is stderr");
println!("Another stdout line");
eprintln!("Another stderr line");
);
assert_eq!(outputs.stdout.recv(), Ok("This is stdout".to_owned()));
assert_eq!(outputs.stderr.recv(), Ok("This is stderr".to_owned()));
assert_eq!(outputs.stdout.recv(), Ok("Another stdout line".to_owned()));
assert_eq!(outputs.stderr.recv(), Ok("Another stderr line".to_owned()));
}
#[test]
fn rc_refcell_etc() {
let mut e = new_context();
eval!(e,
use std::cell::RefCell; use std::rc::Rc;
let r: Rc<RefCell<String>> = Rc::new(RefCell::new(String::new()));
);
eval!(e, let r2: Rc<RefCell<String>> = Rc::clone(&r););
eval!(e, r.borrow_mut().push_str("f"););
eval!(e, let s = "oo";);
eval!(e, r.borrow_mut().push_str(s););
eval!(e, assert!(*r.borrow() == "foo"););
}
#[test]
fn define_then_call_function() {
let mut e = new_context();
eval!(
e,
pub fn bar() -> i32 {
42
}
);
eval!(
e,
pub fn foo() -> i32 {
bar()
}
);
eval!(e, assert_eq!(foo(), 42););
let mut defined_names = e.defined_item_names().collect::<Vec<_>>();
defined_names.sort();
assert_eq!(defined_names, vec!["bar", "foo"]);
}
#[test]
fn function_panics() {
let (mut e, _) = new_context_and_outputs();
eval!(e, let a = vec![1, 2, 3];);
eval!(e, let b = 42;);
eval!(e, panic!("Intentional panic {}", b););
assert_eq!(
eval!(e, format!("{:?}, {}", a, b)),
text_plain("\"[1, 2, 3], 42\"")
);
}
#[test]
fn tls_implementing_drop() {
let mut e = new_context();
eval!(e, pub struct Foo {}
impl Drop for Foo {
fn drop(&mut self) {
println!("Dropping Foo");
}
}
pub fn init_foo() {
thread_local! {
pub static FOO: Foo = Foo {};
}
FOO.with(|f| ())
});
eval!(e, init_foo(););
}
fn text_plain(content: &str) -> HashMap<String, String> {
let mut map = HashMap::new();
map.insert("text/plain".to_owned(), content.to_owned());
map
}
#[test]
fn moved_value() {
let mut e = new_context();
eval!(e, let a = Some("foo".to_owned()););
assert_eq!(
e.variables_and_types().collect::<Vec<_>>(),
vec![("a", "std::option::Option<std::string::String>")]
);
assert_eq!(eval!(e, a.unwrap()), text_plain("\"foo\""));
assert_eq!(e.variables_and_types().collect::<Vec<_>>(), vec![]);
}
#[test]
fn struct_type_inference() {
let mut e = new_context();
eval!(
e,
#[derive(Debug)]
pub struct Point {
pub x: i32,
pub y: i32,
}
);
eval!(e, let p1 = Point {x: 3, y: 8};);
eval!(e, p1);
eval!(e, let Point {x, y: y2} = p1;);
eval!(e, assert_eq!(x, 3););
eval!(e, assert_eq!(y2, 8););
let mut defined_names = e.defined_item_names().collect::<Vec<_>>();
defined_names.sort();
assert_eq!(defined_names, vec!["Point"]);
}
#[test]
fn non_concrete_types() {
let mut e = new_context();
eval!(e, let a = (42, 3.14););
assert_eq!(
e.variables_and_types().collect::<Vec<_>>(),
vec![("a", "(i32, f32)")]
);
}
#[test]
fn statement_and_expression() {
let mut e = new_context();
assert_eq!(
eval!(e, let a = "foo".to_owned() + "bar"; a),
text_plain("\"foobar\"")
);
}
#[test]
fn continue_execution_after_bad_use_statement() {
let mut e = new_context();
match e.eval("use foobar;") {
Err(Error::CompilationErrors(errors)) => {
assert_eq!(errors.len(), 1);
assert_eq!(errors[0].code(), Some("E0432"));
}
x => panic!("Unexpected result: {:?}", x),
}
assert_eq!(eval!(e, "f".to_string() + "oo"), text_plain("\"foo\""));
}
#[test]
fn error_from_macro_expansion() {
let mut e = new_context();
match e.eval("let mut s = String::new(); s.push_str(format!(\"\"));") {
Err(Error::CompilationErrors(errors)) => {
assert_eq!(errors.len(), 1);
assert_eq!(errors[0].code(), Some("E0308")); let mut lines = std::collections::HashSet::new();
for spanned_message in errors[0].spanned_messages() {
for line in &spanned_message.lines {
lines.insert(line.as_str());
}
}
assert_eq!(
lines.into_iter().collect::<Vec<_>>(),
vec!["s.push_str(format!(\"\"));"]
);
}
x => panic!("Unexpected result: {:?}", x),
}
}
#[test]
fn multiple_identical_use_statements() {
let mut e = new_context();
eval!(e, use std::collections::HashMap;);
eval!(e, use std::collections::HashMap;);
}
#[test]
fn redefine_type_reference_old_var() {
let mut e = new_context();
eval!(e,
pub struct Foo {}
impl Foo {
pub fn get_value(&self) -> i32 { 42 }
}
let f = Foo {};
);
eval!(e, let f2 = Foo {};);
eval!(e,
pub struct Foo { pub value: i32 }
impl Foo {
pub fn get_value(&self) -> i32 { self.value }
}
);
eval!(
e,
assert_eq!(f.get_value(), 42);
assert_eq!(f2.get_value(), 42);
);
}
#[test]
fn abort_and_restart() {
let mut e = new_context();
eval!(
e,
pub fn foo() -> i32 {
42
}
let a = 42i32;
);
let result = e.eval(stringify!(std::process::abort();));
if let Err(Error::ChildProcessTerminated(message)) = result {
assert_eq!(message, "Child process terminated with status: signal: 6");
} else {
panic!("Unexpected result: {:?}", result);
}
eval!(e, assert_eq!(foo(), 42));
e.clear().unwrap();
eval!(e, assert_eq!(40 + 2, 42););
}