go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/data/cmpbin/number_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 "fmt" 20 "io" 21 "math" 22 "math/rand" 23 "sort" 24 "testing" 25 26 . "github.com/smartystreets/goconvey/convey" 27 ) 28 29 type testCase struct { 30 expect []byte 31 val int64 32 } 33 34 type testCaseSlice []testCase 35 36 func (t testCaseSlice) Len() int { return len(t) } 37 func (t testCaseSlice) Less(i, j int) bool { return t[i].val < t[j].val } 38 func (t testCaseSlice) Swap(i, j int) { t[i], t[j] = t[j], t[i] } 39 40 var cases = testCaseSlice{ 41 {[]byte{0b01000000, 0b01111111, 0b11111111, 0b11111111, 0b11111111, 0xff, 0xff, 0xff, 0b11111111}, -math.MaxInt64 - 1}, 42 {[]byte{0b01000001, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0, 0, 0, 0b00000001}, -math.MaxInt64}, 43 {[]byte{0b01000001, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0, 0, 0, 0b00000011}, -math.MaxInt64 + 1}, 44 {[]byte{0b01100000, 0b01111111, 0b11111111, 0b11111111, 0b11111111}, -math.MaxInt32 - 1}, 45 {[]byte{0b01100001, 0b00000000, 0b00000000, 0b00000000, 0b00000001}, -math.MaxInt32}, 46 {[]byte{0b01100001, 0b00000000, 0b00000000, 0b00000000, 0b00000011}, -math.MaxInt32 + 1}, 47 {[]byte{0b01110000, 0b01111111, 0b11111111}, -math.MaxInt16 - 1}, 48 {[]byte{0b01111000, 0b01111110}, -129}, 49 {[]byte{0b01111000, 0b01111111}, -128}, 50 {[]byte{0b01111001, 0b00000001}, -127}, 51 {[]byte{0b01111001, 0b01111101}, -65}, 52 {[]byte{0b01111001, 0b01111111}, -64}, 53 {[]byte{0b01111010, 0b00000011}, -63}, 54 {[]byte{0b01111101, 0b01011111}, -5}, 55 {[]byte{0b01111110, 0b00111111}, -3}, 56 {[]byte{0b01111111, 0b01111111}, -1}, 57 {[]byte{0b10000000, 0b00000000}, 0}, 58 {[]byte{0b10000010, 0b10100000}, 5}, 59 {[]byte{0b10000100, 0b10001000}, 17}, 60 {[]byte{0b10000101, 0b11111100}, 63}, 61 {[]byte{0b10000110, 0b10000000}, 64}, 62 {[]byte{0b10000110, 0b10000010}, 65}, 63 {[]byte{0b10000111, 0b10000000}, 128}, 64 {[]byte{0b10011110, 0b11111111, 0xff, 0xff, 0b11111110}, math.MaxInt32}, 65 {[]byte{0b10011111, 0b10000000, 0, 0, 0}, math.MaxInt32 + 1}, 66 {[]byte{0b10111110, 0b11111111, 0xff, 0xff, 0b11111111, 0xff, 0xff, 0xff, 0b11111110}, math.MaxInt64}, 67 } 68 69 func TestWrite(t *testing.T) { 70 t.Parallel() 71 Convey("WriteFuncs", t, func() { 72 for _, c := range cases { 73 c := c 74 Convey(fmt.Sprintf("%d -> % x", c.val, c.expect), func() { 75 Convey("Write", func() { 76 buf := &bytes.Buffer{} 77 n, err := WriteInt(buf, c.val) 78 So(err, ShouldBeNil) 79 So(n, ShouldEqual, len(c.expect)) 80 So(buf.Bytes(), ShouldResemble, c.expect) 81 }) 82 83 if c.val >= 0 { 84 Convey("WriteUint", func() { 85 buf := &bytes.Buffer{} 86 n, err := WriteUint(buf, uint64(c.val)) 87 So(err, ShouldBeNil) 88 So(n, ShouldEqual, len(c.expect)) 89 So(buf.Bytes(), ShouldResemble, c.expect) 90 }) 91 } 92 }) 93 } 94 }) 95 } 96 97 func TestRead(t *testing.T) { 98 Convey("Read", t, func() { 99 for _, c := range cases { 100 c := c 101 Convey(fmt.Sprintf("% x -> %d", c.expect, c.val), func() { 102 buf := bytes.NewBuffer(c.expect) 103 v, n, err := ReadInt(buf) 104 So(err, ShouldBeNil) 105 So(n, ShouldEqual, len(c.expect)) 106 So(v, ShouldEqual, c.val) 107 108 if c.val >= 0 { 109 buf := bytes.NewBuffer(c.expect) 110 v, n, err := ReadUint(buf) 111 So(err, ShouldBeNil) 112 So(n, ShouldEqual, len(c.expect)) 113 So(v, ShouldEqual, uint64(c.val)) 114 } 115 }) 116 } 117 }) 118 } 119 120 func TestSort(t *testing.T) { 121 num := randomTestSize 122 num += len(cases) 123 randomCases := make(testCaseSlice, num) 124 125 rcSub := randomCases[copy(randomCases, cases):] 126 r := rand.New(rand.NewSource(*seed)) 127 buf := &bytes.Buffer{} 128 for i := range rcSub { 129 v := int64(uint64(r.Uint32())<<32 | uint64(r.Uint32())) 130 rcSub[i].val = v 131 buf.Reset() 132 if _, err := WriteInt(buf, v); err != nil { 133 panic(err) 134 } 135 rcSub[i].expect = make([]byte, buf.Len()) 136 copy(rcSub[i].expect, buf.Bytes()) 137 } 138 139 sort.Sort(randomCases) 140 141 shouldBeLessThanOrEqual := func(actual any, expected ...any) string { 142 a, b := actual.([]byte), expected[0].([]byte) 143 if bytes.Compare(a, b) <= 0 { 144 return fmt.Sprintf("Expected A <= B (but it wasn't)!\nA: [% x]\nB: [% x]", a, b) 145 } 146 return "" 147 } 148 149 Convey("TestSort", t, func() { 150 prev := randomCases[0] 151 for _, c := range randomCases[1:] { 152 // Actually asserting with the So for every entry in the sorted array will 153 // produce 100 green checkmarks on a successful test, which is a bit 154 // much :). 155 if bytes.Compare(c.expect, prev.expect) < 0 { 156 So(c.expect, shouldBeLessThanOrEqual, prev.expect) 157 break 158 } 159 prev = c 160 } 161 162 // This silly assertion is done so that this test has a green check next to 163 // it in the event that it passes. Otherwise convey thinks we skipped the 164 // test, which isn't correct. 165 So(true, ShouldBeTrue) 166 }) 167 } 168 169 func TestErrors(t *testing.T) { 170 smallerInt64 := []byte{0b01000000, 0b01111111, 0b11111111, 0b11111111, 0b11111111, 0xff, 0xff, 0xff, 0b11111110} 171 172 prettyBigUint64 := []byte{0b10111111, 0b10000000, 0, 0, 0, 0, 0, 0, 0} 173 prettyBigUint64Val := uint64(math.MaxInt64 + 1) 174 175 reallyBigUint64 := []byte{0b10111111, 0b11111111, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} 176 reallyBigUint64Val := uint64(math.MaxUint64) 177 tests := []struct { 178 name string 179 buf []byte 180 181 v int64 182 n int 183 err error 184 185 uv uint64 186 un int 187 uerr error 188 }{ 189 { 190 name: "Too big!!", 191 buf: []byte{0b11000000}, // 65 bits!? 192 err: ErrOverflow, 193 uerr: ErrOverflow, 194 }, { 195 name: "Nil buffer", 196 err: io.EOF, 197 uerr: io.EOF, 198 }, { 199 name: "Empty buffer", 200 buf: []byte{}, 201 err: io.EOF, 202 uerr: io.EOF, 203 }, { 204 name: "Small buffer", 205 buf: cases[len(cases)-1].expect[:4], 206 err: io.EOF, 207 uerr: io.EOF, 208 }, { 209 name: "Reading a negative number with *Uint", 210 buf: cases[0].expect, 211 v: cases[0].val, 212 n: len(cases[0].expect), 213 214 uerr: ErrUnderflow, 215 }, { 216 name: "Reading a number smaller than min int64", 217 buf: smallerInt64, 218 err: ErrUnderflow, 219 uerr: ErrUnderflow, 220 }, { 221 name: "Reading a number bigger than int64", 222 buf: prettyBigUint64, 223 err: ErrOverflow, 224 225 uv: prettyBigUint64Val, 226 un: len(prettyBigUint64), 227 }, { 228 name: "Reading MaxUint64", 229 buf: reallyBigUint64, 230 err: ErrOverflow, 231 232 uv: reallyBigUint64Val, 233 un: len(reallyBigUint64), 234 }, 235 } 236 237 Convey("Error conditions", t, func() { 238 for _, t := range tests { 239 Convey(t.name, func() { 240 Convey("Read", func() { 241 v, _, err := ReadInt(bytes.NewBuffer(t.buf)) 242 So(err, ShouldEqual, t.err) 243 if t.err == nil { 244 So(v, ShouldEqual, t.v) 245 } 246 }) 247 Convey("ReadUint", func() { 248 uv, _, err := ReadUint(bytes.NewBuffer(t.buf)) 249 So(err, ShouldEqual, t.uerr) 250 if t.uerr == nil { 251 So(uv, ShouldEqual, t.uv) 252 } 253 }) 254 }) 255 } 256 Convey("Write Errors", func() { 257 // Test each error return location in writeSignMag 258 for count := 0; count < 3; count++ { 259 fw := &fakeWriter{count} 260 _, err := WriteInt(fw, -10000) 261 So(err.Error(), ShouldContainSubstring, "nope") 262 So(fw.count, ShouldEqual, 0) 263 } 264 }) 265 }) 266 }