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

View File

@ -61,15 +61,29 @@ pub fn run_roc(args: &[&str]) -> Out {
}
#[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);
for arg in args {
cmd.arg(arg);
}
let output = cmd
.output()
let mut child = cmd
.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));
Out {
@ -80,7 +94,7 @@ pub fn run_cmd(cmd_name: &str, args: &[&str]) -> Out {
}
#[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.
let mut cmd = Command::new("valgrind");
let named_tempfile =
@ -114,8 +128,23 @@ pub fn run_with_valgrind(args: &[&str]) -> (Out, String) {
cmd.arg(arg);
}
let output = cmd
.output()
cmd.stdin(Stdio::piped());
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");
let mut file = named_tempfile.into_file();

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
interface Task
exposes [ Task, succeed, fail, after, map, putLine ]
exposes [ Task, succeed, fail, after, map, putLine, getInt ]
imports [ Effect ]
@ -15,7 +15,6 @@ fail : err -> Task * err
fail = \val ->
Effect.always (Err val)
after : Task a err, (a -> Task b err) -> Task b err
after = \effect, transform ->
Effect.after effect \result ->
@ -32,3 +31,16 @@ map = \effect, transform ->
putLine : Str -> Task {} *
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 expectEqual = testing.expectEqual;
const expect = testing.expect;
const maxInt = std.math.maxInt;
const mem = std.mem;
const Allocator = mem.Allocator;
@ -96,3 +97,39 @@ pub export fn roc_fx_putLine(rocPath: str.RocStr) i64 {
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 "";
}