mirror of
https://github.com/documize/community.git
synced 2025-07-26 00:29:47 +02:00
397 lines
9.1 KiB
Go
397 lines
9.1 KiB
Go
|
package xid
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
"testing"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
type IDParts struct {
|
||
|
id ID
|
||
|
timestamp int64
|
||
|
machine []byte
|
||
|
pid uint16
|
||
|
counter int32
|
||
|
}
|
||
|
|
||
|
var IDs = []IDParts{
|
||
|
IDParts{
|
||
|
ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9},
|
||
|
1300816219,
|
||
|
[]byte{0x60, 0xf4, 0x86},
|
||
|
0xe428,
|
||
|
4271561,
|
||
|
},
|
||
|
IDParts{
|
||
|
ID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||
|
0,
|
||
|
[]byte{0x00, 0x00, 0x00},
|
||
|
0x0000,
|
||
|
0,
|
||
|
},
|
||
|
IDParts{
|
||
|
ID{0x00, 0x00, 0x00, 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x00, 0x00, 0x01},
|
||
|
0,
|
||
|
[]byte{0xaa, 0xbb, 0xcc},
|
||
|
0xddee,
|
||
|
1,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
func TestIDPartsExtraction(t *testing.T) {
|
||
|
for i, v := range IDs {
|
||
|
t.Run(fmt.Sprintf("Test%d", i), func(t *testing.T) {
|
||
|
if got, want := v.id.Time(), time.Unix(v.timestamp, 0); got != want {
|
||
|
t.Errorf("Time() = %v, want %v", got, want)
|
||
|
}
|
||
|
if got, want := v.id.Machine(), v.machine; !bytes.Equal(got, want) {
|
||
|
t.Errorf("Machine() = %v, want %v", got, want)
|
||
|
}
|
||
|
if got, want := v.id.Pid(), v.pid; got != want {
|
||
|
t.Errorf("Pid() = %v, want %v", got, want)
|
||
|
}
|
||
|
if got, want := v.id.Counter(), v.counter; got != want {
|
||
|
t.Errorf("Counter() = %v, want %v", got, want)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestNew(t *testing.T) {
|
||
|
// Generate 10 ids
|
||
|
ids := make([]ID, 10)
|
||
|
for i := 0; i < 10; i++ {
|
||
|
ids[i] = New()
|
||
|
}
|
||
|
for i := 1; i < 10; i++ {
|
||
|
prevID := ids[i-1]
|
||
|
id := ids[i]
|
||
|
// Test for uniqueness among all other 9 generated ids
|
||
|
for j, tid := range ids {
|
||
|
if j != i {
|
||
|
if id.Compare(tid) == 0 {
|
||
|
t.Errorf("generated ID is not unique (%d/%d)", i, j)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// Check that timestamp was incremented and is within 30 seconds of the previous one
|
||
|
secs := id.Time().Sub(prevID.Time()).Seconds()
|
||
|
if secs < 0 || secs > 30 {
|
||
|
t.Error("wrong timestamp in generated ID")
|
||
|
}
|
||
|
// Check that machine ids are the same
|
||
|
if !bytes.Equal(id.Machine(), prevID.Machine()) {
|
||
|
t.Error("machine ID not equal")
|
||
|
}
|
||
|
// Check that pids are the same
|
||
|
if id.Pid() != prevID.Pid() {
|
||
|
t.Error("pid not equal")
|
||
|
}
|
||
|
// Test for proper increment
|
||
|
if got, want := int(id.Counter()-prevID.Counter()), 1; got != want {
|
||
|
t.Errorf("wrong increment in generated ID, delta=%v, want %v", got, want)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestIDString(t *testing.T) {
|
||
|
id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
|
||
|
if got, want := id.String(), "9m4e2mr0ui3e8a215n4g"; got != want {
|
||
|
t.Errorf("String() = %v, want %v", got, want)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestFromString(t *testing.T) {
|
||
|
got, err := FromString("9m4e2mr0ui3e8a215n4g")
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
|
||
|
if got != want {
|
||
|
t.Errorf("FromString() = %v, want %v", got, want)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestFromStringInvalid(t *testing.T) {
|
||
|
_, err := FromString("invalid")
|
||
|
if err != ErrInvalidID {
|
||
|
t.Errorf("FromString(invalid) err=%v, want %v", err, ErrInvalidID)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type jsonType struct {
|
||
|
ID *ID
|
||
|
Str string
|
||
|
}
|
||
|
|
||
|
func TestIDJSONMarshaling(t *testing.T) {
|
||
|
id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
|
||
|
v := jsonType{ID: &id, Str: "test"}
|
||
|
data, err := json.Marshal(&v)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if got, want := string(data), `{"ID":"9m4e2mr0ui3e8a215n4g","Str":"test"}`; got != want {
|
||
|
t.Errorf("json.Marshal() = %v, want %v", got, want)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestIDJSONUnmarshaling(t *testing.T) {
|
||
|
data := []byte(`{"ID":"9m4e2mr0ui3e8a215n4g","Str":"test"}`)
|
||
|
v := jsonType{}
|
||
|
err := json.Unmarshal(data, &v)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
|
||
|
if got := *v.ID; got.Compare(want) != 0 {
|
||
|
t.Errorf("json.Unmarshal() = %v, want %v", got, want)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
func TestIDJSONUnmarshalingError(t *testing.T) {
|
||
|
v := jsonType{}
|
||
|
err := json.Unmarshal([]byte(`{"ID":"9M4E2MR0UI3E8A215N4G"}`), &v)
|
||
|
if err != ErrInvalidID {
|
||
|
t.Errorf("json.Unmarshal() err=%v, want %v", err, ErrInvalidID)
|
||
|
}
|
||
|
err = json.Unmarshal([]byte(`{"ID":"TYjhW2D0huQoQS"}`), &v)
|
||
|
if err != ErrInvalidID {
|
||
|
t.Errorf("json.Unmarshal() err=%v, want %v", err, ErrInvalidID)
|
||
|
}
|
||
|
err = json.Unmarshal([]byte(`{"ID":"TYjhW2D0huQoQS3kdk"}`), &v)
|
||
|
if err != ErrInvalidID {
|
||
|
t.Errorf("json.Unmarshal() err=%v, want %v", err, ErrInvalidID)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestIDDriverValue(t *testing.T) {
|
||
|
id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
|
||
|
got, err := id.Value()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if want := "9m4e2mr0ui3e8a215n4g"; got != want {
|
||
|
t.Errorf("Value() = %v, want %v", got, want)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestIDDriverScan(t *testing.T) {
|
||
|
got := ID{}
|
||
|
err := got.Scan("9m4e2mr0ui3e8a215n4g")
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
|
||
|
if got.Compare(want) != 0 {
|
||
|
t.Errorf("Scan() = %v, want %v", got, want)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestIDDriverScanError(t *testing.T) {
|
||
|
id := ID{}
|
||
|
if got, want := id.Scan(0), errors.New("xid: scanning unsupported type: int"); !reflect.DeepEqual(got, want) {
|
||
|
t.Errorf("Scan() err=%v, want %v", got, want)
|
||
|
}
|
||
|
if got, want := id.Scan("0"), ErrInvalidID; got != want {
|
||
|
t.Errorf("Scan() err=%v, want %v", got, want)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestIDDriverScanByteFromDatabase(t *testing.T) {
|
||
|
got := ID{}
|
||
|
bs := []byte("9m4e2mr0ui3e8a215n4g")
|
||
|
err := got.Scan(bs)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
|
||
|
if got.Compare(want) != 0 {
|
||
|
t.Errorf("Scan() = %v, want %v", got, want)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func BenchmarkNew(b *testing.B) {
|
||
|
b.RunParallel(func(pb *testing.PB) {
|
||
|
for pb.Next() {
|
||
|
_ = New()
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func BenchmarkNewString(b *testing.B) {
|
||
|
b.RunParallel(func(pb *testing.PB) {
|
||
|
for pb.Next() {
|
||
|
_ = New().String()
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func BenchmarkFromString(b *testing.B) {
|
||
|
b.RunParallel(func(pb *testing.PB) {
|
||
|
for pb.Next() {
|
||
|
_, _ = FromString("9m4e2mr0ui3e8a215n4g")
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// func BenchmarkUUIDv1(b *testing.B) {
|
||
|
// b.RunParallel(func(pb *testing.PB) {
|
||
|
// for pb.Next() {
|
||
|
// _ = uuid.NewV1().String()
|
||
|
// }
|
||
|
// })
|
||
|
// }
|
||
|
|
||
|
// func BenchmarkUUIDv4(b *testing.B) {
|
||
|
// b.RunParallel(func(pb *testing.PB) {
|
||
|
// for pb.Next() {
|
||
|
// _ = uuid.NewV4().String()
|
||
|
// }
|
||
|
// })
|
||
|
// }
|
||
|
|
||
|
func TestID_IsNil(t *testing.T) {
|
||
|
tests := []struct {
|
||
|
name string
|
||
|
id ID
|
||
|
want bool
|
||
|
}{
|
||
|
{
|
||
|
name: "ID not nil",
|
||
|
id: New(),
|
||
|
want: false,
|
||
|
},
|
||
|
{
|
||
|
name: "Nil ID",
|
||
|
id: ID{},
|
||
|
want: true,
|
||
|
},
|
||
|
}
|
||
|
for _, tt := range tests {
|
||
|
tt := tt
|
||
|
t.Run(tt.name, func(t *testing.T) {
|
||
|
if got, want := tt.id.IsNil(), tt.want; got != want {
|
||
|
t.Errorf("IsNil() = %v, want %v", got, want)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestNilID(t *testing.T) {
|
||
|
got := ID{}
|
||
|
if want := NilID(); !reflect.DeepEqual(got, want) {
|
||
|
t.Error("NilID() not equal ID{}")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestNilID_IsNil(t *testing.T) {
|
||
|
if !NilID().IsNil() {
|
||
|
t.Error("NilID().IsNil() is not true")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestFromBytes_Invariant(t *testing.T) {
|
||
|
want := New()
|
||
|
got, err := FromBytes(want.Bytes())
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if got.Compare(want) != 0 {
|
||
|
t.Error("FromBytes(id.Bytes()) != id")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestFromBytes_InvalidBytes(t *testing.T) {
|
||
|
cases := []struct {
|
||
|
length int
|
||
|
shouldFail bool
|
||
|
}{
|
||
|
{11, true},
|
||
|
{12, false},
|
||
|
{13, true},
|
||
|
}
|
||
|
for _, c := range cases {
|
||
|
b := make([]byte, c.length, c.length)
|
||
|
_, err := FromBytes(b)
|
||
|
if got, want := err != nil, c.shouldFail; got != want {
|
||
|
t.Errorf("FromBytes() error got %v, want %v", got, want)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestID_Compare(t *testing.T) {
|
||
|
pairs := []struct {
|
||
|
left ID
|
||
|
right ID
|
||
|
expected int
|
||
|
}{
|
||
|
{IDs[1].id, IDs[0].id, -1},
|
||
|
{ID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, IDs[2].id, -1},
|
||
|
{IDs[0].id, IDs[0].id, 0},
|
||
|
}
|
||
|
for _, p := range pairs {
|
||
|
if p.expected != p.left.Compare(p.right) {
|
||
|
t.Errorf("%s Compare to %s should return %d", p.left, p.right, p.expected)
|
||
|
}
|
||
|
if -1*p.expected != p.right.Compare(p.left) {
|
||
|
t.Errorf("%s Compare to %s should return %d", p.right, p.left, -1*p.expected)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var IDList = []ID{IDs[0].id, IDs[1].id, IDs[2].id}
|
||
|
|
||
|
func TestSorter_Len(t *testing.T) {
|
||
|
if got, want := sorter([]ID{}).Len(), 0; got != want {
|
||
|
t.Errorf("Len() %v, want %v", got, want)
|
||
|
}
|
||
|
if got, want := sorter(IDList).Len(), 3; got != want {
|
||
|
t.Errorf("Len() %v, want %v", got, want)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSorter_Less(t *testing.T) {
|
||
|
sorter := sorter(IDList)
|
||
|
if !sorter.Less(1, 0) {
|
||
|
t.Errorf("Less(1, 0) not true")
|
||
|
}
|
||
|
if sorter.Less(2, 1) {
|
||
|
t.Errorf("Less(2, 1) true")
|
||
|
}
|
||
|
if sorter.Less(0, 0) {
|
||
|
t.Errorf("Less(0, 0) true")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSorter_Swap(t *testing.T) {
|
||
|
ids := make([]ID, 0)
|
||
|
ids = append(ids, IDList...)
|
||
|
sorter := sorter(ids)
|
||
|
sorter.Swap(0, 1)
|
||
|
if got, want := ids[0], IDList[1]; !reflect.DeepEqual(got, want) {
|
||
|
t.Error("ids[0] != IDList[1]")
|
||
|
}
|
||
|
if got, want := ids[1], IDList[0]; !reflect.DeepEqual(got, want) {
|
||
|
t.Error("ids[1] != IDList[0]")
|
||
|
}
|
||
|
sorter.Swap(2, 2)
|
||
|
if got, want := ids[2], IDList[2]; !reflect.DeepEqual(got, want) {
|
||
|
t.Error("ids[2], IDList[2]")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSort(t *testing.T) {
|
||
|
ids := make([]ID, 0)
|
||
|
ids = append(ids, IDList...)
|
||
|
Sort(ids)
|
||
|
if got, want := ids, []ID{IDList[1], IDList[2], IDList[0]}; !reflect.DeepEqual(got, want) {
|
||
|
t.Fail()
|
||
|
}
|
||
|
}
|