Skip to content

Commit

Permalink
Cleanup fuzzy tests a bit and run them in CI
Browse files Browse the repository at this point in the history
The fuzzy tests rely on rc.exe/cvtres.exe being in the PATH to check that our output is expected, so they are only run on the Windows CI
  • Loading branch information
squeek502 committed Nov 25, 2024
1 parent 06a086b commit 33575b8
Show file tree
Hide file tree
Showing 18 changed files with 73 additions and 51 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ jobs:
if: ${{ matrix.os != 'macos-latest' }}
run: zig build test -Dtarget=x86-native

- name: Fuzzy Tests
if: ${{ matrix.os == 'windows-latest' }}
run: zig build test_fuzzy

- name: Test big endian (mips)
# TODO: Disabled due to segfault when running the compiled resinator
# binary with qemu-mips, unsure of the cause but might be in the
Expand Down
61 changes: 36 additions & 25 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -83,19 +83,21 @@ pub fn build(b: *std.Build) void {
test_step.dependOn(&run_compiler_tests.step);
test_step.dependOn(run_cli_tests_step);

const test_utils_module = b.createModule(.{
.root_source_file = b.path("test/utils.zig"),
.imports = &.{
.{ .name = "resinator", .module = resinator },
},
});

// Tools
const cvtres_strip = b.addExecutable(.{
.name = "cvtres-strip",
.root_source_file = b.path("tools/cvtres-strip.zig"),
.target = target,
.optimize = mode,
});
cvtres_strip.root_module.addAnonymousImport("utils", .{
.root_source_file = b.path("test/utils.zig"),
.imports = &.{
.{ .name = "resinator", .module = resinator },
},
});
cvtres_strip.root_module.addImport("utils", test_utils_module);
const install_cvtres_strip = b.addInstallArtifact(cvtres_strip, .{});
const cvtres_strip_step = b.step("cvtres-strip", "Build and install cvtres-strip tool");
cvtres_strip_step.dependOn(&install_cvtres_strip.step);
Expand All @@ -105,28 +107,32 @@ pub fn build(b: *std.Build) void {

// Fuzzy tests
const fuzzy_max_iterations = b.option(u64, "fuzzy-iterations", "The max iterations for fuzzy tests (default: 1000)") orelse 1000;
const fuzzy_debug = b.option(bool, "fuzzy-debug", "When enabled, fuzzy tests will write their inputs to the cache dir (default: false)") orelse false;

const test_options = b.addOptions();
test_options.addOption(u64, "max_iterations", fuzzy_max_iterations);
test_options.addOption(bool, "fuzzy_debug", fuzzy_debug);

const all_fuzzy_tests_step = b.step("test_fuzzy", "Run all fuzz/property-testing-like tests with a max number of iterations for each");
_ = addFuzzyTest(b, "numbers", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "number_expressions", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "ascii_strings", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "numeric_types", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "common_resource_attributes", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "raw_data", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "name_or_ordinal", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "code_pages", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "icons", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "bitmaps", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "stringtable", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "fonts", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "dlginclude", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "strings", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "accelerators", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "cvtres", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "res", mode, target, resinator, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "numbers", mode, target, resinator, test_utils_module, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "number_expressions", mode, target, resinator, test_utils_module, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "ascii_strings", mode, target, resinator, test_utils_module, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "numeric_types", mode, target, resinator, test_utils_module, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "common_resource_attributes", mode, target, resinator, test_utils_module, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "raw_data", mode, target, resinator, test_utils_module, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "name_or_ordinal", mode, target, resinator, test_utils_module, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "code_pages", mode, target, resinator, test_utils_module, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "icons", mode, target, resinator, test_utils_module, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "stringtable", mode, target, resinator, test_utils_module, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "strings", mode, target, resinator, test_utils_module, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "cvtres", mode, target, resinator, test_utils_module, all_fuzzy_tests_step, test_options);
_ = addFuzzyTest(b, "res", mode, target, resinator, test_utils_module, all_fuzzy_tests_step, test_options);
// Exclude these fuzzy tests from the test_fuzzy step since they don't fully work as tests
// and are more geared towards gathering info at the moment.
_ = addFuzzyTest(b, "bitmaps", mode, target, resinator, test_utils_module, null, test_options);
_ = addFuzzyTest(b, "fonts", mode, target, resinator, test_utils_module, null, test_options);
_ = addFuzzyTest(b, "dlginclude", mode, target, resinator, test_utils_module, null, test_options);
_ = addFuzzyTest(b, "accelerators", mode, target, resinator, test_utils_module, null, test_options);

_ = addFuzzer(b, "fuzz_rc", &.{}, resinator, target);

Expand Down Expand Up @@ -327,7 +333,8 @@ fn addFuzzyTest(
mode: std.builtin.Mode,
target: std.Build.ResolvedTarget,
resinator: *std.Build.Module,
all_fuzzy_tests_step: *std.Build.Step,
test_utils_module: *std.Build.Module,
all_fuzzy_tests_step: ?*std.Build.Step,
fuzzy_options: *std.Build.Step.Options,
) *std.Build.Step.Compile {
var test_step = b.addTest(.{
Expand All @@ -336,14 +343,18 @@ fn addFuzzyTest(
.optimize = mode,
});
test_step.root_module.addImport("resinator", resinator);
// We use an import to avoid pulling in the test cases of the test utils themselves
test_step.root_module.addImport("test_utils", test_utils_module);
test_step.root_module.addOptions("fuzzy_options", fuzzy_options);

const run_test = b.addRunArtifact(test_step);

var test_run_step = b.step("test_fuzzy_" ++ name, "Some fuzz/property-testing-like tests for " ++ name);
test_run_step.dependOn(&run_test.step);

all_fuzzy_tests_step.dependOn(test_run_step);
if (all_fuzzy_tests_step) |all_step| {
all_step.dependOn(test_run_step);
}

return test_step;
}
Expand Down
2 changes: 1 addition & 1 deletion test/fuzzy_accelerators.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const std = @import("std");
const resinator = @import("resinator");
const utils = @import("utils.zig");
const utils = @import("test_utils");
const fuzzy_options = @import("fuzzy_options");
const iterations = fuzzy_options.max_iterations;

Expand Down
5 changes: 3 additions & 2 deletions test/fuzzy_ascii_strings.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const std = @import("std");
const utils = @import("utils.zig");
const utils = @import("test_utils");
const fuzzy_options = @import("fuzzy_options");
const iterations = fuzzy_options.max_iterations;

Expand Down Expand Up @@ -86,7 +86,8 @@ test "fuzz" {
const source = source_buffer.items;

// write out the source file to disk for debugging
try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_ascii_strings.rc", .data = source });
if (fuzzy_options.fuzzy_debug)
try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_ascii_strings.rc", .data = source });

try utils.expectSameResOutput(allocator, source, .{
.cwd = tmp.dir,
Expand Down
5 changes: 3 additions & 2 deletions test/fuzzy_bitmaps.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const std = @import("std");
const utils = @import("utils.zig");
const utils = @import("test_utils");
const fuzzy_options = @import("fuzzy_options");
const iterations = fuzzy_options.max_iterations;
const resinator = @import("resinator");
Expand Down Expand Up @@ -78,7 +78,8 @@ test "BITMAP fuzz" {
try tmp.dir.writeFile(.{ .sub_path = "test.bin", .data = image_buffer.items });

// also write it to the top-level tmp dir for debugging
try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_bitmaps.bin", .data = image_buffer.items });
if (fuzzy_options.fuzzy_debug)
try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_bitmaps.bin", .data = image_buffer.items });

var diagnostics = resinator.errors.Diagnostics.init(allocator);
defer diagnostics.deinit();
Expand Down
5 changes: 3 additions & 2 deletions test/fuzzy_code_pages.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const std = @import("std");
const utils = @import("utils.zig");
const utils = @import("test_utils");
const fuzzy_options = @import("fuzzy_options");
const iterations = fuzzy_options.max_iterations;

Expand Down Expand Up @@ -112,7 +112,8 @@ test "fuzz" {
const source = source_buffer.items;

// write out the source file to disk for debugging
try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_code_pages.rc", .data = source });
if (fuzzy_options.fuzzy_debug)
try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_code_pages.rc", .data = source });

try utils.expectSameResOutput(allocator, source, .{
.cwd = tmp.dir,
Expand Down
3 changes: 1 addition & 2 deletions test/fuzzy_common_resource_attributes.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const std = @import("std");
const utils = @import("utils.zig");
const utils = @import("test_utils");

const common_resource_attributes: []const []const u8 = &.{
"PRELOAD", "LOADONCALL", "FIXED",
Expand Down Expand Up @@ -41,7 +41,6 @@ test "RCDATA common resource attribute permutations" {
// batching large amounts of permutations together we hugely reduce the amount of time it takes this
// test to run, since the bottleneck is the creation of each `.rc` and `.res` file.
if (is_batch_i) {
std.debug.print("{}\n", .{perm_i});
const source = source_buffer.items;

try utils.expectSameResOutput(allocator, source, .{
Expand Down
5 changes: 3 additions & 2 deletions test/fuzzy_cvtres.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const std = @import("std");
const utils = @import("utils.zig");
const utils = @import("test_utils");
const fuzzy_options = @import("fuzzy_options");
const iterations = fuzzy_options.max_iterations;
const resinator = @import("resinator");
Expand Down Expand Up @@ -64,7 +64,8 @@ test "cvtres fuzz" {
}

// also write it to the top-level tmp dir for debugging
try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_cvtres.res", .data = res_buffer.items });
if (fuzzy_options.fuzzy_debug)
try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_cvtres.res", .data = res_buffer.items });

const random_target: std.coff.MachineType = switch (rand.uintLessThan(u8, 8)) {
0 => .X64,
Expand Down
2 changes: 1 addition & 1 deletion test/fuzzy_dlginclude.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const std = @import("std");
const resinator = @import("resinator");
const utils = @import("utils.zig");
const utils = @import("test_utils");
const fuzzy_options = @import("fuzzy_options");
const iterations = fuzzy_options.max_iterations;

Expand Down
5 changes: 3 additions & 2 deletions test/fuzzy_icons.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const std = @import("std");
const utils = @import("utils.zig");
const utils = @import("test_utils");
const fuzzy_options = @import("fuzzy_options");
const iterations = fuzzy_options.max_iterations;

Expand Down Expand Up @@ -79,7 +79,8 @@ test "ICON fuzz" {
try tmp.dir.writeFile(.{ .sub_path = "test.ico", .data = icon_buffer.items });

// also write it to the top-level tmp dir for debugging
try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_icons.ico", .data = icon_buffer.items });
if (fuzzy_options.fuzzy_debug)
try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_icons.ico", .data = icon_buffer.items });

try utils.expectSameResOutput(allocator, source, .{
.cwd = tmp.dir,
Expand Down
2 changes: 1 addition & 1 deletion test/fuzzy_name_or_ordinal.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const std = @import("std");
const utils = @import("utils.zig");
const utils = @import("test_utils");
const fuzzy_options = @import("fuzzy_options");
const iterations = fuzzy_options.max_iterations;

Expand Down
2 changes: 1 addition & 1 deletion test/fuzzy_number_expressions.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const std = @import("std");
const utils = @import("utils.zig");
const utils = @import("test_utils");
const fuzzy_options = @import("fuzzy_options");
const iterations = fuzzy_options.max_iterations;

Expand Down
2 changes: 1 addition & 1 deletion test/fuzzy_numbers.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const std = @import("std");
const utils = @import("utils.zig");
const utils = @import("test_utils");
const fuzzy_options = @import("fuzzy_options");
const iterations = fuzzy_options.max_iterations;

Expand Down
2 changes: 1 addition & 1 deletion test/fuzzy_numeric_types.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const std = @import("std");
const utils = @import("utils.zig");
const utils = @import("test_utils");

test "raw data" {
const allocator = std.testing.allocator;
Expand Down
2 changes: 1 addition & 1 deletion test/fuzzy_raw_data.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const std = @import("std");
const utils = @import("utils.zig");
const utils = @import("test_utils");

test "single char in raw data block" {
var source_buf = "1 RCDATA { ? }".*;
Expand Down
10 changes: 6 additions & 4 deletions test/fuzzy_res.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const std = @import("std");
const utils = @import("utils.zig");
const utils = @import("test_utils");
const fuzzy_options = @import("fuzzy_options");
const iterations = fuzzy_options.max_iterations;
const resinator = @import("resinator");
Expand All @@ -24,12 +24,13 @@ test "res preface fuzz" {
res_buffer.clearRetainingCapacity();

switch (rand.boolean()) {
true => try utils.writeRandomValidResource(allocator, rand, res_buffer.writer(), .{}),
true => _ = try utils.writeRandomValidResource(allocator, rand, res_buffer.writer(), .{}),
false => try utils.writeRandomPotentiallyInvalidResource(allocator, rand, res_buffer.writer()),
}

// also write it to the top-level tmp dir for debugging
try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_res_preface.res", .data = res_buffer.items });
if (fuzzy_options.fuzzy_debug)
try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_res_preface.res", .data = res_buffer.items });

var fbs = std.io.fixedBufferStream(res_buffer.items);
var resources = resinator.cvtres.parseRes(allocator, fbs.reader(), .{
Expand Down Expand Up @@ -66,7 +67,8 @@ test "res fuzz" {
}

// also write it to the top-level tmp dir for debugging
try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_res.res", .data = res_buffer.items });
if (fuzzy_options.fuzzy_debug)
try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_res.res", .data = res_buffer.items });

var fbs = std.io.fixedBufferStream(res_buffer.items);
var resources = resinator.cvtres.parseRes(allocator, fbs.reader(), .{
Expand Down
2 changes: 1 addition & 1 deletion test/fuzzy_strings.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const std = @import("std");
const resinator = @import("resinator");
const utils = @import("utils.zig");
const utils = @import("test_utils");
const fuzzy_options = @import("fuzzy_options");
const iterations = fuzzy_options.max_iterations;

Expand Down
5 changes: 3 additions & 2 deletions test/fuzzy_stringtable.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const std = @import("std");
const utils = @import("utils.zig");
const utils = @import("test_utils");
const fuzzy_options = @import("fuzzy_options");
const iterations = fuzzy_options.max_iterations;

Expand Down Expand Up @@ -30,7 +30,8 @@ test "fuzz" {
const source = source_buffer.items;

// write out the source file to disk for debugging
try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_stringtable_strings.rc", .data = source });
if (fuzzy_options.fuzzy_debug)
try std.fs.cwd().writeFile(.{ .sub_path = ".zig-cache/tmp/fuzzy_stringtable_strings.rc", .data = source });

try utils.expectSameResOutput(allocator, source, .{
.cwd = tmp.dir,
Expand Down

0 comments on commit 33575b8

Please sign in to comment.