authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-28 18:23:34-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-31 22:10:11-07:00
log2d8d0dd9b0c4bd8d51be33da540b45fef2b20ec2
treec1cab6fd594f8842437c4ebaff236c000c95d396
parentc684b21b4f8f13b32535d799d2c76cdb2dccc8f0

std.compress.flate.Decompress: unfuck the test suite


1 files changed, 143 insertions(+), 83 deletions(-)

lib/std/compress/flate/Decompress.zig+143-83
...@@ -909,85 +909,153 @@ test "zlib decompress non compressed block (type 0)" {...@@ -909,85 +909,153 @@ test "zlib decompress non compressed block (type 0)" {
909 }, "Hello world\n");909 }, "Hello world\n");
910}910}
911911
912test "fuzzing tests" {912test "failing end-of-stream" {
913 const cases = [_]struct {913 try testFailure(@embedFile("testdata/fuzz/end-of-stream.input"), error.EndOfStream);
914 input: []const u8,914}
915 out: []const u8 = "",915test "failing invalid-distance" {
916 err: ?anyerror = null,916 try testFailure(@embedFile("testdata/fuzz/invalid-distance.input"), error.InvalidMatch);
917 }{917}
918 .{ .input = "deflate-stream", .out = @embedFile("testdata/fuzz/deflate-stream.expect") }, // 0918test "failing invalid-tree01" {
919 .{ .input = "empty-distance-alphabet01" },919 try testFailure(@embedFile("testdata/fuzz/invalid-tree01.input"), error.IncompleteHuffmanTree);
920 .{ .input = "empty-distance-alphabet02" },920}
921 .{ .input = "end-of-stream", .err = error.EndOfStream },921test "failing invalid-tree02" {
922 .{ .input = "invalid-distance", .err = error.InvalidMatch },922 try testFailure(@embedFile("testdata/fuzz/invalid-tree02.input"), error.IncompleteHuffmanTree);
923 .{ .input = "invalid-tree01", .err = error.IncompleteHuffmanTree }, // 5923}
924 .{ .input = "invalid-tree02", .err = error.IncompleteHuffmanTree },924test "failing invalid-tree03" {
925 .{ .input = "invalid-tree03", .err = error.IncompleteHuffmanTree },925 try testFailure(@embedFile("testdata/fuzz/invalid-tree03.input"), error.IncompleteHuffmanTree);
926 .{ .input = "lengths-overflow", .err = error.InvalidDynamicBlockHeader },926}
927 .{ .input = "out-of-codes", .err = error.InvalidCode },927test "failing lengths-overflow" {
928 .{ .input = "puff01", .err = error.WrongStoredBlockNlen }, // 10928 try testFailure(@embedFile("testdata/fuzz/lengths-overflow.input"), error.InvalidDynamicBlockHeader);
929 .{ .input = "puff02", .err = error.EndOfStream },929}
930 .{ .input = "puff03", .out = &[_]u8{0xa} },930test "failing out-of-codes" {
931 .{ .input = "puff04", .err = error.InvalidCode },931 try testFailure(@embedFile("testdata/fuzz/out-of-codes.input"), error.InvalidCode);
932 .{ .input = "puff05", .err = error.EndOfStream },932}
933 .{ .input = "puff06", .err = error.EndOfStream },933test "failing puff01" {
934 .{ .input = "puff08", .err = error.InvalidCode },934 try testFailure(@embedFile("testdata/fuzz/puff01.input"), error.WrongStoredBlockNlen);
935 .{ .input = "puff09", .out = "P" },935}
936 .{ .input = "puff10", .err = error.InvalidCode },936test "failing puff02" {
937 .{ .input = "puff11", .err = error.InvalidMatch },937 try testFailure(@embedFile("testdata/fuzz/puff02.input"), error.EndOfStream);
938 .{ .input = "puff12", .err = error.InvalidDynamicBlockHeader }, // 20938}
939 .{ .input = "puff13", .err = error.IncompleteHuffmanTree },939test "failing puff04" {
940 .{ .input = "puff14", .err = error.EndOfStream },940 try testFailure(@embedFile("testdata/fuzz/puff04.input"), error.InvalidCode);
941 .{ .input = "puff15", .err = error.IncompleteHuffmanTree },941}
942 .{ .input = "puff16", .err = error.InvalidDynamicBlockHeader },942test "failing puff05" {
943 .{ .input = "puff17", .err = error.MissingEndOfBlockCode }, // 25943 try testFailure(@embedFile("testdata/fuzz/puff05.input"), error.EndOfStream);
944 .{ .input = "fuzz1", .err = error.InvalidDynamicBlockHeader },944}
945 .{ .input = "fuzz2", .err = error.InvalidDynamicBlockHeader },945test "failing puff06" {
946 .{ .input = "fuzz3", .err = error.InvalidMatch },946 try testFailure(@embedFile("testdata/fuzz/puff06.input"), error.EndOfStream);
947 .{ .input = "fuzz4", .err = error.OversubscribedHuffmanTree },947}
948 .{ .input = "puff18", .err = error.OversubscribedHuffmanTree }, // 30948test "failing puff08" {
949 .{ .input = "puff19", .err = error.OversubscribedHuffmanTree },949 try testFailure(@embedFile("testdata/fuzz/puff08.input"), error.InvalidCode);
950 .{ .input = "puff20", .err = error.OversubscribedHuffmanTree },950}
951 .{ .input = "puff21", .err = error.OversubscribedHuffmanTree },951test "failing puff10" {
952 .{ .input = "puff22", .err = error.OversubscribedHuffmanTree },952 try testFailure(@embedFile("testdata/fuzz/puff10.input"), error.InvalidCode);
953 .{ .input = "puff23", .err = error.OversubscribedHuffmanTree }, // 35953}
954 .{ .input = "puff24", .err = error.IncompleteHuffmanTree },954test "failing puff11" {
955 .{ .input = "puff25", .err = error.OversubscribedHuffmanTree },955 try testFailure(@embedFile("testdata/fuzz/puff11.input"), error.InvalidMatch);
956 .{ .input = "puff26", .err = error.InvalidDynamicBlockHeader },956}
957 .{ .input = "puff27", .err = error.InvalidDynamicBlockHeader },957test "failing puff12" {
958 };958 try testFailure(@embedFile("testdata/fuzz/puff12.input"), error.InvalidDynamicBlockHeader);
959959}
960 inline for (cases) |c| {960test "failing puff13" {
961 var in: Reader = .fixed(@embedFile("testdata/fuzz/" ++ c.input ++ ".input"));961 try testFailure(@embedFile("testdata/fuzz/puff13.input"), error.IncompleteHuffmanTree);
962 var aw: Writer.Allocating = .init(testing.allocator);962}
963 try aw.ensureUnusedCapacity(flate.history_len);963test "failing puff14" {
964 defer aw.deinit();964 try testFailure(@embedFile("testdata/fuzz/puff14.input"), error.EndOfStream);
965965}
966 var decompress: Decompress = .init(&in, .raw, &.{});966test "failing puff15" {
967 const r = &decompress.reader;967 try testFailure(@embedFile("testdata/fuzz/puff15.input"), error.IncompleteHuffmanTree);
968 if (c.err) |expected_err| {968}
969 try testing.expectError(error.ReadFailed, r.streamRemaining(&aw.writer));969test "failing puff16" {
970 try testing.expectEqual(expected_err, decompress.read_err orelse return error.TestFailed);970 try testFailure(@embedFile("testdata/fuzz/puff16.input"), error.InvalidDynamicBlockHeader);
971 } else {971}
972 _ = try r.streamRemaining(&aw.writer);972test "failing puff17" {
973 try testing.expectEqualStrings(c.out, aw.getWritten());973 try testFailure(@embedFile("testdata/fuzz/puff17.input"), error.MissingEndOfBlockCode);
974 }974}
975 }975test "failing fuzz1" {
976 try testFailure(@embedFile("testdata/fuzz/fuzz1.input"), error.InvalidDynamicBlockHeader);
977}
978test "failing fuzz2" {
979 try testFailure(@embedFile("testdata/fuzz/fuzz2.input"), error.InvalidDynamicBlockHeader);
980}
981test "failing fuzz3" {
982 try testFailure(@embedFile("testdata/fuzz/fuzz3.input"), error.InvalidMatch);
983}
984test "failing fuzz4" {
985 try testFailure(@embedFile("testdata/fuzz/fuzz4.input"), error.OversubscribedHuffmanTree);
986}
987test "failing puff18" {
988 try testFailure(@embedFile("testdata/fuzz/puff18.input"), error.OversubscribedHuffmanTree);
989}
990test "failing puff19" {
991 try testFailure(@embedFile("testdata/fuzz/puff19.input"), error.OversubscribedHuffmanTree);
992}
993test "failing puff20" {
994 try testFailure(@embedFile("testdata/fuzz/puff20.input"), error.OversubscribedHuffmanTree);
995}
996test "failing puff21" {
997 try testFailure(@embedFile("testdata/fuzz/puff21.input"), error.OversubscribedHuffmanTree);
998}
999test "failing puff22" {
1000 try testFailure(@embedFile("testdata/fuzz/puff22.input"), error.OversubscribedHuffmanTree);
1001}
1002test "failing puff23" {
1003 try testFailure(@embedFile("testdata/fuzz/puff23.input"), error.OversubscribedHuffmanTree);
1004}
1005test "failing puff24" {
1006 try testFailure(@embedFile("testdata/fuzz/puff24.input"), error.IncompleteHuffmanTree);
1007}
1008test "failing puff25" {
1009 try testFailure(@embedFile("testdata/fuzz/puff25.input"), error.OversubscribedHuffmanTree);
1010}
1011test "failing puff26" {
1012 try testFailure(@embedFile("testdata/fuzz/puff26.input"), error.InvalidDynamicBlockHeader);
1013}
1014test "failing puff27" {
1015 try testFailure(@embedFile("testdata/fuzz/puff27.input"), error.InvalidDynamicBlockHeader);
976}1016}
9771017
978test "bug 18966" {1018fn testFailure(in: []const u8, expected_err: anyerror) !void {
979 const input = @embedFile("testdata/fuzz/bug_18966.input");1019 var reader: Reader = .fixed(in);
980 const expect = @embedFile("testdata/fuzz/bug_18966.expect");
981
982 var in: Reader = .fixed(input);
983 var aw: Writer.Allocating = .init(testing.allocator);1020 var aw: Writer.Allocating = .init(testing.allocator);
984 try aw.ensureUnusedCapacity(flate.history_len);1021 try aw.ensureUnusedCapacity(flate.history_len);
985 defer aw.deinit();1022 defer aw.deinit();
9861023
987 var decompress: Decompress = .init(&in, .gzip, &.{});1024 var decompress: Decompress = .init(&reader, .raw, &.{});
988 const r = &decompress.reader;1025 try testing.expectError(error.ReadFailed, decompress.reader.streamRemaining(&aw.writer));
989 _ = try r.streamRemaining(&aw.writer);1026 try testing.expectEqual(expected_err, decompress.read_err orelse return error.TestFailed);
990 try testing.expectEqualStrings(expect, aw.getWritten());1027}
1028
1029test "deflate-stream" {
1030 try testDecompress(
1031 .raw,
1032 @embedFile("testdata/fuzz/deflate-stream.input"),
1033 @embedFile("testdata/fuzz/deflate-stream.expect"),
1034 );
1035}
1036
1037test "empty-distance-alphabet01" {
1038 try testDecompress(.raw, @embedFile("testdata/fuzz/empty-distance-alphabet01.input"), "");
1039}
1040
1041test "empty-distance-alphabet02" {
1042 try testDecompress(.raw, @embedFile("testdata/fuzz/empty-distance-alphabet02.input"), "");
1043}
1044
1045test "puff03" {
1046 try testDecompress(.raw, @embedFile("testdata/fuzz/puff03.input"), &.{0xa});
1047}
1048
1049test "puff09" {
1050 try testDecompress(.raw, @embedFile("testdata/fuzz/puff09.input"), "P");
1051}
1052
1053test "bug 18966" {
1054 try testDecompress(
1055 .gzip,
1056 @embedFile("testdata/fuzz/bug_18966.input"),
1057 @embedFile("testdata/fuzz/bug_18966.expect"),
1058 );
991}1059}
9921060
993test "reading into empty buffer" {1061test "reading into empty buffer" {
...@@ -1130,21 +1198,13 @@ test "zlib should not overshoot" {...@@ -1130,21 +1198,13 @@ test "zlib should not overshoot" {
1130 var out: [128]u8 = undefined;1198 var out: [128]u8 = undefined;
11311199
1132 {1200 {
1133 const n = try decompress.reader.readSliceShort(out[0..]);1201 const n = try decompress.reader.readSliceShort(&out);
1134
1135 // Expected decompressed data
1136 try std.testing.expectEqual(46, n);1202 try std.testing.expectEqual(46, n);
1137 try std.testing.expectEqualStrings("Copyright Willem van Schaik, Singapore 1995-96", out[0..n]);1203 try std.testing.expectEqualStrings("Copyright Willem van Schaik, Singapore 1995-96", out[0..n]);
1138
1139 // Decompressor don't overshoot underlying reader.
1140 // It is leaving it at the end of compressed data chunk.
1141 try std.testing.expectEqual(data.len - 4, reader.seek);
1142 // TODO what was this testing, exactly?
1143 //try std.testing.expectEqual(0, decompress.unreadBytes());
1144 }1204 }
11451205
1146 // 4 bytes after compressed chunk are available in reader.1206 // 4 bytes after compressed chunk are available in reader.
1147 const n = try reader.readSliceShort(out[0..]);1207 const n = try reader.readSliceShort(&out);
1148 try std.testing.expectEqual(n, 4);1208 try std.testing.expectEqual(n, 4);
1149 try std.testing.expectEqualSlices(u8, data[data.len - 4 .. data.len], out[0..n]);1209 try std.testing.expectEqualSlices(u8, data[data.len - 4 .. data.len], out[0..n]);
1150}1210}