diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f113dab2f6..68638ae46f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -213,6 +213,7 @@ jobs: - run: sudo apt-get install -y inotify-tools - run: zig build test_nlua0 - run: zig build nvim_bin && ./zig-out/bin/nvim --version + - run: zig build unittest - run: zig build functionaltest windows: diff --git a/build.zig b/build.zig index 0361268116..a16115a2de 100644 --- a/build.zig +++ b/build.zig @@ -149,6 +149,8 @@ pub fn build(b: *std.Build) !void { } } + const support_unittests = use_luajit; + const gen_config = b.addWriteFiles(); const version_lua = gen_config.add("nvim_version.lua", lua_version_info(b)); @@ -232,7 +234,7 @@ pub fn build(b: *std.Build) !void { // TODO(zig): using getEmittedIncludeTree() is ugly af. we want run_preprocessor() // to use the std.build.Module include_path thing - const include_path = &.{ + const include_path = [_]LazyPath{ b.path("src/"), gen_config.getDirectory(), lua.getEmittedIncludeTree(), @@ -243,10 +245,10 @@ pub fn build(b: *std.Build) !void { treesitter.artifact("tree-sitter").getEmittedIncludeTree(), }; - const gen_headers, const funcs_data = try gen.nvim_gen_sources(b, nlua0, &nvim_sources, &nvim_headers, &api_headers, include_path, target, versiondef_git, version_lua); + const gen_headers, const funcs_data = try gen.nvim_gen_sources(b, nlua0, &nvim_sources, &nvim_headers, &api_headers, &include_path, target, versiondef_git, version_lua); const test_config_step = b.addWriteFiles(); - _ = test_config_step.add("test/cmakeconfig/paths.lua", try test_config(b, gen_headers.getDirectory())); + _ = test_config_step.add("test/cmakeconfig/paths.lua", try test_config(b)); const test_gen_step = b.step("gen_headers", "debug: output generated headers"); const config_install = b.addInstallDirectory(.{ .source_dir = gen_config.getDirectory(), .install_dir = .prefix, .install_subdir = "config/" }); @@ -258,6 +260,7 @@ pub fn build(b: *std.Build) !void { .target = target, .optimize = optimize, }); + nvim_exe.rdynamic = true; // -E nvim_exe.linkLibrary(lua); nvim_exe.linkLibrary(libuv); @@ -273,16 +276,31 @@ pub fn build(b: *std.Build) !void { nvim_exe.addIncludePath(gen_headers.getDirectory()); build_lua.add_lua_modules(nvim_exe.root_module, lpeg, use_luajit, false); - const src_paths = try b.allocator.alloc([]u8, nvim_sources.items.len); + var unit_test_sources = try std.ArrayList([]u8).initCapacity(b.allocator, 10); + if (support_unittests) { + var unit_test_fixtures = try src_dir.openDir("test/unit/fixtures/", .{ .iterate = true }); + defer unit_test_fixtures.close(); + var it = unit_test_fixtures.iterateAssumeFirstIteration(); + while (try it.next()) |entry| { + if (entry.name.len < 3) continue; + if (std.mem.eql(u8, ".c", entry.name[entry.name.len - 2 ..])) { + try unit_test_sources.append(b.fmt("test/unit/fixtures/{s}", .{entry.name})); + } + } + } + + const src_paths = try b.allocator.alloc([]u8, nvim_sources.items.len + unit_test_sources.items.len); for (nvim_sources.items, 0..) |s, i| { src_paths[i] = b.fmt("src/nvim/{s}", .{s.name}); } + @memcpy(src_paths[nvim_sources.items.len..], unit_test_sources.items); const flags = [_][]const u8{ "-std=gnu99", "-DINCLUDE_GENERATED_DECLARATIONS", "-DZIG_BUILD", "-D_GNU_SOURCE", + if (support_unittests) "-DUNIT_TESTING" else "", if (use_luajit) "" else "-DNVIM_VENDOR_BIT", }; nvim_exe.addCSourceFiles(.{ .files = src_paths, .flags = &flags }); @@ -339,7 +357,9 @@ pub fn build(b: *std.Build) !void { const parser_query = b.dependency("treesitter_query", .{ .target = target, .optimize = optimize }); test_deps.dependOn(add_ts_parser(b, "query", parser_query.path("."), false, target, optimize)); - try tests.test_steps(b, nvim_exe, test_deps, lua_dev_deps.path("."), test_config_step.getDirectory()); + const unit_headers: ?[]const LazyPath = if (support_unittests) &(include_path ++ .{gen_headers.getDirectory()}) else null; + + try tests.test_steps(b, nvim_exe, test_deps, lua_dev_deps.path("."), test_config_step.getDirectory(), unit_headers); } pub fn test_fixture( @@ -401,7 +421,7 @@ pub fn lua_version_info(b: *std.Build) []u8 { , .{ v.major, v.minor, v.patch, v.prerelease.len > 0, v.api_level, v.api_level_compat, v.api_prerelease }); } -pub fn test_config(b: *std.Build, gen_dir: LazyPath) ![]u8 { +pub fn test_config(b: *std.Build) ![]u8 { var buf: [std.fs.max_path_bytes]u8 = undefined; const src_path = try b.build_root.handle.realpath(".", &buf); @@ -409,7 +429,6 @@ pub fn test_config(b: *std.Build, gen_dir: LazyPath) ![]u8 { return b.fmt( \\local M = {{}} \\ - \\M.include_paths = {{}} \\M.apple_sysroot = "" \\M.translations_enabled = "$ENABLE_TRANSLATIONS" == "ON" \\M.is_asan = "$ENABLE_ASAN_UBSAN" == "ON" @@ -419,9 +438,9 @@ pub fn test_config(b: *std.Build, gen_dir: LazyPath) ![]u8 { \\M.test_source_path = "{[src_path]s}" \\M.test_lua_prg = "" \\M.test_luajit_prg = "" - \\table.insert(M.include_paths, "{[gen_dir]}/include") - \\table.insert(M.include_paths, "{[gen_dir]}/src/nvim/auto") + \\ -- include path passed on the cmdline, see test/lua_runner.lua + \\M.include_paths = _G.c_include_path or {{}} \\ \\return M - , .{ .bin_dir = b.install_path, .src_path = src_path, .gen_dir = gen_dir }); + , .{ .bin_dir = b.install_path, .src_path = src_path }); } diff --git a/test/lua_runner.lua b/test/lua_runner.lua index 66ca5fb571..d57bcb9319 100644 --- a/test/lua_runner.lua +++ b/test/lua_runner.lua @@ -1,5 +1,9 @@ local platform = vim.uv.os_uname() local deps_install_dir = table.remove(_G.arg, 1) +_G.c_include_path = {} +while vim.startswith(_G.arg[1], '-I') do + table.insert(_G.c_include_path, string.sub(table.remove(_G.arg, 1), 3)) +end local subcommand = table.remove(_G.arg, 1) local suffix = (platform and platform.sysname:lower():find 'windows') and '.dll' or '.so' package.path = (deps_install_dir .. '/?.lua;') diff --git a/test/run_tests.zig b/test/run_tests.zig index 534337b378..fbcbd4d493 100644 --- a/test/run_tests.zig +++ b/test/run_tests.zig @@ -1,14 +1,19 @@ const std = @import("std"); const LazyPath = std.Build.LazyPath; -pub fn test_steps(b: *std.Build, nvim_bin: *std.Build.Step.Compile, depend_on: *std.Build.Step, lua_deps: LazyPath, config_dir: LazyPath) !void { +pub fn testStep(b: *std.Build, kind: []const u8, nvim_bin: *std.Build.Step.Compile, lua_deps: LazyPath, config_dir: LazyPath, include_path: ?[]const LazyPath) !*std.Build.Step.Run { const test_step = b.addRunArtifact(nvim_bin); test_step.addArg("-ll"); test_step.addFileArg(b.path("./test/lua_runner.lua")); test_step.addDirectoryArg(lua_deps); + if (include_path) |paths| { + for (paths) |path| { + test_step.addPrefixedDirectoryArg("-I", path); + } + } test_step.addArgs(&.{ "busted", "-v", "-o", "test.busted.outputHandlers.nvim", "--lazy" }); // TODO(bfredl): a bit funky with paths, should work even if we run "zig build" in a nested dir - test_step.addArg("./test/functional/preload.lua"); // TEST_TYPE!! + test_step.addArg(b.fmt("./test/{s}/preload.lua", .{kind})); test_step.addArg("--lpath=./src/?.lua"); test_step.addArg("--lpath=./runtime/lua/?.lua"); test_step.addArg("--lpath=./?.lua"); @@ -17,11 +22,9 @@ pub fn test_steps(b: *std.Build, nvim_bin: *std.Build.Step.Compile, depend_on: * if (b.args) |args| { test_step.addArgs(args); // accept TEST_FILE as a positional argument } else { - test_step.addArg("./test/functional/"); + test_step.addArg(b.fmt("./test/{s}/", .{kind})); } - test_step.step.dependOn(depend_on); - const env = test_step.getEnvMap(); try env.put("VIMRUNTIME", "runtime"); try env.put("NVIM_RPLUGIN_MANIFEST", "Xtest_xdg/Xtest_rplugin_manifest"); @@ -33,12 +36,27 @@ pub fn test_steps(b: *std.Build, nvim_bin: *std.Build.Step.Compile, depend_on: * env.remove("NVIM"); env.remove("XDG_DATA_DIRS"); + return test_step; +} +pub fn test_steps(b: *std.Build, nvim_bin: *std.Build.Step.Compile, depend_on: *std.Build.Step, lua_deps: LazyPath, config_dir: LazyPath, unit_paths: ?[]const LazyPath) !void { const empty_dir = b.addWriteFiles(); _ = empty_dir.add(".touch", ""); const tmpdir_create = b.addInstallDirectory(.{ .source_dir = empty_dir.getDirectory(), .install_dir = .prefix, .install_subdir = "Xtest_tmpdir/" }); - test_step.step.dependOn(&tmpdir_create.step); - const functionaltest_step = b.step("functionaltest", "run functionaltests"); - functionaltest_step.dependOn(&test_step.step); + const functional_tests = try testStep(b, "functional", nvim_bin, lua_deps, config_dir, null); + functional_tests.step.dependOn(depend_on); + functional_tests.step.dependOn(&tmpdir_create.step); + + const functionaltest_step = b.step("functionaltest", "run functional tests"); + functionaltest_step.dependOn(&functional_tests.step); + + if (unit_paths) |paths| { + const unit_tests = try testStep(b, "unit", nvim_bin, lua_deps, config_dir, paths); + unit_tests.step.dependOn(depend_on); + unit_tests.step.dependOn(&tmpdir_create.step); + + const unittest_step = b.step("unittest", "run unit tests"); + unittest_step.dependOn(&unit_tests.step); + } }