add test harness for "run translated C" tests

This commit is contained in:
Andrew Kelley 2020-01-02 22:45:48 -05:00
parent f83411b0b1
commit 695c8f756b
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
5 changed files with 224 additions and 1 deletions

View File

@ -137,6 +137,7 @@ pub fn build(b: *Builder) !void {
test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));
test_step.dependOn(tests.addTranslateCTests(b, test_filter));
test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter));
test_step.dependOn(tests.addGenHTests(b, test_filter));
test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));
test_step.dependOn(docs_step);

View File

@ -43,7 +43,6 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
" libc [paths_file] Display native libc paths file or validate one\n"
" run [source] [-- [args]] create executable and run immediately\n"
" translate-c [source] convert c code to zig code\n"
" translate-c-2 [source] experimental self-hosted translate-c\n"
" targets list available compilation targets\n"
" test [source] create and run a test build\n"
" version print version number and exit\n"

24
test/run_translated_c.zig Normal file
View File

@ -0,0 +1,24 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.RunTranslatedCContext) void {
cases.add("hello world",
\\#define _NO_CRT_STDIO_INLINE 1
\\#include <stdio.h>
\\int main(int argc, char **argv) {
\\ printf("hello, world!\n");
\\ return 0;
\\}
, "hello, world!\n");
cases.add("anon struct init",
\\#include <stdlib.h>
\\struct {int a; int b;} x = {1, 2};
\\int main(int argc, char **argv) {
\\ x.a += 2;
\\ x.b += 1;
\\ if (x.a != 3) abort();
\\ if (x.b != 3) abort();
\\ return 0;
\\}
, "");
}

View File

