Commit 2d8d0dd9b0

Andrew Kelley <andrew@ziglang.org>
2025-07-29 03:23:34
std.compress.flate.Decompress: unfuck the test suite
1 parent c684b21
Changed files (1)
lib
std
compress
lib/std/compress/flate/Decompress.zig
@@ -909,85 +909,153 @@ test "zlib decompress non compressed block (type 0)" {
     }, "Hello world\n");
 }
 
-test "fuzzing tests" {
-    const cases = [_]struct {
-        input: []const u8,
-        out: []const u8 = "",
-        err: ?anyerror = null,
-    }{
-        .{ .input = "deflate-stream", .out = @embedFile("testdata/fuzz/deflate-stream.expect") }, // 0
-        .{ .input = "empty-distance-alphabet01" },
-        .{ .input = "empty-distance-alphabet02" },
-        .{ .input = "end-of-stream", .err = error.EndOfStream },
-        .{ .input = "invalid-distance", .err = error.InvalidMatch },
-        .{ .input = "invalid-tree01", .err = error.IncompleteHuffmanTree }, // 5
-        .{ .input = "invalid-tree02", .err = error.IncompleteHuffmanTree },
-        .{ .input = "invalid-tree03", .err = error.IncompleteHuffmanTree },
-        .{ .input = "lengths-overflow", .err = error.InvalidDynamicBlockHeader },
-        .{ .input = "out-of-codes", .err = error.InvalidCode },
-        .{ .input = "puff01", .err = error.WrongStoredBlockNlen }, // 10
-        .{ .input = "puff02", .err = error.EndOfStream },
-        .{ .input = "puff03", .out = &[_]u8{0xa} },
-        .{ .input = "puff04", .err = error.InvalidCode },
-        .{ .input = "puff05", .err = error.EndOfStream },
-        .{ .input = "puff06", .err = error.EndOfStream },
-        .{ .input = "puff08", .err = error.InvalidCode },
-        .{ .input = "puff09", .out = "P" },
-        .{ .input = "puff10", .err = error.InvalidCode },
-        .{ .input = "puff11", .err = error.InvalidMatch },
-        .{ .input = "puff12", .err = error.InvalidDynamicBlockHeader }, // 20
-        .{ .input = "puff13", .err = error.IncompleteHuffmanTree },
-        .{ .input = "puff14", .err = error.EndOfStream },
-        .{ .input = "puff15", .err = error.IncompleteHuffmanTree },
-        .{ .input = "puff16", .err = error.InvalidDynamicBlockHeader },
-        .{ .input = "puff17", .err = error.MissingEndOfBlockCode }, // 25
-        .{ .input = "fuzz1", .err = error.InvalidDynamicBlockHeader },
-        .{ .input = "fuzz2", .err = error.InvalidDynamicBlockHeader },
-        .{ .input = "fuzz3", .err = error.InvalidMatch },
-        .{ .input = "fuzz4", .err = error.OversubscribedHuffmanTree },
-        .{ .input = "puff18", .err = error.OversubscribedHuffmanTree }, // 30
-        .{ .input = "puff19", .err = error.OversubscribedHuffmanTree },
-        .{ .input = "puff20", .err = error.OversubscribedHuffmanTree },
-        .{ .input = "puff21", .err = error.OversubscribedHuffmanTree },
-        .{ .input = "puff22", .err = error.OversubscribedHuffmanTree },
-        .{ .input = "puff23", .err = error.OversubscribedHuffmanTree }, // 35
-        .{ .input = "puff24", .err = error.IncompleteHuffmanTree },
-        .{ .input = "puff25", .err = error.OversubscribedHuffmanTree },
-        .{ .input = "puff26", .err = error.InvalidDynamicBlockHeader },
-        .{ .input = "puff27", .err = error.InvalidDynamicBlockHeader },
-    };
-
-    inline for (cases) |c| {
-        var in: Reader = .fixed(@embedFile("testdata/fuzz/" ++ c.input ++ ".input"));
-        var aw: Writer.Allocating = .init(testing.allocator);
-        try aw.ensureUnusedCapacity(flate.history_len);
-        defer aw.deinit();
-
-        var decompress: Decompress = .init(&in, .raw, &.{});
-        const r = &decompress.reader;
-        if (c.err) |expected_err| {
-            try testing.expectError(error.ReadFailed, r.streamRemaining(&aw.writer));
-            try testing.expectEqual(expected_err, decompress.read_err orelse return error.TestFailed);
-        } else {
-            _ = try r.streamRemaining(&aw.writer);
-            try testing.expectEqualStrings(c.out, aw.getWritten());
-        }
-    }
+test "failing end-of-stream" {
+    try testFailure(@embedFile("testdata/fuzz/end-of-stream.input"), error.EndOfStream);
+}
+test "failing invalid-distance" {
+    try testFailure(@embedFile("testdata/fuzz/invalid-distance.input"), error.InvalidMatch);
+}
+test "failing invalid-tree01" {
+    try testFailure(@embedFile("testdata/fuzz/invalid-tree01.input"), error.IncompleteHuffmanTree);
+}
+test "failing invalid-tree02" {
+    try testFailure(@embedFile("testdata/fuzz/invalid-tree02.input"), error.IncompleteHuffmanTree);
+}
+test "failing invalid-tree03" {
+    try testFailure(@embedFile("testdata/fuzz/invalid-tree03.input"), error.IncompleteHuffmanTree);
+}
+test "failing lengths-overflow" {
+    try testFailure(@embedFile("testdata/fuzz/lengths-overflow.input"), error.InvalidDynamicBlockHeader);
+}
+test "failing out-of-codes" {
+    try testFailure(@embedFile("testdata/fuzz/out-of-codes.input"), error.InvalidCode);
+}
+test "failing puff01" {
+    try testFailure(@embedFile("testdata/fuzz/puff01.input"), error.WrongStoredBlockNlen);
+}
+test "failing puff02" {
+    try testFailure(@embedFile("testdata/fuzz/puff02.input"), error.EndOfStream);
+}
+test "failing puff04" {
+    try testFailure(@embedFile("testdata/fuzz/puff04.input"), error.InvalidCode);
+}
+test "failing puff05" {
+    try testFailure(@embedFile("testdata/fuzz/puff05.input"), error.EndOfStream);
+}
+test "failing puff06" {
+    try testFailure(@embedFile("testdata/fuzz/puff06.input"), error.EndOfStream);
+}
+test "failing puff08" {
+    try testFailure(@embedFile("testdata/fuzz/puff08.input"), error.InvalidCode);
+}
+test "failing puff10" {
+    try testFailure(@embedFile("testdata/fuzz/puff10.input"), error.InvalidCode);
+}
+test "failing puff11" {
+    try testFailure(@embedFile("testdata/fuzz/puff11.input"), error.InvalidMatch);
+}
+test "failing puff12" {
+    try testFailure(@embedFile("testdata/fuzz/puff12.input"), error.InvalidDynamicBlockHeader);
+}
+test "failing puff13" {
+    try testFailure(@embedFile("testdata/fuzz/puff13.input"), error.IncompleteHuffmanTree);
+}
+test "failing puff14" {
+    try testFailure(@embedFile("testdata/fuzz/puff14.input"), error.EndOfStream);
+}
+test "failing puff15" {
+    try testFailure(@embedFile("testdata/fuzz/puff15.input"), error.IncompleteHuffmanTree);
+}
+test "failing puff16" {
+    try testFailure(@embedFile("testdata/fuzz/puff16.input"), error.InvalidDynamicBlockHeader);
+}
+test "failing puff17" {
+    try testFailure(@embedFile("testdata/fuzz/puff17.input"), error.MissingEndOfBlockCode);
+}
+test "failing fuzz1" {
+    try testFailure(@embedFile("testdata/fuzz/fuzz1.input"), error.InvalidDynamicBlockHeader);
+}
+test "failing fuzz2" {
+    try testFailure(@embedFile("testdata/fuzz/fuzz2.input"), error.InvalidDynamicBlockHeader);
+}
+test "failing fuzz3" {
+    try testFailure(@embedFile("testdata/fuzz/fuzz3.input"), error.InvalidMatch);
+}
+test "failing fuzz4" {
+    try testFailure(@embedFile("testdata/fuzz/fuzz4.input"), error.OversubscribedHuffmanTree);
+}
+test "failing puff18" {
+    try testFailure(@embedFile("testdata/fuzz/puff18.input"), error.OversubscribedHuffmanTree);
+}
+test "failing puff19" {
+    try testFailure(@embedFile("testdata/fuzz/puff19.input"), error.OversubscribedHuffmanTree);
+}
+test "failing puff20" {
+    try testFailure(@embedFile("testdata/fuzz/puff20.input"), error.OversubscribedHuffmanTree);
+}
+test "failing puff21" {
+    try testFailure(@embedFile("testdata/fuzz/puff21.input"), error.OversubscribedHuffmanTree);
+}
+test "failing puff22" {
+    try testFailure(@embedFile("testdata/fuzz/puff22.input"), error.OversubscribedHuffmanTree);
+}
+test "failing puff23" {
+    try testFailure(@embedFile("testdata/fuzz/puff23.input"), error.OversubscribedHuffmanTree);
+}
+test "failing puff24" {
+    try testFailure(@embedFile("testdata/fuzz/puff24.input"), error.IncompleteHuffmanTree);
+}
+test "failing puff25" {
+    try testFailure(@embedFile("testdata/fuzz/puff25.input"), error.OversubscribedHuffmanTree);
+}
+test "failing puff26" {
+    try testFailure(@embedFile("testdata/fuzz/puff26.input"), error.InvalidDynamicBlockHeader);
+}
+test "failing puff27" {
+    try testFailure(@embedFile("testdata/fuzz/puff27.input"), error.InvalidDynamicBlockHeader);
 }
 
-test "bug 18966" {
-    const input = @embedFile("testdata/fuzz/bug_18966.input");
-    const expect = @embedFile("testdata/fuzz/bug_18966.expect");
-
-    var in: Reader = .fixed(input);
+fn testFailure(in: []const u8, expected_err: anyerror) !void {
+    var reader: Reader = .fixed(in);
     var aw: Writer.Allocating = .init(testing.allocator);
     try aw.ensureUnusedCapacity(flate.history_len);
     defer aw.deinit();
 
-    var decompress: Decompress = .init(&in, .gzip, &.{});
-    const r = &decompress.reader;
-    _ = try r.streamRemaining(&aw.writer);
-    try testing.expectEqualStrings(expect, aw.getWritten());
+    var decompress: Decompress = .init(&reader, .raw, &.{});
+    try testing.expectError(error.ReadFailed, decompress.reader.streamRemaining(&aw.writer));
+    try testing.expectEqual(expected_err, decompress.read_err orelse return error.TestFailed);
+}
+
+test "deflate-stream" {
+    try testDecompress(
+        .raw,
+        @embedFile("testdata/fuzz/deflate-stream.input"),
+        @embedFile("testdata/fuzz/deflate-stream.expect"),
+    );
+}
+
+test "empty-distance-alphabet01" {
+    try testDecompress(.raw, @embedFile("testdata/fuzz/empty-distance-alphabet01.input"), "");
+}
+
+test "empty-distance-alphabet02" {
+    try testDecompress(.raw, @embedFile("testdata/fuzz/empty-distance-alphabet02.input"), "");
+}
+
+test "puff03" {
+    try testDecompress(.raw, @embedFile("testdata/fuzz/puff03.input"), &.{0xa});
+}
+
+test "puff09" {
+    try testDecompress(.raw, @embedFile("testdata/fuzz/puff09.input"), "P");
+}
+
+test "bug 18966" {
+    try testDecompress(
+        .gzip,
+        @embedFile("testdata/fuzz/bug_18966.input"),
+        @embedFile("testdata/fuzz/bug_18966.expect"),
+    );
 }
 
 test "reading into empty buffer" {
@@ -1130,21 +1198,13 @@ test "zlib should not overshoot" {
     var out: [128]u8 = undefined;
 
     {
-        const n = try decompress.reader.readSliceShort(out[0..]);
-
-        // Expected decompressed data
+        const n = try decompress.reader.readSliceShort(&out);
         try std.testing.expectEqual(46, n);
         try std.testing.expectEqualStrings("Copyright Willem van Schaik, Singapore 1995-96", out[0..n]);
-
-        // Decompressor don't overshoot underlying reader.
-        // It is leaving it at the end of compressed data chunk.
-        try std.testing.expectEqual(data.len - 4, reader.seek);
-        // TODO what was this testing, exactly?
-        //try std.testing.expectEqual(0, decompress.unreadBytes());
     }
 
     // 4 bytes after compressed chunk are available in reader.
-    const n = try reader.readSliceShort(out[0..]);
+    const n = try reader.readSliceShort(&out);
     try std.testing.expectEqual(n, 4);
     try std.testing.expectEqualSlices(u8, data[data.len - 4 .. data.len], out[0..n]);
 }