1package fwd
2
3import (
4	"bytes"
5	"io"
6	"io/ioutil"
7	"math/rand"
8	"testing"
9	"unsafe"
10)
11
12// partialReader reads into only
13// part of the supplied byte slice
14// to the underlying reader
15type partialReader struct {
16	r io.Reader
17}
18
19func (p partialReader) Read(b []byte) (int, error) {
20	n := max(1, rand.Intn(len(b)))
21	return p.r.Read(b[:n])
22}
23
24func randomBts(sz int) []byte {
25	o := make([]byte, sz)
26	for i := 0; i < len(o); i += 8 {
27		j := (*int64)(unsafe.Pointer(&o[i]))
28		*j = rand.Int63()
29	}
30	return o
31}
32
33func TestRead(t *testing.T) {
34	bts := randomBts(512)
35
36	// make the buffer much
37	// smaller than the underlying
38	// bytes to incur multiple fills
39	rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 128)
40
41	if rd.BufferSize() != cap(rd.data) {
42		t.Errorf("BufferSize() returned %d; should return %d", rd.BufferSize(), cap(rd.data))
43	}
44
45	// starting Buffered() should be 0
46	if rd.Buffered() != 0 {
47		t.Errorf("Buffered() should return 0 at initialization; got %d", rd.Buffered())
48	}
49
50	some := make([]byte, 32)
51	n, err := rd.Read(some)
52	if err != nil {
53		t.Fatal(err)
54	}
55	if n == 0 {
56		t.Fatal("read 0 bytes w/ a non-nil error!")
57	}
58	some = some[:n]
59
60	more := make([]byte, 64)
61	j, err := rd.Read(more)
62	if err != nil {
63		t.Fatal(err)
64	}
65	if j == 0 {
66		t.Fatal("read 0 bytes w/ a non-nil error")
67	}
68	more = more[:j]
69
70	out, err := ioutil.ReadAll(rd)
71	if err != nil {
72		t.Fatal(err)
73	}
74
75	all := append(some, more...)
76	all = append(all, out...)
77
78	if !bytes.Equal(bts, all) {
79		t.Errorf("bytes not equal; %d bytes in and %d bytes out", len(bts), len(out))
80	}
81
82	// test filling out of the underlying reader
83	big := randomBts(1 << 21)
84	rd = NewReaderSize(partialReader{bytes.NewReader(big)}, 2048)
85	buf := make([]byte, 3100)
86
87	n, err = rd.ReadFull(buf)
88	if err != nil {
89		t.Fatal(err)
90	}
91	if n != 3100 {
92		t.Errorf("expected 3100 bytes read by ReadFull; got %d", n)
93	}
94	if !bytes.Equal(buf[:n], big[:n]) {
95		t.Error("data parity")
96	}
97	rest := make([]byte, (1<<21)-3100)
98	n, err = io.ReadFull(rd, rest)
99	if err != nil {
100		t.Fatal(err)
101	}
102	if n != len(rest) {
103		t.Errorf("expected %d bytes read by io.ReadFull; got %d", len(rest), n)
104	}
105	if !bytes.Equal(append(buf, rest...), big) {
106		t.Fatal("data parity")
107	}
108}
109
110func TestReadByte(t *testing.T) {
111	bts := randomBts(512)
112	rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 98)
113
114	var (
115		err error
116		i   int
117		b   byte
118	)
119
120	// scan through the whole
121	// array byte-by-byte
122	for err != io.EOF {
123		b, err = rd.ReadByte()
124		if err == nil {
125			if b != bts[i] {
126				t.Fatalf("offset %d: %d in; %d out", i, b, bts[i])
127			}
128		}
129		i++
130	}
131	if err != io.EOF {
132		t.Fatal(err)
133	}
134}
135
136func TestSkipNoSeek(t *testing.T) {
137	bts := randomBts(1024)
138	rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 200)
139
140	n, err := rd.Skip(512)
141	if err != nil {
142		t.Fatal(err)
143	}
144	if n != 512 {
145		t.Fatalf("Skip() returned a nil error, but skipped %d bytes instead of %d", n, 512)
146	}
147
148	var b byte
149	b, err = rd.ReadByte()
150	if err != nil {
151		t.Fatal(err)
152	}
153
154	if b != bts[512] {
155		t.Fatalf("at index %d: %d in; %d out", 512, bts[512], b)
156	}
157
158	n, err = rd.Skip(10)
159	if err != nil {
160		t.Fatal(err)
161	}
162	if n != 10 {
163		t.Fatalf("Skip() returned a nil error, but skipped %d bytes instead of %d", n, 10)
164	}
165
166	// now try to skip past the end
167	rd = NewReaderSize(partialReader{bytes.NewReader(bts)}, 200)
168
169	n, err = rd.Skip(2000)
170	if err != io.ErrUnexpectedEOF {
171		t.Fatalf("expected error %q; got %q", io.EOF, err)
172	}
173	if n != 1024 {
174		t.Fatalf("expected to skip only 1024 bytes; skipped %d", n)
175	}
176}
177
178func TestSkipSeek(t *testing.T) {
179	bts := randomBts(1024)
180
181	// bytes.Reader implements io.Seeker
182	rd := NewReaderSize(bytes.NewReader(bts), 200)
183
184	n, err := rd.Skip(512)
185	if err != nil {
186		t.Fatal(err)
187	}
188	if n != 512 {
189		t.Fatalf("Skip() returned a nil error, but skipped %d bytes instead of %d", n, 512)
190	}
191
192	var b byte
193	b, err = rd.ReadByte()
194	if err != nil {
195		t.Fatal(err)
196	}
197
198	if b != bts[512] {
199		t.Fatalf("at index %d: %d in; %d out", 512, bts[512], b)
200	}
201
202	n, err = rd.Skip(10)
203	if err != nil {
204		t.Fatal(err)
205	}
206	if n != 10 {
207		t.Fatalf("Skip() returned a nil error, but skipped %d bytes instead of %d", n, 10)
208	}
209
210	// now try to skip past the end
211	rd.Reset(bytes.NewReader(bts))
212
213	// because of how bytes.Reader
214	// implements Seek, this should
215	// return (2000, nil)
216	n, err = rd.Skip(2000)
217	if err != nil {
218		t.Fatal(err)
219	}
220	if n != 2000 {
221		t.Fatalf("should have returned %d bytes; returned %d", 2000, n)
222	}
223
224	// the next call to Read()
225	// should return io.EOF
226	n, err = rd.Read([]byte{0, 0, 0})
227	if err != io.EOF {
228		t.Errorf("expected %q; got %q", io.EOF, err)
229	}
230	if n != 0 {
231		t.Errorf("expected 0 bytes read; got %d", n)
232	}
233
234}
235
236func TestPeek(t *testing.T) {
237	bts := randomBts(1024)
238	rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 200)
239
240	// first, a peek < buffer size
241	var (
242		peek []byte
243		err  error
244	)
245	peek, err = rd.Peek(100)
246	if err != nil {
247		t.Fatal(err)
248	}
249	if len(peek) != 100 {
250		t.Fatalf("asked for %d bytes; got %d", 100, len(peek))
251	}
252	if !bytes.Equal(peek, bts[:100]) {
253		t.Fatal("peeked bytes not equal")
254	}
255
256	// now, a peek > buffer size
257	peek, err = rd.Peek(256)
258	if err != nil {
259		t.Fatal(err)
260	}
261	if len(peek) != 256 {
262		t.Fatalf("asked for %d bytes; got %d", 100, len(peek))
263	}
264	if !bytes.Equal(peek, bts[:256]) {
265		t.Fatal("peeked bytes not equal")
266	}
267
268	// now try to peek past EOF
269	peek, err = rd.Peek(2048)
270	if err != io.EOF {
271		t.Fatalf("expected error %q; got %q", io.EOF, err)
272	}
273	if len(peek) != 1024 {
274		t.Fatalf("expected %d bytes peek-able; got %d", 1024, len(peek))
275	}
276}
277
278func TestNext(t *testing.T) {
279	size := 1024
280	bts := randomBts(size)
281	rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 200)
282
283	chunksize := 256
284	chunks := size / chunksize
285
286	for i := 0; i < chunks; i++ {
287		out, err := rd.Next(chunksize)
288		if err != nil {
289			t.Fatal(err)
290		}
291		start := chunksize * i
292		if !bytes.Equal(bts[start:start+chunksize], out) {
293			t.Fatalf("chunk %d: chunks not equal", i+1)
294		}
295	}
296}
297
298func TestWriteTo(t *testing.T) {
299	bts := randomBts(2048)
300	rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 200)
301
302	// cause the buffer
303	// to fill a little, just
304	// to complicate things
305	rd.Peek(25)
306
307	var out bytes.Buffer
308	n, err := rd.WriteTo(&out)
309	if err != nil {
310		t.Fatal(err)
311	}
312	if n != 2048 {
313		t.Fatalf("should have written %d bytes; wrote %d", 2048, n)
314	}
315	if !bytes.Equal(out.Bytes(), bts) {
316		t.Fatal("bytes not equal")
317	}
318}
319
320func TestReadFull(t *testing.T) {
321	bts := randomBts(1024)
322	rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 256)
323
324	// try to ReadFull() the whole thing
325	out := make([]byte, 1024)
326	n, err := rd.ReadFull(out)
327	if err != nil {
328		t.Fatal(err)
329	}
330	if n != 1024 {
331		t.Fatalf("expected to read %d bytes; read %d", 1024, n)
332	}
333	if !bytes.Equal(bts, out) {
334		t.Fatal("bytes not equal")
335	}
336
337	// we've read everything; this should EOF
338	n, err = rd.Read(out)
339	if err != io.EOF {
340		t.Fatalf("expected %q; got %q", io.EOF, err)
341	}
342
343	rd.Reset(partialReader{bytes.NewReader(bts)})
344
345	// now try to read *past* EOF
346	out = make([]byte, 1500)
347	n, err = rd.ReadFull(out)
348	if err != io.ErrUnexpectedEOF {
349		t.Fatalf("expected error %q; got %q", io.EOF, err)
350	}
351	if n != 1024 {
352		t.Fatalf("expected to read %d bytes; read %d", 1024, n)
353	}
354}
355
356type readCounter struct {
357	r     io.Reader
358	count int
359}
360
361func (r *readCounter) Read(p []byte) (int, error) {
362	r.count++
363	return r.r.Read(p)
364}
365
366func TestReadFullPerf(t *testing.T) {
367	const size = 1 << 22
368	data := randomBts(size)
369
370	c := readCounter{
371		r: &partialReader{
372			r: bytes.NewReader(data),
373		},
374	}
375
376	r := NewReader(&c)
377
378	const segments = 4
379	out := make([]byte, size/segments)
380
381	for i := 0; i < segments; i++ {
382		// force an unaligned read
383		_, err := r.Peek(5)
384		if err != nil {
385			t.Fatal(err)
386		}
387
388		n, err := r.ReadFull(out)
389		if err != nil {
390			t.Fatal(err)
391		}
392		if n != size/segments {
393			t.Fatalf("read %d bytes, not %d", n, size/segments)
394		}
395	}
396
397	t.Logf("called Read() on the underlying reader %d times to fill %d buffers", c.count, size/r.BufferSize())
398}
399