Edit

kc3-lang/brotli/go/cbrotli/cbrotli_test.go

Branch :

  • Show log

    Commit

  • Author : Evgenii Kliuchnikov
    Date : 2023-08-29 03:59:55
    Hash : ed738e84
    Message : more sophisticated golang TestEncoderFlush PiperOrigin-RevId: 560982956

  • go/cbrotli/cbrotli_test.go
  • // Copyright 2016 Google Inc. All Rights Reserved.
    //
    // Distributed under MIT license.
    // See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
    
    package cbrotli
    
    import (
    	"bytes"
    	"fmt"
    	"io"
    	"io/ioutil"
    	"math"
    	"math/rand"
    	"testing"
    	"time"
    )
    
    func checkCompressedData(compressedData, wantOriginalData []byte) error {
    	uncompressed, err := Decode(compressedData)
    	if err != nil {
    		return fmt.Errorf("brotli decompress failed: %v", err)
    	}
    	if !bytes.Equal(uncompressed, wantOriginalData) {
    		if len(wantOriginalData) != len(uncompressed) {
    			return fmt.Errorf(""+
    				"Data doesn't uncompress to the original value.\n"+
    				"Length of original: %v\n"+
    				"Length of uncompressed: %v",
    				len(wantOriginalData), len(uncompressed))
    		}
    		for i := range wantOriginalData {
    			if wantOriginalData[i] != uncompressed[i] {
    				return fmt.Errorf(""+
    					"Data doesn't uncompress to the original value.\n"+
    					"Original at %v is %v\n"+
    					"Uncompressed at %v is %v",
    					i, wantOriginalData[i], i, uncompressed[i])
    			}
    		}
    	}
    	return nil
    }
    
    func TestEncoderNoWrite(t *testing.T) {
    	out := bytes.Buffer{}
    	e := NewWriter(&out, WriterOptions{Quality: 5})
    	if err := e.Close(); err != nil {
    		t.Errorf("Close()=%v, want nil", err)
    	}
    	// Check Write after close.
    	if _, err := e.Write([]byte("hi")); err == nil {
    		t.Errorf("No error after Close() + Write()")
    	}
    }
    
    func TestEncoderEmptyWrite(t *testing.T) {
    	out := bytes.Buffer{}
    	e := NewWriter(&out, WriterOptions{Quality: 5})
    	n, err := e.Write([]byte(""))
    	if n != 0 || err != nil {
    		t.Errorf("Write()=%v,%v, want 0, nil", n, err)
    	}
    	if err := e.Close(); err != nil {
    		t.Errorf("Close()=%v, want nil", err)
    	}
    }
    
    func TestWriter(t *testing.T) {
    	// Test basic encoder usage.
    	input := []byte("<html><body><H1>Hello world</H1></body></html>")
    	out := bytes.Buffer{}
    	e := NewWriter(&out, WriterOptions{Quality: 1})
    	in := bytes.NewReader([]byte(input))
    	n, err := io.Copy(e, in)
    	if err != nil {
    		t.Errorf("Copy Error: %v", err)
    	}
    	if int(n) != len(input) {
    		t.Errorf("Copy() n=%v, want %v", n, len(input))
    	}
    	if err := e.Close(); err != nil {
    		t.Errorf("Close Error after copied %d bytes: %v", n, err)
    	}
    	if err := checkCompressedData(out.Bytes(), input); err != nil {
    		t.Error(err)
    	}
    }
    
    func TestEncoderStreams(t *testing.T) {
    	// Test that output is streamed.
    	// Adjust window size to ensure the encoder outputs at least enough bytes
    	// to fill the window.
    	const lgWin = 16
    	windowSize := int(math.Pow(2, lgWin))
    	input := make([]byte, 8*windowSize)
    	rand.Read(input)
    	out := bytes.Buffer{}
    	e := NewWriter(&out, WriterOptions{Quality: 11, LGWin: lgWin})
    	halfInput := input[:len(input)/2]
    	in := bytes.NewReader(halfInput)
    
    	n, err := io.Copy(e, in)
    	if err != nil {
    		t.Errorf("Copy Error: %v", err)
    	}
    
    	// We've fed more data than the sliding window size. Check that some
    	// compressed data has been output.
    	if out.Len() == 0 {
    		t.Errorf("Output length is 0 after %d bytes written", n)
    	}
    	if err := e.Close(); err != nil {
    		t.Errorf("Close Error after copied %d bytes: %v", n, err)
    	}
    	if err := checkCompressedData(out.Bytes(), halfInput); err != nil {
    		t.Error(err)
    	}
    }
    
    func TestEncoderLargeInput(t *testing.T) {
    	input := make([]byte, 1000000)
    	rand.Read(input)
    	out := bytes.Buffer{}
    	e := NewWriter(&out, WriterOptions{Quality: 5})
    	in := bytes.NewReader(input)
    
    	n, err := io.Copy(e, in)
    	if err != nil {
    		t.Errorf("Copy Error: %v", err)
    	}
    	if int(n) != len(input) {
    		t.Errorf("Copy() n=%v, want %v", n, len(input))
    	}
    	if err := e.Close(); err != nil {
    		t.Errorf("Close Error after copied %d bytes: %v", n, err)
    	}
    	if err := checkCompressedData(out.Bytes(), input); err != nil {
    		t.Error(err)
    	}
    }
    
    func TestEncoderFlush(t *testing.T) {
    	const payload = 32766 // Enough to force encoder emit 2 chunks
    	input := make([]byte, payload)
    	rand.Read(input)
    	out := bytes.Buffer{}
    	e := NewWriter(&out, WriterOptions{Quality: 5})
    	in := bytes.NewReader(input)
    	_, err := io.Copy(e, in)
    	if err != nil {
    		t.Fatalf("Copy Error: %v", err)
    	}
    	if err := e.Flush(); err != nil {
    		t.Fatalf("Flush(): %v", err)
    	}
    	if out.Len() == 0 {
    		t.Fatalf("0 bytes written after Flush()")
    	}
    	decompressed := make([]byte, payload)
    	reader := NewReader(bytes.NewReader(out.Bytes()))
    	n, err := reader.Read(decompressed)
    	if n >= len(decompressed) || err != nil {
    		t.Errorf("Expected {<%v, nil}, but got {%v, %v}", len(decompressed), n, err)
    	}
    	m, err := reader.Read(decompressed[n:])
    	n += m
    	if n != len(decompressed) || err != nil {
    		t.Errorf("Expected {%v, nil}, but got {%v, %v}", len(decompressed), n, err)
    	}
    	reader.Close()
    	if !bytes.Equal(decompressed, input) {
    		t.Errorf(""+
    			"Decompress after flush: %v\n"+
    			"%q\n"+
    			"want:\n%q",
    			err, decompressed, input)
    	}
    	if err := e.Close(); err != nil {
    		t.Errorf("Close(): %v", err)
    	}
    }
    
    type readerWithTimeout struct {
    	io.ReadCloser
    }
    
    func (r readerWithTimeout) Read(p []byte) (int, error) {
    	type result struct {
    		n   int
    		err error
    	}
    	ch := make(chan result)
    	go func() {
    		n, err := r.ReadCloser.Read(p)
    		ch <- result{n, err}
    	}()
    	select {
    	case result := <-ch:
    		return result.n, result.err
    	case <-time.After(5 * time.Second):
    		return 0, fmt.Errorf("read timed out")
    	}
    }
    
    func TestDecoderStreaming(t *testing.T) {
    	pr, pw := io.Pipe()
    	writer := NewWriter(pw, WriterOptions{Quality: 5, LGWin: 20})
    	reader := readerWithTimeout{NewReader(pr)}
    	defer func() {
    		if err := reader.Close(); err != nil {
    			t.Errorf("reader.Close: %v", err)
    		}
    		go ioutil.ReadAll(pr) // swallow the "EOF" token from writer.Close
    		if err := writer.Close(); err != nil {
    			t.Errorf("writer.Close: %v", err)
    		}
    	}()
    
    	ch := make(chan []byte)
    	errch := make(chan error)
    	go func() {
    		for {
    			segment, ok := <-ch
    			if !ok {
    				return
    			}
    			if n, err := writer.Write(segment); err != nil || n != len(segment) {
    				errch <- fmt.Errorf("write=%v,%v, want %v,%v", n, err, len(segment), nil)
    				return
    			}
    			if err := writer.Flush(); err != nil {
    				errch <- fmt.Errorf("flush: %v", err)
    				return
    			}
    		}
    	}()
    	defer close(ch)
    
    	segments := [...][]byte{
    		[]byte("first"),
    		[]byte("second"),
    		[]byte("third"),
    	}
    	for k, segment := range segments {
    		t.Run(fmt.Sprintf("Segment%d", k), func(t *testing.T) {
    			select {
    			case ch <- segment:
    			case err := <-errch:
    				t.Fatalf("write: %v", err)
    			case <-time.After(5 * time.Second):
    				t.Fatalf("timed out")
    			}
    			wantLen := len(segment)
    			got := make([]byte, wantLen)
    			if n, err := reader.Read(got); err != nil || n != wantLen || !bytes.Equal(got, segment) {
    				t.Fatalf("read[%d]=%q,%v,%v, want %q,%v,%v", k, got, n, err, segment, wantLen, nil)
    			}
    		})
    	}
    }
    
    func TestReader(t *testing.T) {
    	content := bytes.Repeat([]byte("hello world!"), 10000)
    	encoded, _ := Encode(content, WriterOptions{Quality: 5})
    	r := NewReader(bytes.NewReader(encoded))
    	var decodedOutput bytes.Buffer
    	n, err := io.Copy(&decodedOutput, r)
    	if err != nil {
    		t.Fatalf("Copy(): n=%v, err=%v", n, err)
    	}
    	if err := r.Close(); err != nil {
    		t.Errorf("Close(): %v", err)
    	}
    	if got := decodedOutput.Bytes(); !bytes.Equal(got, content) {
    		t.Errorf(""+
    			"Reader output:\n"+
    			"%q\n"+
    			"want:\n"+
    			"<%d bytes>",
    			got, len(content))
    	}
    	buf := make([]byte, 4)
    	if _, err := r.Read(buf); err != errReaderClosed {
    		t.Errorf("Read-after-Close returned %v, expected %v", err, errReaderClosed)
    	}
    }
    
    func TestDecode(t *testing.T) {
    	content := bytes.Repeat([]byte("hello world!"), 10000)
    	encoded, _ := Encode(content, WriterOptions{Quality: 5})
    	decoded, err := Decode(encoded)
    	if err != nil {
    		t.Errorf("Decode: %v", err)
    	}
    	if !bytes.Equal(decoded, content) {
    		t.Errorf(""+
    			"Decode content:\n"+
    			"%q\n"+
    			"want:\n"+
    			"<%d bytes>",
    			decoded, len(content))
    	}
    }
    
    func TestDecodeFuzz(t *testing.T) {
    	// Test that the decoder terminates with corrupted input.
    	content := bytes.Repeat([]byte("hello world!"), 100)
    	src := rand.NewSource(0)
    	encoded, err := Encode(content, WriterOptions{Quality: 5})
    	if err != nil {
    		t.Fatalf("Encode(<%d bytes>, _) = _, %s", len(content), err)
    	}
    	if len(encoded) == 0 {
    		t.Fatalf("Encode(<%d bytes>, _) produced empty output", len(content))
    	}
    	for i := 0; i < 100; i++ {
    		enc := append([]byte{}, encoded...)
    		for j := 0; j < 5; j++ {
    			enc[int(src.Int63())%len(enc)] = byte(src.Int63() % 256)
    		}
    		Decode(enc)
    	}
    }
    
    func TestDecodeTrailingData(t *testing.T) {
    	content := bytes.Repeat([]byte("hello world!"), 100)
    	encoded, _ := Encode(content, WriterOptions{Quality: 5})
    	_, err := Decode(append(encoded, 0))
    	if err == nil {
    		t.Errorf("Expected 'excessive input' error")
    	}
    }
    
    func TestEncodeDecode(t *testing.T) {
    	for _, test := range []struct {
    		data    []byte
    		repeats int
    	}{
    		{nil, 0},
    		{[]byte("A"), 1},
    		{[]byte("<html><body><H1>Hello world</H1></body></html>"), 10},
    		{[]byte("<html><body><H1>Hello world</H1></body></html>"), 1000},
    	} {
    		t.Logf("case %q x %d", test.data, test.repeats)
    		input := bytes.Repeat(test.data, test.repeats)
    		encoded, err := Encode(input, WriterOptions{Quality: 5})
    		if err != nil {
    			t.Errorf("Encode: %v", err)
    		}
    		// Inputs are compressible, but may be too small to compress.
    		if maxSize := len(input)/2 + 20; len(encoded) >= maxSize {
    			t.Errorf(""+
    				"Encode returned %d bytes, want <%d\n"+
    				"Encoded=%q",
    				len(encoded), maxSize, encoded)
    		}
    		decoded, err := Decode(encoded)
    		if err != nil {
    			t.Errorf("Decode: %v", err)
    		}
    		if !bytes.Equal(decoded, input) {
    			var want string
    			if len(input) > 320 {
    				want = fmt.Sprintf("<%d bytes>", len(input))
    			} else {
    				want = fmt.Sprintf("%q", input)
    			}
    			t.Errorf(""+
    				"Decode content:\n"+
    				"%q\n"+
    				"want:\n"+
    				"%s",
    				decoded, want)
    		}
    	}
    }