• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2016 Google Inc. All Rights Reserved.
2//
3// Distributed under MIT license.
4// See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
5
6package cbrotli
7
8import (
9	"bytes"
10	"fmt"
11	"io"
12	"math"
13	"math/rand"
14	"testing"
15)
16
17func checkCompressedData(compressedData, wantOriginalData []byte) error {
18	uncompressed, err := Decode(compressedData)
19	if err != nil {
20		return fmt.Errorf("brotli decompress failed: %v", err)
21	}
22	if !bytes.Equal(uncompressed, wantOriginalData) {
23		if len(wantOriginalData) != len(uncompressed) {
24			return fmt.Errorf(""+
25				"Data doesn't uncompress to the original value.\n"+
26				"Length of original: %v\n"+
27				"Length of uncompressed: %v",
28				len(wantOriginalData), len(uncompressed))
29		}
30		for i := range wantOriginalData {
31			if wantOriginalData[i] != uncompressed[i] {
32				return fmt.Errorf(""+
33					"Data doesn't uncompress to the original value.\n"+
34					"Original at %v is %v\n"+
35					"Uncompressed at %v is %v",
36					i, wantOriginalData[i], i, uncompressed[i])
37			}
38		}
39	}
40	return nil
41}
42
43func TestEncoderNoWrite(t *testing.T) {
44	out := bytes.Buffer{}
45	e := NewWriter(&out, WriterOptions{Quality: 5})
46	if err := e.Close(); err != nil {
47		t.Errorf("Close()=%v, want nil", err)
48	}
49	// Check Write after close.
50	if _, err := e.Write([]byte("hi")); err == nil {
51		t.Errorf("No error after Close() + Write()")
52	}
53}
54
55func TestEncoderEmptyWrite(t *testing.T) {
56	out := bytes.Buffer{}
57	e := NewWriter(&out, WriterOptions{Quality: 5})
58	n, err := e.Write([]byte(""))
59	if n != 0 || err != nil {
60		t.Errorf("Write()=%v,%v, want 0, nil", n, err)
61	}
62	if err := e.Close(); err != nil {
63		t.Errorf("Close()=%v, want nil", err)
64	}
65}
66
67func TestWriter(t *testing.T) {
68	// Test basic encoder usage.
69	input := []byte("<html><body><H1>Hello world</H1></body></html>")
70	out := bytes.Buffer{}
71	e := NewWriter(&out, WriterOptions{Quality: 1})
72	in := bytes.NewReader([]byte(input))
73	n, err := io.Copy(e, in)
74	if err != nil {
75		t.Errorf("Copy Error: %v", err)
76	}
77	if int(n) != len(input) {
78		t.Errorf("Copy() n=%v, want %v", n, len(input))
79	}
80	if err := e.Close(); err != nil {
81		t.Errorf("Close Error after copied %d bytes: %v", n, err)
82	}
83	if err := checkCompressedData(out.Bytes(), input); err != nil {
84		t.Error(err)
85	}
86}
87
88func TestEncoderStreams(t *testing.T) {
89	// Test that output is streamed.
90	// Adjust window size to ensure the encoder outputs at least enough bytes
91	// to fill the window.
92	const lgWin = 16
93	windowSize := int(math.Pow(2, lgWin))
94	input := make([]byte, 2*windowSize)
95	rand.Read(input)
96	out := bytes.Buffer{}
97	e := NewWriter(&out, WriterOptions{Quality: 11, LGWin: lgWin})
98	halfInput := input[:len(input)/2]
99	in := bytes.NewReader(halfInput)
100
101	n, err := io.Copy(e, in)
102	if err != nil {
103		t.Errorf("Copy Error: %v", err)
104	}
105
106	// We've fed more data than the sliding window size. Check that some
107	// compressed data has been output.
108	if out.Len() == 0 {
109		t.Errorf("Output length is after %d bytes written", n)
110	}
111	if err := e.Close(); err != nil {
112		t.Errorf("Close Error after copied %d bytes: %v", n, err)
113	}
114	if err := checkCompressedData(out.Bytes(), halfInput); err != nil {
115		t.Error(err)
116	}
117}
118
119func TestEncoderLargeInput(t *testing.T) {
120	input := make([]byte, 1000000)
121	rand.Read(input)
122	out := bytes.Buffer{}
123	e := NewWriter(&out, WriterOptions{Quality: 5})
124	in := bytes.NewReader(input)
125
126	n, err := io.Copy(e, in)
127	if err != nil {
128		t.Errorf("Copy Error: %v", err)
129	}
130	if int(n) != len(input) {
131		t.Errorf("Copy() n=%v, want %v", n, len(input))
132	}
133	if err := e.Close(); err != nil {
134		t.Errorf("Close Error after copied %d bytes: %v", n, err)
135	}
136	if err := checkCompressedData(out.Bytes(), input); err != nil {
137		t.Error(err)
138	}
139}
140
141func TestEncoderFlush(t *testing.T) {
142	input := make([]byte, 1000)
143	rand.Read(input)
144	out := bytes.Buffer{}
145	e := NewWriter(&out, WriterOptions{Quality: 5})
146	in := bytes.NewReader(input)
147	_, err := io.Copy(e, in)
148	if err != nil {
149		t.Fatalf("Copy Error: %v", err)
150	}
151	if err := e.Flush(); err != nil {
152		t.Fatalf("Flush(): %v", err)
153	}
154	decompressed := make([]byte, 1000)
155	reader := NewReader(bytes.NewReader(out.Bytes()))
156	n, err := reader.Read(decompressed)
157	if n != len(decompressed) || err != nil {
158		t.Errorf("Expected <%v, nil>, but <%v, %v>", len(decompressed), n, err)
159	}
160	reader.Close()
161	if !bytes.Equal(decompressed, input) {
162		t.Errorf(""+
163			"Decompress after flush: %v\n"+
164			"%q\n"+
165			"want:\n%q",
166			err, decompressed, input)
167	}
168	if err := e.Close(); err != nil {
169		t.Errorf("Close(): %v", err)
170	}
171}
172
173func TestReader(t *testing.T) {
174	content := bytes.Repeat([]byte("hello world!"), 10000)
175	encoded, _ := Encode(content, WriterOptions{Quality: 5})
176	r := NewReader(bytes.NewReader(encoded))
177	var decodedOutput bytes.Buffer
178	n, err := io.Copy(&decodedOutput, r)
179	if err != nil {
180		t.Fatalf("Copy(): n=%v, err=%v", n, err)
181	}
182	if err := r.Close(); err != nil {
183		t.Errorf("Close(): %v", err)
184	}
185	if got := decodedOutput.Bytes(); !bytes.Equal(got, content) {
186		t.Errorf(""+
187			"Reader output:\n"+
188			"%q\n"+
189			"want:\n"+
190			"%q",
191			got, content)
192	}
193}
194
195func TestDecode(t *testing.T) {
196	content := bytes.Repeat([]byte("hello world!"), 10000)
197	encoded, _ := Encode(content, WriterOptions{Quality: 5})
198	decoded, err := Decode(encoded)
199	if err != nil {
200		t.Errorf("Decode: %v", err)
201	}
202	if !bytes.Equal(decoded, content) {
203		t.Errorf(""+
204			"Decode content:\n"+
205			"%q\n"+
206			"want:\n"+
207			"%q",
208			decoded, content)
209	}
210}
211
212func TestDecodeFuzz(t *testing.T) {
213	// Test that the decoder terminates with corrupted input.
214	content := bytes.Repeat([]byte("hello world!"), 100)
215	src := rand.NewSource(0)
216	encoded, _ := Encode(content, WriterOptions{Quality: 5})
217	for i := 0; i < 100; i++ {
218		enc := append([]byte{}, encoded...)
219		for j := 0; j < 5; j++ {
220			enc[int(src.Int63())%len(enc)] = byte(src.Int63() % 256)
221		}
222		Decode(enc)
223	}
224}
225
226func TestDecodeTrailingData(t *testing.T) {
227	content := bytes.Repeat([]byte("hello world!"), 100)
228	encoded, _ := Encode(content, WriterOptions{Quality: 5})
229	_, err := Decode(append(encoded, 0))
230	if err == nil {
231		t.Errorf("Expected 'excessive input' error")
232	}
233}
234
235func TestEncodeDecode(t *testing.T) {
236	for _, test := range []struct {
237		data    []byte
238		repeats int
239	}{
240		{nil, 0},
241		{[]byte("A"), 1},
242		{[]byte("<html><body><H1>Hello world</H1></body></html>"), 10},
243		{[]byte("<html><body><H1>Hello world</H1></body></html>"), 1000},
244	} {
245		t.Logf("case %q x %d", test.data, test.repeats)
246		input := bytes.Repeat(test.data, test.repeats)
247		encoded, err := Encode(input, WriterOptions{Quality: 5})
248		if err != nil {
249			t.Errorf("Encode: %v", err)
250		}
251		// Inputs are compressible, but may be too small to compress.
252		if maxSize := len(input)/2 + 20; len(encoded) >= maxSize {
253			t.Errorf(""+
254				"Encode returned %d bytes, want <%d\n"+
255				"Encoded=%q",
256				len(encoded), maxSize, encoded)
257		}
258		decoded, err := Decode(encoded)
259		if err != nil {
260			t.Errorf("Decode: %v", err)
261		}
262		if !bytes.Equal(decoded, input) {
263			t.Errorf(""+
264				"Decode content:\n"+
265				"%q\n"+
266				"want:\n"+
267				"%q",
268				decoded, input)
269		}
270	}
271}
272