go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/data/cmpbin/string_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 cmpbin
    16  
    17  import (
    18  	"bytes"
    19  	"io"
    20  	"math/rand"
    21  	"sort"
    22  	"testing"
    23  
    24  	. "github.com/smartystreets/goconvey/convey"
    25  )
    26  
    27  func TestBytes(t *testing.T) {
    28  	t.Parallel()
    29  
    30  	Convey("bytes", t, func() {
    31  		b := &bytes.Buffer{}
    32  		t := []byte("this is a test")
    33  
    34  		Convey("good", func() {
    35  			_, err := WriteBytes(b, t)
    36  			So(err, ShouldBeNil)
    37  			exn := b.Len()
    38  			r, n, err := ReadBytes(b)
    39  			So(err, ShouldBeNil)
    40  			So(n, ShouldEqual, exn)
    41  			So(r, ShouldResemble, t)
    42  		})
    43  
    44  		Convey("bad (truncated buffer)", func() {
    45  			_, err := WriteBytes(b, t)
    46  			So(err, ShouldBeNil)
    47  			bs := b.Bytes()[:b.Len()-4]
    48  			_, n, err := ReadBytes(bytes.NewBuffer(bs))
    49  			So(err, ShouldEqual, io.EOF)
    50  			So(n, ShouldEqual, len(bs))
    51  		})
    52  
    53  		Convey("bad (bad varint)", func() {
    54  			_, n, err := ReadBytes(bytes.NewBuffer([]byte{0b10001111}))
    55  			So(err, ShouldEqual, io.EOF)
    56  			So(n, ShouldEqual, 1)
    57  		})
    58  
    59  		Convey("bad (huge data)", func() {
    60  			n, err := WriteBytes(b, make([]byte, 2*1024*1024+1))
    61  			So(err, ShouldBeNil)
    62  			So(n, ShouldEqual, ReadByteLimit+2)
    63  			_, n, err = ReadBytes(b)
    64  			So(err.Error(), ShouldContainSubstring, "too big!")
    65  			So(n, ShouldEqual, ReadByteLimit)
    66  		})
    67  
    68  		Convey("bad (write errors)", func() {
    69  			_, err := WriteBytes(&fakeWriter{1}, t)
    70  			So(err.Error(), ShouldContainSubstring, "nope")
    71  
    72  			// transition boundary
    73  			_, err = WriteBytes(&fakeWriter{7}, t)
    74  			So(err.Error(), ShouldContainSubstring, "nope")
    75  		})
    76  	})
    77  }
    78  
    79  func TestStrings(t *testing.T) {
    80  	t.Parallel()
    81  
    82  	Convey("strings", t, func() {
    83  		b := &bytes.Buffer{}
    84  
    85  		Convey("empty", func() {
    86  			n, err := WriteString(b, "")
    87  			So(err, ShouldBeNil)
    88  			So(n, ShouldEqual, 1)
    89  			So(b.Bytes(), ShouldResemble, []byte{0})
    90  			r, n, err := ReadString(b)
    91  			So(err, ShouldBeNil)
    92  			So(n, ShouldEqual, 1)
    93  			So(r, ShouldEqual, "")
    94  		})
    95  
    96  		Convey("nulls", func() {
    97  			s := "\x00\x00\x00\x00\x00"
    98  			n, err := WriteString(b, s)
    99  			So(n, ShouldEqual, 6)
   100  			So(err, ShouldBeNil)
   101  			exn := b.Len()
   102  			r, n, err := ReadString(b)
   103  			So(err, ShouldBeNil)
   104  			So(n, ShouldEqual, exn)
   105  			So(r, ShouldEqual, s)
   106  		})
   107  
   108  		Convey("bad (truncated buffer)", func() {
   109  			s := "this is a test"
   110  			n, err := WriteString(b, s)
   111  			So(n, ShouldEqual, (len(s)*8)/7+1)
   112  			So(err, ShouldBeNil)
   113  			bs := b.Bytes()[:b.Len()-1]
   114  			_, n, err = ReadString(bytes.NewBuffer(bs))
   115  			So(err, ShouldEqual, io.EOF)
   116  			So(n, ShouldEqual, len(bs))
   117  		})
   118  
   119  		Convey("single", func() {
   120  			n, err := WriteString(b, "1")
   121  			So(err, ShouldBeNil)
   122  			So(n, ShouldEqual, 2)
   123  			So(b.Bytes(), ShouldResemble, []byte{0b00110001, 0b10000000})
   124  			r, n, err := ReadString(b)
   125  			So(err, ShouldBeNil)
   126  			So(n, ShouldEqual, 2)
   127  			So(r, ShouldEqual, "1")
   128  		})
   129  
   130  		Convey("good", func() {
   131  			s := "this is a test"
   132  			n, err := WriteString(b, s)
   133  			So(err, ShouldBeNil)
   134  			So(n, ShouldEqual, (len(s)*8)/7+1)
   135  			So(b.Bytes()[:8], ShouldResemble, []byte{
   136  				0b01110101, 0b00110101, 0b00011011, 0b00101111, 0b00110011, 0b00000011,
   137  				0b10100101, 0b11100111,
   138  			})
   139  			exn := b.Len()
   140  			r, n, err := ReadString(b)
   141  			So(err, ShouldBeNil)
   142  			So(len(r), ShouldEqual, len(s))
   143  			So(n, ShouldEqual, exn)
   144  			So(r, ShouldEqual, s)
   145  		})
   146  
   147  	})
   148  }
   149  
   150  // TODO(riannucci): make it [][]string instead
   151  
   152  func TestStringSortability(t *testing.T) {
   153  	t.Parallel()
   154  
   155  	Convey("strings maintain sort order", t, func() {
   156  		special := []string{
   157  			"",
   158  			"\x00",
   159  			"\x00\x00",
   160  			"\x00\x00\x00",
   161  			"\x00\x00\x00\x00",
   162  			"\x00\x00\x00\x00\x00",
   163  			"\x00\x00\x00\x00\x01",
   164  			"\x00\x00\x00\x00\xFF",
   165  			"1234567",
   166  			"12345678",
   167  			"123456789",
   168  			string(make([]byte, 7*8)),
   169  		}
   170  		orig := make(sort.StringSlice, randomTestSize, randomTestSize+len(special))
   171  
   172  		r := rand.New(rand.NewSource(*seed))
   173  		for i := range orig {
   174  			buf := make([]byte, r.Intn(100))
   175  			for j := range buf {
   176  				buf[j] = byte(r.Uint32()) // watch me not care!
   177  			}
   178  			orig[i] = string(buf)
   179  		}
   180  		orig = append(orig, special...)
   181  
   182  		enc := make(sort.StringSlice, len(orig))
   183  		b := &bytes.Buffer{}
   184  		for i := range enc {
   185  			b.Reset()
   186  			_, err := WriteString(b, orig[i])
   187  			So(err, ShouldBeNil)
   188  			enc[i] = b.String()
   189  		}
   190  
   191  		orig.Sort()
   192  		enc.Sort()
   193  
   194  		for i := range orig {
   195  			decoded, _, err := ReadString(bytes.NewBufferString(enc[i]))
   196  			So(err, ShouldBeNil)
   197  			So(decoded, ShouldResemble, orig[i])
   198  		}
   199  	})
   200  }
   201  
   202  type StringSliceSlice [][]string
   203  
   204  func (s StringSliceSlice) Len() int      { return len(s) }
   205  func (s StringSliceSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
   206  func (s StringSliceSlice) Less(i, j int) bool {
   207  	a, b := s[i], s[j]
   208  	lim := len(a)
   209  	if len(b) < lim {
   210  		lim = len(b)
   211  	}
   212  	for k := 0; k < lim; k++ {
   213  		if a[k] > b[k] {
   214  			return false
   215  		} else if a[k] < b[k] {
   216  			return true
   217  		}
   218  	}
   219  	return len(a) < len(b)
   220  }
   221  
   222  func TestConcatenatedStringSortability(t *testing.T) {
   223  	t.Parallel()
   224  
   225  	Convey("concatenated strings maintain sort order", t, func() {
   226  		orig := make(StringSliceSlice, randomTestSize)
   227  
   228  		r := rand.New(rand.NewSource(*seed))
   229  		for i := range orig {
   230  			count := r.Intn(10)
   231  			for j := 0; j < count; j++ {
   232  				buf := make([]byte, r.Intn(100))
   233  				for j := range buf {
   234  					buf[j] = byte(r.Uint32()) // watch me not care!
   235  				}
   236  				orig[i] = append(orig[i], string(buf))
   237  			}
   238  		}
   239  		orig = append(orig, [][]string{
   240  			nil,
   241  			{"", "aaa"},
   242  			{"a", "aa"},
   243  			{"aa", "a"},
   244  		}...)
   245  
   246  		enc := make(sort.StringSlice, len(orig))
   247  		b := &bytes.Buffer{}
   248  		for i, slice := range orig {
   249  			b.Reset()
   250  			for _, s := range slice {
   251  				_, err := WriteString(b, s)
   252  				So(err, ShouldBeNil)
   253  			}
   254  			enc[i] = b.String()
   255  		}
   256  
   257  		sort.Sort(orig)
   258  		enc.Sort()
   259  
   260  		for i := range orig {
   261  			decoded := []string(nil)
   262  			buf := bytes.NewBufferString(enc[i])
   263  			for {
   264  				dec, _, err := ReadString(buf)
   265  				if err != nil {
   266  					break
   267  				}
   268  				decoded = append(decoded, dec)
   269  			}
   270  			So(decoded, ShouldResemble, orig[i])
   271  		}
   272  	})
   273  }