How to use `build_options` inside test blocks

I’ve been reading through the testing docs and posts but I don’t understand how to use build options inside a test block without defining a test file specifically for it.

I have the following line in my build code where I add the depenency to my module.

    const build_options = b.addOptions();
    // ...
    bootloader.root_module.addOptions("build_options", build_options);

Inside the main file of the bootloader module, I defined the following test:

const build_options = @import("build_options")
//  ...
test "Test std.elf.Header read and itereate" {
    log.info("trying: {any}", .{build_options});
}

Despite normal code detects the build_options, I get the following error when I try to run my test.

zsh in ~/Desktop/Github/hypervisor ❱ zig test src/bootloader/boot.zig
src/bootloader/boot.zig:4:31: error: no module named 'build_options' available within module 'test'
const build_options = @import("build_options");
                              ^~~~~~~~~~~~~~~
referenced by:
    test.Test std.elf.Header read and itereate: src/bootloader/boot.zig:234:33

I guess the test code follows a different build system, but I couldn’t found docs about it.

Can somebody explain this or point me to the right documentation to read?

Thanks in advance

Use the build system to define and run your test:

const exe_tests = b.addTest(.{
    .name = "test",
    .root_module = bootloader.root_module,
});

const run_unit_tests = b.addRunArtifact(exe_tests);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_unit_tests.step);

You also could manually pass the module import command-line options to the lower-level zig test invocation but that is more complicated and (in my opinion) not worth it, instead I would always invoke it from the build system and possibly use zig build test --verbose to see how to do the lower-level invocation

1 Like

Thanks for your reply.
I’ve tried to run it using the build system but it doesn’t work. Seems zig is trying to build the whole module and run the main function of it:

 zsh in ~/Desktop/Github/hypervisor  1 ❱ zig build test --verbose
/snap/zig/16117/zig test -gdwarf64 -ODebug -target x86_64-uefi -mcpu baseline --dep build_options -Mroot=/home/nachh/Desktop/Github/hypervisor/src/bootloader/boot.zig -Mbuild_options=.zig-cache/c/ebba3969a561e850a6852bd8bbf87481/options.zig --cache-dir .zig-cache --global-cache-dir /home/nachh/.cache/zig --name test --zig-lib-dir /snap/zig/16117/lib/ --listen=-
test
└─ run test
   └─ compile test Debug x86_64-uefi 3 errors
/snap/zig/16117/lib/std/Io/Threaded.zig:2064:45: error: struct 'posix.system__struct_10467' has no member named 'getrandom'
const use_dev_urandom = @TypeOf(posix.system.getrandom) == void and native_os == .linux;
                                ~~~~~~~~~~~~^~~~~~~~~~
/snap/zig/16117/lib/std/posix.zig:57:13: note: struct declared here
    else => struct {
            ^~~~~~
referenced by:
    RandomFile: /snap/zig/16117/lib/std/Io/Threaded.zig:373:17
    Io.Threaded: /snap/zig/16117/lib/std/Io/Threaded.zig:81:14
    10 reference(s) hidden; use '-freference-trace=12' to see all references
/snap/zig/16117/lib/std/posix.zig:90:27: error: struct 'posix.system__struct_10467' has no member named 'IOV_MAX'
pub const IOV_MAX = system.IOV_MAX;
                    ~~~~~~^~~~~~~~
/snap/zig/16117/lib/std/posix.zig:57:13: note: struct declared here
    else => struct {
            ^~~~~~
/snap/zig/16117/lib/std/start.zig:114:17: error: expected 1 argument(s), found 0
            root.main();
            ~~~~^~~~~
/snap/zig/16117/lib/compiler/test_runner.zig:35:5: note: function declared here
pub fn main(init: std.process.Init.Minimal) void {
~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: 3 compilation errors
failed command: /snap/zig/16117/zig test -gdwarf64 -ODebug -target x86_64-uefi -mcpu baseline --dep build_options -Mroot=/home/nachh/Desktop/Github/hypervisor/src/bootloader/boot.zig -Mbuild_options=.zig-cache/c/ebba3969a561e850a6852bd8bbf87481/options.zig --cache-dir .zig-cache --global-cache-dir /home/nachh/.cache/zig --name test --zig-lib-dir /snap/zig/16117/lib/ --listen=-

I’m unsure if this is the best way to do it because it seem to compile a x64-uefi test binary.
I know I can create a new file with only test code I want to run there but imv is much clear to have test cases right near each function definition, so I would like to avoid that unless it is the only way to do this.

I’ve also tried to pass zig test command using --dep flag with build or build.zig without success.
May be there is no way to use options defined for build inside test blocks?

The problem is indeed that it tries to compile the test runner for the x86_64-uefi target, but you want to run the tests on the host architecture. (The errors don’t come from trying to compile your main function, but trying to compile the main function of the test runner)

I assume that you are hardcoding the x86_64-uefi target when creating your root module. If you use:

b.createModule(.{
    // ...
    .target = b.standardTargetOptions(.{}),
})

You can then specify the target from the command line with -Dtarget=.

An alternative is to create the module twice: one as x86_64-uefi for the real binary and one with the host target for running the tests.

1 Like

Oh my god, thanks that what’s it :melting_face:

Now when I create another module for the test cases with that architecture, I can define test blocks in the same boot.zig file and it works.

1 Like