Source file src/compress/flate/fuzz_test.go

     1  // Copyright 2026 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package flate
     6  
     7  import (
     8  	"bytes"
     9  	"flag"
    10  	"io"
    11  	"strconv"
    12  	"testing"
    13  )
    14  
    15  // Fuzzing tweaks:
    16  var fuzzStartF = flag.Int("start", HuffmanOnly, "Start fuzzing at this level")
    17  var fuzzEndF = flag.Int("end", BestCompression, "End fuzzing at this level (inclusive)")
    18  var fuzzMaxF = flag.Int("max", 1<<20, "Maximum input size")
    19  
    20  // FuzzEncoding tests the fuzzer by doing roundtrips.
    21  // Every input is run through the fuzzer at every level.
    22  // Note: When running the fuzzer, it may hit the 10-second timeout on slower CPUs.
    23  func FuzzEncoding(f *testing.F) {
    24  	startFuzz := *fuzzStartF
    25  	endFuzz := *fuzzEndF
    26  	maxSize := *fuzzMaxF
    27  
    28  	decoder := NewReader(nil)
    29  	buf, buf2 := new(bytes.Buffer), new(bytes.Buffer)
    30  	encs := make([]*Writer, endFuzz-startFuzz+1)
    31  	for i := range encs {
    32  		var err error
    33  		encs[i], err = NewWriter(nil, i+startFuzz)
    34  		if err != nil {
    35  			f.Fatal(err.Error())
    36  		}
    37  	}
    38  
    39  	f.Fuzz(func(t *testing.T, data []byte) {
    40  		if len(data) > maxSize {
    41  			return
    42  		}
    43  		for level := startFuzz; level <= endFuzz; level++ {
    44  			if level == DefaultCompression {
    45  				continue // Already covered.
    46  			}
    47  			msg := "level " + strconv.Itoa(level) + ":"
    48  			buf.Reset()
    49  			fw := encs[level-startFuzz]
    50  			fw.Reset(buf)
    51  			n, err := fw.Write(data)
    52  			if n != len(data) {
    53  				t.Fatal(msg, "short write")
    54  			}
    55  			if err != nil {
    56  				t.Fatal(msg, err.Error())
    57  			}
    58  			err = fw.Close()
    59  			if err != nil {
    60  				t.Fatal(msg, err.Error())
    61  			}
    62  			compressed := buf.Bytes()
    63  			err = decoder.(Resetter).Reset(buf, nil)
    64  			if err != nil {
    65  				t.Fatal(msg, err.Error())
    66  			}
    67  			data2, err := io.ReadAll(decoder)
    68  			if err != nil {
    69  				t.Fatal(msg, err.Error())
    70  			}
    71  			if !bytes.Equal(data, data2) {
    72  				t.Fatal(msg + "decompressed not equal")
    73  			}
    74  
    75  			// Do it again...
    76  			msg = "level " + strconv.Itoa(level) + " (reset):"
    77  			buf2.Reset()
    78  			fw.Reset(buf2)
    79  			n, err = fw.Write(data)
    80  			if n != len(data) {
    81  				t.Fatal(msg + "short write")
    82  			}
    83  			if err != nil {
    84  				t.Fatal(msg, err.Error())
    85  			}
    86  			err = fw.Close()
    87  			if err != nil {
    88  				t.Fatal(msg, err.Error())
    89  			}
    90  			compressed2 := buf2.Bytes()
    91  			err = decoder.(Resetter).Reset(buf2, nil)
    92  			if err != nil {
    93  				t.Fatal(msg, err.Error())
    94  			}
    95  			data2, err = io.ReadAll(decoder)
    96  			if err != nil {
    97  				t.Fatal(msg, err.Error())
    98  			}
    99  			if !bytes.Equal(data, data2) {
   100  				t.Fatal(msg, "decompressed not equal")
   101  			}
   102  			// Determinism checks will usually not be reproducible,
   103  			// since it often relies on the internal state of the compressor.
   104  			if !bytes.Equal(compressed, compressed2) {
   105  				t.Fatal(msg, "non-deterministic output")
   106  			}
   107  		}
   108  	})
   109  }
   110  

View as plain text