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 }