| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- // 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)
- }
- }
- }
- func TestEncodeDecodeWithDictionary(t *testing.T) {
- q := 5
- l := 4096
- input := make([]byte, l)
- for i := 0; i < l; i++ {
- input[i] = byte(i*7 + i*i*5)
- }
- // use dictionary same as input
- pd := NewPreparedDictionary(input, DtRaw, q)
- defer pd.Close()
- encoded, err := Encode(input, WriterOptions{Quality: q, Dictionary: pd})
- if err != nil {
- t.Errorf("Encode: %v", err)
- }
- limit := 20
- if len(encoded) > limit {
- t.Errorf("Output length exceeds expectations: %d > %d", len(encoded), limit)
- }
- decoded, err := DecodeWithRawDictionary(encoded, input)
- 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)
- }
- }
|