@ -0,0 +1,180 @@
// This is the implementation of the test harness for running translated
// C code. For the actual test cases, see test/run_translated_c.zig.
const std = @import("std");
const build = std.build;
const ArrayList = std.ArrayList;
const fmt = std.fmt;
const mem = std.mem;
const fs = std.fs;
const warn = std.debug.warn;
pub const RunTranslatedCContext = struct {
b: *build.Builder,
step: *build.Step,
test_index: usize,
test_filter: ?[]const u8,
const TestCase = struct {
name: []const u8,
sources: ArrayList(SourceFile),
expected_stdout: []const u8,
allow_warnings: bool,
const SourceFile = struct {
filename: []const u8,
source: []const u8,
};
pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void {
self.sources.append(SourceFile{
.filename = filename,
.source = source,
}) catch unreachable;
}
};
const DoEverythingStep = struct {
step: build.Step,
context: *RunTranslatedCContext,
name: []const u8,
case: *const TestCase,
test_index: usize,
pub fn create(
context: *RunTranslatedCContext,
name: []const u8,
case: *const TestCase,
) *DoEverythingStep {
const allocator = context.b.allocator;
const ptr = allocator.create(DoEverythingStep) catch unreachable;
ptr.* = DoEverythingStep{
.context = context,
.name = name,
.case = case,
.test_index = context.test_index,
.step = build.Step.init("RunTranslatedC", allocator, make),
};
context.test_index += 1;
return ptr;
}
fn make(step: *build.Step) !void {
const self = @fieldParentPtr(DoEverythingStep, "step", step);
const b = self.context.b;
warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name });
// translate from c to zig
const translated_c_code = blk: {
var zig_args = ArrayList([]const u8).init(b.allocator);
defer zig_args.deinit();
const rel_c_filename = try fs.path.join(b.allocator, &[_][]const u8{
b.cache_root,
self.case.sources.toSliceConst()[0].filename,
});
try zig_args.append(b.zig_exe);
try zig_args.append("translate-c");
try zig_args.append("-lc");
try zig_args.append(b.pathFromRoot(rel_c_filename));
break :blk try b.exec(zig_args.toSliceConst());
};
// write stdout to a file
const translated_c_path = try fs.path.join(b.allocator,
&[_][]const u8{ b.cache_root, "translated_c.zig" });
try fs.cwd().writeFile(translated_c_path, translated_c_code);
// zig run the result
const run_stdout = blk: {
var zig_args = ArrayList([]const u8).init(b.allocator);
defer zig_args.deinit();
try zig_args.append(b.zig_exe);
try zig_args.append("-lc");
try zig_args.append("run");
try zig_args.append(translated_c_path);
break :blk try b.exec(zig_args.toSliceConst());
};
// compare stdout
if (!mem.eql(u8, self.case.expected_stdout, run_stdout)) {
warn(
\\
\\========= Expected this output: =========
\\{}
\\========= But found: ====================
\\{}
\\
, .{ self.case.expected_stdout, run_stdout });
return error.TestFailed;
}
warn("OK\n", .{});
}
};
pub fn create(
self: *RunTranslatedCContext,
allow_warnings: bool,
filename: []const u8,
name: []const u8,
source: []const u8,
expected_stdout: []const u8,
) *TestCase {
const tc = self.b.allocator.create(TestCase) catch unreachable;
tc.* = TestCase{
.name = name,
.sources = ArrayList(TestCase.SourceFile).init(self.b.allocator),
.expected_stdout = expected_stdout,
.allow_warnings = allow_warnings,
};
tc.addSourceFile(filename, source);
return tc;
}
pub fn add(
self: *RunTranslatedCContext,
name: []const u8,
source: []const u8,
expected_stdout: []const u8,
) void {
const tc = self.create(false, "source.c", name, source, expected_stdout);
self.addCase(tc);
}
pub fn addAllowWarnings(
self: *RunTranslatedCContext,
name: []const u8,
source: []const u8,
expected_stdout: []const u8,
) void {
const tc = self.create(true, "source.c", name, source, expected_stdout);
self.addCase(tc);
}
pub fn addCase(self: *RunTranslatedCContext, case: *const TestCase) void {
const b = self.b;
const annotated_case_name = fmt.allocPrint(self.b.allocator, "run-translated-c {}", .{ case.name }) catch unreachable;
if (self.test_filter) |filter| {
if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
}
const do_everything_step = DoEverythingStep.create(self, annotated_case_name, case);
self.step.dependOn(&do_everything_step.step);
for (case.sources.toSliceConst()) |src_file| {
const expanded_src_path = fs.path.join(
b.allocator,
&[_][]const u8{ b.cache_root, src_file.filename },
) catch unreachable;
const write_src = b.addWriteFile(expanded_src_path, src_file.source);
do_everything_step.step.dependOn(&write_src.step);
}
}
};

View File

@ -14,6 +14,7 @@ const builtin = @import("builtin");
const Mode = builtin.Mode;
const LibExeObjStep = build.LibExeObjStep;
// Cases
const compare_output = @import("compare_output.zig");
const standalone = @import("standalone.zig");
const stack_traces = @import("stack_traces.zig");
@ -21,8 +22,12 @@ const compile_errors = @import("compile_errors.zig");
const assemble_and_link = @import("assemble_and_link.zig");
const runtime_safety = @import("runtime_safety.zig");
const translate_c = @import("translate_c.zig");
const run_translated_c = @import("run_translated_c.zig");
const gen_h = @import("gen_h.zig");
// Implementations
pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext;
const TestTarget = struct {
target: Target = .Native,
mode: builtin.Mode = .Debug,
@ -383,6 +388,20 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St
return cases.step;
}
pub fn addRunTranslatedCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
const cases = b.allocator.create(RunTranslatedCContext) catch unreachable;
cases.* = .{
.b = b,
.step = b.step("test-run-translated-c", "Run the Run-Translated-C tests"),
.test_index = 0,
.test_filter = test_filter,
};
run_translated_c.addCases(cases);
return cases.step;
}
pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
const cases = b.allocator.create(GenHContext) catch unreachable;
cases.* = GenHContext{