| author | |
| committer | |
| log | 927f233ff82bd422b387b4eef7fcbc67823e4b85 |
| tree | b921686a62e6e60efa6db973488d9fffb93c8ebb |
| parent | 6a7ca4b8b0fcce9e5f6a4d3f799e83021929c975 |
This is fairly straightforward; the actual compiler changes are limited
to the CLI, since `Compilation` already supports this combination.
A new `std.Build` API is introduced to allow representing this. By
passing the `emit_object` option to `std.Build.addTest`, you get a
`Step.Compile` which emits an object file; you can then use that as you
would any other object, such as either installing it for external use,
or linking it into another step.
A standalone test is added to cover the build system API. It builds a
test into an object, and links it into a final executable, which it then
runs.
Using this build system mechanism prevents the build system from
noticing that you're running a `zig test`, so the build runner and test
runner do not communicate over stdio. However, that's okay, because the
real-world use cases for this feature don't want to do that anyway!
Resolves: #233748 files changed, 118 insertions(+), 42 deletions(-)
lib/std/Build.zig+5-1| ... | @@ -1019,6 +1019,10 @@ pub const TestOptions = struct { | ... | @@ -1019,6 +1019,10 @@ pub const TestOptions = struct { |
| 1019 | use_llvm: ?bool = null, | 1019 | use_llvm: ?bool = null, |
| 1020 | use_lld: ?bool = null, | 1020 | use_lld: ?bool = null, |
| 1021 | zig_lib_dir: ?LazyPath = null, | 1021 | zig_lib_dir: ?LazyPath = null, |
| 1022 | /// Emits an object file instead of a test binary. | ||
| 1023 | /// The object must be linked separately. | ||
| 1024 | /// Usually used in conjunction with a custom `test_runner`. | ||
| 1025 | emit_object: bool = false, | ||
| 1022 | 1026 | ||
| 1023 | /// Prefer populating this field (using e.g. `createModule`) instead of populating | 1027 | /// Prefer populating this field (using e.g. `createModule`) instead of populating |
| 1024 | /// the following fields (`root_source_file` etc). In a future release, those fields | 1028 | /// the following fields (`root_source_file` etc). In a future release, those fields |
| ... | @@ -1067,7 +1071,7 @@ pub fn addTest(b: *Build, options: TestOptions) *Step.Compile { | ... | @@ -1067,7 +1071,7 @@ pub fn addTest(b: *Build, options: TestOptions) *Step.Compile { |
| 1067 | } | 1071 | } |
| 1068 | return .create(b, .{ | 1072 | return .create(b, .{ |
| 1069 | .name = options.name, | 1073 | .name = options.name, |
| 1070 | .kind = .@"test", | 1074 | .kind = if (options.emit_object) .test_obj else .@"test", |
| 1071 | .root_module = options.root_module orelse b.createModule(.{ | 1075 | .root_module = options.root_module orelse b.createModule(.{ |
| 1072 | .root_source_file = options.root_source_file orelse @panic("`root_module` and `root_source_file` cannot both be null"), | 1076 | .root_source_file = options.root_source_file orelse @panic("`root_module` and `root_source_file` cannot both be null"), |
| 1073 | .target = options.target orelse b.graph.host, | 1077 | .target = options.target orelse b.graph.host, |
lib/std/Build/Module.zig+1-1| ... | @@ -474,7 +474,7 @@ pub fn addObjectFile(m: *Module, object: LazyPath) void { | ... | @@ -474,7 +474,7 @@ pub fn addObjectFile(m: *Module, object: LazyPath) void { |
| 474 | } | 474 | } |
| 475 | 475 | ||
| 476 | pub fn addObject(m: *Module, object: *Step.Compile) void { | 476 | pub fn addObject(m: *Module, object: *Step.Compile) void { |
| 477 | assert(object.kind == .obj); | 477 | assert(object.kind == .obj or object.kind == .test_obj); |
| 478 | m.linkLibraryOrObject(object); | 478 | m.linkLibraryOrObject(object); |
| 479 | } | 479 | } |
| 480 | 480 |
lib/std/Build/Step/Compile.zig+7-4| ... | @@ -293,6 +293,7 @@ pub const Kind = enum { | ... | @@ -293,6 +293,7 @@ pub const Kind = enum { |
| 293 | lib, | 293 | lib, |
| 294 | obj, | 294 | obj, |
| 295 | @"test", | 295 | @"test", |
| 296 | test_obj, | ||
| 296 | }; | 297 | }; |
| 297 | 298 | ||
| 298 | pub const HeaderInstallation = union(enum) { | 299 | pub const HeaderInstallation = union(enum) { |
| ... | @@ -370,7 +371,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile { | ... | @@ -370,7 +371,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile { |
| 370 | } | 371 | } |
| 371 | 372 | ||
| 372 | // Avoid the common case of the step name looking like "zig test test". | 373 | // Avoid the common case of the step name looking like "zig test test". |
| 373 | const name_adjusted = if (options.kind == .@"test" and mem.eql(u8, name, "test")) | 374 | const name_adjusted = if ((options.kind == .@"test" or options.kind == .test_obj) and mem.eql(u8, name, "test")) |
| 374 | "" | 375 | "" |
| 375 | else | 376 | else |
| 376 | owner.fmt("{s} ", .{name}); | 377 | owner.fmt("{s} ", .{name}); |
| ... | @@ -385,6 +386,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile { | ... | @@ -385,6 +386,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile { |
| 385 | .lib => "zig build-lib", | 386 | .lib => "zig build-lib", |
| 386 | .obj => "zig build-obj", | 387 | .obj => "zig build-obj", |
| 387 | .@"test" => "zig test", | 388 | .@"test" => "zig test", |
| 389 | .test_obj => "zig test-obj", | ||
| 388 | }, | 390 | }, |
| 389 | name_adjusted, | 391 | name_adjusted, |
| 390 | @tagName(options.root_module.optimize orelse .Debug), | 392 | @tagName(options.root_module.optimize orelse .Debug), |
| ... | @@ -396,7 +398,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile { | ... | @@ -396,7 +398,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile { |
| 396 | .target = target, | 398 | .target = target, |
| 397 | .output_mode = switch (options.kind) { | 399 | .output_mode = switch (options.kind) { |
| 398 | .lib => .Lib, | 400 | .lib => .Lib, |
| 399 | .obj => .Obj, | 401 | .obj, .test_obj => .Obj, |
| 400 | .exe, .@"test" => .Exe, | 402 | .exe, .@"test" => .Exe, |
| 401 | }, | 403 | }, |
| 402 | .link_mode = options.linkage, | 404 | .link_mode = options.linkage, |
| ... | @@ -1053,6 +1055,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { | ... | @@ -1053,6 +1055,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { |
| 1053 | .exe => "build-exe", | 1055 | .exe => "build-exe", |
| 1054 | .obj => "build-obj", | 1056 | .obj => "build-obj", |
| 1055 | .@"test" => "test", | 1057 | .@"test" => "test", |
| 1058 | .test_obj => "test-obj", | ||
| 1056 | }; | 1059 | }; |
| 1057 | try zig_args.append(cmd); | 1060 | try zig_args.append(cmd); |
| 1058 | 1061 | ||
| ... | @@ -1222,9 +1225,9 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { | ... | @@ -1222,9 +1225,9 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { |
| 1222 | switch (other.kind) { | 1225 | switch (other.kind) { |
| 1223 | .exe => return step.fail("cannot link with an executable build artifact", .{}), | 1226 | .exe => return step.fail("cannot link with an executable build artifact", .{}), |
| 1224 | .@"test" => return step.fail("cannot link with a test", .{}), | 1227 | .@"test" => return step.fail("cannot link with a test", .{}), |
| 1225 | .obj => { | 1228 | .obj, .test_obj => { |
| 1226 | const included_in_lib_or_obj = !my_responsibility and | 1229 | const included_in_lib_or_obj = !my_responsibility and |
| 1227 | (dep_compile.kind == .lib or dep_compile.kind == .obj); | 1230 | (dep_compile.kind == .lib or dep_compile.kind == .obj or dep_compile.kind == .test_obj); |
| 1228 | if (!already_linked and !included_in_lib_or_obj) { | 1231 | if (!already_linked and !included_in_lib_or_obj) { |
| 1229 | try zig_args.append(other.getEmittedBin().getPath2(b, step)); | 1232 | try zig_args.append(other.getEmittedBin().getPath2(b, step)); |
| 1230 | total_linker_objects += 1; | 1233 | total_linker_objects += 1; |
lib/std/Build/Step/InstallArtifact.zig+1-1| ... | @@ -56,7 +56,7 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins | ... | @@ -56,7 +56,7 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins |
| 56 | const dest_dir: ?InstallDir = switch (options.dest_dir) { | 56 | const dest_dir: ?InstallDir = switch (options.dest_dir) { |
| 57 | .disabled => null, | 57 | .disabled => null, |
| 58 | .default => switch (artifact.kind) { | 58 | .default => switch (artifact.kind) { |
| 59 | .obj => @panic("object files have no standard installation procedure"), | 59 | .obj, .test_obj => @panic("object files have no standard installation procedure"), |
| 60 | .exe, .@"test" => .bin, | 60 | .exe, .@"test" => .bin, |
| 61 | .lib => if (artifact.isDll()) .bin else .lib, | 61 | .lib => if (artifact.isDll()) .bin else .lib, |
| 62 | }, | 62 | }, |
src/main.zig+52-35| ... | @@ -278,6 +278,9 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { | ... | @@ -278,6 +278,9 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 278 | } else if (mem.eql(u8, cmd, "test")) { | 278 | } else if (mem.eql(u8, cmd, "test")) { |
| 279 | dev.check(.test_command); | 279 | dev.check(.test_command); |
| 280 | return buildOutputType(gpa, arena, args, .zig_test); | 280 | return buildOutputType(gpa, arena, args, .zig_test); |
| 281 | } else if (mem.eql(u8, cmd, "test-obj")) { | ||
| 282 | dev.check(.test_command); | ||
| 283 | return buildOutputType(gpa, arena, args, .zig_test_obj); | ||
| 281 | } else if (mem.eql(u8, cmd, "run")) { | 284 | } else if (mem.eql(u8, cmd, "run")) { |
| 282 | dev.check(.run_command); | 285 | dev.check(.run_command); |
| 283 | return buildOutputType(gpa, arena, args, .run); | 286 | return buildOutputType(gpa, arena, args, .run); |
| ... | @@ -764,6 +767,7 @@ const ArgMode = union(enum) { | ... | @@ -764,6 +767,7 @@ const ArgMode = union(enum) { |
| 764 | cpp, | 767 | cpp, |
| 765 | translate_c, | 768 | translate_c, |
| 766 | zig_test, | 769 | zig_test, |
| 770 | zig_test_obj, | ||
| 767 | run, | 771 | run, |
| 768 | }; | 772 | }; |
| 769 | 773 | ||
| ... | @@ -975,7 +979,10 @@ fn buildOutputType( | ... | @@ -975,7 +979,10 @@ fn buildOutputType( |
| 975 | .dynamic_linker = null, | 979 | .dynamic_linker = null, |
| 976 | .modules = .{}, | 980 | .modules = .{}, |
| 977 | .opts = .{ | 981 | .opts = .{ |
| 978 | .is_test = arg_mode == .zig_test, | 982 | .is_test = switch (arg_mode) { |
| 983 | .zig_test, .zig_test_obj => true, | ||
| 984 | .build, .cc, .cpp, .translate_c, .run => false, | ||
| 985 | }, | ||
| 979 | // Populated while parsing CLI args. | 986 | // Populated while parsing CLI args. |
| 980 | .output_mode = undefined, | 987 | .output_mode = undefined, |
| 981 | // Populated in the call to `createModule` for the root module. | 988 | // Populated in the call to `createModule` for the root module. |
| ... | @@ -1030,7 +1037,7 @@ fn buildOutputType( | ... | @@ -1030,7 +1037,7 @@ fn buildOutputType( |
| 1030 | var n_jobs: ?u32 = null; | 1037 | var n_jobs: ?u32 = null; |
| 1031 | 1038 | ||
| 1032 | switch (arg_mode) { | 1039 | switch (arg_mode) { |
| 1033 | .build, .translate_c, .zig_test, .run => { | 1040 | .build, .translate_c, .zig_test, .zig_test_obj, .run => { |
| 1034 | switch (arg_mode) { | 1041 | switch (arg_mode) { |
| 1035 | .build => |m| { | 1042 | .build => |m| { |
| 1036 | create_module.opts.output_mode = m; | 1043 | create_module.opts.output_mode = m; |
| ... | @@ -1042,6 +1049,9 @@ fn buildOutputType( | ... | @@ -1042,6 +1049,9 @@ fn buildOutputType( |
| 1042 | .zig_test, .run => { | 1049 | .zig_test, .run => { |
| 1043 | create_module.opts.output_mode = .Exe; | 1050 | create_module.opts.output_mode = .Exe; |
| 1044 | }, | 1051 | }, |
| 1052 | .zig_test_obj => { | ||
| 1053 | create_module.opts.output_mode = .Obj; | ||
| 1054 | }, | ||
| 1045 | else => unreachable, | 1055 | else => unreachable, |
| 1046 | } | 1056 | } |
| 1047 | 1057 | ||
| ... | @@ -2834,6 +2844,10 @@ fn buildOutputType( | ... | @@ -2834,6 +2844,10 @@ fn buildOutputType( |
| 2834 | }, | 2844 | }, |
| 2835 | } | 2845 | } |
| 2836 | 2846 | ||
| 2847 | if (arg_mode == .zig_test_obj and !test_no_exec and listen == .none) { | ||
| 2848 | fatal("test-obj requires --test-no-exec", .{}); | ||
| 2849 | } | ||
| 2850 | |||
| 2837 | if (arg_mode == .translate_c and create_module.c_source_files.items.len != 1) { | 2851 | if (arg_mode == .translate_c and create_module.c_source_files.items.len != 1) { |
| 2838 | fatal("translate-c expects exactly 1 source file (found {d})", .{create_module.c_source_files.items.len}); | 2852 | fatal("translate-c expects exactly 1 source file (found {d})", .{create_module.c_source_files.items.len}); |
| 2839 | } | 2853 | } |
| ... | @@ -2903,10 +2917,10 @@ fn buildOutputType( | ... | @@ -2903,10 +2917,10 @@ fn buildOutputType( |
| 2903 | create_module.opts.any_error_tracing = true; | 2917 | create_module.opts.any_error_tracing = true; |
| 2904 | 2918 | ||
| 2905 | const src_path = try introspect.resolvePath(arena, unresolved_src_path); | 2919 | const src_path = try introspect.resolvePath(arena, unresolved_src_path); |
| 2906 | const name = if (arg_mode == .zig_test) | 2920 | const name = switch (arg_mode) { |
| 2907 | "test" | 2921 | .zig_test => "test", |
| 2908 | else | 2922 | .build, .cc, .cpp, .translate_c, .zig_test_obj, .run => fs.path.stem(fs.path.basename(src_path)), |
| 2909 | fs.path.stem(fs.path.basename(src_path)); | 2923 | }; |
| 2910 | 2924 | ||
| 2911 | try create_module.modules.put(arena, name, .{ | 2925 | try create_module.modules.put(arena, name, .{ |
| 2912 | .paths = .{ | 2926 | .paths = .{ |
| ... | @@ -2935,7 +2949,7 @@ fn buildOutputType( | ... | @@ -2935,7 +2949,7 @@ fn buildOutputType( |
| 2935 | rc_source_files_owner_index = create_module.rc_source_files.items.len; | 2949 | rc_source_files_owner_index = create_module.rc_source_files.items.len; |
| 2936 | } | 2950 | } |
| 2937 | 2951 | ||
| 2938 | if (!create_module.opts.have_zcu and arg_mode == .zig_test) { | 2952 | if (!create_module.opts.have_zcu and create_module.opts.is_test) { |
| 2939 | fatal("`zig test` expects a zig source file argument", .{}); | 2953 | fatal("`zig test` expects a zig source file argument", .{}); |
| 2940 | } | 2954 | } |
| 2941 | 2955 | ||
| ... | @@ -3037,16 +3051,36 @@ fn buildOutputType( | ... | @@ -3037,16 +3051,36 @@ fn buildOutputType( |
| 3037 | break :m null; | 3051 | break :m null; |
| 3038 | }; | 3052 | }; |
| 3039 | 3053 | ||
| 3040 | const root_mod = if (arg_mode == .zig_test) root_mod: { | 3054 | const root_mod = switch (arg_mode) { |
| 3041 | const test_mod = if (test_runner_path) |test_runner| test_mod: { | 3055 | .zig_test, .zig_test_obj => root_mod: { |
| 3042 | const test_mod = try Package.Module.create(arena, .{ | 3056 | const test_mod = if (test_runner_path) |test_runner| test_mod: { |
| 3057 | const test_mod = try Package.Module.create(arena, .{ | ||
| 3058 | .global_cache_directory = global_cache_directory, | ||
| 3059 | .paths = .{ | ||
| 3060 | .root = .{ | ||
| 3061 | .root_dir = Cache.Directory.cwd(), | ||
| 3062 | .sub_path = fs.path.dirname(test_runner) orelse "", | ||
| 3063 | }, | ||
| 3064 | .root_src_path = fs.path.basename(test_runner), | ||
| 3065 | }, | ||
| 3066 | .fully_qualified_name = "root", | ||
| 3067 | .cc_argv = &.{}, | ||
| 3068 | .inherited = .{}, | ||
| 3069 | .global = create_module.resolved_options, | ||
| 3070 | .parent = main_mod, | ||
| 3071 | .builtin_mod = main_mod.getBuiltinDependency(), | ||
| 3072 | .builtin_modules = null, // `builtin_mod` is specified | ||
| 3073 | }); | ||
| 3074 | test_mod.deps = try main_mod.deps.clone(arena); | ||
| 3075 | break :test_mod test_mod; | ||
| 3076 | } else try Package.Module.create(arena, .{ | ||
| 3043 | .global_cache_directory = global_cache_directory, | 3077 | .global_cache_directory = global_cache_directory, |
| 3044 | .paths = .{ | 3078 | .paths = .{ |
| 3045 | .root = .{ | 3079 | .root = .{ |
| 3046 | .root_dir = Cache.Directory.cwd(), | 3080 | .root_dir = zig_lib_directory, |
| 3047 | .sub_path = fs.path.dirname(test_runner) orelse "", | 3081 | .sub_path = "compiler", |
| 3048 | }, | 3082 | }, |
| 3049 | .root_src_path = fs.path.basename(test_runner), | 3083 | .root_src_path = "test_runner.zig", |
| 3050 | }, | 3084 | }, |
| 3051 | .fully_qualified_name = "root", | 3085 | .fully_qualified_name = "root", |
| 3052 | .cc_argv = &.{}, | 3086 | .cc_argv = &.{}, |
| ... | @@ -3056,28 +3090,11 @@ fn buildOutputType( | ... | @@ -3056,28 +3090,11 @@ fn buildOutputType( |
| 3056 | .builtin_mod = main_mod.getBuiltinDependency(), | 3090 | .builtin_mod = main_mod.getBuiltinDependency(), |
| 3057 | .builtin_modules = null, // `builtin_mod` is specified | 3091 | .builtin_modules = null, // `builtin_mod` is specified |
| 3058 | }); | 3092 | }); |
| 3059 | test_mod.deps = try main_mod.deps.clone(arena); | ||
| 3060 | break :test_mod test_mod; | ||
| 3061 | } else try Package.Module.create(arena, .{ | ||
| 3062 | .global_cache_directory = global_cache_directory, | ||
| 3063 | .paths = .{ | ||
| 3064 | .root = .{ | ||
| 3065 | .root_dir = zig_lib_directory, | ||
| 3066 | .sub_path = "compiler", | ||
| 3067 | }, | ||
| 3068 | .root_src_path = "test_runner.zig", | ||
| 3069 | }, | ||
| 3070 | .fully_qualified_name = "root", | ||
| 3071 | .cc_argv = &.{}, | ||
| 3072 | .inherited = .{}, | ||
| 3073 | .global = create_module.resolved_options, | ||
| 3074 | .parent = main_mod, | ||
| 3075 | .builtin_mod = main_mod.getBuiltinDependency(), | ||
| 3076 | .builtin_modules = null, // `builtin_mod` is specified | ||
| 3077 | }); | ||
| 3078 | 3093 | ||
| 3079 | break :root_mod test_mod; | 3094 | break :root_mod test_mod; |
| 3080 | } else main_mod; | 3095 | }, |
| 3096 | else => main_mod, | ||
| 3097 | }; | ||
| 3081 | 3098 | ||
| 3082 | const target = main_mod.resolved_target.result; | 3099 | const target = main_mod.resolved_target.result; |
| 3083 | 3100 | ||
| ... | @@ -3202,7 +3219,7 @@ fn buildOutputType( | ... | @@ -3202,7 +3219,7 @@ fn buildOutputType( |
| 3202 | .directory = blk: { | 3219 | .directory = blk: { |
| 3203 | switch (arg_mode) { | 3220 | switch (arg_mode) { |
| 3204 | .run, .zig_test => break :blk null, | 3221 | .run, .zig_test => break :blk null, |
| 3205 | else => { | 3222 | .build, .cc, .cpp, .translate_c, .zig_test_obj => { |
| 3206 | if (output_to_cache) { | 3223 | if (output_to_cache) { |
| 3207 | break :blk null; | 3224 | break :blk null; |
| 3208 | } else { | 3225 | } else { |
test/standalone/build.zig.zon+3| ... | @@ -5,6 +5,9 @@ | ... | @@ -5,6 +5,9 @@ |
| 5 | .simple = .{ | 5 | .simple = .{ |
| 6 | .path = "simple", | 6 | .path = "simple", |
| 7 | }, | 7 | }, |
| 8 | .test_obj_link_run = .{ | ||
| 9 | .path = "test_obj_link_run", | ||
| 10 | }, | ||
| 8 | .test_runner_path = .{ | 11 | .test_runner_path = .{ |
| 9 | .path = "test_runner_path", | 12 | .path = "test_runner_path", |
| 10 | }, | 13 | }, |
test/standalone/test_obj_link_run/build.zig created+32| ... | @@ -0,0 +1,32 @@ | ||
| 1 | pub fn build(b: *std.Build) void { | ||
| 2 | // To avoid having to explicitly link required system libraries into the final test | ||
| 3 | // executable (e.g. ntdll on Windows), we'll just link everything with libc here. | ||
| 4 | |||
| 5 | const test_obj = b.addTest(.{ | ||
| 6 | .emit_object = true, | ||
| 7 | .root_module = b.createModule(.{ | ||
| 8 | .root_source_file = b.path("src/main.zig"), | ||
| 9 | .target = b.graph.host, | ||
| 10 | .link_libc = true, | ||
| 11 | }), | ||
| 12 | }); | ||
| 13 | |||
| 14 | const test_exe_mod = b.createModule(.{ | ||
| 15 | .root_source_file = null, | ||
| 16 | .target = b.graph.host, | ||
| 17 | .link_libc = true, | ||
| 18 | }); | ||
| 19 | test_exe_mod.addObject(test_obj); | ||
| 20 | const test_exe = b.addExecutable(.{ | ||
| 21 | .name = "test", | ||
| 22 | .root_module = test_exe_mod, | ||
| 23 | }); | ||
| 24 | |||
| 25 | const test_step = b.step("test", "Test the program"); | ||
| 26 | b.default_step = test_step; | ||
| 27 | |||
| 28 | const test_run = b.addRunArtifact(test_exe); | ||
| 29 | test_step.dependOn(&test_run.step); | ||
| 30 | } | ||
| 31 | |||
| 32 | const std = @import("std"); | ||
test/standalone/test_obj_link_run/src/main.zig created+17| ... | @@ -0,0 +1,17 @@ | ||
| 1 | test { | ||
| 2 | try std.testing.expect(true); | ||
| 3 | } | ||
| 4 | |||
| 5 | test "equality" { | ||
| 6 | try std.testing.expect(one() == 1); | ||
| 7 | } | ||
| 8 | |||
| 9 | test "arithmetic" { | ||
| 10 | try std.testing.expect(one() + 2 == 3); | ||
| 11 | } | ||
| 12 | |||
| 13 | fn one() u32 { | ||
| 14 | return 1; | ||
| 15 | } | ||
| 16 | |||
| 17 | const std = @import("std"); | ||