go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/data/recordio/reader_test.go (about)

     1  // Copyright 2015 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package recordio
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/binary"
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"testing"
    24  
    25  	. "github.com/smartystreets/goconvey/convey"
    26  )
    27  
    28  // plainReader implements the io.Reader interface on top of a bytes.Buffer;
    29  // however, it does not also implement io.ByteReader.
    30  type plainReader struct {
    31  	buf bytes.Buffer
    32  	err error
    33  }
    34  
    35  func (r *plainReader) loadFrames(chunks ...[]byte) {
    36  	for _, chunk := range chunks {
    37  		_, err := WriteFrame(&r.buf, chunk)
    38  		if err != nil {
    39  			panic(err)
    40  		}
    41  	}
    42  }
    43  
    44  func (r *plainReader) Read(data []byte) (int, error) {
    45  	if r.err != nil {
    46  		return 0, r.err
    47  	}
    48  	return r.buf.Read(data)
    49  }
    50  
    51  type testByteReader struct {
    52  	plainReader
    53  
    54  	readByteErr error
    55  	readBytes   int
    56  }
    57  
    58  func (r *testByteReader) ReadByte() (b byte, err error) {
    59  	if r.readByteErr != nil {
    60  		return 0, r.readByteErr
    61  	}
    62  
    63  	b, err = r.buf.ReadByte()
    64  	if err == nil {
    65  		r.readBytes++
    66  	}
    67  	return
    68  }
    69  
    70  func btos(b ...[]byte) []string {
    71  	s := make([]string, len(b))
    72  	for i, v := range b {
    73  		s[i] = string(v)
    74  	}
    75  	return s
    76  }
    77  
    78  // TestReader tests the default Reader implementation, "reader".
    79  func TestReader(t *testing.T) {
    80  	t.Parallel()
    81  
    82  	Convey(`A frame reader with max size 1MB using a plain io.Reader`, t, func() {
    83  		maxSize := int64(1024 * 1024)
    84  		tr := plainReader{}
    85  		r := NewReader(&tr, maxSize)
    86  
    87  		Convey(`Will return io.EOF with an empty reader.`, func() {
    88  			_, err := r.ReadFrameAll()
    89  			So(err, ShouldEqual, io.EOF)
    90  		})
    91  
    92  		Convey(`Can successfully read a frame.`, func() {
    93  			data := []byte{0x13, 0x37, 0xd0, 0x65}
    94  			tr.loadFrames(data)
    95  
    96  			f, err := r.ReadFrameAll()
    97  			So(err, ShouldBeNil)
    98  			So(f, ShouldResemble, data)
    99  		})
   100  
   101  		Convey(`Can successfully read two frames.`, func() {
   102  			data := [][]byte{
   103  				{0x13, 0x37, 0xd0, 0x65},
   104  				{0xd0, 0x06, 0xea, 0x15, 0xf0, 0x0d},
   105  			}
   106  			tr.loadFrames(data...)
   107  
   108  			c, fr, err := r.ReadFrame()
   109  			So(err, ShouldBeNil)
   110  			So(c, ShouldEqual, 4)
   111  
   112  			d, err := io.ReadAll(fr)
   113  			So(err, ShouldBeNil)
   114  			So(d, ShouldResemble, data[0])
   115  
   116  			c, fr, err = r.ReadFrame()
   117  			So(err, ShouldBeNil)
   118  			So(c, ShouldEqual, 6)
   119  
   120  			d, err = io.ReadAll(fr)
   121  			So(err, ShouldBeNil)
   122  			So(d, ShouldResemble, data[1])
   123  		})
   124  
   125  		Convey(`When reading a frame, will return EOF if the frame is exceeded.`, func() {
   126  			data := []byte{0x13, 0x37, 0xd0, 0x65}
   127  			tr.loadFrames(data)
   128  
   129  			count, fr, err := r.ReadFrame()
   130  			So(err, ShouldBeNil)
   131  			So(count, ShouldEqual, 4)
   132  
   133  			buf := make([]byte, 5)
   134  			c, err := fr.Read(make([]byte, 5))
   135  			So(c, ShouldEqual, 4)
   136  			So(err, ShouldBeNil)
   137  
   138  			buf = buf[count:]
   139  			_, err = fr.Read(buf)
   140  			So(err, ShouldEqual, io.EOF)
   141  		})
   142  
   143  		Convey(`Will fail if the underlying frame exceeds the maximum size.`, func() {
   144  			var sizeBuf [binary.MaxVarintLen64]byte
   145  			tr.buf.Write(sizeBuf[:binary.PutUvarint(sizeBuf[:], uint64(maxSize+1))])
   146  
   147  			_, err := r.ReadFrameAll()
   148  			So(err, ShouldEqual, ErrFrameTooLarge)
   149  		})
   150  
   151  		Convey(`Will fail if the frame contains an invalid size header.`, func() {
   152  			tr.buf.Write([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF})
   153  			_, err := r.ReadFrameAll()
   154  			So(err, ShouldNotBeNil)
   155  		})
   156  
   157  		Convey(`Can read conscutive frames, then io.EOF.`, func() {
   158  			data := [][]byte{}
   159  			for _, size := range []int{
   160  				0,
   161  				14,
   162  				1024 * 1024,
   163  				0,
   164  				511,
   165  			} {
   166  				data = append(data, bytes.Repeat([]byte{0x5A}, size))
   167  				tr.loadFrames(data[len(data)-1])
   168  			}
   169  
   170  			for _, expected := range data {
   171  				f, err := r.ReadFrameAll()
   172  				So(err, ShouldBeNil)
   173  
   174  				if len(expected) == 0 {
   175  					expected = nil
   176  				}
   177  				So(f, ShouldResemble, expected)
   178  			}
   179  
   180  			_, err := r.ReadFrameAll()
   181  			So(err, ShouldEqual, io.EOF)
   182  		})
   183  	})
   184  
   185  	Convey(`A frame reader with max size 1MB using an io.Reader+io.ByteReader`, t, func() {
   186  		tr := testByteReader{}
   187  		r := NewReader(&tr, 1024*1024)
   188  
   189  		Convey(`Will return io.EOF with an empty reader.`, func() {
   190  			_, err := r.ReadFrameAll()
   191  			So(err, ShouldEqual, io.EOF)
   192  		})
   193  
   194  		Convey(`Will use io.ByteReader to read the frame header.`, func() {
   195  			data := []byte{0x13, 0x37, 0xd0, 0x65}
   196  			tr.loadFrames(data)
   197  
   198  			f, err := r.ReadFrameAll()
   199  			So(err, ShouldBeNil)
   200  			So(f, ShouldResemble, data)
   201  			So(tr.readBytes, ShouldEqual, 1)
   202  		})
   203  
   204  		Convey(`Will fail if the underlying io.Reader returns an error.`, func() {
   205  			tr.loadFrames([]byte{})
   206  			tr.err = errors.New("test: test-induced error")
   207  			tr.readByteErr = tr.err
   208  			_, err := r.ReadFrameAll()
   209  			So(err, ShouldEqual, tr.err)
   210  		})
   211  
   212  		Convey(`Will fail if an error is returned while reading frame's data.`, func() {
   213  			data := []byte{0x13, 0x37, 0xd0, 0x65}
   214  			tr.loadFrames(data)
   215  
   216  			// Have "ReadByte()" calls ignore the configured error. This will cause
   217  			// the frame size to be read without incident, but the frame data to still
   218  			// return an error.
   219  			tr.err = errors.New("test: test-induced error")
   220  			data, err := r.ReadFrameAll()
   221  			So(err, ShouldEqual, tr.err)
   222  		})
   223  	})
   224  }
   225  
   226  func TestSplit(t *testing.T) {
   227  	t.Parallel()
   228  
   229  	Convey(`Testing Split`, t, func() {
   230  		for _, v := range [][]string{
   231  			{},
   232  			{""},
   233  			{"", "foo", ""},
   234  			{"foo", "bar", "baz"},
   235  		} {
   236  			Convey(fmt.Sprintf(`Can Split stream: %#v`, v), func() {
   237  				// Write frames to "buf".
   238  				var buf bytes.Buffer
   239  				for _, s := range v {
   240  					_, err := WriteFrame(&buf, []byte(s))
   241  					if err != nil {
   242  						panic(err)
   243  					}
   244  				}
   245  
   246  				// Confirm that Split works.
   247  				sp, err := Split(buf.Bytes())
   248  				So(err, ShouldBeNil)
   249  				So(btos(sp...), ShouldResemble, v)
   250  			})
   251  		}
   252  
   253  		Convey(`Will refuse to split a frame that is too large.`, func() {
   254  			_, err := Split([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00})
   255  			So(err, ShouldEqual, ErrFrameTooLarge)
   256  		})
   257  
   258  		Convey(`Will fail to split if there aren't enough bytes.`, func() {
   259  			sp, err := Split([]byte{0x01, 0xAA, 0x02}) // 1-byte {0xAA}, 2-bytes ... EOF!
   260  			So(sp, ShouldResemble, [][]byte{{0xAA}})
   261  			So(err, ShouldEqual, ErrFrameTooLarge)
   262  		})
   263  	})
   264  }