| // Copyright 2026 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 flate | |
| import ( | |
| "bytes" | |
| "flag" | |
| "io" | |
| "strconv" | |
| "testing" | |
| ) | |
| // Fuzzing tweaks: | |
| var fuzzStartF = flag.Int("start", HuffmanOnly, "Start fuzzing at this level") | |
| var fuzzEndF = flag.Int("end", BestCompression, "End fuzzing at this level (inclusive)") | |
| var fuzzMaxF = flag.Int("max", 1<<20, "Maximum input size") | |
| // FuzzEncoding tests the fuzzer by doing roundtrips. | |
| // Every input is run through the fuzzer at every level. | |
| // Note: When running the fuzzer, it may hit the 10-second timeout on slower CPUs. | |
| func FuzzEncoding(f *testing.F) { | |
| startFuzz := *fuzzStartF | |
| endFuzz := *fuzzEndF | |
| maxSize := *fuzzMaxF | |
| decoder := NewReader(nil) | |
| buf, buf2 := new(bytes.Buffer), new(bytes.Buffer) | |
| encs := make([]*Writer, endFuzz-startFuzz+1) | |
| for i := range encs { | |
| var err error | |
| encs[i], err = NewWriter(nil, i+startFuzz) | |
| if err != nil { | |
| f.Fatal(err.Error()) | |
| } | |
| } | |
| f.Fuzz(func(t *testing.T, data []byte) { | |
| if len(data) > maxSize { | |
| return | |
| } | |
| for level := startFuzz; level <= endFuzz; level++ { | |
| if level == DefaultCompression { | |
| continue // Already covered. | |
| } | |
| msg := "level " + strconv.Itoa(level) + ":" | |
| buf.Reset() | |
| fw := encs[level-startFuzz] | |
| fw.Reset(buf) | |
| n, err := fw.Write(data) | |
| if n != len(data) { | |
| t.Fatal(msg, "short write") | |
| } | |
| if err != nil { | |
| t.Fatal(msg, err.Error()) | |
| } | |
| err = fw.Close() | |
| if err != nil { | |
| t.Fatal(msg, err.Error()) | |
| } | |
| compressed := buf.Bytes() | |
| err = decoder.(Resetter).Reset(buf, nil) | |
| if err != nil { | |
| t.Fatal(msg, err.Error()) | |
| } | |
| data2, err := io.ReadAll(decoder) | |
| if err != nil { | |
| t.Fatal(msg, err.Error()) | |
| } | |
| if !bytes.Equal(data, data2) { | |
| t.Fatal(msg + "decompressed not equal") | |
| } | |
| // Do it again... | |
| msg = "level " + strconv.Itoa(level) + " (reset):" | |
| buf2.Reset() | |
| fw.Reset(buf2) | |
| n, err = fw.Write(data) | |
| if n != len(data) { | |
| t.Fatal(msg + "short write") | |
| } | |
| if err != nil { | |
| t.Fatal(msg, err.Error()) | |
| } | |
| err = fw.Close() | |
| if err != nil { | |
| t.Fatal(msg, err.Error()) | |
| } | |
| compressed2 := buf2.Bytes() | |
| err = decoder.(Resetter).Reset(buf2, nil) | |
| if err != nil { | |
| t.Fatal(msg, err.Error()) | |
| } | |
| data2, err = io.ReadAll(decoder) | |
| if err != nil { | |
| t.Fatal(msg, err.Error()) | |
| } | |
| if !bytes.Equal(data, data2) { | |
| t.Fatal(msg, "decompressed not equal") | |
| } | |
| // Determinism checks will usually not be reproducible, | |
| // since it often relies on the internal state of the compressor. | |
| if !bytes.Equal(compressed, compressed2) { | |
| t.Fatal(msg, "non-deterministic output") | |
| } | |
| } | |
| }) | |
| } |