| 1 | module zlib |
| 2 | |
| 3 | import encoding.hex |
| 4 | |
| 5 | fn must_decode_hex(s string) []u8 { |
| 6 | return hex.decode(s) or { panic(err) } |
| 7 | } |
| 8 | |
| 9 | fn assert_decompress_error(data []u8, reason string) ! { |
| 10 | decompress(data) or { |
| 11 | assert err.msg() == reason |
| 12 | return |
| 13 | } |
| 14 | return error('did not error') |
| 15 | } |
| 16 | |
| 17 | fn test_zlib_roundtrip_text() { |
| 18 | data := 'Hello world!'.bytes() |
| 19 | compressed := compress(data)! |
| 20 | decompressed := decompress(compressed)! |
| 21 | assert decompressed == data |
| 22 | } |
| 23 | |
| 24 | fn test_zlib_roundtrip_empty() { |
| 25 | data := []u8{} |
| 26 | compressed := compress(data)! |
| 27 | decompressed := decompress(compressed)! |
| 28 | assert decompressed == data |
| 29 | } |
| 30 | |
| 31 | fn test_zlib_roundtrip_binary() { |
| 32 | data := [u8(0), 1, 2, 3, 127, 128, 254, 255] |
| 33 | compressed := compress(data)! |
| 34 | decompressed := decompress(compressed)! |
| 35 | assert decompressed == data |
| 36 | } |
| 37 | |
| 38 | fn test_zlib_roundtrip_large() { |
| 39 | data := 'abcdefgh'.repeat(1000).bytes() |
| 40 | compressed := compress(data)! |
| 41 | assert compressed.len < data.len |
| 42 | decompressed := decompress(compressed)! |
| 43 | assert decompressed == data |
| 44 | } |
| 45 | |
| 46 | fn test_zlib_decompress_known_python_vector() { |
| 47 | compressed := must_decode_hex('789ccb48cdc9c95728cf2fca49e102001e720467') |
| 48 | decompressed := decompress(compressed)! |
| 49 | assert decompressed == 'hello world\n'.bytes() |
| 50 | } |
| 51 | |
| 52 | fn test_zlib_invalid_too_short() { |
| 53 | assert_decompress_error([]u8{}, 'invalid zlib stream: too short')! |
| 54 | } |
| 55 | |
| 56 | fn test_zlib_invalid_header_checksum() { |
| 57 | assert_decompress_error([u8(0x78), 0x9d, 0x00, 0x00, 0x00, 0x01], |
| 58 | 'invalid zlib stream: bad header checksum')! |
| 59 | } |
| 60 | |
| 61 | fn test_zlib_invalid_truncated_payload() { |
| 62 | decompress([u8(0x78), 0x9c, 0x03, 0x00, 0x00, 0x00, 0x01]) or { |
| 63 | assert err.msg().contains('unexpected end of stream') |
| 64 | return |
| 65 | } |
| 66 | assert false |
| 67 | } |
| 68 | |
| 69 | fn test_zlib_invalid_inserted_bytes_before_adler() { |
| 70 | enc := compress('zlib edge-case regression'.repeat(5).bytes())! |
| 71 | mut bad := []u8{cap: enc.len + 1} |
| 72 | bad << enc[..enc.len - 4] |
| 73 | bad << u8(0x7f) |
| 74 | bad << enc[enc.len - 4..] |
| 75 | assert_decompress_error(bad, 'invalid zlib stream: trailing data before adler32')! |
| 76 | } |
| 77 | |
| 78 | fn test_zlib_decompress_callback() { |
| 79 | uncompressed := '321323'.repeat(10_000) |
| 80 | gz := compress(uncompressed.bytes())! |
| 81 | mut size := 0 |
| 82 | mut ref := &size |
| 83 | decoded := decompress_with_callback(gz, fn (chunk []u8, ref &int) int { |
| 84 | unsafe { |
| 85 | *ref += chunk.len |
| 86 | } |
| 87 | return chunk.len |
| 88 | }, ref)! |
| 89 | assert decoded == size |
| 90 | assert decoded == uncompressed.len |
| 91 | } |
| 92 | |