1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package zlib
6
7import (
8	"bytes"
9	"io"
10	"testing"
11)
12
13type zlibTest struct {
14	desc       string
15	raw        string
16	compressed []byte
17	dict       []byte
18	err        error
19}
20
21// Compare-to-golden test data was generated by the ZLIB example program at
22// https://www.zlib.net/zpipe.c
23
24var zlibTests = []zlibTest{
25	{
26		"truncated empty",
27		"",
28		[]byte{},
29		nil,
30		io.ErrUnexpectedEOF,
31	},
32	{
33		"truncated dict",
34		"",
35		[]byte{0x78, 0xbb},
36		[]byte{0x00},
37		io.ErrUnexpectedEOF,
38	},
39	{
40		"truncated checksum",
41		"",
42		[]byte{0x78, 0xbb, 0x00, 0x01, 0x00, 0x01, 0xca, 0x48,
43			0xcd, 0xc9, 0xc9, 0xd7, 0x51, 0x28, 0xcf, 0x2f,
44			0xca, 0x49, 0x01, 0x04, 0x00, 0x00, 0xff, 0xff,
45		},
46		[]byte{0x00},
47		io.ErrUnexpectedEOF,
48	},
49	{
50		"empty",
51		"",
52		[]byte{0x78, 0x9c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01},
53		nil,
54		nil,
55	},
56	{
57		"goodbye",
58		"goodbye, world",
59		[]byte{
60			0x78, 0x9c, 0x4b, 0xcf, 0xcf, 0x4f, 0x49, 0xaa,
61			0x4c, 0xd5, 0x51, 0x28, 0xcf, 0x2f, 0xca, 0x49,
62			0x01, 0x00, 0x28, 0xa5, 0x05, 0x5e,
63		},
64		nil,
65		nil,
66	},
67	{
68		"bad header (CINFO)",
69		"",
70		[]byte{0x88, 0x98, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01},
71		nil,
72		ErrHeader,
73	},
74	{
75		"bad header (FCHECK)",
76		"",
77		[]byte{0x78, 0x9f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01},
78		nil,
79		ErrHeader,
80	},
81	{
82		"bad checksum",
83		"",
84		[]byte{0x78, 0x9c, 0x03, 0x00, 0x00, 0x00, 0x00, 0xff},
85		nil,
86		ErrChecksum,
87	},
88	{
89		"not enough data",
90		"",
91		[]byte{0x78, 0x9c, 0x03, 0x00, 0x00, 0x00},
92		nil,
93		io.ErrUnexpectedEOF,
94	},
95	{
96		"excess data is silently ignored",
97		"",
98		[]byte{
99			0x78, 0x9c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01,
100			0x78, 0x9c, 0xff,
101		},
102		nil,
103		nil,
104	},
105	{
106		"dictionary",
107		"Hello, World!\n",
108		[]byte{
109			0x78, 0xbb, 0x1c, 0x32, 0x04, 0x27, 0xf3, 0x00,
110			0xb1, 0x75, 0x20, 0x1c, 0x45, 0x2e, 0x00, 0x24,
111			0x12, 0x04, 0x74,
112		},
113		[]byte{
114			0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x0a,
115		},
116		nil,
117	},
118	{
119		"wrong dictionary",
120		"",
121		[]byte{
122			0x78, 0xbb, 0x1c, 0x32, 0x04, 0x27, 0xf3, 0x00,
123			0xb1, 0x75, 0x20, 0x1c, 0x45, 0x2e, 0x00, 0x24,
124			0x12, 0x04, 0x74,
125		},
126		[]byte{
127			0x48, 0x65, 0x6c, 0x6c,
128		},
129		ErrDictionary,
130	},
131	{
132		"truncated zlib stream amid raw-block",
133		"hello",
134		[]byte{
135			0x78, 0x9c, 0x00, 0x0c, 0x00, 0xf3, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f,
136		},
137		nil,
138		io.ErrUnexpectedEOF,
139	},
140	{
141		"truncated zlib stream amid fixed-block",
142		"He",
143		[]byte{
144			0x78, 0x9c, 0xf2, 0x48, 0xcd,
145		},
146		nil,
147		io.ErrUnexpectedEOF,
148	},
149}
150
151func TestDecompressor(t *testing.T) {
152	b := new(bytes.Buffer)
153	for _, tt := range zlibTests {
154		in := bytes.NewReader(tt.compressed)
155		zr, err := NewReaderDict(in, tt.dict)
156		if err != nil {
157			if err != tt.err {
158				t.Errorf("%s: NewReader: %s", tt.desc, err)
159			}
160			continue
161		}
162		defer zr.Close()
163
164		// Read and verify correctness of data.
165		b.Reset()
166		n, err := io.Copy(b, zr)
167		if err != nil {
168			if err != tt.err {
169				t.Errorf("%s: io.Copy: %v want %v", tt.desc, err, tt.err)
170			}
171			continue
172		}
173		s := b.String()
174		if s != tt.raw {
175			t.Errorf("%s: got %d-byte %q want %d-byte %q", tt.desc, n, s, len(tt.raw), tt.raw)
176		}
177
178		// Check for sticky errors.
179		if n, err := zr.Read([]byte{0}); n != 0 || err != io.EOF {
180			t.Errorf("%s: Read() = (%d, %v), want (0, io.EOF)", tt.desc, n, err)
181		}
182		if err := zr.Close(); err != nil {
183			t.Errorf("%s: Close() = %v, want nil", tt.desc, err)
184		}
185	}
186}
187