add stdin support for cli tests (currently unused)

This commit is contained in:
Folkert 2021-01-29 00:13:09 +01:00
parent 999f3cfce6
commit 4bd9d417d1
7 changed files with 120 additions and 20 deletions

View file

@ -23,6 +23,24 @@ mod cli_run {
flags: &[&str], flags: &[&str],
expected_ending: &str, expected_ending: &str,
use_valgrind: bool, use_valgrind: bool,
) {
check_output_with_stdin(
file,
"",
executable_filename,
flags,
expected_ending,
use_valgrind,
)
}
fn check_output_with_stdin(
file: &Path,
stdin_str: &str,
executable_filename: &str,
flags: &[&str],
expected_ending: &str,
use_valgrind: bool,
) { ) {
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags].concat()); let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags].concat());
if !compile_out.stderr.is_empty() { if !compile_out.stderr.is_empty() {
@ -31,8 +49,10 @@ mod cli_run {
assert!(compile_out.status.success()); assert!(compile_out.status.success());
let out = if use_valgrind { let out = if use_valgrind {
let (valgrind_out, raw_xml) = let (valgrind_out, raw_xml) = run_with_valgrind(
run_with_valgrind(&[file.with_file_name(executable_filename).to_str().unwrap()]); stdin_str,
&[file.with_file_name(executable_filename).to_str().unwrap()],
);
if valgrind_out.status.success() { if valgrind_out.status.success() {
let memory_errors = extract_valgrind_errors(&raw_xml).unwrap_or_else(|err| { let memory_errors = extract_valgrind_errors(&raw_xml).unwrap_or_else(|err| {
@ -55,6 +75,7 @@ mod cli_run {
} else { } else {
run_cmd( run_cmd(
file.with_file_name(executable_filename).to_str().unwrap(), file.with_file_name(executable_filename).to_str().unwrap(),
stdin_str,
&[], &[],
) )
}; };
@ -174,8 +195,9 @@ mod cli_run {
#[test] #[test]
#[serial(nqueens)] #[serial(nqueens)]
fn run_nqueens_not_optimized() { fn run_nqueens_not_optimized() {
check_output( check_output_with_stdin(
&example_file("benchmarks", "NQueens.roc"), &example_file("benchmarks", "NQueens.roc"),
"",
"nqueens", "nqueens",
&[], &[],
"4\n", "4\n",

View file

@ -61,15 +61,29 @@ pub fn run_roc(args: &[&str]) -> Out {
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn run_cmd(cmd_name: &str, args: &[&str]) -> Out { pub fn run_cmd(cmd_name: &str, stdin_str: &str, args: &[&str]) -> Out {
let mut cmd = Command::new(cmd_name); let mut cmd = Command::new(cmd_name);
for arg in args { for arg in args {
cmd.arg(arg); cmd.arg(arg);
} }
let output = cmd let mut child = cmd
.output() .stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap_or_else(|_| panic!("failed to execute cmd `{}` in CLI test", cmd_name));
{
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
stdin
.write_all(stdin_str.as_bytes())
.expect("Failed to write to stdin");
}
let output = child
.wait_with_output()
.unwrap_or_else(|_| panic!("failed to execute cmd `{}` in CLI test", cmd_name)); .unwrap_or_else(|_| panic!("failed to execute cmd `{}` in CLI test", cmd_name));
Out { Out {
@ -80,7 +94,7 @@ pub fn run_cmd(cmd_name: &str, args: &[&str]) -> Out {
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn run_with_valgrind(args: &[&str]) -> (Out, String) { pub fn run_with_valgrind(stdin_str: &str, args: &[&str]) -> (Out, String) {
//TODO: figure out if there is a better way to get the valgrind executable. //TODO: figure out if there is a better way to get the valgrind executable.
let mut cmd = Command::new("valgrind"); let mut cmd = Command::new("valgrind");
let named_tempfile = let named_tempfile =
@ -114,8 +128,23 @@ pub fn run_with_valgrind(args: &[&str]) -> (Out, String) {
cmd.arg(arg); cmd.arg(arg);
} }
let output = cmd cmd.stdin(Stdio::piped());
.output() cmd.stdout(Stdio::piped());
cmd.stderr(Stdio::piped());
let mut child = cmd
.spawn()
.expect("failed to execute compiled `valgrind` binary in CLI test");
{
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
stdin
.write_all(stdin_str.as_bytes())
.expect("Failed to write to stdin");
}
let output = child
.wait_with_output()
.expect("failed to execute compiled `valgrind` binary in CLI test"); .expect("failed to execute compiled `valgrind` binary in CLI test");
let mut file = named_tempfile.into_file(); let mut file = named_tempfile.into_file();

View file

@ -1791,14 +1791,12 @@ fn update<'a>(
Proc::insert_refcount_operations(arena, &mut state.procedures); Proc::insert_refcount_operations(arena, &mut state.procedures);
if false { Proc::optimize_refcount_operations(
Proc::optimize_refcount_operations( arena,
arena, module_id,
module_id, &mut ident_ids,
&mut ident_ids, &mut state.procedures,
&mut state.procedures, );
);
}
state.constrained_ident_ids.insert(module_id, ident_ids); state.constrained_ident_ids.insert(module_id, ident_ids);

View file

@ -5,6 +5,7 @@ app "nqueens"
main : Task.Task {} [] main : Task.Task {} []
main = main =
# Task.after Task.getInt \n ->
queens 6 queens 6
|> Str.fromInt |> Str.fromInt
|> Task.putLine |> Task.putLine

View file

@ -6,7 +6,8 @@ platform folkertdev/foo
provides [ mainForHost ] provides [ mainForHost ]
effects Effect effects Effect
{ {
putLine : Str -> Effect {} putLine : Str -> Effect {},
getInt : Effect { value: I64, errorCode: [ A, B ], isError: Bool }
} }
mainForHost : Task.Task {} [] as Fx mainForHost : Task.Task {} [] as Fx

View file

@ -1,5 +1,5 @@
interface Task interface Task
exposes [ Task, succeed, fail, after, map, putLine ] exposes [ Task, succeed, fail, after, map, putLine, getInt ]
imports [ Effect ] imports [ Effect ]
@ -15,7 +15,6 @@ fail : err -> Task * err
fail = \val -> fail = \val ->
Effect.always (Err val) Effect.always (Err val)
after : Task a err, (a -> Task b err) -> Task b err after : Task a err, (a -> Task b err) -> Task b err
after = \effect, transform -> after = \effect, transform ->
Effect.after effect \result -> Effect.after effect \result ->
@ -32,3 +31,16 @@ map = \effect, transform ->
putLine : Str -> Task {} * putLine : Str -> Task {} *
putLine = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {}) putLine = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {})
getInt : Task I64 []
getInt =
Effect.after Effect.getInt \{ isError, value, errorCode } ->
when isError is
True ->
when errorCode is
# A -> Task.fail InvalidCharacter
# B -> Task.fail IOError
_ -> Task.succeed -1
False ->
Task.succeed value

View file

@ -4,6 +4,7 @@ const RocStr = str.RocStr;
const testing = std.testing; const testing = std.testing;
const expectEqual = testing.expectEqual; const expectEqual = testing.expectEqual;
const expect = testing.expect; const expect = testing.expect;
const maxInt = std.math.maxInt;
const mem = std.mem; const mem = std.mem;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
@ -96,3 +97,39 @@ pub export fn roc_fx_putLine(rocPath: str.RocStr) i64 {
return 0; return 0;
} }
const GetInt = extern struct {
value: i64,
error_code: u8,
is_error: bool,
};
pub export fn roc_fx_getInt() GetInt {
if (roc_fx_getInt_help()) |value| {
const get_int = GetInt{ .is_error = false, .value = value, .error_code = 0 };
return get_int;
} else |err| switch (err) {
error.InvalidCharacter => {
return GetInt{ .is_error = true, .value = 0, .error_code = 0 };
},
else => {
return GetInt{ .is_error = true, .value = 0, .error_code = 1 };
},
}
return 0;
}
fn roc_fx_getInt_help() !i64 {
const stdin = std.io.getStdIn().inStream();
var buf: [40]u8 = undefined;
const line: []u8 = (try stdin.readUntilDelimiterOrEof(&buf, '\n')) orelse "";
return std.fmt.parseInt(i64, line, 10);
}
fn readLine() []u8 {
const stdin = std.io.getStdIn().reader();
return (stdin.readUntilDelimiterOrEof(&line_buf, '\n') catch unreachable) orelse "";
}