go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/data/chunkstream/view_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 chunkstream
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"io"
    21  	"testing"
    22  
    23  	. "github.com/smartystreets/goconvey/convey"
    24  )
    25  
    26  func TestView(t *testing.T) {
    27  	Convey(`An empty Buffer with generation 42`, t, func() {
    28  		b := Buffer{}
    29  
    30  		for _, chunks := range [][]*testChunk{
    31  			[]*testChunk(nil),
    32  			{tc()},
    33  			{tc(0)},
    34  			{tc(0, 0, 1)},
    35  			{tc(0), tc(0), tc(0)},
    36  			{tc(1, 2, 3, 0)},
    37  			{tc(1, 2), tc(3, 0, 4, 0), tc(0, 5)},
    38  		} {
    39  			Convey(fmt.Sprintf(`With Chunks %v`, chunks), func() {
    40  				aggregate := []byte{}
    41  				size := int64(0)
    42  				for _, c := range chunks {
    43  					aggregate = append(aggregate, c.Bytes()...)
    44  					size += int64(c.Len())
    45  					b.Append(c)
    46  				}
    47  
    48  				// This is pretty stupid to do, but for testing it's a nice edge case
    49  				// to hammer.
    50  				Convey(`A View with limit 0`, func() {
    51  					br := b.ViewLimit(0)
    52  
    53  					So(br.Remaining(), ShouldEqual, 0)
    54  					So(br.Consumed(), ShouldEqual, 0)
    55  
    56  					Convey(`Read() returns EOF.`, func() {
    57  						buf := make([]byte, 16)
    58  						a, err := br.Read(buf)
    59  						So(err, ShouldEqual, io.EOF)
    60  						So(a, ShouldEqual, 0)
    61  					})
    62  
    63  					Convey(`ReadByte() returns EOF.`, func() {
    64  						_, err := br.ReadByte()
    65  						So(err, ShouldEqual, io.EOF)
    66  					})
    67  
    68  					Convey(`Skip() panics.`, func() {
    69  						So(func() { br.Skip(1) }, ShouldPanic)
    70  					})
    71  
    72  					Convey(`Index() returns -1.`, func() {
    73  						So(br.Index([]byte{0}), ShouldEqual, -1)
    74  					})
    75  				})
    76  
    77  				Convey(`An unlimited View`, func() {
    78  					br := b.View()
    79  					So(br.Remaining(), ShouldEqual, b.Len())
    80  					So(br.Consumed(), ShouldEqual, 0)
    81  
    82  					Convey(`Can Read() the full block of data.`, func() {
    83  						buf := make([]byte, len(aggregate))
    84  						amt, err := br.Read(buf)
    85  						So(amt, ShouldEqual, len(aggregate))
    86  						So(err, ShouldEqual, io.EOF)
    87  						So(buf[:amt], ShouldResemble, aggregate)
    88  
    89  						Convey(`Subsequent Read() will return io.EOF.`, func() {
    90  							amt, err := br.Read(buf)
    91  							So(amt, ShouldEqual, 0)
    92  							So(err, ShouldEqual, io.EOF)
    93  						})
    94  					})
    95  
    96  					Convey(`Can Read() the full block of data byte-by-byte.`, func() {
    97  						buf := make([]byte, 1)
    98  						for i, d := range aggregate {
    99  							amt, err := br.Read(buf)
   100  							if i == len(aggregate)-1 {
   101  								So(err, ShouldEqual, io.EOF)
   102  							} else {
   103  								So(err, ShouldBeNil)
   104  							}
   105  
   106  							So(amt, ShouldEqual, 1)
   107  							So(buf[0], ShouldEqual, d)
   108  						}
   109  
   110  						Convey(`Subsequent Read() will return io.EOF.`, func() {
   111  							amt, err := br.Read(buf)
   112  							So(amt, ShouldEqual, 0)
   113  							So(err, ShouldEqual, io.EOF)
   114  						})
   115  					})
   116  
   117  					Convey(`Can ReadByte() the full block of data.`, func() {
   118  						for _, d := range aggregate {
   119  							b, err := br.ReadByte()
   120  							So(err, ShouldBeNil)
   121  							So(b, ShouldEqual, d)
   122  						}
   123  
   124  						Convey(`Subsequent ReadByte() will return io.EOF.`, func() {
   125  							_, err := br.ReadByte()
   126  							So(err, ShouldEqual, io.EOF)
   127  						})
   128  					})
   129  
   130  					for _, needle := range [][]byte{
   131  						{0},
   132  						{0, 0},
   133  						{0, 0, 0, 0},
   134  					} {
   135  						expected := bytes.Index(aggregate, needle)
   136  						Convey(fmt.Sprintf(`Index() of %v returns %v.`, needle, expected), func() {
   137  							So(br.Index(needle), ShouldEqual, expected)
   138  						})
   139  					}
   140  				})
   141  			})
   142  		}
   143  
   144  		Convey(`An unlimited View`, func() {
   145  			br := b.View()
   146  
   147  			Convey(`Has chunksRemaining() value of 0.`, func() {
   148  				So(br.chunkRemaining(), ShouldEqual, 0)
   149  			})
   150  		})
   151  
   152  		Convey(`With chunks [{0x01, 0x02, 0x00}, {0x00, 0x03}, {0x00}, {0x00, 0x00}, {0x04}]`, func() {
   153  			for _, c := range []Chunk{tc(1, 2, 0), tc(0, 3), tc(0), tc(0, 0), tc(4)} {
   154  				b.Append(c)
   155  			}
   156  
   157  			Convey(`An unlimited View`, func() {
   158  				br := b.View()
   159  
   160  				Convey(`Should have Remaining() value of 9.`, func() {
   161  					So(br.Remaining(), ShouldEqual, 9)
   162  				})
   163  
   164  				Convey(`Can spawn a limited clone.`, func() {
   165  					buf := bytes.Buffer{}
   166  					_, err := buf.ReadFrom(br.CloneLimit(7))
   167  					So(err, ShouldBeNil)
   168  					So(buf.Bytes(), ShouldResemble, []byte{0x01, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00})
   169  				})
   170  
   171  				for _, s := range []struct {
   172  					needle []byte
   173  					index  int64
   174  				}{
   175  					{[]byte(nil), 0},
   176  					{[]byte{0x01}, 0},
   177  					{[]byte{0x01, 0x02}, 0},
   178  					{[]byte{0x02, 0x00}, 1},
   179  					{[]byte{0x00}, 2},
   180  					{[]byte{0x00, 0x00}, 2},
   181  					{[]byte{0x00, 0x00, 0x00}, 5},
   182  					{[]byte{0x03, 0x00, 0x00}, 4},
   183  				} {
   184  					Convey(fmt.Sprintf(`Has Index %v for needle %v`, s.index, s.needle), func() {
   185  						So(br.Index(s.needle), ShouldEqual, s.index)
   186  					})
   187  				}
   188  			})
   189  
   190  			Convey(`A View with a limit of 6`, func() {
   191  				br := b.ViewLimit(6)
   192  
   193  				Convey(`Should have Remaining() value of 6.`, func() {
   194  					So(br.Remaining(), ShouldEqual, 6)
   195  				})
   196  
   197  				Convey(`Has index of -1 for needle [0x00, 0x04]`, func() {
   198  					So(br.Index([]byte{0x00, 0x04}), ShouldEqual, -1)
   199  				})
   200  			})
   201  
   202  			Convey(`A View with a limit of 20`, func() {
   203  				br := b.ViewLimit(20)
   204  
   205  				Convey(`Should have Remaining() value of 9.`, func() {
   206  					So(br.Remaining(), ShouldEqual, 9)
   207  				})
   208  			})
   209  		})
   210  
   211  		Convey(`With chunks [{0x0F}..{0x00}]`, func() {
   212  			for i := 0x0F; i >= 0x00; i-- {
   213  				b.Append(tc(byte(i)))
   214  			}
   215  			br := b.View()
   216  
   217  			Convey(`Has index of -1 for needle [0x0f, 0x10]`, func() {
   218  				So(br.Index([]byte{0x0f, 0x10}), ShouldEqual, -1)
   219  			})
   220  
   221  			for _, s := range []struct {
   222  				needle []byte
   223  				index  int64
   224  			}{
   225  				{[]byte{0x0F}, 0},
   226  				{[]byte{0x04, 0x03, 0x02}, 11},
   227  				{[]byte{0x01, 0x00}, 14},
   228  				{[]byte{0x00}, 15},
   229  				{[]byte{0x00, 0xFF}, -1},
   230  			} {
   231  				Convey(fmt.Sprintf(`Has Index %v for needle %v`, s.index, s.needle), func() {
   232  					So(br.Index(s.needle), ShouldEqual, s.index)
   233  				})
   234  			}
   235  		})
   236  	})
   237  }