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