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  }