diff --git a/src/cli/test/roc_subcommands.zig b/src/cli/test/roc_subcommands.zig index 012c15b63e..626f6fcdbc 100644 --- a/src/cli/test/roc_subcommands.zig +++ b/src/cli/test/roc_subcommands.zig @@ -420,3 +420,122 @@ test "roc test/str/app.roc runs successfully" { // 1. Command succeeded (zero exit code) try testing.expect(result.term == .Exited and result.term.Exited == 0); } + +// ============================================================================= +// roc build tests +// ============================================================================= + +test "roc build creates executable from test/int/app.roc" { + const testing = std.testing; + const gpa = testing.allocator; + + // Create a temp directory for the output + var tmp_dir = testing.tmpDir(.{}); + defer tmp_dir.cleanup(); + + const tmp_path = try tmp_dir.dir.realpathAlloc(gpa, "."); + defer gpa.free(tmp_path); + + const output_path = try std.fs.path.join(gpa, &.{ tmp_path, "test_app" }); + defer gpa.free(output_path); + + const output_arg = try std.fmt.allocPrint(gpa, "--output={s}", .{output_path}); + defer gpa.free(output_arg); + + const result = try util.runRoc(gpa, &.{ "build", output_arg }, "test/int/app.roc"); + defer gpa.free(result.stdout); + defer gpa.free(result.stderr); + + // Verify that: + // 1. Command succeeded (zero exit code) + try testing.expect(result.term == .Exited and result.term.Exited == 0); + + // 2. Output file was created + const stat = tmp_dir.dir.statFile("test_app") catch |err| { + std.debug.print("Failed to stat output file: {}\nstderr: {s}\n", .{ err, result.stderr }); + return err; + }; + + // 3. Output file is executable (non-zero size) + try testing.expect(stat.size > 0); +} + +test "roc build executable runs correctly" { + const testing = std.testing; + const gpa = testing.allocator; + + // Create a temp directory for the output + var tmp_dir = testing.tmpDir(.{}); + defer tmp_dir.cleanup(); + + const tmp_path = try tmp_dir.dir.realpathAlloc(gpa, "."); + defer gpa.free(tmp_path); + + const output_path = try std.fs.path.join(gpa, &.{ tmp_path, "test_app" }); + defer gpa.free(output_path); + + const output_arg = try std.fmt.allocPrint(gpa, "--output={s}", .{output_path}); + defer gpa.free(output_arg); + + // Build the app + const build_result = try util.runRoc(gpa, &.{ "build", output_arg }, "test/int/app.roc"); + defer gpa.free(build_result.stdout); + defer gpa.free(build_result.stderr); + + try testing.expect(build_result.term == .Exited and build_result.term.Exited == 0); + + // Run the built executable + const run_result = try std.process.Child.run(.{ + .allocator = gpa, + .argv = &.{output_path}, + .max_output_bytes = 10 * 1024 * 1024, + }); + defer gpa.free(run_result.stdout); + defer gpa.free(run_result.stderr); + + // Verify that: + // 1. Executable ran successfully + try testing.expect(run_result.term == .Exited and run_result.term.Exited == 0); + + // 2. Output contains expected success message + const has_success = std.mem.indexOf(u8, run_result.stdout, "SUCCESS") != null or + std.mem.indexOf(u8, run_result.stdout, "PASSED") != null; + try testing.expect(has_success); +} + +test "roc build fails with file not found error" { + const testing = std.testing; + const gpa = testing.allocator; + + const result = try util.runRoc(gpa, &.{"build"}, "nonexistent_file.roc"); + defer gpa.free(result.stdout); + defer gpa.free(result.stderr); + + // Verify that: + // 1. Command failed (non-zero exit code) + try testing.expect(result.term != .Exited or result.term.Exited != 0); + + // 2. Stderr contains file not found error + const has_error = std.mem.indexOf(u8, result.stderr, "FileNotFound") != null or + std.mem.indexOf(u8, result.stderr, "not found") != null or + std.mem.indexOf(u8, result.stderr, "Failed") != null; + try testing.expect(has_error); +} + +test "roc build fails with invalid target error" { + const testing = std.testing; + const gpa = testing.allocator; + + const result = try util.runRoc(gpa, &.{ "build", "--target=invalid_target_name" }, "test/int/app.roc"); + defer gpa.free(result.stdout); + defer gpa.free(result.stderr); + + // Verify that: + // 1. Command failed (non-zero exit code) + try testing.expect(result.term != .Exited or result.term.Exited != 0); + + // 2. Stderr contains invalid target error + const has_error = std.mem.indexOf(u8, result.stderr, "Invalid target") != null or + std.mem.indexOf(u8, result.stderr, "invalid") != null; + try testing.expect(has_error); +}