diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index b2d90626ff..bfe630abf5 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -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", diff --git a/cli/tests/helpers.rs b/cli/tests/helpers.rs index ebe4240203..376158d678 100644 --- a/cli/tests/helpers.rs +++ b/cli/tests/helpers.rs @@ -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(); diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index a3d97298ee..2a4f6c93be 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -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); diff --git a/examples/benchmarks/NQueens.roc b/examples/benchmarks/NQueens.roc index 5b9aad5f5b..9d5acaf2f5 100644 --- a/examples/benchmarks/NQueens.roc +++ b/examples/benchmarks/NQueens.roc @@ -5,6 +5,7 @@ app "nqueens" main : Task.Task {} [] main = + # Task.after Task.getInt \n -> queens 6 |> Str.fromInt |> Task.putLine diff --git a/examples/benchmarks/platform/Pkg-Config.roc b/examples/benchmarks/platform/Pkg-Config.roc index d3c15ab35e..f2137c6e22 100644 --- a/examples/benchmarks/platform/Pkg-Config.roc +++ b/examples/benchmarks/platform/Pkg-Config.roc @@ -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 diff --git a/examples/benchmarks/platform/Task.roc b/examples/benchmarks/platform/Task.roc index fd5769361e..82eb9c9539 100644 --- a/examples/benchmarks/platform/Task.roc +++ b/examples/benchmarks/platform/Task.roc @@ -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 diff --git a/examples/benchmarks/platform/host.zig b/examples/benchmarks/platform/host.zig index af91e0bbb4..55ef2f7e26 100644 --- a/examples/benchmarks/platform/host.zig +++ b/examples/benchmarks/platform/host.zig @@ -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 ""; +}