1 package lz4_test
2
3 import (
4 "bytes"
5 "errors"
6 "fmt"
7 "io"
8 "io/ioutil"
9 "os"
10 "reflect"
11 "runtime"
12 "strings"
13 "testing"
14
15 "github.com/pierrec/lz4/v4"
16 )
17
18 func _o(s ...lz4.Option) []lz4.Option {
19 return s
20 }
21
22 func TestReader(t *testing.T) {
23 goldenFiles := []struct {
24 name string
25 isText bool
26 }{
27 {
28 name: "testdata/e.txt.lz4",
29 isText: true,
30 },
31 {
32 name: "testdata/gettysburg.txt.lz4",
33 isText: true,
34 },
35 {
36 name: "testdata/Mark.Twain-Tom.Sawyer.txt.lz4",
37 isText: true,
38 },
39 {
40 name: "testdata/Mark.Twain-Tom.Sawyer_long.txt.lz4",
41 isText: true,
42 },
43 {
44 name: "testdata/Mark.Twain-Tom.Sawyer_linked.txt.lz4",
45 isText: true,
46 },
47 {
48 name: "testdata/pg1661.txt.lz4",
49 isText: false,
50 },
51 {
52 name: "testdata/pi.txt.lz4",
53 isText: true,
54 },
55 {
56 name: "testdata/random.data.lz4",
57 isText: false,
58 },
59 {
60 name: "testdata/repeat.txt.lz4",
61 isText: true,
62 },
63 {
64 name: "testdata/pg_control.tar.lz4",
65 isText: false,
66 },
67 }
68
69 for _, golden := range goldenFiles {
70 for _, opts := range [][]lz4.Option{
71 nil,
72 _o(lz4.ConcurrencyOption(-1)),
73 } {
74 fname := golden.name
75 isText := golden.isText
76 label := fmt.Sprintf("%s %v", fname, opts)
77 t.Run(label, func(t *testing.T) {
78 t.Parallel()
79
80 f, err := os.Open(fname)
81 if err != nil {
82 t.Fatal(err)
83 }
84 defer f.Close()
85
86 rawfile := strings.TrimSuffix(fname, ".lz4")
87 _raw, err := ioutil.ReadFile(rawfile)
88 if err != nil {
89 t.Fatal(err)
90 }
91 var raw []byte
92 if isText && runtime.GOOS == "windows" {
93 raw = []byte(strings.ReplaceAll(string(_raw), "\r\n", "\n"))
94 } else {
95 raw = _raw
96 }
97
98 out := new(bytes.Buffer)
99 zr := lz4.NewReader(f)
100 if err := zr.Apply(opts...); err != nil {
101 t.Fatal(err)
102 }
103 n, err := io.Copy(out, zr)
104 if err != nil {
105 t.Error(err)
106 }
107
108 if got, want := int(n), len(raw); got != want {
109 t.Errorf("invalid size: got %d; want %d", got, want)
110 }
111
112 if got, want := out.Bytes(), raw; !reflect.DeepEqual(got, want) {
113 t.Fatal("uncompressed data does not match original")
114 }
115
116 if len(raw) < 20 {
117 return
118 }
119
120 f2, err := os.Open(fname)
121 if err != nil {
122 t.Fatal(err)
123 }
124 defer f2.Close()
125
126 out.Reset()
127 zr = lz4.NewReader(f2)
128 _, err = io.CopyN(out, zr, 10)
129 if err != nil {
130 t.Fatal(err)
131 }
132 if !reflect.DeepEqual(out.Bytes(), raw[:10]) {
133 t.Fatal("partial read does not match original")
134 }
135 })
136 }
137 }
138 }
139
140 func TestReader_Reset(t *testing.T) {
141 data := pg1661LZ4
142 buf := new(bytes.Buffer)
143 src := bytes.NewReader(data)
144 zr := lz4.NewReader(src)
145
146
147 _, _ = io.CopyN(buf, zr, int64(len(data))/2)
148
149 buf.Reset()
150 src.Reset(data)
151
152 src.Reset(data)
153 zr.Reset(src)
154 if _, err := io.Copy(buf, zr); err != nil {
155 t.Fatal(err)
156 }
157 if !reflect.DeepEqual(buf.Bytes(), pg1661) {
158 t.Fatal("result does not match original")
159 }
160 }
161
162 type brokenWriter int
163
164 func (w *brokenWriter) Write(p []byte) (n int, err error) {
165 n = len(p)
166 if n > int(*w) {
167 n = int(*w)
168 err = errors.New("broken")
169 }
170 *w -= brokenWriter(n)
171 return
172 }
173
174
175
176 func TestWriteToBrokenWriter(t *testing.T) {
177 const capacity = 10
178 w := brokenWriter(capacity)
179 r := lz4.NewReader(bytes.NewReader(pg1661LZ4))
180
181 n, err := r.WriteTo(&w)
182 switch {
183 case n > capacity:
184 t.Errorf("reported number of bytes written %d too big", n)
185 case err == nil:
186 t.Error("no error from broken Writer")
187 case err.Error() != "broken":
188 t.Errorf("unexpected error %q", err.Error())
189 }
190 }
191
192 func TestReaderLegacy(t *testing.T) {
193 goldenFiles := []string{
194 "testdata/vmlinux_LZ4_19377.lz4",
195 "testdata/bzImage_lz4_isolated.lz4",
196 }
197
198 for _, fname := range goldenFiles {
199 for _, opts := range [][]lz4.Option{
200 nil,
201 _o(lz4.ConcurrencyOption(-1)),
202 } {
203 label := fmt.Sprintf("%s %v", fname, opts)
204 t.Run(label, func(t *testing.T) {
205 fname := fname
206 t.Parallel()
207
208 var out bytes.Buffer
209 rawfile := strings.TrimSuffix(fname, ".lz4")
210 raw, err := ioutil.ReadFile(rawfile)
211 if err != nil {
212 t.Fatal(err)
213 }
214
215 f, err := os.Open(fname)
216 if err != nil {
217 t.Fatal(err)
218 }
219 defer f.Close()
220
221 zr := lz4.NewReader(f)
222 if err := zr.Apply(opts...); err != nil {
223 t.Fatal(err)
224 }
225 n, err := io.Copy(&out, zr)
226 if err != nil {
227 t.Fatal(err, n)
228 }
229
230 if got, want := int(n), len(raw); got != want {
231 t.Errorf("invalid sizes: got %d; want %d", got, want)
232 }
233
234 if got, want := out.Bytes(), raw; !bytes.Equal(got, want) {
235 t.Fatal("uncompressed data does not match original")
236 }
237
238 if len(raw) < 20 {
239 return
240 }
241
242 f2, err := os.Open(fname)
243 if err != nil {
244 t.Fatal(err)
245 }
246 defer f2.Close()
247
248 out.Reset()
249 zr = lz4.NewReader(f2)
250 _, err = io.CopyN(&out, zr, 10)
251 if err != nil {
252 t.Fatal(err)
253 }
254
255 if !bytes.Equal(out.Bytes(), raw[:10]) {
256 t.Fatal("partial read does not match original")
257 }
258
259 out.Reset()
260 _, err = io.CopyN(&out, zr, 10)
261 if err != nil {
262 t.Fatal(err)
263 }
264
265 if !bytes.Equal(out.Bytes(), raw[10:20]) {
266 t.Fatal("after seek, partial read does not match original")
267 }
268 })
269 }
270 }
271 }
272
273 func TestUncompressBadBlock(t *testing.T) {
274 f, err := os.Open("testdata/malformed.block.lz4")
275 if err != nil {
276 t.Fatal(err)
277 }
278 defer f.Close()
279 zr := lz4.NewReader(f)
280 if err := zr.Apply(lz4.ConcurrencyOption(4)); err != nil {
281 t.Fatal(err)
282 }
283 _, err = ioutil.ReadAll(zr)
284 if err == nil || !strings.Contains(err.Error(), "invalid block checksum") {
285 t.Error("bad block is not detected")
286 }
287 }
288
289 func TestValidFrameHeader(t *testing.T) {
290 for _, tt := range []struct {
291 name string
292 inBytes []byte
293 want bool
294 errNil bool
295 }{
296 {
297 name: "It is a LZ4",
298 inBytes: []byte{4, 34, 77, 24, 96, 112, 115, 113, 199, 14, 0, 194, 48, 55, 48, 55, 48, 49, 48, 48, 48, 48, 48, 48, 8, 0, 67, 52, 49, 101, 100, 16, 0, 12, 2, 0, 139, 49, 51, 53, 102, 48, 51, 56, 98, 23, 0, 15, 2, 0, 14, 20, 50, 33, 0, 41, 46, 0, 112, 0, 31, 50, 112, 0},
299 want: true,
300 errNil: true,
301 },
302 {
303 name: "Not a LZ4",
304 inBytes: []byte("I am not a lz4 header"),
305 want: false,
306 errNil: true,
307 },
308 {
309 name: "It is a legacy lz4",
310 inBytes: []byte{2, 33, 76, 24, 191, 4, 0, 0, 240, 18, 32, 32, 70, 111, 117, 114, 32, 115, 99, 111, 114, 101, 32, 97, 110, 100, 32, 115, 101, 118, 101, 110, 32, 121, 101, 97, 114, 115, 32, 97, 103, 111, 32, 30, 0, 240, 39, 102, 97, 116, 104, 101, 114, 115, 32, 98, 114, 111, 117, 103, 104, 116, 32, 102},
311 want: true,
312 errNil: true,
313 },
314 } {
315 t.Run(tt.name, func(t *testing.T) {
316 got, err := lz4.ValidFrameHeader(tt.inBytes)
317 if err != nil {
318 if tt.errNil {
319 t.Errorf("ValidFrameHeader(bytes.NewReader(%v)) returned error %v, want nil", tt.inBytes, err)
320 }
321 return
322 }
323 if got != tt.want {
324 t.Errorf("ValidFrameHeader(bytes.NewReader(%v)) returned %t, want %t", tt.inBytes, got, tt.want)
325 }
326 })
327 }
328 }
329
View as plain text