Added Parsers and Tests fixes #6

This commit is contained in:
Bruce Marriner 2019-04-12 17:06:54 -05:00
parent 0684ace7ee
commit 80c99c0e83
3 changed files with 330 additions and 48 deletions

View File

@ -1,6 +1,6 @@
snowflake snowflake
==== ====
[![GoDoc](https://godoc.org/github.com/bwmarrin/snowflake?status.svg)](https://godoc.org/github.com/bwmarrin/snowflake) [![Go report](http://goreportcard.com/badge/bwmarrin/snowflake)](http://goreportcard.com/report/bwmarrin/snowflake) [![Build Status](https://travis-ci.org/bwmarrin/snowflake.svg?branch=master)](https://travis-ci.org/bwmarrin/snowflake) [![Discord Gophers](https://img.shields.io/badge/Discord%20Gophers-%23info-blue.svg)](https://discord.gg/0f1SbxBZjYq9jLBk) [![GoDoc](https://godoc.org/github.com/bwmarrin/snowflake?status.svg)](https://godoc.org/github.com/bwmarrin/snowflake) [![Go report](http://goreportcard.com/badge/bwmarrin/snowflake)](http://goreportcard.com/report/bwmarrin/snowflake) [![Coverage](http://gocover.io/_badge/github.com/bwmarrin/snowflake)](https://gocover.io/github.com/bwmarrin/snowflake) [![Build Status](https://travis-ci.org/bwmarrin/snowflake.svg?branch=master)](https://travis-ci.org/bwmarrin/snowflake) [![Discord Gophers](https://img.shields.io/badge/Discord%20Gophers-%23info-blue.svg)](https://discord.gg/0f1SbxBZjYq9jLBk)
snowflake is a [Go](https://golang.org/) package that provides snowflake is a [Go](https://golang.org/) package that provides
* A very simple Twitter snowflake generator. * A very simple Twitter snowflake generator.

View File

@ -166,25 +166,38 @@ func (f ID) Int64() int64 {
return int64(f) return int64(f)
} }
// ParseInt64 converts an int64 into a snowflake ID
func ParseInt64(id int64) ID {
return ID(id)
}
// String returns a string of the snowflake ID // String returns a string of the snowflake ID
func (f ID) String() string { func (f ID) String() string {
return strconv.FormatInt(int64(f), 10) return strconv.FormatInt(int64(f), 10)
} }
// ParseString converts a string into a snowflake ID
func ParseString(id string) (ID, error) {
i, err := strconv.ParseInt(id, 10, 64)
return ID(i), err
}
// Base2 returns a string base2 of the snowflake ID // Base2 returns a string base2 of the snowflake ID
func (f ID) Base2() string { func (f ID) Base2() string {
return strconv.FormatInt(int64(f), 2) return strconv.FormatInt(int64(f), 2)
} }
// Base36 returns a base36 string of the snowflake ID // ParseBase2 converts a Base2 string into a snowflake ID
func (f ID) Base36() string { func ParseBase2(id string) (ID, error) {
return strconv.FormatInt(int64(f), 36) i, err := strconv.ParseInt(id, 2, 64)
return ID(i), err
} }
// Base32 uses the z-base-32 character set but encodes and decodes similar // Base32 uses the z-base-32 character set but encodes and decodes similar
// to base58, allowing it to create an even smaller result string. // to base58, allowing it to create an even smaller result string.
// NOTE: There are many different base32 implementations so becareful when // NOTE: There are many different base32 implementations so becareful when
// doing any interoperation interop with other packages. // doing any interoperation.
func (f ID) Base32() string { func (f ID) Base32() string {
if f < 32 { if f < 32 {
@ -207,7 +220,7 @@ func (f ID) Base32() string {
// ParseBase32 parses a base32 []byte into a snowflake ID // ParseBase32 parses a base32 []byte into a snowflake ID
// NOTE: There are many different base32 implementations so becareful when // NOTE: There are many different base32 implementations so becareful when
// doing any interoperation interop with other packages. // doing any interoperation.
func ParseBase32(b []byte) (ID, error) { func ParseBase32(b []byte) (ID, error) {
var id int64 var id int64
@ -222,6 +235,17 @@ func ParseBase32(b []byte) (ID, error) {
return ID(id), nil return ID(id), nil
} }
// Base36 returns a base36 string of the snowflake ID
func (f ID) Base36() string {
return strconv.FormatInt(int64(f), 36)
}
// ParseBase36 converts a Base36 string into a snowflake ID
func ParseBase36(id string) (ID, error) {
i, err := strconv.ParseInt(id, 36, 64)
return ID(i), err
}
// Base58 returns a base58 string of the snowflake ID // Base58 returns a base58 string of the snowflake ID
func (f ID) Base58() string { func (f ID) Base58() string {
@ -263,11 +287,27 @@ func (f ID) Base64() string {
return base64.StdEncoding.EncodeToString(f.Bytes()) return base64.StdEncoding.EncodeToString(f.Bytes())
} }
// ParseBase64 converts a base64 string into a snowflake ID
func ParseBase64(id string) (ID, error) {
b, err := base64.StdEncoding.DecodeString(id)
if err != nil {
return -1, err
}
return ParseBytes(b)
}
// Bytes returns a byte slice of the snowflake ID // Bytes returns a byte slice of the snowflake ID
func (f ID) Bytes() []byte { func (f ID) Bytes() []byte {
return []byte(f.String()) return []byte(f.String())
} }
// ParseBytes converts a byte slice into a snowflake ID
func ParseBytes(id []byte) (ID, error) {
i, err := strconv.ParseInt(string(id), 10, 64)
return ID(i), err
}
// IntBytes returns an array of bytes of the snowflake ID, encoded as a // IntBytes returns an array of bytes of the snowflake ID, encoded as a
// big endian integer. // big endian integer.
func (f ID) IntBytes() [8]byte { func (f ID) IntBytes() [8]byte {
@ -276,6 +316,12 @@ func (f ID) IntBytes() [8]byte {
return b return b
} }
// ParseIntBytes converts an array of bytes encoded as big endian integer as
// a snowflake ID
func ParseIntBytes(id [8]byte) ID {
return ID(int64(binary.BigEndian.Uint64(id[:])))
}
// Time returns an int64 unix timestamp in milliseconds of the snowflake ID time // Time returns an int64 unix timestamp in milliseconds of the snowflake ID time
// DEPRECATED: the below function will be removed in a future release. // DEPRECATED: the below function will be removed in a future release.
func (f ID) Time() int64 { func (f ID) Time() int64 {

View File

@ -6,13 +6,17 @@ import (
"testing" "testing"
) )
// check if Generate will create duplicate IDs //******************************************************************************
// General Test funcs
// lazy check if Generate will create duplicate IDs
// would be good to later enhance this with more smarts
func TestGenerateDuplicateID(t *testing.T) { func TestGenerateDuplicateID(t *testing.T) {
node, _ := NewNode(1) node, _ := NewNode(1)
var x, y ID var x, y ID
for i := 0; i < 100000000; i++ { for i := 0; i < 1000000; i++ {
y = node.Generate() y = node.Generate()
if x == y { if x == y {
t.Errorf("x(%d) & y(%d) are the same", x, y) t.Errorf("x(%d) & y(%d) are the same", x, y)
@ -40,17 +44,282 @@ func TestRace(t *testing.T) {
} }
//******************************************************************************
// Converters/Parsers Test funcs
// We should have funcs here to test conversion both ways for everything
func TestPrintAll(t *testing.T) {
node, err := NewNode(0)
if err != nil {
t.Fatalf("error creating NewNode, %s", err)
}
id := node.Generate()
t.Logf("Int64 : %#v", id.Int64())
t.Logf("String : %#v", id.String())
t.Logf("Base2 : %#v", id.Base2())
t.Logf("Base32 : %#v", id.Base32())
t.Logf("Base36 : %#v", id.Base36())
t.Logf("Base58 : %#v", id.Base58())
t.Logf("Base64 : %#v", id.Base64())
t.Logf("Bytes : %#v", id.Bytes())
t.Logf("IntBytes : %#v", id.IntBytes())
}
func TestInt64(t *testing.T) {
node, err := NewNode(0)
if err != nil {
t.Fatalf("error creating NewNode, %s", err)
}
oID := node.Generate()
i := oID.Int64()
pID := ParseInt64(i)
if pID != oID {
t.Fatalf("pID %v != oID %v", pID, oID)
}
mi := int64(1116766490855473152)
pID = ParseInt64(mi)
if pID.Int64() != mi {
t.Fatalf("pID %v != mi %v", pID.Int64(), mi)
}
}
func TestString(t *testing.T) {
node, err := NewNode(0)
if err != nil {
t.Fatalf("error creating NewNode, %s", err)
}
oID := node.Generate()
si := oID.String()
pID, err := ParseString(si)
if err != nil {
t.Fatalf("error parsing, %s", err)
}
if pID != oID {
t.Fatalf("pID %v != oID %v", pID, oID)
}
ms := `1116766490855473152`
_, err = ParseString(ms)
if err != nil {
t.Fatalf("error parsing, %s", err)
}
ms = `1112316766490855473152`
_, err = ParseString(ms)
if err == nil {
t.Fatalf("no error parsing %s", ms)
}
}
func TestBase2(t *testing.T) {
node, err := NewNode(0)
if err != nil {
t.Fatalf("error creating NewNode, %s", err)
}
oID := node.Generate()
i := oID.Base2()
pID, err := ParseBase2(i)
if err != nil {
t.Fatalf("error parsing, %s", err)
}
if pID != oID {
t.Fatalf("pID %v != oID %v", pID, oID)
}
ms := `111101111111101110110101100101001000000000000000000000000000`
_, err = ParseBase2(ms)
if err != nil {
t.Fatalf("error parsing, %s", err)
}
ms = `1112316766490855473152`
_, err = ParseBase2(ms)
if err == nil {
t.Fatalf("no error parsing %s", ms)
}
}
func TestBase32(t *testing.T) {
node, err := NewNode(0)
if err != nil {
t.Fatalf("error creating NewNode, %s", err)
}
for i := 0; i < 100; i++ {
sf := node.Generate()
b32i := sf.Base32()
psf, err := ParseBase32([]byte(b32i))
if err != nil {
t.Fatal(err)
}
if sf != psf {
t.Fatal("Parsed does not match String.")
}
}
}
func TestBase36(t *testing.T) {
node, err := NewNode(0)
if err != nil {
t.Fatalf("error creating NewNode, %s", err)
}
oID := node.Generate()
i := oID.Base36()
pID, err := ParseBase36(i)
if err != nil {
t.Fatalf("error parsing, %s", err)
}
if pID != oID {
t.Fatalf("pID %v != oID %v", pID, oID)
}
ms := `8hgmw4blvlkw`
_, err = ParseBase36(ms)
if err != nil {
t.Fatalf("error parsing, %s", err)
}
ms = `68h5gmw443blv2lk1w`
_, err = ParseBase36(ms)
if err == nil {
t.Fatalf("no error parsing, %s", err)
}
}
func TestBase58(t *testing.T) {
node, err := NewNode(0)
if err != nil {
t.Fatalf("error creating NewNode, %s", err)
}
for i := 0; i < 10; i++ {
sf := node.Generate()
b58 := sf.Base58()
psf, err := ParseBase58([]byte(b58))
if err != nil {
t.Fatal(err)
}
if sf != psf {
t.Fatal("Parsed does not match String.")
}
}
}
func TestBase64(t *testing.T) {
node, err := NewNode(0)
if err != nil {
t.Fatalf("error creating NewNode, %s", err)
}
oID := node.Generate()
i := oID.Base64()
pID, err := ParseBase64(i)
if err != nil {
t.Fatalf("error parsing, %s", err)
}
if pID != oID {
t.Fatalf("pID %v != oID %v", pID, oID)
}
ms := `MTExNjgxOTQ5NDY2MDk5NzEyMA==`
_, err = ParseBase64(ms)
if err != nil {
t.Fatalf("error parsing, %s", err)
}
ms = `MTExNjgxOTQ5NDY2MDk5NzEyMA`
_, err = ParseBase64(ms)
if err == nil {
t.Fatalf("no error parsing, %s", err)
}
}
func TestBytes(t *testing.T) {
node, err := NewNode(0)
if err != nil {
t.Fatalf("error creating NewNode, %s", err)
}
oID := node.Generate()
i := oID.Bytes()
pID, err := ParseBytes(i)
if err != nil {
t.Fatalf("error parsing, %s", err)
}
if pID != oID {
t.Fatalf("pID %v != oID %v", pID, oID)
}
ms := []byte{0x31, 0x31, 0x31, 0x36, 0x38, 0x32, 0x31, 0x36, 0x37, 0x39, 0x35, 0x37, 0x30, 0x34, 0x31, 0x39, 0x37, 0x31, 0x32}
_, err = ParseBytes(ms)
if err != nil {
t.Fatalf("error parsing, %#v", err)
}
ms = []byte{0xFF, 0xFF, 0xFF, 0x31, 0x31, 0x31, 0x36, 0x38, 0x32, 0x31, 0x36, 0x37, 0x39, 0x35, 0x37, 0x30, 0x34, 0x31, 0x39, 0x37, 0x31, 0x32}
_, err = ParseBytes(ms)
if err == nil {
t.Fatalf("no error parsing, %#v", err)
}
}
func TestIntBytes(t *testing.T) {
node, err := NewNode(0)
if err != nil {
t.Fatalf("error creating NewNode, %s", err)
}
oID := node.Generate()
i := oID.IntBytes()
pID := ParseIntBytes(i)
if pID != oID {
t.Fatalf("pID %v != oID %v", pID, oID)
}
ms := [8]uint8{0xf, 0x7f, 0xc0, 0xfc, 0x2f, 0x80, 0x0, 0x0}
mi := int64(1116823421972381696)
pID = ParseIntBytes(ms)
if pID.Int64() != mi {
t.Fatalf("pID %v != mi %v", pID.Int64(), mi)
}
}
//******************************************************************************
// Marshall Test Methods
func TestMarshalJSON(t *testing.T) { func TestMarshalJSON(t *testing.T) {
id := ID(13587) id := ID(13587)
expected := "\"13587\"" expected := "\"13587\""
bytes, err := id.MarshalJSON() bytes, err := id.MarshalJSON()
if err != nil { if err != nil {
t.Error("Unexpected error during MarshalJSON") t.Fatalf("Unexpected error during MarshalJSON")
} }
if string(bytes) != expected { if string(bytes) != expected {
t.Errorf("Got %s, expected %s", string(bytes), expected) t.Fatalf("Got %s, expected %s", string(bytes), expected)
} }
} }
@ -58,7 +327,7 @@ func TestMarshalsIntBytes(t *testing.T) {
id := ID(13587).IntBytes() id := ID(13587).IntBytes()
expected := []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x35, 0x13} expected := []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x35, 0x13}
if !bytes.Equal(id[:], expected) { if !bytes.Equal(id[:], expected) {
t.Errorf("Expected ID to be encoded as %v, got %v", expected, id) t.Fatalf("Expected ID to be encoded as %v, got %v", expected, id)
} }
} }
@ -77,32 +346,17 @@ func TestUnmarshalJSON(t *testing.T) {
var id ID var id ID
err := id.UnmarshalJSON([]byte(tc.json)) err := id.UnmarshalJSON([]byte(tc.json))
if !reflect.DeepEqual(err, tc.expectedErr) { if !reflect.DeepEqual(err, tc.expectedErr) {
t.Errorf("Expected to get error '%s' decoding JSON, but got '%s'", tc.expectedErr, err) t.Fatalf("Expected to get error '%s' decoding JSON, but got '%s'", tc.expectedErr, err)
} }
if id != tc.expectedID { if id != tc.expectedID {
t.Errorf("Expected to get ID '%s' decoding JSON, but got '%s'", tc.expectedID, id) t.Fatalf("Expected to get ID '%s' decoding JSON, but got '%s'", tc.expectedID, id)
} }
} }
} }
func TestBase32(t *testing.T) { // ****************************************************************************
// Benchmark Methods
node, _ := NewNode(1)
for i := 0; i < 100; i++ {
sf := node.Generate()
b32i := sf.Base32()
psf, err := ParseBase32([]byte(b32i))
if err != nil {
t.Fatal(err)
}
if sf != psf {
t.Fatal("Parsed does not match String.")
}
}
}
func BenchmarkParseBase32(b *testing.B) { func BenchmarkParseBase32(b *testing.B) {
@ -129,24 +383,6 @@ func BenchmarkBase32(b *testing.B) {
sf.Base32() sf.Base32()
} }
} }
func TestBase58(t *testing.T) {
node, _ := NewNode(1)
for i := 0; i < 10; i++ {
sf := node.Generate()
b58 := sf.Base58()
psf, err := ParseBase58([]byte(b58))
if err != nil {
t.Fatal(err)
}
if sf != psf {
t.Fatal("Parsed does not match String.")
}
}
}
func BenchmarkParseBase58(b *testing.B) { func BenchmarkParseBase58(b *testing.B) {
node, _ := NewNode(1) node, _ := NewNode(1)