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 }