|
|
@ -13,6 +13,13 @@ const assert = std.debug.assert;
|
|
|
|
const join = std.fs.path.join;
|
|
|
|
const join = std.fs.path.join;
|
|
|
|
const print = std.debug.print;
|
|
|
|
const print = std.debug.print;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const Kind = enum {
|
|
|
|
|
|
|
|
/// Run the artifact as a normal executable.
|
|
|
|
|
|
|
|
exe,
|
|
|
|
|
|
|
|
/// Run the artifact as a test.
|
|
|
|
|
|
|
|
@"test",
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
pub const Exercise = struct {
|
|
|
|
pub const Exercise = struct {
|
|
|
|
/// main_file must have the format key_name.zig.
|
|
|
|
/// main_file must have the format key_name.zig.
|
|
|
|
/// The key will be used as a shorthand to build just one example.
|
|
|
|
/// The key will be used as a shorthand to build just one example.
|
|
|
@ -34,9 +41,8 @@ pub const Exercise = struct {
|
|
|
|
/// We need to keep track of this, so we compile with libc.
|
|
|
|
/// We need to keep track of this, so we compile with libc.
|
|
|
|
link_libc: bool = false,
|
|
|
|
link_libc: bool = false,
|
|
|
|
|
|
|
|
|
|
|
|
/// This exercise doesn't have a main function.
|
|
|
|
/// This exercise kind.
|
|
|
|
/// We only call the test.
|
|
|
|
kind: Kind = .exe,
|
|
|
|
run_test: bool = false,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// This exercise is not supported by the current Zig compiler.
|
|
|
|
/// This exercise is not supported by the current Zig compiler.
|
|
|
|
skip: bool = false,
|
|
|
|
skip: bool = false,
|
|
|
@ -225,18 +231,6 @@ const ZiglingStep = struct {
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Test exercise.
|
|
|
|
|
|
|
|
if (self.exercise.run_test) {
|
|
|
|
|
|
|
|
self.is_testing = true;
|
|
|
|
|
|
|
|
const result_msg = self.testing(prog_node) catch {
|
|
|
|
|
|
|
|
std.os.exit(2);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
const output = try trimLines(self.step.owner.allocator, result_msg);
|
|
|
|
|
|
|
|
print("\n{s}PASSED:\n{s}{s}\n\n", .{ green_text, output, reset_text });
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Normal exercise.
|
|
|
|
|
|
|
|
const exe_path = self.compile(prog_node) catch {
|
|
|
|
const exe_path = self.compile(prog_node) catch {
|
|
|
|
if (self.exercise.hint) |hint|
|
|
|
|
if (self.exercise.hint) |hint|
|
|
|
|
print("\n{s}Ziglings hint: {s}{s}", .{ bold_text, hint, reset_text });
|
|
|
|
print("\n{s}Ziglings hint: {s}{s}", .{ bold_text, hint, reset_text });
|
|
|
@ -276,10 +270,14 @@ const ZiglingStep = struct {
|
|
|
|
return err;
|
|
|
|
return err;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const raw_output = if (self.exercise.check_stdout)
|
|
|
|
switch (self.exercise.kind) {
|
|
|
|
result.stdout
|
|
|
|
.exe => return self.check_output(result),
|
|
|
|
else
|
|
|
|
.@"test" => return self.check_test(result),
|
|
|
|
result.stderr;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn check_output(self: *ZiglingStep, result: Child.ExecResult) !void {
|
|
|
|
|
|
|
|
const b = self.step.owner;
|
|
|
|
|
|
|
|
|
|
|
|
// Make sure it exited cleanly.
|
|
|
|
// Make sure it exited cleanly.
|
|
|
|
switch (result.term) {
|
|
|
|
switch (result.term) {
|
|
|
@ -299,6 +297,11 @@ const ZiglingStep = struct {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const raw_output = if (self.exercise.check_stdout)
|
|
|
|
|
|
|
|
result.stdout
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
result.stderr;
|
|
|
|
|
|
|
|
|
|
|
|
// Validate the output.
|
|
|
|
// Validate the output.
|
|
|
|
// NOTE: exercise.output can never contain a CR character.
|
|
|
|
// NOTE: exercise.output can never contain a CR character.
|
|
|
|
// See https://ziglang.org/documentation/master/#Source-Encoding.
|
|
|
|
// See https://ziglang.org/documentation/master/#Source-Encoding.
|
|
|
@ -323,55 +326,28 @@ const ZiglingStep = struct {
|
|
|
|
print("{s}PASSED:\n{s}{s}\n\n", .{ green_text, output, reset_text });
|
|
|
|
print("{s}PASSED:\n{s}{s}\n\n", .{ green_text, output, reset_text });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn testing(self: *ZiglingStep, prog_node: *std.Progress.Node) ![]const u8 {
|
|
|
|
fn check_test(self: *ZiglingStep, result: Child.ExecResult) !void {
|
|
|
|
print("Testing {s}...\n", .{self.exercise.main_file});
|
|
|
|
switch (result.term) {
|
|
|
|
|
|
|
|
.Exited => |code| {
|
|
|
|
const b = self.step.owner;
|
|
|
|
if (code != 0) {
|
|
|
|
const exercise_path = self.exercise.main_file;
|
|
|
|
// The test failed.
|
|
|
|
const path = join(b.allocator, &.{ self.work_path, exercise_path }) catch
|
|
|
|
print("{s}{s}{s}\n", .{
|
|
|
|
@panic("OOM");
|
|
|
|
red_text, result.stderr, reset_text,
|
|
|
|
|
|
|
|
|
|
|
|
var zig_args = std.ArrayList([]const u8).init(b.allocator);
|
|
|
|
|
|
|
|
defer zig_args.deinit();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
zig_args.append(b.zig_exe) catch @panic("OOM");
|
|
|
|
|
|
|
|
zig_args.append("test") catch @panic("OOM");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
zig_args.append(b.pathFromRoot(path)) catch @panic("OOM");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const argv = zig_args.items;
|
|
|
|
|
|
|
|
var code: u8 = undefined;
|
|
|
|
|
|
|
|
_ = self.eval(argv, &code, prog_node) catch |err| {
|
|
|
|
|
|
|
|
self.printErrors();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (err) {
|
|
|
|
|
|
|
|
error.FileNotFound => {
|
|
|
|
|
|
|
|
print("{s}{s}: Unable to spawn the following command: file not found{s}\n", .{
|
|
|
|
|
|
|
|
red_text, self.exercise.main_file, reset_text,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
dumpArgs(argv);
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
error.ExitCodeFailure => {
|
|
|
|
|
|
|
|
// Expected when test fails.
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
error.ProcessTerminated => {
|
|
|
|
|
|
|
|
print("{s}{s}: The following command terminated unexpectedly:{s}\n", .{
|
|
|
|
|
|
|
|
red_text, self.exercise.main_file, reset_text,
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
dumpArgs(argv);
|
|
|
|
|
|
|
|
|
|
|
|
return error.TestFailed;
|
|
|
|
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
else => {
|
|
|
|
else => {
|
|
|
|
print("{s}{s}: Unexpected error: {s}{s}\n", .{
|
|
|
|
print("{s}{s} terminated unexpectedly{s}\n", .{
|
|
|
|
red_text, self.exercise.main_file, @errorName(err), reset_text,
|
|
|
|
red_text, self.exercise.main_file, reset_text,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
dumpArgs(argv);
|
|
|
|
|
|
|
|
|
|
|
|
return error.UnexpectedTermination;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
print("{s}PASSED{s}\n\n", .{ green_text, reset_text });
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return self.result_messages;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn compile(self: *ZiglingStep, prog_node: *std.Progress.Node) ![]const u8 {
|
|
|
|
fn compile(self: *ZiglingStep, prog_node: *std.Progress.Node) ![]const u8 {
|
|
|
@ -386,7 +362,12 @@ const ZiglingStep = struct {
|
|
|
|
defer zig_args.deinit();
|
|
|
|
defer zig_args.deinit();
|
|
|
|
|
|
|
|
|
|
|
|
zig_args.append(b.zig_exe) catch @panic("OOM");
|
|
|
|
zig_args.append(b.zig_exe) catch @panic("OOM");
|
|
|
|
zig_args.append("build-exe") catch @panic("OOM");
|
|
|
|
|
|
|
|
|
|
|
|
const cmd = switch (self.exercise.kind) {
|
|
|
|
|
|
|
|
.exe => "build-exe",
|
|
|
|
|
|
|
|
.@"test" => "test",
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
zig_args.append(cmd) catch @panic("OOM");
|
|
|
|
|
|
|
|
|
|
|
|
// Enable C support for exercises that use C functions.
|
|
|
|
// Enable C support for exercises that use C functions.
|
|
|
|
if (self.exercise.link_libc) {
|
|
|
|
if (self.exercise.link_libc) {
|
|
|
@ -1220,7 +1201,7 @@ const exercises = [_]Exercise{
|
|
|
|
.{
|
|
|
|
.{
|
|
|
|
.main_file = "102_testing.zig",
|
|
|
|
.main_file = "102_testing.zig",
|
|
|
|
.output = "",
|
|
|
|
.output = "",
|
|
|
|
.run_test = true,
|
|
|
|
.kind = .@"test",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
.{
|
|
|
|
.{
|
|
|
|
.main_file = "999_the_end.zig",
|
|
|
|
.main_file = "999_the_end.zig",
|
|
|
|