diff --git a/build.zig b/build.zig index 24653ab155..a8b167bef9 100644 --- a/build.zig +++ b/build.zig @@ -10,27 +10,23 @@ pub fn build(b: &Builder) { const run_tests_cmd = b.addCommand(b.out_dir, b.env_map, "./run_tests", [][]const u8{}); run_tests_cmd.step.dependOn(&run_tests_exe.step); - const self_hosted_tests_debug_nolibc = b.addTest("test/self_hosted.zig"); - - const self_hosted_tests_release_nolibc = b.addTest("test/self_hosted.zig"); - self_hosted_tests_release_nolibc.setRelease(true); - - const self_hosted_tests_debug_libc = b.addTest("test/self_hosted.zig"); - self_hosted_tests_debug_libc.linkLibrary("c"); - - const self_hosted_tests_release_libc = b.addTest("test/self_hosted.zig"); - self_hosted_tests_release_libc.setRelease(true); - self_hosted_tests_release_libc.linkLibrary("c"); - const self_hosted_tests = b.step("test-self-hosted", "Run the self-hosted tests"); - self_hosted_tests.dependOn(&self_hosted_tests_debug_nolibc.step); - self_hosted_tests.dependOn(&self_hosted_tests_release_nolibc.step); - self_hosted_tests.dependOn(&self_hosted_tests_debug_libc.step); - self_hosted_tests.dependOn(&self_hosted_tests_release_libc.step); + for ([]bool{false, true}) |release| { + for ([]bool{false, true}) |link_libc| { + const these_tests = b.addTest("test/self_hosted.zig"); + // TODO add prefix to test names + // TODO pass test_filter to these_tests + these_tests.setRelease(release); + if (link_libc) { + these_tests.linkLibrary("c"); + } + self_hosted_tests.dependOn(&these_tests.step); + } + } test_step.dependOn(self_hosted_tests); //test_step.dependOn(&run_tests_cmd.step); test_step.dependOn(tests.addCompareOutputTests(b, test_filter)); - //test_step.dependOn(tests.addBuildExampleTests(b, test_filter)); + test_step.dependOn(tests.addBuildExampleTests(b, test_filter)); } diff --git a/test/build_examples.zig b/test/build_examples.zig new file mode 100644 index 0000000000..174dc14bed --- /dev/null +++ b/test/build_examples.zig @@ -0,0 +1,60 @@ +const std = @import("std"); +const build = std.build; +const mem = std.mem; +const fmt = std.fmt; + +pub fn addBuildExampleTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step { + const cases = %%b.allocator.create(BuildExamplesContext); + *cases = BuildExamplesContext { + .b = b, + .step = b.step("test-build-examples", "Build the examples"), + .test_index = 0, + .test_filter = test_filter, + }; + + cases.add("example/hello_world/hello.zig"); + cases.addC("example/hello_world/hello_libc.zig"); + cases.add("example/cat/main.zig"); + cases.add("example/guess_number/main.zig"); + + return cases.step; +} + +const BuildExamplesContext = struct { + b: &build.Builder, + step: &build.Step, + test_index: usize, + test_filter: ?[]const u8, + + pub fn addC(self: &BuildExamplesContext, root_src: []const u8) { + self.addAllArgs(root_src, true); + } + + pub fn add(self: &BuildExamplesContext, root_src: []const u8) { + self.addAllArgs(root_src, false); + } + + pub fn addAllArgs(self: &BuildExamplesContext, root_src: []const u8, link_libc: bool) { + const b = self.b; + + for ([]bool{false, true}) |release| { + const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "build {} ({})", + root_src, if (release) "release" else "debug"); + if (const filter ?= self.test_filter) { + if (mem.indexOf(u8, annotated_case_name, filter) == null) + continue; + } + + const exe = b.addExecutable("test", root_src); + exe.setRelease(release); + if (link_libc) { + exe.linkLibrary("c"); + } + + const log_step = b.addLog("PASS {}\n", annotated_case_name); + log_step.step.dependOn(&exe.step); + + self.step.dependOn(&log_step.step); + } + } +}; diff --git a/test/compare_output.zig b/test/compare_output.zig new file mode 100644 index 0000000000..f971d7eef1 --- /dev/null +++ b/test/compare_output.zig @@ -0,0 +1,587 @@ +const std = @import("std"); +const debug = std.debug; +const build = std.build; +const os = std.os; +const StdIo = os.ChildProcess.StdIo; +const Term = os.ChildProcess.Term; +const Buffer0 = std.cstr.Buffer0; +const io = std.io; +const mem = std.mem; +const fmt = std.fmt; +const List = std.list.List; + +error TestFailed; + +pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step { + const cases = %%b.allocator.create(CompareOutputContext); + *cases = CompareOutputContext { + .b = b, + .compare_output_tests = b.step("test-compare-output", "Run the compare output tests"), + .test_index = 0, + .test_filter = test_filter, + }; + + cases.addC("hello world with libc", + \\const c = @cImport(@cInclude("stdio.h")); + \\export fn main(argc: c_int, argv: &&u8) -> c_int { + \\ _ = c.puts(c"Hello, world!"); + \\ return 0; + \\} + , "Hello, world!" ++ os.line_sep); + + cases.addCase({ + var tc = cases.create("multiple files with private function", + \\use @import("std").io; + \\use @import("foo.zig"); + \\ + \\pub fn main() -> %void { + \\ privateFunction(); + \\ %%stdout.printf("OK 2\n"); + \\} + \\ + \\fn privateFunction() { + \\ printText(); + \\} + , "OK 1\nOK 2\n"); + + tc.addSourceFile("foo.zig", + \\use @import("std").io; + \\ + \\// purposefully conflicting function with main.zig + \\// but it's private so it should be OK + \\fn privateFunction() { + \\ %%stdout.printf("OK 1\n"); + \\} + \\ + \\pub fn printText() { + \\ privateFunction(); + \\} + ); + + tc + }); + + cases.addCase({ + var tc = cases.create("import segregation", + \\use @import("foo.zig"); + \\use @import("bar.zig"); + \\ + \\pub fn main() -> %void { + \\ foo_function(); + \\ bar_function(); + \\} + , "OK\nOK\n"); + + tc.addSourceFile("foo.zig", + \\use @import("std").io; + \\pub fn foo_function() { + \\ %%stdout.printf("OK\n"); + \\} + ); + + tc.addSourceFile("bar.zig", + \\use @import("other.zig"); + \\use @import("std").io; + \\ + \\pub fn bar_function() { + \\ if (foo_function()) { + \\ %%stdout.printf("OK\n"); + \\ } + \\} + ); + + tc.addSourceFile("other.zig", + \\pub fn foo_function() -> bool { + \\ // this one conflicts with the one from foo + \\ return true; + \\} + ); + + tc + }); + + cases.addCase({ + var tc = cases.create("two files use import each other", + \\use @import("a.zig"); + \\ + \\pub fn main() -> %void { + \\ ok(); + \\} + , "OK\n"); + + tc.addSourceFile("a.zig", + \\use @import("b.zig"); + \\const io = @import("std").io; + \\ + \\pub const a_text = "OK\n"; + \\ + \\pub fn ok() { + \\ %%io.stdout.printf(b_text); + \\} + ); + + tc.addSourceFile("b.zig", + \\use @import("a.zig"); + \\ + \\pub const b_text = a_text; + ); + + tc + }); + + cases.add("hello world without libc", + \\const io = @import("std").io; + \\ + \\pub fn main() -> %void { + \\ %%io.stdout.printf("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a')); + \\} + , "Hello, world!\n0012 012 a\n"); + + cases.addC("number literals", + \\const c = @cImport(@cInclude("stdio.h")); + \\ + \\export fn main(argc: c_int, argv: &&u8) -> c_int { + \\ _ = c.printf(c"0: %llu\n", + \\ u64(0)); + \\ _ = c.printf(c"320402575052271: %llu\n", + \\ u64(320402575052271)); + \\ _ = c.printf(c"0x01236789abcdef: %llu\n", + \\ u64(0x01236789abcdef)); + \\ _ = c.printf(c"0xffffffffffffffff: %llu\n", + \\ u64(0xffffffffffffffff)); + \\ _ = c.printf(c"0x000000ffffffffffffffff: %llu\n", + \\ u64(0x000000ffffffffffffffff)); + \\ _ = c.printf(c"0o1777777777777777777777: %llu\n", + \\ u64(0o1777777777777777777777)); + \\ _ = c.printf(c"0o0000001777777777777777777777: %llu\n", + \\ u64(0o0000001777777777777777777777)); + \\ _ = c.printf(c"0b1111111111111111111111111111111111111111111111111111111111111111: %llu\n", + \\ u64(0b1111111111111111111111111111111111111111111111111111111111111111)); + \\ _ = c.printf(c"0b0000001111111111111111111111111111111111111111111111111111111111111111: %llu\n", + \\ u64(0b0000001111111111111111111111111111111111111111111111111111111111111111)); + \\ + \\ _ = c.printf(c"\n"); + \\ + \\ _ = c.printf(c"0.0: %a\n", + \\ f64(0.0)); + \\ _ = c.printf(c"0e0: %a\n", + \\ f64(0e0)); + \\ _ = c.printf(c"0.0e0: %a\n", + \\ f64(0.0e0)); + \\ _ = c.printf(c"000000000000000000000000000000000000000000000000000000000.0e0: %a\n", + \\ f64(000000000000000000000000000000000000000000000000000000000.0e0)); + \\ _ = c.printf(c"0.000000000000000000000000000000000000000000000000000000000e0: %a\n", + \\ f64(0.000000000000000000000000000000000000000000000000000000000e0)); + \\ _ = c.printf(c"0.0e000000000000000000000000000000000000000000000000000000000: %a\n", + \\ f64(0.0e000000000000000000000000000000000000000000000000000000000)); + \\ _ = c.printf(c"1.0: %a\n", + \\ f64(1.0)); + \\ _ = c.printf(c"10.0: %a\n", + \\ f64(10.0)); + \\ _ = c.printf(c"10.5: %a\n", + \\ f64(10.5)); + \\ _ = c.printf(c"10.5e5: %a\n", + \\ f64(10.5e5)); + \\ _ = c.printf(c"10.5e+5: %a\n", + \\ f64(10.5e+5)); + \\ _ = c.printf(c"50.0e-2: %a\n", + \\ f64(50.0e-2)); + \\ _ = c.printf(c"50e-2: %a\n", + \\ f64(50e-2)); + \\ + \\ _ = c.printf(c"\n"); + \\ + \\ _ = c.printf(c"0x1.0: %a\n", + \\ f64(0x1.0)); + \\ _ = c.printf(c"0x10.0: %a\n", + \\ f64(0x10.0)); + \\ _ = c.printf(c"0x100.0: %a\n", + \\ f64(0x100.0)); + \\ _ = c.printf(c"0x103.0: %a\n", + \\ f64(0x103.0)); + \\ _ = c.printf(c"0x103.7: %a\n", + \\ f64(0x103.7)); + \\ _ = c.printf(c"0x103.70: %a\n", + \\ f64(0x103.70)); + \\ _ = c.printf(c"0x103.70p4: %a\n", + \\ f64(0x103.70p4)); + \\ _ = c.printf(c"0x103.70p5: %a\n", + \\ f64(0x103.70p5)); + \\ _ = c.printf(c"0x103.70p+5: %a\n", + \\ f64(0x103.70p+5)); + \\ _ = c.printf(c"0x103.70p-5: %a\n", + \\ f64(0x103.70p-5)); + \\ + \\ _ = c.printf(c"\n"); + \\ + \\ _ = c.printf(c"0b10100.00010e0: %a\n", + \\ f64(0b10100.00010e0)); + \\ _ = c.printf(c"0o10700.00010e0: %a\n", + \\ f64(0o10700.00010e0)); + \\ + \\ return 0; + \\} + , + \\0: 0 + \\320402575052271: 320402575052271 + \\0x01236789abcdef: 320402575052271 + \\0xffffffffffffffff: 18446744073709551615 + \\0x000000ffffffffffffffff: 18446744073709551615 + \\0o1777777777777777777777: 18446744073709551615 + \\0o0000001777777777777777777777: 18446744073709551615 + \\0b1111111111111111111111111111111111111111111111111111111111111111: 18446744073709551615 + \\0b0000001111111111111111111111111111111111111111111111111111111111111111: 18446744073709551615 + \\ + \\0.0: 0x0p+0 + \\0e0: 0x0p+0 + \\0.0e0: 0x0p+0 + \\000000000000000000000000000000000000000000000000000000000.0e0: 0x0p+0 + \\0.000000000000000000000000000000000000000000000000000000000e0: 0x0p+0 + \\0.0e000000000000000000000000000000000000000000000000000000000: 0x0p+0 + \\1.0: 0x1p+0 + \\10.0: 0x1.4p+3 + \\10.5: 0x1.5p+3 + \\10.5e5: 0x1.0059p+20 + \\10.5e+5: 0x1.0059p+20 + \\50.0e-2: 0x1p-1 + \\50e-2: 0x1p-1 + \\ + \\0x1.0: 0x1p+0 + \\0x10.0: 0x1p+4 + \\0x100.0: 0x1p+8 + \\0x103.0: 0x1.03p+8 + \\0x103.7: 0x1.037p+8 + \\0x103.70: 0x1.037p+8 + \\0x103.70p4: 0x1.037p+12 + \\0x103.70p5: 0x1.037p+13 + \\0x103.70p+5: 0x1.037p+13 + \\0x103.70p-5: 0x1.037p+3 + \\ + \\0b10100.00010e0: 0x1.41p+4 + \\0o10700.00010e0: 0x1.1c0001p+12 + \\ + ); + + cases.add("order-independent declarations", + \\const io = @import("std").io; + \\const z = io.stdin_fileno; + \\const x : @typeOf(y) = 1234; + \\const y : u16 = 5678; + \\pub fn main() -> %void { + \\ var x_local : i32 = print_ok(x); + \\} + \\fn print_ok(val: @typeOf(x)) -> @typeOf(foo) { + \\ %%io.stdout.printf("OK\n"); + \\ return 0; + \\} + \\const foo : i32 = 0; + , "OK\n"); + + cases.addC("expose function pointer to C land", + \\const c = @cImport(@cInclude("stdlib.h")); + \\ + \\export fn compare_fn(a: ?&const c_void, b: ?&const c_void) -> c_int { + \\ const a_int = @ptrcast(&i32, a ?? unreachable); + \\ const b_int = @ptrcast(&i32, b ?? unreachable); + \\ if (*a_int < *b_int) { + \\ -1 + \\ } else if (*a_int > *b_int) { + \\ 1 + \\ } else { + \\ c_int(0) + \\ } + \\} + \\ + \\export fn main() -> c_int { + \\ var array = []u32 { 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; + \\ + \\ c.qsort(@ptrcast(&c_void, &array[0]), c_ulong(array.len), @sizeOf(i32), compare_fn); + \\ + \\ for (array) |item, i| { + \\ if (item != i) { + \\ c.abort(); + \\ } + \\ } + \\ + \\ return 0; + \\} + , ""); + + cases.addC("casting between float and integer types", + \\const c = @cImport(@cInclude("stdio.h")); + \\export fn main(argc: c_int, argv: &&u8) -> c_int { + \\ const small: f32 = 3.25; + \\ const x: f64 = small; + \\ const y = i32(x); + \\ const z = f64(y); + \\ _ = c.printf(c"%.2f\n%d\n%.2f\n%.2f\n", x, y, z, f64(-0.4)); + \\ return 0; + \\} + , "3.25\n3\n3.00\n-0.40\n"); + + cases.add("same named methods in incomplete struct", + \\const io = @import("std").io; + \\ + \\const Foo = struct { + \\ field1: Bar, + \\ + \\ fn method(a: &const Foo) -> bool { true } + \\}; + \\ + \\const Bar = struct { + \\ field2: i32, + \\ + \\ fn method(b: &const Bar) -> bool { true } + \\}; + \\ + \\pub fn main() -> %void { + \\ const bar = Bar {.field2 = 13,}; + \\ const foo = Foo {.field1 = bar,}; + \\ if (!foo.method()) { + \\ %%io.stdout.printf("BAD\n"); + \\ } + \\ if (!bar.method()) { + \\ %%io.stdout.printf("BAD\n"); + \\ } + \\ %%io.stdout.printf("OK\n"); + \\} + , "OK\n"); + + cases.add("defer with only fallthrough", + \\const io = @import("std").io; + \\pub fn main() -> %void { + \\ %%io.stdout.printf("before\n"); + \\ defer %%io.stdout.printf("defer1\n"); + \\ defer %%io.stdout.printf("defer2\n"); + \\ defer %%io.stdout.printf("defer3\n"); + \\ %%io.stdout.printf("after\n"); + \\} + , "before\nafter\ndefer3\ndefer2\ndefer1\n"); + + cases.add("defer with return", + \\const io = @import("std").io; + \\const os = @import("std").os; + \\pub fn main() -> %void { + \\ %%io.stdout.printf("before\n"); + \\ defer %%io.stdout.printf("defer1\n"); + \\ defer %%io.stdout.printf("defer2\n"); + \\ if (os.args.count() == 1) return; + \\ defer %%io.stdout.printf("defer3\n"); + \\ %%io.stdout.printf("after\n"); + \\} + , "before\ndefer2\ndefer1\n"); + + cases.add("%defer and it fails", + \\const io = @import("std").io; + \\pub fn main() -> %void { + \\ do_test() %% return; + \\} + \\fn do_test() -> %void { + \\ %%io.stdout.printf("before\n"); + \\ defer %%io.stdout.printf("defer1\n"); + \\ %defer %%io.stdout.printf("deferErr\n"); + \\ %return its_gonna_fail(); + \\ defer %%io.stdout.printf("defer3\n"); + \\ %%io.stdout.printf("after\n"); + \\} + \\error IToldYouItWouldFail; + \\fn its_gonna_fail() -> %void { + \\ return error.IToldYouItWouldFail; + \\} + , "before\ndeferErr\ndefer1\n"); + + cases.add("%defer and it passes", + \\const io = @import("std").io; + \\pub fn main() -> %void { + \\ do_test() %% return; + \\} + \\fn do_test() -> %void { + \\ %%io.stdout.printf("before\n"); + \\ defer %%io.stdout.printf("defer1\n"); + \\ %defer %%io.stdout.printf("deferErr\n"); + \\ %return its_gonna_pass(); + \\ defer %%io.stdout.printf("defer3\n"); + \\ %%io.stdout.printf("after\n"); + \\} + \\fn its_gonna_pass() -> %void { } + , "before\nafter\ndefer3\ndefer1\n"); + + cases.addCase({ + var tc = cases.create("@embedFile", + \\const foo_txt = @embedFile("foo.txt"); + \\const io = @import("std").io; + \\ + \\pub fn main() -> %void { + \\ %%io.stdout.printf(foo_txt); + \\} + , "1234\nabcd\n"); + + tc.addSourceFile("foo.txt", "1234\nabcd\n"); + + tc + }); + + return cases.compare_output_tests; +} + +const CompareOutputContext = struct { + b: &build.Builder, + compare_output_tests: &build.Step, + test_index: usize, + test_filter: ?[]const u8, + + const TestCase = struct { + name: []const u8, + sources: List(SourceFile), + expected_output: []const u8, + link_libc: bool, + + const SourceFile = struct { + filename: []const u8, + source: []const u8, + }; + + pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) { + %%self.sources.append(SourceFile { + .filename = filename, + .source = source, + }); + } + }; + + pub fn create(self: &CompareOutputContext, name: []const u8, source: []const u8, + expected_output: []const u8) -> TestCase + { + var tc = TestCase { + .name = name, + .sources = List(TestCase.SourceFile).init(self.b.allocator), + .expected_output = expected_output, + .link_libc = false, + }; + tc.addSourceFile("source.zig", source); + return tc; + } + + pub fn addC(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) { + var tc = self.create(name, source, expected_output); + tc.link_libc = true; + self.addCase(tc); + } + + pub fn add(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) { + const tc = self.create(name, source, expected_output); + self.addCase(tc); + } + + pub fn addCase(self: &CompareOutputContext, case: &const TestCase) { + const b = self.b; + + const root_src = %%os.path.join(b.allocator, "test_artifacts", case.sources.items[0].filename); + const exe_path = %%os.path.join(b.allocator, "test_artifacts", "test"); + + for ([]bool{false, true}) |release| { + const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} ({})", + case.name, if (release) "release" else "debug"); + if (const filter ?= self.test_filter) { + if (mem.indexOf(u8, annotated_case_name, filter) == null) + continue; + } + + const exe = b.addExecutable("test", root_src); + exe.setOutputPath(exe_path); + exe.setRelease(release); + if (case.link_libc) { + exe.linkLibrary("c"); + } + + for (case.sources.toSliceConst()) |src_file| { + const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename); + const write_src = b.addWriteFile(expanded_src_path, src_file.source); + exe.step.dependOn(&write_src.step); + } + + const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name, + case.expected_output); + run_and_cmp_output.step.dependOn(&exe.step); + + self.compare_output_tests.dependOn(&run_and_cmp_output.step); + } + } +}; + +const RunCompareOutputStep = struct { + step: build.Step, + context: &CompareOutputContext, + exe_path: []const u8, + name: []const u8, + expected_output: []const u8, + test_index: usize, + + pub fn create(context: &CompareOutputContext, exe_path: []const u8, + name: []const u8, expected_output: []const u8) -> &RunCompareOutputStep + { + const allocator = context.b.allocator; + const ptr = %%allocator.create(RunCompareOutputStep); + *ptr = RunCompareOutputStep { + .context = context, + .exe_path = exe_path, + .name = name, + .expected_output = expected_output, + .test_index = context.test_index, + .step = build.Step.init("RunCompareOutput", allocator, make), + }; + context.test_index += 1; + return ptr; + } + + fn make(step: &build.Step) -> %void { + const self = @fieldParentPtr(RunCompareOutputStep, "step", step); + const b = self.context.b; + + const full_exe_path = b.pathFromRoot(self.exe_path); + + %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name); + + var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, &b.env_map, + StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err| + { + debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err)); + }; + + const term = child.wait() %% |err| { + debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err)); + }; + switch (term) { + Term.Clean => |code| { + if (code != 0) { + %%io.stderr.printf("Process {} exited with error code {}\n", full_exe_path, code); + return error.TestFailed; + } + }, + else => { + %%io.stderr.printf("Process {} terminated unexpectedly\n", full_exe_path); + return error.TestFailed; + }, + }; + + var stdout = %%Buffer0.initEmpty(b.allocator); + var stderr = %%Buffer0.initEmpty(b.allocator); + + %%(??child.stdout).readAll(&stdout); + %%(??child.stderr).readAll(&stderr); + + if (!mem.eql(u8, self.expected_output, stdout.toSliceConst())) { + %%io.stderr.printf( + \\ + \\========= Expected this output: ========= + \\{} + \\================================================ + \\{} + \\ + , self.expected_output, stdout.toSliceConst()); + return error.TestFailed; + } + %%io.stderr.printf("OK\n"); + } +}; + diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 1be5f69bab..ef89ee81b7 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -272,13 +272,6 @@ static TestCase *add_example_compile_libc(const char *root_source_file) { //////////////////////////////////////////////////////////////////////////////////// -static void add_build_examples(void) { - add_example_compile("example/hello_world/hello.zig"); - add_example_compile_libc("example/hello_world/hello_libc.zig"); - add_example_compile("example/cat/main.zig"); - add_example_compile("example/guess_number/main.zig"); -} - //////////////////////////////////////////////////////////////////////////////////// @@ -2615,7 +2608,6 @@ int main(int argc, char **argv) { } } } - add_build_examples(); add_debug_safety_test_cases(); add_compile_failure_test_cases(); add_parse_error_tests(); diff --git a/test/tests.zig b/test/tests.zig index f971d7eef1..d398133db3 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1,587 +1,2 @@ -const std = @import("std"); -const debug = std.debug; -const build = std.build; -const os = std.os; -const StdIo = os.ChildProcess.StdIo; -const Term = os.ChildProcess.Term; -const Buffer0 = std.cstr.Buffer0; -const io = std.io; -const mem = std.mem; -const fmt = std.fmt; -const List = std.list.List; - -error TestFailed; - -pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step { - const cases = %%b.allocator.create(CompareOutputContext); - *cases = CompareOutputContext { - .b = b, - .compare_output_tests = b.step("test-compare-output", "Run the compare output tests"), - .test_index = 0, - .test_filter = test_filter, - }; - - cases.addC("hello world with libc", - \\const c = @cImport(@cInclude("stdio.h")); - \\export fn main(argc: c_int, argv: &&u8) -> c_int { - \\ _ = c.puts(c"Hello, world!"); - \\ return 0; - \\} - , "Hello, world!" ++ os.line_sep); - - cases.addCase({ - var tc = cases.create("multiple files with private function", - \\use @import("std").io; - \\use @import("foo.zig"); - \\ - \\pub fn main() -> %void { - \\ privateFunction(); - \\ %%stdout.printf("OK 2\n"); - \\} - \\ - \\fn privateFunction() { - \\ printText(); - \\} - , "OK 1\nOK 2\n"); - - tc.addSourceFile("foo.zig", - \\use @import("std").io; - \\ - \\// purposefully conflicting function with main.zig - \\// but it's private so it should be OK - \\fn privateFunction() { - \\ %%stdout.printf("OK 1\n"); - \\} - \\ - \\pub fn printText() { - \\ privateFunction(); - \\} - ); - - tc - }); - - cases.addCase({ - var tc = cases.create("import segregation", - \\use @import("foo.zig"); - \\use @import("bar.zig"); - \\ - \\pub fn main() -> %void { - \\ foo_function(); - \\ bar_function(); - \\} - , "OK\nOK\n"); - - tc.addSourceFile("foo.zig", - \\use @import("std").io; - \\pub fn foo_function() { - \\ %%stdout.printf("OK\n"); - \\} - ); - - tc.addSourceFile("bar.zig", - \\use @import("other.zig"); - \\use @import("std").io; - \\ - \\pub fn bar_function() { - \\ if (foo_function()) { - \\ %%stdout.printf("OK\n"); - \\ } - \\} - ); - - tc.addSourceFile("other.zig", - \\pub fn foo_function() -> bool { - \\ // this one conflicts with the one from foo - \\ return true; - \\} - ); - - tc - }); - - cases.addCase({ - var tc = cases.create("two files use import each other", - \\use @import("a.zig"); - \\ - \\pub fn main() -> %void { - \\ ok(); - \\} - , "OK\n"); - - tc.addSourceFile("a.zig", - \\use @import("b.zig"); - \\const io = @import("std").io; - \\ - \\pub const a_text = "OK\n"; - \\ - \\pub fn ok() { - \\ %%io.stdout.printf(b_text); - \\} - ); - - tc.addSourceFile("b.zig", - \\use @import("a.zig"); - \\ - \\pub const b_text = a_text; - ); - - tc - }); - - cases.add("hello world without libc", - \\const io = @import("std").io; - \\ - \\pub fn main() -> %void { - \\ %%io.stdout.printf("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a')); - \\} - , "Hello, world!\n0012 012 a\n"); - - cases.addC("number literals", - \\const c = @cImport(@cInclude("stdio.h")); - \\ - \\export fn main(argc: c_int, argv: &&u8) -> c_int { - \\ _ = c.printf(c"0: %llu\n", - \\ u64(0)); - \\ _ = c.printf(c"320402575052271: %llu\n", - \\ u64(320402575052271)); - \\ _ = c.printf(c"0x01236789abcdef: %llu\n", - \\ u64(0x01236789abcdef)); - \\ _ = c.printf(c"0xffffffffffffffff: %llu\n", - \\ u64(0xffffffffffffffff)); - \\ _ = c.printf(c"0x000000ffffffffffffffff: %llu\n", - \\ u64(0x000000ffffffffffffffff)); - \\ _ = c.printf(c"0o1777777777777777777777: %llu\n", - \\ u64(0o1777777777777777777777)); - \\ _ = c.printf(c"0o0000001777777777777777777777: %llu\n", - \\ u64(0o0000001777777777777777777777)); - \\ _ = c.printf(c"0b1111111111111111111111111111111111111111111111111111111111111111: %llu\n", - \\ u64(0b1111111111111111111111111111111111111111111111111111111111111111)); - \\ _ = c.printf(c"0b0000001111111111111111111111111111111111111111111111111111111111111111: %llu\n", - \\ u64(0b0000001111111111111111111111111111111111111111111111111111111111111111)); - \\ - \\ _ = c.printf(c"\n"); - \\ - \\ _ = c.printf(c"0.0: %a\n", - \\ f64(0.0)); - \\ _ = c.printf(c"0e0: %a\n", - \\ f64(0e0)); - \\ _ = c.printf(c"0.0e0: %a\n", - \\ f64(0.0e0)); - \\ _ = c.printf(c"000000000000000000000000000000000000000000000000000000000.0e0: %a\n", - \\ f64(000000000000000000000000000000000000000000000000000000000.0e0)); - \\ _ = c.printf(c"0.000000000000000000000000000000000000000000000000000000000e0: %a\n", - \\ f64(0.000000000000000000000000000000000000000000000000000000000e0)); - \\ _ = c.printf(c"0.0e000000000000000000000000000000000000000000000000000000000: %a\n", - \\ f64(0.0e000000000000000000000000000000000000000000000000000000000)); - \\ _ = c.printf(c"1.0: %a\n", - \\ f64(1.0)); - \\ _ = c.printf(c"10.0: %a\n", - \\ f64(10.0)); - \\ _ = c.printf(c"10.5: %a\n", - \\ f64(10.5)); - \\ _ = c.printf(c"10.5e5: %a\n", - \\ f64(10.5e5)); - \\ _ = c.printf(c"10.5e+5: %a\n", - \\ f64(10.5e+5)); - \\ _ = c.printf(c"50.0e-2: %a\n", - \\ f64(50.0e-2)); - \\ _ = c.printf(c"50e-2: %a\n", - \\ f64(50e-2)); - \\ - \\ _ = c.printf(c"\n"); - \\ - \\ _ = c.printf(c"0x1.0: %a\n", - \\ f64(0x1.0)); - \\ _ = c.printf(c"0x10.0: %a\n", - \\ f64(0x10.0)); - \\ _ = c.printf(c"0x100.0: %a\n", - \\ f64(0x100.0)); - \\ _ = c.printf(c"0x103.0: %a\n", - \\ f64(0x103.0)); - \\ _ = c.printf(c"0x103.7: %a\n", - \\ f64(0x103.7)); - \\ _ = c.printf(c"0x103.70: %a\n", - \\ f64(0x103.70)); - \\ _ = c.printf(c"0x103.70p4: %a\n", - \\ f64(0x103.70p4)); - \\ _ = c.printf(c"0x103.70p5: %a\n", - \\ f64(0x103.70p5)); - \\ _ = c.printf(c"0x103.70p+5: %a\n", - \\ f64(0x103.70p+5)); - \\ _ = c.printf(c"0x103.70p-5: %a\n", - \\ f64(0x103.70p-5)); - \\ - \\ _ = c.printf(c"\n"); - \\ - \\ _ = c.printf(c"0b10100.00010e0: %a\n", - \\ f64(0b10100.00010e0)); - \\ _ = c.printf(c"0o10700.00010e0: %a\n", - \\ f64(0o10700.00010e0)); - \\ - \\ return 0; - \\} - , - \\0: 0 - \\320402575052271: 320402575052271 - \\0x01236789abcdef: 320402575052271 - \\0xffffffffffffffff: 18446744073709551615 - \\0x000000ffffffffffffffff: 18446744073709551615 - \\0o1777777777777777777777: 18446744073709551615 - \\0o0000001777777777777777777777: 18446744073709551615 - \\0b1111111111111111111111111111111111111111111111111111111111111111: 18446744073709551615 - \\0b0000001111111111111111111111111111111111111111111111111111111111111111: 18446744073709551615 - \\ - \\0.0: 0x0p+0 - \\0e0: 0x0p+0 - \\0.0e0: 0x0p+0 - \\000000000000000000000000000000000000000000000000000000000.0e0: 0x0p+0 - \\0.000000000000000000000000000000000000000000000000000000000e0: 0x0p+0 - \\0.0e000000000000000000000000000000000000000000000000000000000: 0x0p+0 - \\1.0: 0x1p+0 - \\10.0: 0x1.4p+3 - \\10.5: 0x1.5p+3 - \\10.5e5: 0x1.0059p+20 - \\10.5e+5: 0x1.0059p+20 - \\50.0e-2: 0x1p-1 - \\50e-2: 0x1p-1 - \\ - \\0x1.0: 0x1p+0 - \\0x10.0: 0x1p+4 - \\0x100.0: 0x1p+8 - \\0x103.0: 0x1.03p+8 - \\0x103.7: 0x1.037p+8 - \\0x103.70: 0x1.037p+8 - \\0x103.70p4: 0x1.037p+12 - \\0x103.70p5: 0x1.037p+13 - \\0x103.70p+5: 0x1.037p+13 - \\0x103.70p-5: 0x1.037p+3 - \\ - \\0b10100.00010e0: 0x1.41p+4 - \\0o10700.00010e0: 0x1.1c0001p+12 - \\ - ); - - cases.add("order-independent declarations", - \\const io = @import("std").io; - \\const z = io.stdin_fileno; - \\const x : @typeOf(y) = 1234; - \\const y : u16 = 5678; - \\pub fn main() -> %void { - \\ var x_local : i32 = print_ok(x); - \\} - \\fn print_ok(val: @typeOf(x)) -> @typeOf(foo) { - \\ %%io.stdout.printf("OK\n"); - \\ return 0; - \\} - \\const foo : i32 = 0; - , "OK\n"); - - cases.addC("expose function pointer to C land", - \\const c = @cImport(@cInclude("stdlib.h")); - \\ - \\export fn compare_fn(a: ?&const c_void, b: ?&const c_void) -> c_int { - \\ const a_int = @ptrcast(&i32, a ?? unreachable); - \\ const b_int = @ptrcast(&i32, b ?? unreachable); - \\ if (*a_int < *b_int) { - \\ -1 - \\ } else if (*a_int > *b_int) { - \\ 1 - \\ } else { - \\ c_int(0) - \\ } - \\} - \\ - \\export fn main() -> c_int { - \\ var array = []u32 { 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; - \\ - \\ c.qsort(@ptrcast(&c_void, &array[0]), c_ulong(array.len), @sizeOf(i32), compare_fn); - \\ - \\ for (array) |item, i| { - \\ if (item != i) { - \\ c.abort(); - \\ } - \\ } - \\ - \\ return 0; - \\} - , ""); - - cases.addC("casting between float and integer types", - \\const c = @cImport(@cInclude("stdio.h")); - \\export fn main(argc: c_int, argv: &&u8) -> c_int { - \\ const small: f32 = 3.25; - \\ const x: f64 = small; - \\ const y = i32(x); - \\ const z = f64(y); - \\ _ = c.printf(c"%.2f\n%d\n%.2f\n%.2f\n", x, y, z, f64(-0.4)); - \\ return 0; - \\} - , "3.25\n3\n3.00\n-0.40\n"); - - cases.add("same named methods in incomplete struct", - \\const io = @import("std").io; - \\ - \\const Foo = struct { - \\ field1: Bar, - \\ - \\ fn method(a: &const Foo) -> bool { true } - \\}; - \\ - \\const Bar = struct { - \\ field2: i32, - \\ - \\ fn method(b: &const Bar) -> bool { true } - \\}; - \\ - \\pub fn main() -> %void { - \\ const bar = Bar {.field2 = 13,}; - \\ const foo = Foo {.field1 = bar,}; - \\ if (!foo.method()) { - \\ %%io.stdout.printf("BAD\n"); - \\ } - \\ if (!bar.method()) { - \\ %%io.stdout.printf("BAD\n"); - \\ } - \\ %%io.stdout.printf("OK\n"); - \\} - , "OK\n"); - - cases.add("defer with only fallthrough", - \\const io = @import("std").io; - \\pub fn main() -> %void { - \\ %%io.stdout.printf("before\n"); - \\ defer %%io.stdout.printf("defer1\n"); - \\ defer %%io.stdout.printf("defer2\n"); - \\ defer %%io.stdout.printf("defer3\n"); - \\ %%io.stdout.printf("after\n"); - \\} - , "before\nafter\ndefer3\ndefer2\ndefer1\n"); - - cases.add("defer with return", - \\const io = @import("std").io; - \\const os = @import("std").os; - \\pub fn main() -> %void { - \\ %%io.stdout.printf("before\n"); - \\ defer %%io.stdout.printf("defer1\n"); - \\ defer %%io.stdout.printf("defer2\n"); - \\ if (os.args.count() == 1) return; - \\ defer %%io.stdout.printf("defer3\n"); - \\ %%io.stdout.printf("after\n"); - \\} - , "before\ndefer2\ndefer1\n"); - - cases.add("%defer and it fails", - \\const io = @import("std").io; - \\pub fn main() -> %void { - \\ do_test() %% return; - \\} - \\fn do_test() -> %void { - \\ %%io.stdout.printf("before\n"); - \\ defer %%io.stdout.printf("defer1\n"); - \\ %defer %%io.stdout.printf("deferErr\n"); - \\ %return its_gonna_fail(); - \\ defer %%io.stdout.printf("defer3\n"); - \\ %%io.stdout.printf("after\n"); - \\} - \\error IToldYouItWouldFail; - \\fn its_gonna_fail() -> %void { - \\ return error.IToldYouItWouldFail; - \\} - , "before\ndeferErr\ndefer1\n"); - - cases.add("%defer and it passes", - \\const io = @import("std").io; - \\pub fn main() -> %void { - \\ do_test() %% return; - \\} - \\fn do_test() -> %void { - \\ %%io.stdout.printf("before\n"); - \\ defer %%io.stdout.printf("defer1\n"); - \\ %defer %%io.stdout.printf("deferErr\n"); - \\ %return its_gonna_pass(); - \\ defer %%io.stdout.printf("defer3\n"); - \\ %%io.stdout.printf("after\n"); - \\} - \\fn its_gonna_pass() -> %void { } - , "before\nafter\ndefer3\ndefer1\n"); - - cases.addCase({ - var tc = cases.create("@embedFile", - \\const foo_txt = @embedFile("foo.txt"); - \\const io = @import("std").io; - \\ - \\pub fn main() -> %void { - \\ %%io.stdout.printf(foo_txt); - \\} - , "1234\nabcd\n"); - - tc.addSourceFile("foo.txt", "1234\nabcd\n"); - - tc - }); - - return cases.compare_output_tests; -} - -const CompareOutputContext = struct { - b: &build.Builder, - compare_output_tests: &build.Step, - test_index: usize, - test_filter: ?[]const u8, - - const TestCase = struct { - name: []const u8, - sources: List(SourceFile), - expected_output: []const u8, - link_libc: bool, - - const SourceFile = struct { - filename: []const u8, - source: []const u8, - }; - - pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) { - %%self.sources.append(SourceFile { - .filename = filename, - .source = source, - }); - } - }; - - pub fn create(self: &CompareOutputContext, name: []const u8, source: []const u8, - expected_output: []const u8) -> TestCase - { - var tc = TestCase { - .name = name, - .sources = List(TestCase.SourceFile).init(self.b.allocator), - .expected_output = expected_output, - .link_libc = false, - }; - tc.addSourceFile("source.zig", source); - return tc; - } - - pub fn addC(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) { - var tc = self.create(name, source, expected_output); - tc.link_libc = true; - self.addCase(tc); - } - - pub fn add(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) { - const tc = self.create(name, source, expected_output); - self.addCase(tc); - } - - pub fn addCase(self: &CompareOutputContext, case: &const TestCase) { - const b = self.b; - - const root_src = %%os.path.join(b.allocator, "test_artifacts", case.sources.items[0].filename); - const exe_path = %%os.path.join(b.allocator, "test_artifacts", "test"); - - for ([]bool{false, true}) |release| { - const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} ({})", - case.name, if (release) "release" else "debug"); - if (const filter ?= self.test_filter) { - if (mem.indexOf(u8, annotated_case_name, filter) == null) - continue; - } - - const exe = b.addExecutable("test", root_src); - exe.setOutputPath(exe_path); - exe.setRelease(release); - if (case.link_libc) { - exe.linkLibrary("c"); - } - - for (case.sources.toSliceConst()) |src_file| { - const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename); - const write_src = b.addWriteFile(expanded_src_path, src_file.source); - exe.step.dependOn(&write_src.step); - } - - const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name, - case.expected_output); - run_and_cmp_output.step.dependOn(&exe.step); - - self.compare_output_tests.dependOn(&run_and_cmp_output.step); - } - } -}; - -const RunCompareOutputStep = struct { - step: build.Step, - context: &CompareOutputContext, - exe_path: []const u8, - name: []const u8, - expected_output: []const u8, - test_index: usize, - - pub fn create(context: &CompareOutputContext, exe_path: []const u8, - name: []const u8, expected_output: []const u8) -> &RunCompareOutputStep - { - const allocator = context.b.allocator; - const ptr = %%allocator.create(RunCompareOutputStep); - *ptr = RunCompareOutputStep { - .context = context, - .exe_path = exe_path, - .name = name, - .expected_output = expected_output, - .test_index = context.test_index, - .step = build.Step.init("RunCompareOutput", allocator, make), - }; - context.test_index += 1; - return ptr; - } - - fn make(step: &build.Step) -> %void { - const self = @fieldParentPtr(RunCompareOutputStep, "step", step); - const b = self.context.b; - - const full_exe_path = b.pathFromRoot(self.exe_path); - - %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name); - - var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, &b.env_map, - StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err| - { - debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err)); - }; - - const term = child.wait() %% |err| { - debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err)); - }; - switch (term) { - Term.Clean => |code| { - if (code != 0) { - %%io.stderr.printf("Process {} exited with error code {}\n", full_exe_path, code); - return error.TestFailed; - } - }, - else => { - %%io.stderr.printf("Process {} terminated unexpectedly\n", full_exe_path); - return error.TestFailed; - }, - }; - - var stdout = %%Buffer0.initEmpty(b.allocator); - var stderr = %%Buffer0.initEmpty(b.allocator); - - %%(??child.stdout).readAll(&stdout); - %%(??child.stderr).readAll(&stderr); - - if (!mem.eql(u8, self.expected_output, stdout.toSliceConst())) { - %%io.stderr.printf( - \\ - \\========= Expected this output: ========= - \\{} - \\================================================ - \\{} - \\ - , self.expected_output, stdout.toSliceConst()); - return error.TestFailed; - } - %%io.stderr.printf("OK\n"); - } -}; - +pub const addCompareOutputTests = @import("compare_output.zig").addCompareOutputTests; +pub const addBuildExampleTests = @import("build_examples.zig").addBuildExampleTests;