| // Copyright 2011 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package bzip2 | |
| import ( | |
| "bytes" | |
| "encoding/hex" | |
| "fmt" | |
| "internal/obscuretestdata" | |
| "io" | |
| "os" | |
| "strings" | |
| "testing" | |
| ) | |
| func mustDecodeHex(s string) []byte { | |
| b, err := hex.DecodeString(s) | |
| if err != nil { | |
| panic(err) | |
| } | |
| return b | |
| } | |
| func mustLoadFile(f string) []byte { | |
| if strings.HasSuffix(f, ".base64") { | |
| b, err := obscuretestdata.ReadFile(f) | |
| if err != nil { | |
| panic(fmt.Sprintf("obscuretestdata.ReadFile(%s): %v", f, err)) | |
| } | |
| return b | |
| } | |
| b, err := os.ReadFile(f) | |
| if err != nil { | |
| panic(err) | |
| } | |
| return b | |
| } | |
| func trim(b []byte) string { | |
| const limit = 1024 | |
| if len(b) < limit { | |
| return fmt.Sprintf("%q", b) | |
| } | |
| return fmt.Sprintf("%q...", b[:limit]) | |
| } | |
| func TestReader(t *testing.T) { | |
| var vectors = []struct { | |
| desc string | |
| input []byte | |
| output []byte | |
| fail bool | |
| }{{ | |
| desc: "hello world", | |
| input: mustDecodeHex("" + | |
| "425a68393141592653594eece83600000251800010400006449080200031064c" + | |
| "4101a7a9a580bb9431f8bb9229c28482776741b0", | |
| ), | |
| output: []byte("hello world\n"), | |
| }, { | |
| desc: "concatenated files", | |
| input: mustDecodeHex("" + | |
| "425a68393141592653594eece83600000251800010400006449080200031064c" + | |
| "4101a7a9a580bb9431f8bb9229c28482776741b0425a68393141592653594eec" + | |
| "e83600000251800010400006449080200031064c4101a7a9a580bb9431f8bb92" + | |
| "29c28482776741b0", | |
| ), | |
| output: []byte("hello world\nhello world\n"), | |
| }, { | |
| desc: "32B zeros", | |
| input: mustDecodeHex("" + | |
| "425a6839314159265359b5aa5098000000600040000004200021008283177245" + | |
| "385090b5aa5098", | |
| ), | |
| output: make([]byte, 32), | |
| }, { | |
| desc: "1MiB zeros", | |
| input: mustDecodeHex("" + | |
| "425a683931415926535938571ce50008084000c0040008200030cc0529a60806" + | |
| "c4201e2ee48a70a12070ae39ca", | |
| ), | |
| output: make([]byte, 1<<20), | |
| }, { | |
| desc: "random data", | |
| input: mustLoadFile("testdata/pass-random1.bz2"), | |
| output: mustLoadFile("testdata/pass-random1.bin"), | |
| }, { | |
| desc: "random data - full symbol range", | |
| input: mustLoadFile("testdata/pass-random2.bz2"), | |
| output: mustLoadFile("testdata/pass-random2.bin"), | |
| }, { | |
| desc: "random data - uses RLE1 stage", | |
| input: mustDecodeHex("" + | |
| "425a6839314159265359d992d0f60000137dfe84020310091c1e280e100e0428" + | |
| "01099210094806c0110002e70806402000546034000034000000f28300000320" + | |
| "00d3403264049270eb7a9280d308ca06ad28f6981bee1bf8160727c7364510d7" + | |
| "3a1e123083421b63f031f63993a0f40051fbf177245385090d992d0f60", | |
| ), | |
| output: mustDecodeHex("" + | |
| "92d5652616ac444a4a04af1a8a3964aca0450d43d6cf233bd03233f4ba92f871" + | |
| "9e6c2a2bd4f5f88db07ecd0da3a33b263483db9b2c158786ad6363be35d17335" + | |
| "ba", | |
| ), | |
| }, { | |
| desc: "1MiB sawtooth", | |
| input: mustLoadFile("testdata/pass-sawtooth.bz2"), | |
| output: func() []byte { | |
| b := make([]byte, 1<<20) | |
| for i := range b { | |
| b[i] = byte(i) | |
| } | |
| return b | |
| }(), | |
| }, { | |
| desc: "RLE2 buffer overrun - issue 5747", | |
| input: mustLoadFile("testdata/fail-issue5747.bz2.base64"), | |
| fail: true, | |
| }, { | |
| desc: "out-of-range selector - issue 8363", | |
| input: mustDecodeHex("" + | |
| "425a68393141592653594eece83600000251800010400006449080200031064c" + | |
| "4101a7a9a580bb943117724538509000000000", | |
| ), | |
| fail: true, | |
| }, { | |
| desc: "bad block size - issue 13941", | |
| input: mustDecodeHex("" + | |
| "425a683131415926535936dc55330063ffc0006000200020a40830008b0008b8" + | |
| "bb9229c28481b6e2a998", | |
| ), | |
| fail: true, | |
| }, { | |
| desc: "bad huffman delta", | |
| input: mustDecodeHex("" + | |
| "425a6836314159265359b1f7404b000000400040002000217d184682ee48a70a" + | |
| "12163ee80960", | |
| ), | |
| fail: true, | |
| }} | |
| for i, v := range vectors { | |
| rd := NewReader(bytes.NewReader(v.input)) | |
| buf, err := io.ReadAll(rd) | |
| if fail := bool(err != nil); fail != v.fail { | |
| if fail { | |
| t.Errorf("test %d (%s), unexpected failure: %v", i, v.desc, err) | |
| } else { | |
| t.Errorf("test %d (%s), unexpected success", i, v.desc) | |
| } | |
| } | |
| if !v.fail && !bytes.Equal(buf, v.output) { | |
| t.Errorf("test %d (%s), output mismatch:\ngot %s\nwant %s", i, v.desc, trim(buf), trim(v.output)) | |
| } | |
| } | |
| } | |
| func TestBitReader(t *testing.T) { | |
| var vectors = []struct { | |
| nbits uint // Number of bits to read | |
| value int // Expected output value (0 for error) | |
| fail bool // Expected operation failure? | |
| }{ | |
| {nbits: 1, value: 1}, | |
| {nbits: 1, value: 0}, | |
| {nbits: 1, value: 1}, | |
| {nbits: 5, value: 11}, | |
| {nbits: 32, value: 0x12345678}, | |
| {nbits: 15, value: 14495}, | |
| {nbits: 3, value: 6}, | |
| {nbits: 6, value: 13}, | |
| {nbits: 1, fail: true}, | |
| } | |
| rd := bytes.NewReader([]byte{0xab, 0x12, 0x34, 0x56, 0x78, 0x71, 0x3f, 0x8d}) | |
| br := newBitReader(rd) | |
| for i, v := range vectors { | |
| val := br.ReadBits(v.nbits) | |
| if fail := bool(br.err != nil); fail != v.fail { | |
| if fail { | |
| t.Errorf("test %d, unexpected failure: ReadBits(%d) = %v", i, v.nbits, br.err) | |
| } else { | |
| t.Errorf("test %d, unexpected success: ReadBits(%d) = nil", i, v.nbits) | |
| } | |
| } | |
| if !v.fail && val != v.value { | |
| t.Errorf("test %d, mismatching value: ReadBits(%d) = %d, want %d", i, v.nbits, val, v.value) | |
| } | |
| } | |
| } | |
| func TestMTF(t *testing.T) { | |
| var vectors = []struct { | |
| idx int // Input index | |
| sym uint8 // Expected output symbol | |
| }{ | |
| {idx: 1, sym: 1}, // [1 0 2 3 4] | |
| {idx: 0, sym: 1}, // [1 0 2 3 4] | |
| {idx: 1, sym: 0}, // [0 1 2 3 4] | |
| {idx: 4, sym: 4}, // [4 0 1 2 3] | |
| {idx: 1, sym: 0}, // [0 4 1 2 3] | |
| } | |
| mtf := newMTFDecoderWithRange(5) | |
| for i, v := range vectors { | |
| sym := mtf.Decode(v.idx) | |
| t.Log(mtf) | |
| if sym != v.sym { | |
| t.Errorf("test %d, symbol mismatch: Decode(%d) = %d, want %d", i, v.idx, sym, v.sym) | |
| } | |
| } | |
| } | |
| func TestZeroRead(t *testing.T) { | |
| b := mustDecodeHex("425a6839314159265359b5aa5098000000600040000004200021008283177245385090b5aa5098") | |
| r := NewReader(bytes.NewReader(b)) | |
| if n, err := r.Read(nil); n != 0 || err != nil { | |
| t.Errorf("Read(nil) = (%d, %v), want (0, nil)", n, err) | |
| } | |
| } | |
| var ( | |
| digits = mustLoadFile("testdata/e.txt.bz2") | |
| newton = mustLoadFile("testdata/Isaac.Newton-Opticks.txt.bz2") | |
| random = mustLoadFile("testdata/random.data.bz2") | |
| ) | |
| func benchmarkDecode(b *testing.B, compressed []byte) { | |
| // Determine the uncompressed size of testfile. | |
| uncompressedSize, err := io.Copy(io.Discard, NewReader(bytes.NewReader(compressed))) | |
| if err != nil { | |
| b.Fatal(err) | |
| } | |
| b.SetBytes(uncompressedSize) | |
| b.ReportAllocs() | |
| b.ResetTimer() | |
| for i := 0; i < b.N; i++ { | |
| r := bytes.NewReader(compressed) | |
| io.Copy(io.Discard, NewReader(r)) | |
| } | |
| } | |
| func BenchmarkDecodeDigits(b *testing.B) { benchmarkDecode(b, digits) } | |
| func BenchmarkDecodeNewton(b *testing.B) { benchmarkDecode(b, newton) } | |
| func BenchmarkDecodeRand(b *testing.B) { benchmarkDecode(b, random) } |