1 package xid
2
3 import (
4 "bytes"
5 "encoding/json"
6 "errors"
7 "fmt"
8 "math/rand"
9 "reflect"
10 "testing"
11 "testing/quick"
12 "time"
13 )
14
15 type IDParts struct {
16 id ID
17 timestamp int64
18 machine []byte
19 pid uint16
20 counter int32
21 }
22
23 var IDs = []IDParts{
24 {
25 ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9},
26 1300816219,
27 []byte{0x60, 0xf4, 0x86},
28 0xe428,
29 4271561,
30 },
31 {
32 ID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
33 0,
34 []byte{0x00, 0x00, 0x00},
35 0x0000,
36 0,
37 },
38 {
39 ID{0x00, 0x00, 0x00, 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x00, 0x00, 0x01},
40 0,
41 []byte{0xaa, 0xbb, 0xcc},
42 0xddee,
43 1,
44 },
45 }
46
47 func TestIDPartsExtraction(t *testing.T) {
48 for i, v := range IDs {
49 t.Run(fmt.Sprintf("Test%d", i), func(t *testing.T) {
50 if got, want := v.id.Time(), time.Unix(v.timestamp, 0); got != want {
51 t.Errorf("Time() = %v, want %v", got, want)
52 }
53 if got, want := v.id.Machine(), v.machine; !bytes.Equal(got, want) {
54 t.Errorf("Machine() = %v, want %v", got, want)
55 }
56 if got, want := v.id.Pid(), v.pid; got != want {
57 t.Errorf("Pid() = %v, want %v", got, want)
58 }
59 if got, want := v.id.Counter(), v.counter; got != want {
60 t.Errorf("Counter() = %v, want %v", got, want)
61 }
62 })
63 }
64 }
65
66 func TestNew(t *testing.T) {
67
68 ids := make([]ID, 10)
69 for i := 0; i < 10; i++ {
70 ids[i] = New()
71 }
72 for i := 1; i < 10; i++ {
73 prevID := ids[i-1]
74 id := ids[i]
75
76 for j, tid := range ids {
77 if j != i {
78 if id.Compare(tid) == 0 {
79 t.Errorf("generated ID is not unique (%d/%d)", i, j)
80 }
81 }
82 }
83
84 secs := id.Time().Sub(prevID.Time()).Seconds()
85 if secs < 0 || secs > 30 {
86 t.Error("wrong timestamp in generated ID")
87 }
88
89 if !bytes.Equal(id.Machine(), prevID.Machine()) {
90 t.Error("machine ID not equal")
91 }
92
93 if id.Pid() != prevID.Pid() {
94 t.Error("pid not equal")
95 }
96
97 if got, want := int(id.Counter()-prevID.Counter()), 1; got != want {
98 t.Errorf("wrong increment in generated ID, delta=%v, want %v", got, want)
99 }
100 }
101 }
102
103 func TestIDString(t *testing.T) {
104 id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
105 if got, want := id.String(), "9m4e2mr0ui3e8a215n4g"; got != want {
106 t.Errorf("String() = %v, want %v", got, want)
107 }
108 }
109
110 func TestIDEncode(t *testing.T) {
111 id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
112 text := make([]byte, encodedLen)
113 if got, want := string(id.Encode(text)), "9m4e2mr0ui3e8a215n4g"; got != want {
114 t.Errorf("Encode() = %v, want %v", got, want)
115 }
116 }
117
118 func TestFromString(t *testing.T) {
119 got, err := FromString("9m4e2mr0ui3e8a215n4g")
120 if err != nil {
121 t.Fatal(err)
122 }
123 want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
124 if got != want {
125 t.Errorf("FromString() = %v, want %v", got, want)
126 }
127 }
128
129 func TestFromStringInvalid(t *testing.T) {
130 _, err := FromString("invalid")
131 if err != ErrInvalidID {
132 t.Errorf("FromString(invalid) err=%v, want %v", err, ErrInvalidID)
133 }
134 }
135
136 type jsonType struct {
137 ID *ID
138 Str string
139 }
140
141 func TestIDJSONMarshaling(t *testing.T) {
142 id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
143 v := jsonType{ID: &id, Str: "test"}
144 data, err := json.Marshal(&v)
145 if err != nil {
146 t.Fatal(err)
147 }
148 if got, want := string(data), `{"ID":"9m4e2mr0ui3e8a215n4g","Str":"test"}`; got != want {
149 t.Errorf("json.Marshal() = %v, want %v", got, want)
150 }
151 }
152
153 func TestIDJSONUnmarshaling(t *testing.T) {
154 data := []byte(`{"ID":"9m4e2mr0ui3e8a215n4g","Str":"test"}`)
155 v := jsonType{}
156 err := json.Unmarshal(data, &v)
157 if err != nil {
158 t.Fatal(err)
159 }
160 want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
161 if got := *v.ID; got.Compare(want) != 0 {
162 t.Errorf("json.Unmarshal() = %v, want %v", got, want)
163 }
164
165 }
166
167 func TestIDJSONUnmarshalingError(t *testing.T) {
168 v := jsonType{}
169 err := json.Unmarshal([]byte(`{"ID":"9M4E2MR0UI3E8A215N4G"}`), &v)
170 if err != ErrInvalidID {
171 t.Errorf("json.Unmarshal() err=%v, want %v", err, ErrInvalidID)
172 }
173 err = json.Unmarshal([]byte(`{"ID":"TYjhW2D0huQoQS"}`), &v)
174 if err != ErrInvalidID {
175 t.Errorf("json.Unmarshal() err=%v, want %v", err, ErrInvalidID)
176 }
177 err = json.Unmarshal([]byte(`{"ID":"TYjhW2D0huQoQS3kdk"}`), &v)
178 if err != ErrInvalidID {
179 t.Errorf("json.Unmarshal() err=%v, want %v", err, ErrInvalidID)
180 }
181 err = json.Unmarshal([]byte(`{"ID":1}`), &v)
182 if err != ErrInvalidID {
183 t.Errorf("json.Unmarshal() err=%v, want %v", err, ErrInvalidID)
184 }
185 }
186
187 func TestIDDriverValue(t *testing.T) {
188 id := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
189 got, err := id.Value()
190 if err != nil {
191 t.Fatal(err)
192 }
193 if want := "9m4e2mr0ui3e8a215n4g"; got != want {
194 t.Errorf("Value() = %v, want %v", got, want)
195 }
196 }
197
198 func TestIDDriverScan(t *testing.T) {
199 got := ID{}
200 err := got.Scan("9m4e2mr0ui3e8a215n4g")
201 if err != nil {
202 t.Fatal(err)
203 }
204 want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
205 if got.Compare(want) != 0 {
206 t.Errorf("Scan() = %v, want %v", got, want)
207 }
208 }
209
210 func TestIDDriverScanError(t *testing.T) {
211 id := ID{}
212 if got, want := id.Scan(0), errors.New("xid: scanning unsupported type: int"); !reflect.DeepEqual(got, want) {
213 t.Errorf("Scan() err=%v, want %v", got, want)
214 }
215 if got, want := id.Scan("0"), ErrInvalidID; got != want {
216 t.Errorf("Scan() err=%v, want %v", got, want)
217 }
218 }
219
220 func TestIDDriverScanByteFromDatabase(t *testing.T) {
221 got := ID{}
222 bs := []byte("9m4e2mr0ui3e8a215n4g")
223 err := got.Scan(bs)
224 if err != nil {
225 t.Fatal(err)
226 }
227 want := ID{0x4d, 0x88, 0xe1, 0x5b, 0x60, 0xf4, 0x86, 0xe4, 0x28, 0x41, 0x2d, 0xc9}
228 if got.Compare(want) != 0 {
229 t.Errorf("Scan() = %v, want %v", got, want)
230 }
231 }
232
233 func BenchmarkNew(b *testing.B) {
234 b.RunParallel(func(pb *testing.PB) {
235 for pb.Next() {
236 _ = New()
237 }
238 })
239 }
240
241 func BenchmarkNewString(b *testing.B) {
242 b.RunParallel(func(pb *testing.PB) {
243 for pb.Next() {
244 _ = New().String()
245 }
246 })
247 }
248
249 func BenchmarkFromString(b *testing.B) {
250 b.RunParallel(func(pb *testing.PB) {
251 for pb.Next() {
252 _, _ = FromString("9m4e2mr0ui3e8a215n4g")
253 }
254 })
255 }
256
257 func TestFromStringQuick(t *testing.T) {
258 f := func(id1 ID, c byte) bool {
259 s1 := id1.String()
260 for i := range s1 {
261 s2 := []byte(s1)
262 s2[i] = c
263 id2, err := FromString(string(s2))
264 if id1 == id2 && err == nil && c != s1[i] {
265 t.Logf("comparing XIDs:\na: %q\nb: %q (index %d changed to %c)", s1, s2, i, c)
266 return false
267 }
268 }
269 return true
270 }
271 err := quick.Check(f, &quick.Config{
272 Values: func(args []reflect.Value, r *rand.Rand) {
273 i := r.Intn(len(encoding))
274 args[0] = reflect.ValueOf(New())
275 args[1] = reflect.ValueOf(byte(encoding[i]))
276 },
277 MaxCount: 1000,
278 })
279 if err != nil {
280 t.Error(err)
281 }
282 }
283
284 func TestFromStringQuickInvalidChars(t *testing.T) {
285 f := func(id1 ID, c byte) bool {
286 s1 := id1.String()
287 for i := range s1 {
288 s2 := []byte(s1)
289 s2[i] = c
290 id2, err := FromString(string(s2))
291 if id1 == id2 && err == nil && c != s1[i] {
292 t.Logf("comparing XIDs:\na: %q\nb: %q (index %d changed to %c)", s1, s2, i, c)
293 return false
294 }
295 }
296 return true
297 }
298 err := quick.Check(f, &quick.Config{
299 Values: func(args []reflect.Value, r *rand.Rand) {
300 i := r.Intn(0xFF)
301 args[0] = reflect.ValueOf(New())
302 args[1] = reflect.ValueOf(byte(i))
303 },
304 MaxCount: 2000,
305 })
306 if err != nil {
307 t.Error(err)
308 }
309 }
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327 func TestID_IsNil(t *testing.T) {
328 tests := []struct {
329 name string
330 id ID
331 want bool
332 }{
333 {
334 name: "ID not nil",
335 id: New(),
336 want: false,
337 },
338 {
339 name: "Nil ID",
340 id: ID{},
341 want: true,
342 },
343 }
344 for _, tt := range tests {
345 tt := tt
346 t.Run(tt.name, func(t *testing.T) {
347 if got, want := tt.id.IsNil(), tt.want; got != want {
348 t.Errorf("IsNil() = %v, want %v", got, want)
349 }
350 })
351 }
352 }
353
354 func TestNilID(t *testing.T) {
355 got := ID{}
356 if want := NilID(); !reflect.DeepEqual(got, want) {
357 t.Error("NilID() not equal ID{}")
358 }
359 }
360
361 func TestNilID_IsNil(t *testing.T) {
362 if !NilID().IsNil() {
363 t.Error("NilID().IsNil() is not true")
364 }
365 }
366
367 func TestFromBytes_Invariant(t *testing.T) {
368 want := New()
369 got, err := FromBytes(want.Bytes())
370 if err != nil {
371 t.Fatal(err)
372 }
373 if got.Compare(want) != 0 {
374 t.Error("FromBytes(id.Bytes()) != id")
375 }
376 }
377
378 func TestFromBytes_InvalidBytes(t *testing.T) {
379 cases := []struct {
380 length int
381 shouldFail bool
382 }{
383 {11, true},
384 {12, false},
385 {13, true},
386 }
387 for _, c := range cases {
388 b := make([]byte, c.length)
389 _, err := FromBytes(b)
390 if got, want := err != nil, c.shouldFail; got != want {
391 t.Errorf("FromBytes() error got %v, want %v", got, want)
392 }
393 }
394 }
395
396 func TestID_Compare(t *testing.T) {
397 pairs := []struct {
398 left ID
399 right ID
400 expected int
401 }{
402 {IDs[1].id, IDs[0].id, -1},
403 {ID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, IDs[2].id, -1},
404 {IDs[0].id, IDs[0].id, 0},
405 }
406 for _, p := range pairs {
407 if p.expected != p.left.Compare(p.right) {
408 t.Errorf("%s Compare to %s should return %d", p.left, p.right, p.expected)
409 }
410 if -1*p.expected != p.right.Compare(p.left) {
411 t.Errorf("%s Compare to %s should return %d", p.right, p.left, -1*p.expected)
412 }
413 }
414 }
415
416 var IDList = []ID{IDs[0].id, IDs[1].id, IDs[2].id}
417
418 func TestSorter_Len(t *testing.T) {
419 if got, want := sorter([]ID{}).Len(), 0; got != want {
420 t.Errorf("Len() %v, want %v", got, want)
421 }
422 if got, want := sorter(IDList).Len(), 3; got != want {
423 t.Errorf("Len() %v, want %v", got, want)
424 }
425 }
426
427 func TestSorter_Less(t *testing.T) {
428 sorter := sorter(IDList)
429 if !sorter.Less(1, 0) {
430 t.Errorf("Less(1, 0) not true")
431 }
432 if sorter.Less(2, 1) {
433 t.Errorf("Less(2, 1) true")
434 }
435 if sorter.Less(0, 0) {
436 t.Errorf("Less(0, 0) true")
437 }
438 }
439
440 func TestSorter_Swap(t *testing.T) {
441 ids := make([]ID, 0)
442 ids = append(ids, IDList...)
443 sorter := sorter(ids)
444 sorter.Swap(0, 1)
445 if got, want := ids[0], IDList[1]; !reflect.DeepEqual(got, want) {
446 t.Error("ids[0] != IDList[1]")
447 }
448 if got, want := ids[1], IDList[0]; !reflect.DeepEqual(got, want) {
449 t.Error("ids[1] != IDList[0]")
450 }
451 sorter.Swap(2, 2)
452 if got, want := ids[2], IDList[2]; !reflect.DeepEqual(got, want) {
453 t.Error("ids[2], IDList[2]")
454 }
455 }
456
457 func TestSort(t *testing.T) {
458 ids := make([]ID, 0)
459 ids = append(ids, IDList...)
460 Sort(ids)
461 if got, want := ids, []ID{IDList[1], IDList[2], IDList[0]}; !reflect.DeepEqual(got, want) {
462 t.Fail()
463 }
464 }
465
View as plain text