github.com/Schaudge/hts@v0.0.0-20240223063651-737b4d69d68c/bgzf/bgzf_test.go (about)

     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 Go LICENSE file.
     4  
     5  // Changes copyright ©2012 The bíogo Authors. All rights reserved.
     6  // Use of this source code is governed by a BSD-style
     7  // license that can be found in the LICENSE file.
     8  
     9  package bgzf_test
    10  
    11  import (
    12  	"bytes"
    13  	"compress/gzip"
    14  	"errors"
    15  	"flag"
    16  	"fmt"
    17  	"io"
    18  	"io/ioutil"
    19  	"os"
    20  	"runtime"
    21  	"strings"
    22  	"sync"
    23  	"testing"
    24  	"time"
    25  
    26  	. "github.com/Schaudge/hts/bgzf"
    27  	"github.com/Schaudge/hts/bgzf/cache"
    28  )
    29  
    30  var (
    31  	go1_8 bool
    32  
    33  	conc = flag.Int("conc", 1, "sets the level of concurrency for compression")
    34  	file = flag.String("bench.file", "", "bgzf file to read for benchmarking decompression")
    35  )
    36  
    37  type countWriter struct {
    38  	bytes int64
    39  	w     io.Writer
    40  }
    41  
    42  func (cw *countWriter) Write(p []byte) (n int, err error) {
    43  	n, err = cw.w.Write(p)
    44  	cw.bytes += int64(n)
    45  	return
    46  }
    47  
    48  // TestEmpty tests that an empty payload still forms a valid GZIP stream.
    49  func TestEmpty(t *testing.T) {
    50  	buf := new(bytes.Buffer)
    51  
    52  	if err := NewWriter(buf, *conc).Close(); err != nil {
    53  		t.Fatalf("Writer.Close: %v", err)
    54  	}
    55  
    56  	r, err := NewReader(buf, *conc)
    57  	if err != nil {
    58  		t.Fatalf("NewReader: %v", err)
    59  	}
    60  	b, err := ioutil.ReadAll(r)
    61  	if err != nil {
    62  		t.Fatalf("ReadAll: %v", err)
    63  	}
    64  	if len(b) != 0 {
    65  		t.Fatalf("got %d bytes, want 0", len(b))
    66  	}
    67  	if err := r.Close(); err != nil {
    68  		t.Fatalf("Reader.Close: %v", err)
    69  	}
    70  }
    71  
    72  type crippledReaderAt struct {
    73  	r *bytes.Reader
    74  }
    75  
    76  func (r crippledReaderAt) ReadAt(b []byte, off int64) (int, error) {
    77  	return r.r.ReadAt(b, off)
    78  }
    79  
    80  // TestEOF tests HasEOF can find the EOF magic block.
    81  func TestEOF(t *testing.T) {
    82  	// os.File cases
    83  	f, err := ioutil.TempFile(os.TempDir(), "bgzf_EOF_test_")
    84  	if err != nil {
    85  		t.Fatalf("Create temp file: %v", err)
    86  	}
    87  	fname := f.Name()
    88  
    89  	if err := NewWriter(f, *conc).Close(); err != nil {
    90  		t.Fatalf("Writer.Close: %v", err)
    91  	}
    92  
    93  	f, err = os.Open(fname)
    94  	if err != nil {
    95  		t.Fatalf("Open temp file: %v", err)
    96  	}
    97  	ok, err := HasEOF(f)
    98  	if err != nil {
    99  		t.Errorf("HasEOF: %v", err)
   100  	}
   101  	if !ok {
   102  		t.Error("Expected EOF in os.File: not found.")
   103  	}
   104  
   105  	os.Remove(fname)
   106  
   107  	f, err = os.Open(os.TempDir())
   108  	if err != nil {
   109  		t.Fatalf("Open temp dir: %v", err)
   110  	}
   111  	ok, err = HasEOF(f)
   112  	if want := "read " + os.TempDir() + ": is a directory"; err.Error() != want {
   113  		t.Errorf("Expected error:%s got:%v", want, err)
   114  	}
   115  	if ok {
   116  		t.Error("Unexpected EOF in os.File IsDir: found.")
   117  	}
   118  
   119  	// {bytes,strings}.Reader cases
   120  	var buf bytes.Buffer
   121  	if err := NewWriter(&buf, *conc).Close(); err != nil {
   122  		t.Fatalf("Writer.Close: %v", err)
   123  	}
   124  
   125  	ok, err = HasEOF(bytes.NewReader(buf.Bytes()))
   126  	if err != nil {
   127  		t.Errorf("HasEOF: %v", err)
   128  	}
   129  	if !ok {
   130  		t.Error("Expected EOF in []byte: not found.")
   131  	}
   132  
   133  	ok, err = HasEOF(strings.NewReader(buf.String()))
   134  	if err != nil {
   135  		t.Errorf("HasEOF: %v", err)
   136  	}
   137  	if !ok {
   138  		t.Error("Expected EOF in string: not found.")
   139  	}
   140  
   141  	ok, err = HasEOF(crippledReaderAt{bytes.NewReader(buf.Bytes())})
   142  	if err != ErrNoEnd {
   143  		t.Errorf("Expected error:%s got:%v", ErrNoEnd, err)
   144  	}
   145  	if ok {
   146  		t.Error("Unexpected EOF in crippled ReaderAt: found.")
   147  	}
   148  }
   149  
   150  // TestRoundTrip tests that bgzipping and then bgunzipping is the identity
   151  // function.
   152  func TestRoundTrip(t *testing.T) {
   153  	buf := new(bytes.Buffer)
   154  
   155  	w := NewWriter(buf, *conc)
   156  	w.Comment = "comment"
   157  	w.Extra = []byte("extra")
   158  	w.ModTime = time.Unix(1e8, 0)
   159  	w.Name = "name"
   160  	if _, err := w.Write([]byte("payload")); err != nil {
   161  		t.Fatalf("Write: %v", err)
   162  	}
   163  	if err := w.Close(); err != nil {
   164  		t.Fatalf("Writer.Close: %v", err)
   165  	}
   166  	// FIXME(kortschak) The magic block is written on close,
   167  	// so we need to discount that until we have the capacity
   168  	// to see every header again.
   169  	wbl := buf.Len() - len(MagicBlock)
   170  
   171  	r, err := NewReader(buf, *conc)
   172  	if err != nil {
   173  		t.Fatalf("NewReader: %v", err)
   174  	}
   175  
   176  	if bl := ExpectedMemberSize(r.Header); bl != wbl {
   177  		t.Errorf("expectedMemberSize is %d, want %d", bl, wbl)
   178  	}
   179  	blEnc := string([]byte{byte(wbl - 1), byte((wbl - 1) >> 8)})
   180  	if string(r.Extra) != "BC\x02\x00"+blEnc+"extra" {
   181  		t.Errorf("extra is %q, want %q", r.Extra, "BC\x02\x00"+blEnc+"extra")
   182  	}
   183  	b, err := ioutil.ReadAll(r)
   184  	if err != nil {
   185  		t.Fatalf("ReadAll: %v", err)
   186  	}
   187  	if string(b) != "payload" {
   188  		t.Fatalf("payload is %q, want %q", string(b), "payload")
   189  	}
   190  	if r.Comment != "comment" {
   191  		t.Errorf("comment is %q, want %q", r.Comment, "comment")
   192  	}
   193  	if bl := ExpectedMemberSize(r.Header); bl != len(MagicBlock) {
   194  		t.Errorf("expectedMemberSize is %d, want %d", bl, len(MagicBlock))
   195  	}
   196  	if string(r.Extra) != "BC\x02\x00\x1b\x00" {
   197  		t.Errorf("extra is %q, want %q", r.Extra, "BC\x02\x00\x1b\x00")
   198  	}
   199  	if r.ModTime.Unix() != 1e8 {
   200  		t.Errorf("mtime is %d, want %d", r.ModTime.Unix(), uint32(1e8))
   201  	}
   202  	if r.Name != "name" {
   203  		t.Errorf("name is %q, want %q", r.Name, "name")
   204  	}
   205  	if r.OS != 0xff {
   206  		t.Errorf("os is %x, want %x", r.OS, 0xff)
   207  	}
   208  	if err := r.Close(); err != nil {
   209  		t.Errorf("Reader.Close: %v", err)
   210  	}
   211  }
   212  
   213  // TestRoundTripMulti tests that bgzipping and then bgunzipping is the identity
   214  // function for a multiple member bgzf.
   215  func TestRoundTripMulti(t *testing.T) {
   216  	var wbl [2]int
   217  	buf := new(bytes.Buffer)
   218  
   219  	w := NewWriter(buf, *conc)
   220  	w.Comment = "comment"
   221  	w.Extra = []byte("extra")
   222  	w.ModTime = time.Unix(1e8, 0)
   223  	w.Name = "name"
   224  	if _, err := w.Write([]byte("payload1")); err != nil {
   225  		t.Fatalf("Write: %v", err)
   226  	}
   227  	if err := w.Flush(); err != nil {
   228  		t.Fatalf("Flush: %v", err)
   229  	}
   230  	if err := w.Wait(); err != nil {
   231  		t.Fatalf("Wait: %v", err)
   232  	}
   233  	wbl[0] = buf.Len()
   234  	if _, err := w.Write([]byte("payloadTwo")); err != nil {
   235  		t.Fatalf("Write: %v", err)
   236  	}
   237  	if err := w.Close(); err != nil {
   238  		t.Fatalf("Writer.Close: %v", err)
   239  	}
   240  	wbl[1] = buf.Len() - wbl[0] - len(MagicBlock)
   241  
   242  	var (
   243  		b     []byte
   244  		bl, n int
   245  		err   error
   246  	)
   247  	r, err := NewReader(buf, *conc)
   248  	if err != nil {
   249  		t.Fatalf("NewReader: %v", err)
   250  	}
   251  
   252  	if r.Comment != "comment" {
   253  		t.Errorf("comment is %q, want %q", r.Comment, "comment")
   254  	}
   255  	blEnc := string([]byte{byte(wbl[0] - 1), byte((wbl[0] - 1) >> 8)})
   256  	if string(r.Extra) != "BC\x02\x00"+blEnc+"extra" {
   257  		t.Errorf("extra is %q, want %q", r.Extra, "BC\x02\x00"+blEnc+"extra")
   258  	}
   259  	if r.ModTime.Unix() != 1e8 {
   260  		t.Errorf("mtime is %d, want %d", r.ModTime.Unix(), uint32(1e8))
   261  	}
   262  	if r.Name != "name" {
   263  		t.Errorf("name is %q, want %q", r.Name, "name")
   264  	}
   265  
   266  	bl = ExpectedMemberSize(r.Header)
   267  	if bl != wbl[0] {
   268  		t.Errorf("expectedMemberSize is %d, want %d", bl, wbl[0])
   269  	}
   270  	b = make([]byte, len("payload1payloadTwo"))
   271  	n, err = r.Read(b)
   272  	if string(b[:n]) != "payload1payloadTwo" {
   273  		t.Errorf("payload is %q, want %q", string(b[:n]), "payload1payloadTwo")
   274  	}
   275  	if err != nil {
   276  		t.Errorf("Read: %v", err)
   277  	}
   278  
   279  	bl = ExpectedMemberSize(r.Header)
   280  	if bl != wbl[1] {
   281  		t.Errorf("expectedMemberSize is %d, want %d", bl, wbl[1])
   282  	}
   283  	b = make([]byte, 1)
   284  	n, err = r.Read(b)
   285  	if string(b[:n]) != "" {
   286  		t.Errorf("payload is %q, want %q", string(b[:n]), "")
   287  	}
   288  	if err != io.EOF {
   289  		t.Errorf("Read: %v", err)
   290  	}
   291  	r.Close()
   292  }
   293  
   294  // See https://github.com/biogo/hts/issues/57
   295  func TestHeaderIssue57(t *testing.T) {
   296  	var stamp time.Time
   297  	if !go1_8 {
   298  		unixEpoch := time.Unix(0, 0)
   299  		stamp = unixEpoch
   300  	}
   301  
   302  	var buf bytes.Buffer
   303  	bg := NewWriter(&buf, *conc)
   304  	bg.ModTime = stamp
   305  	bg.OS = 0xff
   306  	err := bg.Close()
   307  	if err != nil {
   308  		t.Fatal("error closing Writer")
   309  	}
   310  	got := buf.Bytes()[:16]
   311  	want := []byte(MagicBlock[:16])
   312  	if !bytes.Equal(got, want) {
   313  		t.Fatalf("unexpected header:\ngot: %0#2v\nwant:%0#2v", got, want)
   314  	}
   315  }
   316  
   317  // TestRoundTripMultiSeek tests that bgzipping and then bgunzipping is the identity
   318  // function for a multiple member bgzf with an underlying Seeker.
   319  func TestRoundTripMultiSeek(t *testing.T) {
   320  	f, err := ioutil.TempFile(os.TempDir(), "bgzf_test_")
   321  	if err != nil {
   322  		t.Fatalf("Create temp file: %v", err)
   323  	}
   324  	fname := f.Name()
   325  
   326  	var wbl [2]int
   327  	cw := &countWriter{w: f}
   328  	w := NewWriter(cw, *conc)
   329  	w.Comment = "comment"
   330  	w.Extra = []byte("extra")
   331  	w.ModTime = time.Unix(1e8, 0)
   332  	w.Name = "name"
   333  	if _, err := w.Write([]byte("payload1")); err != nil {
   334  		t.Fatalf("Write: %v", err)
   335  	}
   336  	if err := w.Flush(); err != nil {
   337  		t.Fatalf("Flush: %v", err)
   338  	}
   339  	if err := w.Wait(); err != nil {
   340  		t.Fatalf("Wait: %v", err)
   341  	}
   342  	offset := cw.bytes
   343  	wbl[0] = int(offset)
   344  	if _, err := w.Write([]byte("payloadTwo")); err != nil {
   345  		t.Fatalf("Write: %v", err)
   346  	}
   347  	if err := w.Close(); err != nil {
   348  		t.Fatalf("Writer.Close: %v", err)
   349  	}
   350  	if err := f.Close(); err != nil {
   351  		t.Fatalf("os.File.Close: %v", err)
   352  	}
   353  	wbl[1] = int(cw.bytes-offset) - len(MagicBlock)
   354  
   355  	var (
   356  		b     []byte
   357  		bl, n int
   358  	)
   359  
   360  	f, err = os.Open(fname)
   361  	if err != nil {
   362  		t.Fatalf("Reopen temp file: %v", err)
   363  	}
   364  	r, err := NewReader(f, *conc)
   365  	if err != nil {
   366  		t.Fatalf("NewReader: %v", err)
   367  	}
   368  
   369  	// Insert a HasEOF to ensure it does not corrupt subsequent reads.
   370  	HasEOF(f)
   371  
   372  	if r.Comment != "comment" {
   373  		t.Errorf("comment is %q, want %q", r.Comment, "comment")
   374  	}
   375  	blEnc := string([]byte{byte(wbl[0] - 1), byte((wbl[0] - 1) >> 8)})
   376  	if string(r.Extra) != "BC\x02\x00"+blEnc+"extra" {
   377  		t.Errorf("extra is %q, want %q", r.Extra, "BC\x02\x00"+blEnc+"extra")
   378  	}
   379  	if r.ModTime.Unix() != 1e8 {
   380  		t.Errorf("mtime is %d, want %d", r.ModTime.Unix(), uint32(1e8))
   381  	}
   382  	if r.Name != "name" {
   383  		t.Errorf("name is %q, want %q", r.Name, "name")
   384  	}
   385  	bl = ExpectedMemberSize(r.Header)
   386  	if bl != wbl[0] {
   387  		t.Errorf("expectedMemberSize is %d, want %d", bl, wbl[0])
   388  	}
   389  	b = make([]byte, len("payload1payloadTwo")+1)
   390  	n, err = r.Read(b)
   391  	if err != io.EOF {
   392  		t.Errorf("Read: %v", err)
   393  	}
   394  	if bl := ExpectedMemberSize(r.Header); bl != len(MagicBlock) {
   395  		t.Errorf("expectedMemberSize is %d, want %d", bl, len(MagicBlock))
   396  	}
   397  	if string(r.Extra) != "BC\x02\x00\x1b\x00" {
   398  		t.Errorf("extra is %q, want %q", r.Extra, "BC\x02\x00\x1b\x00")
   399  	}
   400  	if string(b[:n]) != "payload1payloadTwo" {
   401  		t.Errorf("payload is %q, want %q", string(b[:n]), "payload1payloadTwo")
   402  	}
   403  	if err := r.Seek(Offset{}); err != nil {
   404  		t.Errorf("Seek: %v", err)
   405  	}
   406  	n, err = r.Read(b)
   407  	if err != io.EOF {
   408  		t.Errorf("Read: %v", err)
   409  	}
   410  	if string(b[:n]) != "payload1payloadTwo" {
   411  		t.Errorf("payload is %q, want %q", string(b[:n]), "payload1payloadTwo")
   412  	}
   413  	if err := r.Seek(Offset{File: offset}); err != nil {
   414  		t.Fatalf("Seek: %v", err)
   415  	}
   416  	bl = ExpectedMemberSize(r.Header)
   417  	if bl != wbl[1] {
   418  		t.Errorf("expectedMemberSize is %d, want %d", bl, wbl[1])
   419  	}
   420  	b = make([]byte, bl+1)
   421  	n, err = r.Read(b)
   422  	if err != io.EOF {
   423  		t.Errorf("Read: %v", err)
   424  	}
   425  	r.Close()
   426  	if string(b[:n]) != "payloadTwo" {
   427  		t.Errorf("payload is %q, want %q", string(b[:n]), "payloadTwo")
   428  	}
   429  	os.Remove(fname)
   430  }
   431  
   432  type errorReadSeeker struct {
   433  	r   io.ReadSeeker
   434  	err error
   435  }
   436  
   437  func (r errorReadSeeker) Read(p []byte) (int, error) {
   438  	n, err := r.r.Read(p)
   439  	if err == nil && r.err != nil {
   440  		err = r.err
   441  	}
   442  	return n, err
   443  }
   444  
   445  func (r errorReadSeeker) Seek(offset int64, whence int) (int64, error) {
   446  	n, err := r.r.Seek(offset, whence)
   447  	if r.err != nil {
   448  		err = r.err
   449  	}
   450  	return n, err
   451  }
   452  
   453  func TestSeekErrorDeadlock(t *testing.T) {
   454  	buf := new(bytes.Buffer)
   455  
   456  	w := NewWriter(buf, *conc)
   457  	w.Comment = "comment"
   458  	w.Extra = []byte("extra")
   459  	w.ModTime = time.Unix(1e8, 0)
   460  	w.Name = "name"
   461  	if _, err := w.Write([]byte("payload")); err != nil {
   462  		t.Fatalf("Write: %v", err)
   463  	}
   464  	if err := w.Close(); err != nil {
   465  		t.Fatalf("Writer.Close: %v", err)
   466  	}
   467  	e := &errorReadSeeker{r: bytes.NewReader(buf.Bytes())}
   468  	r, err := NewReader(e, *conc)
   469  	if err != nil {
   470  		t.Fatalf("NewReader: %v", err)
   471  	}
   472  	r.Seek(Offset{File: 0})
   473  	e.err = errors.New("bad seek error")
   474  	err = r.Seek(Offset{File: 1})
   475  	if err == nil {
   476  		t.Error("Expected error.", err)
   477  	}
   478  	r.Close()
   479  }
   480  
   481  type countReadSeeker struct {
   482  	mu       sync.Mutex
   483  	r        io.ReadSeeker
   484  	_didSeek bool
   485  	n        int64
   486  }
   487  
   488  func (r *countReadSeeker) offset() int64 {
   489  	r.mu.Lock()
   490  	defer r.mu.Unlock()
   491  
   492  	return r.n
   493  }
   494  
   495  func (r *countReadSeeker) didSeek() bool {
   496  	r.mu.Lock()
   497  	defer r.mu.Unlock()
   498  
   499  	return r._didSeek
   500  }
   501  
   502  func (r *countReadSeeker) Read(p []byte) (int, error) {
   503  	r.mu.Lock()
   504  	defer r.mu.Unlock()
   505  
   506  	r._didSeek = false
   507  	n, err := r.r.Read(p)
   508  	r.n += int64(n)
   509  	return n, err
   510  }
   511  
   512  func (r *countReadSeeker) Seek(offset int64, whence int) (int64, error) {
   513  	r.mu.Lock()
   514  	defer r.mu.Unlock()
   515  
   516  	r._didSeek = true
   517  	return r.r.Seek(offset, whence)
   518  }
   519  
   520  func TestSeekFast(t *testing.T) {
   521  	// Under these conditions we cannot guarantee that a worker
   522  	// will not read bytes after a Seek call has been made.
   523  	if *conc != 1 && runtime.GOMAXPROCS(0) > 1 {
   524  		return
   525  	}
   526  	const (
   527  		infix  = "payload"
   528  		blocks = 10
   529  	)
   530  
   531  	// Use different caches.
   532  	for _, cache := range []Cache{
   533  		nil, // Explicitly nil.
   534  
   535  		cache.NewLRU(0), // Functionally nil.
   536  		cache.NewLRU(1),
   537  		cache.NewLRU(blocks / 2),
   538  		cache.NewLRU(blocks),
   539  		cache.NewLRU(blocks + 1),
   540  
   541  		cache.NewRandom(0), // Functionally nil.
   542  		cache.NewRandom(1),
   543  		cache.NewRandom(blocks / 2),
   544  		cache.NewRandom(blocks),
   545  		cache.NewRandom(blocks + 1),
   546  	} {
   547  		var (
   548  			buf     bytes.Buffer
   549  			offsets = []int{0}
   550  		)
   551  		w := NewWriter(&buf, 1)
   552  		for i := 0; i < blocks; i++ {
   553  			if _, err := fmt.Fprintf(w, "%d%[2]s%[1]d", i, infix); err != nil {
   554  				t.Fatalf("Write: %v", err)
   555  			}
   556  			if err := w.Flush(); err != nil {
   557  				t.Fatalf("Flush: %v", err)
   558  			}
   559  			if err := w.Wait(); err != nil {
   560  				t.Fatalf("Wait: %v", err)
   561  			}
   562  			offsets = append(offsets, buf.Len())
   563  		}
   564  		w.Close()
   565  		offsets = offsets[:len(offsets)-1]
   566  
   567  		c := &countReadSeeker{r: bytes.NewReader(buf.Bytes())}
   568  
   569  		// Insert a HasEOF to ensure it does not corrupt subsequent reads.
   570  		HasEOF(bytes.NewReader(buf.Bytes()))
   571  
   572  		r, err := NewReader(c, *conc)
   573  		if err != nil {
   574  			t.Fatalf("NewReader: %v", err)
   575  		}
   576  
   577  		r.SetCache(cache)
   578  		p := make([]byte, len(infix)+2)
   579  
   580  		func() {
   581  			defer func() {
   582  				r := recover()
   583  				if r != nil {
   584  					t.Fatalf("Seek on unread reader panicked: %v", r)
   585  				}
   586  			}()
   587  			err := r.Seek(Offset{})
   588  			if err != nil {
   589  				t.Fatalf("Seek: %v", err)
   590  			}
   591  		}()
   592  
   593  		// Standard read through of the data.
   594  		for i := range offsets {
   595  			n, err := r.Read(p)
   596  			if n != len(p) {
   597  				t.Fatalf("Unexpected read length: got:%d want:%d", n, len(p))
   598  			}
   599  			if err != nil {
   600  				t.Fatalf("Read: %v", err)
   601  			}
   602  			got := string(p)
   603  			want := fmt.Sprintf("%d%[2]s%[1]d", i, infix)
   604  			if got != want {
   605  				t.Errorf("Unexpected result: got:%q want:%q", got, want)
   606  			}
   607  		}
   608  
   609  		// Seek to each block in turn
   610  		for i, o := range offsets {
   611  			err := r.Seek(Offset{File: int64(o)})
   612  			if err != nil {
   613  				t.Fatalf("Seek: %v", err)
   614  			}
   615  			n, err := r.Read(p)
   616  			if n != len(p) {
   617  				t.Errorf("Unexpected read length: got:%d want:%d", n, len(p))
   618  			}
   619  			if err != nil {
   620  				t.Fatalf("Read: %v", err)
   621  			}
   622  			got := string(p)
   623  			want := fmt.Sprintf("%d%[2]s%[1]d", i, infix)
   624  			if got != want {
   625  				t.Errorf("Unexpected result: got:%q want:%q", got, want)
   626  			}
   627  		}
   628  
   629  		// Seek to each block in turn, but read the infix and then the first 2 bytes.
   630  		for i, o := range offsets {
   631  			if err := r.Seek(Offset{File: int64(o), Block: 1}); err != nil {
   632  				t.Fatalf("Seek: %v", err)
   633  			}
   634  			p = p[:len(infix)]
   635  			n, err := r.Read(p)
   636  			if n != len(p) {
   637  				t.Fatalf("Unexpected read length: got:%d want:%d", n, len(p))
   638  			}
   639  			if err != nil {
   640  				t.Fatalf("Read: %v", err)
   641  			}
   642  			got := string(p)
   643  			want := infix
   644  			if got != want {
   645  				t.Fatalf("Unexpected result: got:%q want:%q", got, want)
   646  			}
   647  
   648  			// Check whether the underlying reader was seeked or read.
   649  			hasRead := c.offset()
   650  			if err = r.Seek(Offset{File: int64(o), Block: 0}); err != nil {
   651  				t.Fatalf("Seek: %v", err)
   652  			}
   653  			if b := c.offset() - hasRead; b != 0 {
   654  				t.Errorf("Seek performed unexpected read: %d bytes", b)
   655  			}
   656  			if c.didSeek() {
   657  				t.Error("Seek caused underlying Seek.")
   658  			}
   659  
   660  			p = p[:2]
   661  			n, err = r.Read(p)
   662  			if n != len(p) {
   663  				t.Fatalf("Unexpected read length: got:%d want:%d", n, len(p))
   664  			}
   665  			if err != nil {
   666  				t.Fatalf("Read: %v", err)
   667  			}
   668  			got = string(p)
   669  			want = fmt.Sprintf("%dp", i)
   670  			if got != want {
   671  				t.Fatalf("Unexpected result: got:%q want:%q", got, want)
   672  			}
   673  		}
   674  		r.Close()
   675  	}
   676  }
   677  
   678  func TestCache(t *testing.T) {
   679  	// Under these conditions we cannot guarantee that the order of
   680  	// blocks returned by nextBlock work will not result in additional
   681  	// cache puts.
   682  	if *conc != 1 {
   683  		return
   684  	}
   685  	const (
   686  		infix  = "payload"
   687  		blocks = 10
   688  	)
   689  
   690  	// Each pattern is a series of seek-and-read (when the element >= 0)
   691  	// or read (when the element < 0). Each read is exactly one block
   692  	// worth of data.
   693  	type opPair struct{ seekBlock, blockID int }
   694  	patterns := []struct {
   695  		ops []opPair
   696  
   697  		// One for each cache case below. If new caches are added to the
   698  		// test list, stats must be added here.
   699  		expectedStats []cache.Stats
   700  	}{
   701  		{
   702  			ops: []opPair{
   703  				{seekBlock: -1, blockID: 0},
   704  				{seekBlock: -1, blockID: 1},
   705  				{seekBlock: -1, blockID: 2},
   706  				{seekBlock: +0, blockID: 0},
   707  				{seekBlock: -1, blockID: 1},
   708  				{seekBlock: -1, blockID: 2},
   709  				{seekBlock: -1, blockID: 3},
   710  				{seekBlock: -1, blockID: 4},
   711  			},
   712  			expectedStats: []cache.Stats{
   713  				{}, // nil cache.
   714  				{}, // nil cache: LRU(0)
   715  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 6}, // LRU(1)
   716  				{Gets: 7, Misses: 4, Puts: 7, Retains: 7, Evictions: 0}, // LRU(5)
   717  				{Gets: 7, Misses: 4, Puts: 7, Retains: 7, Evictions: 0}, // LRU(10)
   718  				{Gets: 7, Misses: 4, Puts: 7, Retains: 7, Evictions: 0}, // LRU(11)
   719  				{}, // nil cache: FIFO(0)
   720  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 6}, // FIFO(1)
   721  				{Gets: 7, Misses: 4, Puts: 7, Retains: 4, Evictions: 0}, // FIFO(5)
   722  				{Gets: 7, Misses: 4, Puts: 7, Retains: 4, Evictions: 0}, // FIFO(10)
   723  				{Gets: 7, Misses: 4, Puts: 7, Retains: 4, Evictions: 0}, // FIFO(11)
   724  				{}, // nil cache: Random(0)
   725  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 6}, // Random(1)
   726  				{Gets: 7, Misses: 4, Puts: 7, Retains: 7, Evictions: 0}, // Random(5)
   727  				{Gets: 7, Misses: 4, Puts: 7, Retains: 7, Evictions: 0}, // Random(10)
   728  				{Gets: 7, Misses: 4, Puts: 7, Retains: 7, Evictions: 0}, // Random(11)
   729  			},
   730  		},
   731  		{
   732  			ops: []opPair{
   733  				{seekBlock: -1, blockID: 0},
   734  				{seekBlock: -1, blockID: 1},
   735  				{seekBlock: -1, blockID: 2},
   736  				{seekBlock: +1, blockID: 1},
   737  				{seekBlock: -1, blockID: 2},
   738  				{seekBlock: -1, blockID: 3},
   739  				{seekBlock: -1, blockID: 4},
   740  				{seekBlock: -1, blockID: 5},
   741  			},
   742  			expectedStats: []cache.Stats{
   743  				{}, // nil cache.
   744  				{}, // nil cache.
   745  				{Gets: 7, Misses: 5, Puts: 7, Retains: 7, Evictions: 4}, // LRU(1)
   746  				{Gets: 7, Misses: 5, Puts: 7, Retains: 7, Evictions: 0}, // LRU(5)
   747  				{Gets: 7, Misses: 5, Puts: 7, Retains: 7, Evictions: 0}, // LRU(10)
   748  				{Gets: 7, Misses: 5, Puts: 7, Retains: 7, Evictions: 0}, // LRU(11)
   749  				{}, // nil cache: FIFO(0)
   750  				{Gets: 7, Misses: 5, Puts: 7, Retains: 7, Evictions: 6}, // FIFO(1)
   751  				{Gets: 7, Misses: 5, Puts: 7, Retains: 5, Evictions: 0}, // FIFO(5)
   752  				{Gets: 7, Misses: 5, Puts: 7, Retains: 5, Evictions: 0}, // FIFO(10)
   753  				{Gets: 7, Misses: 5, Puts: 7, Retains: 5, Evictions: 0}, // FIFO(11)
   754  				{}, // nil cache: Random(0)
   755  				{Gets: 7, Misses: 5, Puts: 7, Retains: 7, Evictions: 4}, // Random(1)
   756  				{Gets: 7, Misses: 5, Puts: 7, Retains: 7, Evictions: 0}, // Random(5)
   757  				{Gets: 7, Misses: 5, Puts: 7, Retains: 7, Evictions: 0}, // Random(10)
   758  				{Gets: 7, Misses: 5, Puts: 7, Retains: 7, Evictions: 0}, // Random(11)
   759  			},
   760  		},
   761  		{
   762  			ops: []opPair{
   763  				{seekBlock: -1, blockID: 0},
   764  				{seekBlock: -1, blockID: 1},
   765  				{seekBlock: -1, blockID: 2},
   766  				{seekBlock: +2, blockID: 2},
   767  				{seekBlock: -1, blockID: 3},
   768  				{seekBlock: -1, blockID: 4},
   769  				{seekBlock: -1, blockID: 5},
   770  				{seekBlock: -1, blockID: 6},
   771  			},
   772  			// Re-reading the same block avoids a cache look-up.
   773  			expectedStats: []cache.Stats{
   774  				{}, // nil cache.
   775  				{}, // nil cache.
   776  				{Gets: 6, Misses: 6, Puts: 6, Retains: 6, Evictions: 5}, // LRU(1)
   777  				{Gets: 6, Misses: 6, Puts: 6, Retains: 6, Evictions: 1}, // LRU(5)
   778  				{Gets: 6, Misses: 6, Puts: 6, Retains: 6, Evictions: 0}, // LRU(10)
   779  				{Gets: 6, Misses: 6, Puts: 6, Retains: 6, Evictions: 0}, // LRU(11)
   780  				{}, // nil cache: FIFO(0)
   781  				{Gets: 6, Misses: 6, Puts: 6, Retains: 6, Evictions: 5}, // FIFO(1)
   782  				{Gets: 6, Misses: 6, Puts: 6, Retains: 6, Evictions: 1}, // FIFO(5)
   783  				{Gets: 6, Misses: 6, Puts: 6, Retains: 6, Evictions: 0}, // FIFO(10)
   784  				{Gets: 6, Misses: 6, Puts: 6, Retains: 6, Evictions: 0}, // FIFO(11)
   785  				{}, // nil cache: Random(0)
   786  				{Gets: 6, Misses: 6, Puts: 6, Retains: 6, Evictions: 5}, // Random(1)
   787  				{Gets: 6, Misses: 6, Puts: 6, Retains: 6, Evictions: 1}, // Random(5)
   788  				{Gets: 6, Misses: 6, Puts: 6, Retains: 6, Evictions: 0}, // Random(10)
   789  				{Gets: 6, Misses: 6, Puts: 6, Retains: 6, Evictions: 0}, // Random(11)
   790  			},
   791  		},
   792  		{
   793  			ops: []opPair{
   794  				{seekBlock: -1, blockID: 0},
   795  				{seekBlock: -1, blockID: 1},
   796  				{seekBlock: -1, blockID: 2},
   797  				{seekBlock: +3, blockID: 3},
   798  				{seekBlock: -1, blockID: 4},
   799  				{seekBlock: -1, blockID: 5},
   800  				{seekBlock: -1, blockID: 6},
   801  				{seekBlock: -1, blockID: 7},
   802  			},
   803  			expectedStats: []cache.Stats{
   804  				{}, // nil cache.
   805  				{}, // nil cache.
   806  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 6}, // LRU(1)
   807  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 2}, // LRU(5)
   808  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 0}, // LRU(10)
   809  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 0}, // LRU(11)
   810  				{}, // nil cache: FIFO(0)
   811  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 6}, // FIFO(1)
   812  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 2}, // FIFO(5)
   813  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 0}, // FIFO(10)
   814  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 0}, // FIFO(11)
   815  				{}, // nil cache: Random(0)
   816  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 6}, // Random(1)
   817  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 2}, // Random(5)
   818  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 0}, // Random(10)
   819  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 0}, // Random(11)
   820  			},
   821  		},
   822  		{
   823  			ops: []opPair{
   824  				{seekBlock: -1, blockID: 0},
   825  				{seekBlock: -1, blockID: 1},
   826  				{seekBlock: -1, blockID: 2},
   827  				{seekBlock: +4, blockID: 4},
   828  				{seekBlock: -1, blockID: 5},
   829  				{seekBlock: -1, blockID: 6},
   830  				{seekBlock: -1, blockID: 7},
   831  				{seekBlock: -1, blockID: 8},
   832  			},
   833  			expectedStats: []cache.Stats{
   834  				{}, // nil cache.
   835  				{}, // nil cache.
   836  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 6}, // LRU(1)
   837  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 2}, // LRU(5)
   838  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 0}, // LRU(10)
   839  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 0}, // LRU(11)
   840  				{}, // nil cache: FIFO(0)
   841  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 6}, // FIFO(1)
   842  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 2}, // FIFO(5)
   843  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 0}, // FIFO(10)
   844  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 0}, // FIFO(11)
   845  				{}, // nil cache: Random(0)
   846  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 6}, // Random(1)
   847  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 2}, // Random(5)
   848  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 0}, // Random(10)
   849  				{Gets: 7, Misses: 7, Puts: 7, Retains: 7, Evictions: 0}, // Random(11)
   850  			},
   851  		},
   852  		{
   853  			ops: []opPair{
   854  				{seekBlock: -1, blockID: 0},
   855  				{seekBlock: -1, blockID: 1},
   856  				{seekBlock: -1, blockID: 2},
   857  				{seekBlock: +1, blockID: 1},
   858  				{seekBlock: +2, blockID: 2},
   859  				{seekBlock: +1, blockID: 1},
   860  				{seekBlock: +1, blockID: 1},
   861  				{seekBlock: -1, blockID: 2},
   862  				{seekBlock: +7, blockID: 7},
   863  				{seekBlock: -1, blockID: 8},
   864  				{seekBlock: -1, blockID: 9},
   865  			},
   866  			expectedStats: []cache.Stats{
   867  				{}, // nil cache.
   868  				{}, // nil cache.
   869  				{Gets: 9, Misses: 5, Puts: 9, Retains: 9, Evictions: 4}, // LRU(1)
   870  				{Gets: 9, Misses: 5, Puts: 9, Retains: 9, Evictions: 0}, // LRU(5)
   871  				{Gets: 9, Misses: 5, Puts: 9, Retains: 9, Evictions: 0}, // LRU(10)
   872  				{Gets: 9, Misses: 5, Puts: 9, Retains: 9, Evictions: 0}, // LRU(11)
   873  				{}, // nil cache: FIFO(0)
   874  				{Gets: 9, Misses: 5, Puts: 9, Retains: 9, Evictions: 8}, // FIFO(1)
   875  				{Gets: 9, Misses: 5, Puts: 9, Retains: 5, Evictions: 0}, // FIFO(5)
   876  				{Gets: 9, Misses: 5, Puts: 9, Retains: 5, Evictions: 0}, // FIFO(10)
   877  				{Gets: 9, Misses: 5, Puts: 9, Retains: 5, Evictions: 0}, // FIFO(11)
   878  				{}, // nil cache: Random(0)
   879  				{Gets: 9, Misses: 5, Puts: 9, Retains: 9, Evictions: 4}, // Random(1)
   880  				{Gets: 9, Misses: 5, Puts: 9, Retains: 9, Evictions: 0}, // Random(5)
   881  				{Gets: 9, Misses: 5, Puts: 9, Retains: 9, Evictions: 0}, // Random(10)
   882  				{Gets: 9, Misses: 5, Puts: 9, Retains: 9, Evictions: 0}, // Random(11)
   883  			},
   884  		},
   885  	}
   886  
   887  	for k, pat := range patterns {
   888  		// Use different caches.
   889  		for j, s := range []Cache{
   890  			nil, // Explicitly nil.
   891  
   892  			cache.NewLRU(0), // Functionally nil.
   893  			cache.NewLRU(1),
   894  			cache.NewLRU(blocks / 2),
   895  			cache.NewLRU(blocks),
   896  			cache.NewLRU(blocks + 1),
   897  
   898  			cache.NewFIFO(0), // Functionally nil.
   899  			cache.NewFIFO(1),
   900  			cache.NewFIFO(blocks / 2),
   901  			cache.NewFIFO(blocks),
   902  			cache.NewFIFO(blocks + 1),
   903  
   904  			cache.NewRandom(0), // Functionally nil.
   905  			cache.NewRandom(1),
   906  			cache.NewRandom(blocks / 2),
   907  			cache.NewRandom(blocks),
   908  			cache.NewRandom(blocks + 1),
   909  		} {
   910  			var (
   911  				buf     bytes.Buffer
   912  				offsets = []int{0}
   913  			)
   914  			w := NewWriter(&buf, 1)
   915  			for i := 0; i < blocks; i++ {
   916  				if _, err := fmt.Fprintf(w, "%d%[2]s%[1]d", i, infix); err != nil {
   917  					t.Fatalf("Write: %v", err)
   918  				}
   919  				if err := w.Flush(); err != nil {
   920  					t.Fatalf("Flush: %v", err)
   921  				}
   922  				if err := w.Wait(); err != nil {
   923  					t.Fatalf("Wait: %v", err)
   924  				}
   925  				offsets = append(offsets, buf.Len())
   926  			}
   927  			w.Close()
   928  			offsets = offsets[:len(offsets)-1]
   929  
   930  			br := bytes.NewReader(buf.Bytes())
   931  			// Insert a HasEOF to ensure it does not corrupt subsequent reads.
   932  			HasEOF(br)
   933  
   934  			r, err := NewReader(br, *conc)
   935  			if err != nil {
   936  				t.Fatalf("NewReader: %v", err)
   937  			}
   938  			var stats *cache.StatsRecorder
   939  			if s != nil {
   940  				stats = &cache.StatsRecorder{Cache: s}
   941  				s = stats
   942  			}
   943  			r.SetCache(s)
   944  			p := make([]byte, len(infix)+2)
   945  
   946  			for _, op := range pat.ops {
   947  				if op.seekBlock >= 0 {
   948  					err := r.Seek(Offset{File: int64(offsets[op.seekBlock])})
   949  					if err != nil {
   950  						t.Fatalf("Seek: %v", err)
   951  					}
   952  				}
   953  				n, err := r.Read(p)
   954  				if n != len(p) {
   955  					t.Errorf("Unexpected read length: got:%d want:%d", n, len(p))
   956  				}
   957  				if err != nil {
   958  					t.Fatalf("Read: %v", err)
   959  				}
   960  				got := string(p)
   961  				want := fmt.Sprintf("%d%[2]s%[1]d", op.blockID, infix)
   962  				if got != want {
   963  					t.Errorf("Unexpected result: got:%q want:%q", got, want)
   964  				}
   965  			}
   966  			if stats != nil && stats.Stats() != pat.expectedStats[j] {
   967  				t.Errorf("Unexpected result for cache %d pattern %d: got:%+v want:%+v", j, k, stats.Stats(), pat.expectedStats[j])
   968  			}
   969  			r.Close()
   970  		}
   971  	}
   972  }
   973  
   974  func TestBlocked(t *testing.T) {
   975  	const (
   976  		infix  = "payload"
   977  		blocks = 10
   978  	)
   979  
   980  	for _, blocked := range []bool{false, true} {
   981  		var (
   982  			buf  bytes.Buffer
   983  			want bytes.Buffer
   984  		)
   985  		w := NewWriter(&buf, 1)
   986  		for i := 0; i < blocks; i++ {
   987  			if _, err := fmt.Fprintf(w, "%d%[2]s%[1]d\n", i, infix); err != nil {
   988  				t.Fatalf("Write: %v", err)
   989  			}
   990  			if err := w.Flush(); err != nil {
   991  				t.Fatalf("Flush: %v", err)
   992  			}
   993  			if _, err := fmt.Fprintf(&want, "%d%[2]s%[1]d\n", i, infix); err != nil {
   994  				t.Fatalf("Write: %v", err)
   995  			}
   996  		}
   997  		err := w.Close()
   998  		if err != nil {
   999  			t.Fatalf("unexpected error on Close: %v", err)
  1000  		}
  1001  
  1002  		r, err := NewReader(bytes.NewReader(buf.Bytes()), *conc)
  1003  		if err != nil {
  1004  			t.Fatalf("NewReader: %v", err)
  1005  		}
  1006  		r.Blocked = blocked
  1007  
  1008  		p := make([]byte, len(infix))
  1009  		var (
  1010  			got       []byte
  1011  			gotBlocks int
  1012  		)
  1013  		for {
  1014  			n, err := r.Read(p)
  1015  			got = append(got, p[:n]...)
  1016  			if err != nil {
  1017  				if err == io.EOF && n != 0 {
  1018  					gotBlocks++
  1019  					continue
  1020  				}
  1021  				break
  1022  			}
  1023  		}
  1024  		if !blocked && gotBlocks != 1 {
  1025  			t.Errorf("unexpected number of blocks:\n\tgot:%d\n\twant:%d", gotBlocks, 1)
  1026  		}
  1027  		if blocked && gotBlocks != blocks {
  1028  			t.Errorf("unexpected number of blocks:\n\tgot:%d\n\twant:%d", gotBlocks, blocks)
  1029  		}
  1030  		if !bytes.Equal(got, want.Bytes()) {
  1031  			t.Errorf("unexpected result:\n\tgot:%q\n\twant:%q", got, want.Bytes())
  1032  		}
  1033  		r.Close()
  1034  	}
  1035  }
  1036  
  1037  var fuzzCrashers = []string{
  1038  	// Invalid block size.
  1039  	"\x1f\x8b\bu0000000\x0000000000" +
  1040  		"000000000BC\x02\x000\x0000000" +
  1041  		"00000000000000000000" +
  1042  		"00000000000000000000" +
  1043  		"000000000000000000\x00",
  1044  	"\x1f\x8b\b\xc4000000V\x0000000000" +
  1045  		"00000000000000000000" +
  1046  		"00000000000000000000" +
  1047  		"00000000000000000000" +
  1048  		"000000000000BC\x02\x00w\x00\x030" +
  1049  		"\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x8b\bu000000\x00\x00" +
  1050  		"\x1f\x8b\bu000000\b\x00BC\x02\x00\x00\x0000" +
  1051  		"\x00",
  1052  
  1053  	// Zero block size.
  1054  	"\x1f\x8b\bu000000V\x0000000000" +
  1055  		"00000000000000000000" +
  1056  		"00000000000000000000" +
  1057  		"00000000000000000000" +
  1058  		"000000000000BC\x02\x00k\x0000" +
  1059  		"0000000\x00",
  1060  	"\x1f\x8b\bu\xe8k\x15k\x00sV\x00bcdefghi" +
  1061  		"jklmnxpq\xc49\xbf\x1f\x8b\x0f\a/\x85\xba\xb0Q" +
  1062  		"\xef (\x01\xbd\xbf\xefrde\a/\x85fghmjt\x00" +
  1063  		"\xff\x00\x00v\x97x\x92zB\x80\x00142261869" +
  1064  		"48039093abxdBC\x02\x00i\x00sV" +
  1065  		"\xbbghmj\x00\x00",
  1066  }
  1067  
  1068  func TestFuzzCrashers(t *testing.T) {
  1069  	for i, test := range fuzzCrashers {
  1070  		func() {
  1071  			i := i
  1072  			defer func() {
  1073  				r := recover()
  1074  				if r != nil {
  1075  					t.Errorf("unexpected panic for crasher %d: %v", i, r)
  1076  				}
  1077  			}()
  1078  			r, err := NewReader(strings.NewReader(test), 0)
  1079  			switch err {
  1080  			case nil:
  1081  				// Pass through.
  1082  			case io.EOF, ErrCorrupt:
  1083  				return
  1084  			default:
  1085  				t.Fatalf("unexpected error creating reader: %v", err)
  1086  			}
  1087  			tmp := make([]byte, 1024)
  1088  			for {
  1089  				_, err := r.Read(tmp)
  1090  				if err != nil {
  1091  					break
  1092  				}
  1093  			}
  1094  		}()
  1095  	}
  1096  }
  1097  
  1098  func TestZeroNonZero(t *testing.T) {
  1099  	const wrote = "second block"
  1100  	buf := bytes.NewBuffer([]byte(MagicBlock))
  1101  	w := NewWriter(buf, 1)
  1102  	_, err := w.Write([]byte(wrote))
  1103  	if err != nil {
  1104  		w.Close()
  1105  		t.Fatalf("unexpected error writing second block: %v", err)
  1106  	}
  1107  	err = w.Close()
  1108  	if err != nil {
  1109  		t.Fatalf("unexpected error closing writer: %v", err)
  1110  	}
  1111  	r, err := NewReader(buf, 1)
  1112  	if err != nil {
  1113  		t.Fatalf("unexpected error opening reader: %v", err)
  1114  	}
  1115  	defer r.Close()
  1116  	var b [1024]byte
  1117  	var got []byte
  1118  	for {
  1119  		n, err := r.Read(b[:])
  1120  		got = append(got, b[:n]...)
  1121  		if err != nil {
  1122  			break
  1123  		}
  1124  	}
  1125  	if string(got) != wrote {
  1126  		t.Errorf("unexpected round trip: got:%q want:%q", got, wrote)
  1127  	}
  1128  }
  1129  
  1130  type zero struct{}
  1131  
  1132  func (z zero) Read(p []byte) (int, error) {
  1133  	for i := range p {
  1134  		p[i] = 0
  1135  	}
  1136  	return len(p), nil
  1137  }
  1138  
  1139  func TestWriteByteCount(t *testing.T) {
  1140  	cw, _ := NewWriterLevel(ioutil.Discard, gzip.BestCompression, 4)
  1141  	defer cw.Close()
  1142  	n, err := io.Copy(cw, &io.LimitedReader{R: new(zero), N: 100000})
  1143  	if n != 100000 {
  1144  		t.Errorf("Unexpected number of bytes, got:%d, want:%d", n, 100000)
  1145  	}
  1146  	if err != nil {
  1147  		t.Errorf("Unexpected error got:%v", err)
  1148  	}
  1149  }
  1150  
  1151  func BenchmarkWrite(b *testing.B) {
  1152  	bg := NewWriter(ioutil.Discard, *conc)
  1153  	block := bytes.Repeat([]byte("repeated"), 50)
  1154  	for i := 0; i < b.N; i++ {
  1155  		for j := 0; j < 1000000; j++ {
  1156  			bg.Write(block)
  1157  		}
  1158  		bg.Wait()
  1159  	}
  1160  }
  1161  
  1162  func BenchmarkRead(b *testing.B) {
  1163  	if *file == "" {
  1164  		b.Skip("no bgzf file specified")
  1165  	}
  1166  	f, err := os.Open(*file)
  1167  	if err != nil {
  1168  		b.Fatalf("file open failed: %v", err)
  1169  	}
  1170  	defer f.Close()
  1171  
  1172  	buf := make([]byte, 16384)
  1173  	b.ResetTimer()
  1174  
  1175  	for i := 0; i < b.N; i++ {
  1176  		f.Seek(0, os.SEEK_SET)
  1177  		bg, err := NewReader(f, *conc)
  1178  		if err != nil {
  1179  			b.Fatalf("bgzf open failed: %v", err)
  1180  		}
  1181  		for {
  1182  			_, err = bg.Read(buf)
  1183  			if err == io.EOF {
  1184  				break
  1185  			}
  1186  			if err != nil {
  1187  				b.Fatalf("bgzf read failed: %v", err)
  1188  			}
  1189  		}
  1190  		bg.Close()
  1191  	}
  1192  }