blob: a8e426f0bb409a96369acbd4f1b95b0a8adc5380 [file] [log] [blame]
// Copyright 2015 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmpbin
import (
"bytes"
"io"
"math/rand"
"sort"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestBytes(t *testing.T) {
t.Parallel()
Convey("bytes", t, func() {
b := &bytes.Buffer{}
t := []byte("this is a test")
Convey("good", func() {
_, err := WriteBytes(b, t)
So(err, ShouldBeNil)
exn := b.Len()
r, n, err := ReadBytes(b)
So(err, ShouldBeNil)
So(n, ShouldEqual, exn)
So(r, ShouldResemble, t)
})
Convey("bad (truncated buffer)", func() {
_, err := WriteBytes(b, t)
So(err, ShouldBeNil)
bs := b.Bytes()[:b.Len()-4]
_, n, err := ReadBytes(bytes.NewBuffer(bs))
So(err, ShouldEqual, io.EOF)
So(n, ShouldEqual, len(bs))
})
Convey("bad (bad varint)", func() {
_, n, err := ReadBytes(bytes.NewBuffer([]byte{0b10001111}))
So(err, ShouldEqual, io.EOF)
So(n, ShouldEqual, 1)
})
Convey("bad (huge data)", func() {
n, err := WriteBytes(b, make([]byte, 2*1024*1024+1))
So(err, ShouldBeNil)
So(n, ShouldEqual, ReadByteLimit+2)
_, n, err = ReadBytes(b)
So(err.Error(), ShouldContainSubstring, "too big!")
So(n, ShouldEqual, ReadByteLimit)
})
Convey("bad (write errors)", func() {
_, err := WriteBytes(&fakeWriter{1}, t)
So(err.Error(), ShouldContainSubstring, "nope")
// transition boundary
_, err = WriteBytes(&fakeWriter{7}, t)
So(err.Error(), ShouldContainSubstring, "nope")
})
})
}
func TestStrings(t *testing.T) {
t.Parallel()
Convey("strings", t, func() {
b := &bytes.Buffer{}
Convey("empty", func() {
n, err := WriteString(b, "")
So(err, ShouldBeNil)
So(n, ShouldEqual, 1)
So(b.Bytes(), ShouldResemble, []byte{0})
r, n, err := ReadString(b)
So(err, ShouldBeNil)
So(n, ShouldEqual, 1)
So(r, ShouldEqual, "")
})
Convey("nulls", func() {
s := "\x00\x00\x00\x00\x00"
n, err := WriteString(b, s)
So(n, ShouldEqual, 6)
So(err, ShouldBeNil)
exn := b.Len()
r, n, err := ReadString(b)
So(err, ShouldBeNil)
So(n, ShouldEqual, exn)
So(r, ShouldEqual, s)
})
Convey("bad (truncated buffer)", func() {
s := "this is a test"
n, err := WriteString(b, s)
So(n, ShouldEqual, (len(s)*8)/7+1)
So(err, ShouldBeNil)
bs := b.Bytes()[:b.Len()-1]
_, n, err = ReadString(bytes.NewBuffer(bs))
So(err, ShouldEqual, io.EOF)
So(n, ShouldEqual, len(bs))
})
Convey("single", func() {
n, err := WriteString(b, "1")
So(err, ShouldBeNil)
So(n, ShouldEqual, 2)
So(b.Bytes(), ShouldResemble, []byte{0b00110001, 0b10000000})
r, n, err := ReadString(b)
So(err, ShouldBeNil)
So(n, ShouldEqual, 2)
So(r, ShouldEqual, "1")
})
Convey("good", func() {
s := "this is a test"
n, err := WriteString(b, s)
So(err, ShouldBeNil)
So(n, ShouldEqual, (len(s)*8)/7+1)
So(b.Bytes()[:8], ShouldResemble, []byte{
0b01110101, 0b00110101, 0b00011011, 0b00101111, 0b00110011, 0b00000011,
0b10100101, 0b11100111,
})
exn := b.Len()
r, n, err := ReadString(b)
So(err, ShouldBeNil)
So(len(r), ShouldEqual, len(s))
So(n, ShouldEqual, exn)
So(r, ShouldEqual, s)
})
})
}
// TODO(riannucci): make it [][]string instead
func TestStringSortability(t *testing.T) {
t.Parallel()
Convey("strings maintain sort order", t, func() {
special := []string{
"",
"\x00",
"\x00\x00",
"\x00\x00\x00",
"\x00\x00\x00\x00",
"\x00\x00\x00\x00\x00",
"\x00\x00\x00\x00\x01",
"\x00\x00\x00\x00\xFF",
"1234567",
"12345678",
"123456789",
string(make([]byte, 7*8)),
}
orig := make(sort.StringSlice, randomTestSize, randomTestSize+len(special))
r := rand.New(rand.NewSource(*seed))
for i := range orig {
buf := make([]byte, r.Intn(100))
for j := range buf {
buf[j] = byte(r.Uint32()) // watch me not care!
}
orig[i] = string(buf)
}
orig = append(orig, special...)
enc := make(sort.StringSlice, len(orig))
b := &bytes.Buffer{}
for i := range enc {
b.Reset()
_, err := WriteString(b, orig[i])
So(err, ShouldBeNil)
enc[i] = b.String()
}
orig.Sort()
enc.Sort()
for i := range orig {
decoded, _, err := ReadString(bytes.NewBufferString(enc[i]))
So(err, ShouldBeNil)
So(decoded, ShouldResemble, orig[i])
}
})
}
type StringSliceSlice [][]string
func (s StringSliceSlice) Len() int { return len(s) }
func (s StringSliceSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s StringSliceSlice) Less(i, j int) bool {
a, b := s[i], s[j]
lim := len(a)
if len(b) < lim {
lim = len(b)
}
for k := 0; k < lim; k++ {
if a[k] > b[k] {
return false
} else if a[k] < b[k] {
return true
}
}
return len(a) < len(b)
}
func TestConcatenatedStringSortability(t *testing.T) {
t.Parallel()
Convey("concatenated strings maintain sort order", t, func() {
orig := make(StringSliceSlice, randomTestSize)
r := rand.New(rand.NewSource(*seed))
for i := range orig {
count := r.Intn(10)
for j := 0; j < count; j++ {
buf := make([]byte, r.Intn(100))
for j := range buf {
buf[j] = byte(r.Uint32()) // watch me not care!
}
orig[i] = append(orig[i], string(buf))
}
}
orig = append(orig, [][]string{
nil,
{"", "aaa"},
{"a", "aa"},
{"aa", "a"},
}...)
enc := make(sort.StringSlice, len(orig))
b := &bytes.Buffer{}
for i, slice := range orig {
b.Reset()
for _, s := range slice {
_, err := WriteString(b, s)
So(err, ShouldBeNil)
}
enc[i] = b.String()
}
sort.Sort(orig)
enc.Sort()
for i := range orig {
decoded := []string(nil)
buf := bytes.NewBufferString(enc[i])
for {
dec, _, err := ReadString(buf)
if err != nil {
break
}
decoded = append(decoded, dec)
}
So(decoded, ShouldResemble, orig[i])
}
})
}