diff --git a/docs/rules.md b/docs/rules.md
index b698e3b0..57beb4ff 100644
--- a/docs/rules.md
+++ b/docs/rules.md
@@ -65,3 +65,24 @@ zig_package(name, de
| srcs | Other source files required when building the package. | List of labels | optional | [] |
+
+
+## zig_test
+
+
+zig_test(name, deps, main, srcs) ++ + + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| deps | Packages or libraries required to build the target. | List of labels | optional |
[] |
+| main | The main source file. | Label | required | |
+| srcs | Other source files required to build the target. | List of labels | optional | [] |
+
+
diff --git a/e2e/workspace/multiple-sources-and-packages-test/BUILD.bazel b/e2e/workspace/multiple-sources-and-packages-test/BUILD.bazel
new file mode 100644
index 00000000..ded7351c
--- /dev/null
+++ b/e2e/workspace/multiple-sources-and-packages-test/BUILD.bazel
@@ -0,0 +1,18 @@
+load("@rules_zig//zig:defs.bzl", "zig_package", "zig_test")
+
+zig_test(
+ name = "test",
+ srcs = [
+ "test/a.zig",
+ "test/b.zig",
+ ],
+ main = "main.zig",
+ deps = [
+ ":pkg",
+ ],
+)
+
+zig_package(
+ name = "pkg",
+ main = "pkg/main.zig",
+)
diff --git a/e2e/workspace/multiple-sources-and-packages-test/main.zig b/e2e/workspace/multiple-sources-and-packages-test/main.zig
new file mode 100644
index 00000000..f62985b8
--- /dev/null
+++ b/e2e/workspace/multiple-sources-and-packages-test/main.zig
@@ -0,0 +1,11 @@
+const std = @import("std");
+const pkg = @import("pkg");
+
+test "1 + 1" {
+ try std.testing.expectEqual(@as(i64, 2), pkg.add(1, 1));
+}
+
+test {
+ _ = @import("test/a.zig");
+ _ = @import("test/b.zig");
+}
diff --git a/e2e/workspace/multiple-sources-and-packages-test/pkg/main.zig b/e2e/workspace/multiple-sources-and-packages-test/pkg/main.zig
new file mode 100644
index 00000000..0e0658d1
--- /dev/null
+++ b/e2e/workspace/multiple-sources-and-packages-test/pkg/main.zig
@@ -0,0 +1,3 @@
+pub fn add(a: i64, b: i64) i64 {
+ return a + b;
+}
diff --git a/e2e/workspace/multiple-sources-and-packages-test/test/a.zig b/e2e/workspace/multiple-sources-and-packages-test/test/a.zig
new file mode 100644
index 00000000..2348dd81
--- /dev/null
+++ b/e2e/workspace/multiple-sources-and-packages-test/test/a.zig
@@ -0,0 +1,6 @@
+const std = @import("std");
+const pkg = @import("pkg");
+
+test "1 + 2" {
+ try std.testing.expectEqual(@as(i64, 3), pkg.add(1, 2));
+}
diff --git a/e2e/workspace/multiple-sources-and-packages-test/test/b.zig b/e2e/workspace/multiple-sources-and-packages-test/test/b.zig
new file mode 100644
index 00000000..092b6d74
--- /dev/null
+++ b/e2e/workspace/multiple-sources-and-packages-test/test/b.zig
@@ -0,0 +1,6 @@
+const std = @import("std");
+const pkg = @import("pkg");
+
+test "1 + 3" {
+ try std.testing.expectEqual(@as(i64, 4), pkg.add(1, 3));
+}
diff --git a/e2e/workspace/simple-test/BUILD.bazel b/e2e/workspace/simple-test/BUILD.bazel
new file mode 100644
index 00000000..81e9ffb8
--- /dev/null
+++ b/e2e/workspace/simple-test/BUILD.bazel
@@ -0,0 +1,6 @@
+load("@rules_zig//zig:defs.bzl", "zig_test")
+
+zig_test(
+ name = "test",
+ main = "main.zig",
+)
diff --git a/e2e/workspace/simple-test/main.zig b/e2e/workspace/simple-test/main.zig
new file mode 100644
index 00000000..f740796c
--- /dev/null
+++ b/e2e/workspace/simple-test/main.zig
@@ -0,0 +1,5 @@
+const std = @import("std");
+
+test "simple" {
+ try std.testing.expectEqual(2, 1 + 1);
+}
diff --git a/zig/BUILD.bazel b/zig/BUILD.bazel
index 5b3f2a0b..cad3e968 100644
--- a/zig/BUILD.bazel
+++ b/zig/BUILD.bazel
@@ -31,6 +31,7 @@ bzl_library(
"//zig/private:zig_binary",
"//zig/private:zig_library",
"//zig/private:zig_package",
+ "//zig/private:zig_test",
],
)
diff --git a/zig/defs.bzl b/zig/defs.bzl
index 8104f184..e7c82ada 100644
--- a/zig/defs.bzl
+++ b/zig/defs.bzl
@@ -3,7 +3,9 @@
load("//zig/private:zig_binary.bzl", _zig_binary = "zig_binary")
load("//zig/private:zig_library.bzl", _zig_library = "zig_library")
load("//zig/private:zig_package.bzl", _zig_package = "zig_package")
+load("//zig/private:zig_test.bzl", _zig_test = "zig_test")
zig_binary = _zig_binary
zig_library = _zig_library
zig_package = _zig_package
+zig_test = _zig_test
diff --git a/zig/private/BUILD.bazel b/zig/private/BUILD.bazel
index 0fac081b..16bdb357 100644
--- a/zig/private/BUILD.bazel
+++ b/zig/private/BUILD.bazel
@@ -32,6 +32,17 @@ bzl_library(
],
)
+bzl_library(
+ name = "zig_test",
+ srcs = ["zig_test.bzl"],
+ visibility = ["//zig:__subpackages__"],
+ deps = [
+ "//zig/private/common:filetypes",
+ "//zig/private/common:zig_cache",
+ "//zig/private/providers:zig_package_info",
+ ],
+)
+
bzl_library(
name = "toolchains_repo",
srcs = ["toolchains_repo.bzl"],
diff --git a/zig/private/zig_test.bzl b/zig/private/zig_test.bzl
new file mode 100644
index 00000000..f408701c
--- /dev/null
+++ b/zig/private/zig_test.bzl
@@ -0,0 +1,90 @@
+"""Implementation of the zig_test rule."""
+
+load("//zig/private/common:filetypes.bzl", "ZIG_SOURCE_EXTENSIONS")
+load("//zig/private/common:zig_cache.bzl", "zig_cache_output")
+load(
+ "//zig/private/providers:zig_package_info.bzl",
+ "ZigPackageInfo",
+ "zig_package_dependencies",
+)
+
+DOC = """\
+"""
+
+ATTRS = {
+ "main": attr.label(
+ allow_single_file = ZIG_SOURCE_EXTENSIONS,
+ doc = "The main source file.",
+ mandatory = True,
+ ),
+ "srcs": attr.label_list(
+ allow_files = ZIG_SOURCE_EXTENSIONS,
+ doc = "Other source files required to build the target.",
+ mandatory = False,
+ ),
+ "deps": attr.label_list(
+ doc = "Packages or libraries required to build the target.",
+ mandatory = False,
+ providers = [ZigPackageInfo],
+ ),
+}
+
+def _zig_test_impl(ctx):
+ ziginfo = ctx.toolchains["//zig:toolchain_type"].ziginfo
+
+ outputs = []
+
+ direct_inputs = []
+ transitive_inputs = []
+
+ args = ctx.actions.args()
+ args.use_param_file("@%s")
+
+ # TODO[AH] Append `.exe` extension on Windows.
+ output = ctx.actions.declare_file(ctx.label.name)
+ outputs.append(output)
+ args.add(output, format = "-femit-bin=%s")
+
+ direct_inputs.append(ctx.file.main)
+ direct_inputs.extend(ctx.files.srcs)
+ args.add(ctx.file.main)
+
+ zig_package_dependencies(
+ deps = ctx.attr.deps,
+ inputs = transitive_inputs,
+ args = args,
+ )
+
+ zig_cache_output(
+ actions = ctx.actions,
+ name = ctx.label.name,
+ outputs = outputs,
+ args = args,
+ )
+
+ ctx.actions.run(
+ outputs = outputs,
+ inputs = depset(direct = direct_inputs, transitive = transitive_inputs),
+ executable = ziginfo.target_tool_path,
+ tools = ziginfo.tool_files,
+ arguments = ["test", "--test-no-exec", args],
+ mnemonic = "ZigBuildTest",
+ progress_message = "Building %{input} as Zig test %{output}",
+ execution_requirements = {tag: "" for tag in ctx.attr.tags},
+ )
+
+ default = DefaultInfo(
+ executable = output,
+ files = depset([output]),
+ runfiles = ctx.runfiles(files = [output]),
+ )
+
+ return [default]
+
+zig_test = rule(
+ _zig_test_impl,
+ attrs = ATTRS,
+ doc = DOC,
+ test = True,
+ toolchains = ["//zig:toolchain_type"],
+)