479 lines
9.3 KiB
Go
479 lines
9.3 KiB
Go
|
package orderedmap
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"math/rand"
|
||
|
"strconv"
|
||
|
"sync"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/spf13/cast"
|
||
|
)
|
||
|
|
||
|
func TestOrderedMap(t *testing.T) {
|
||
|
o := New()
|
||
|
// number
|
||
|
o.Set("number", 3)
|
||
|
v, _ := o.Get("number")
|
||
|
if v.(int) != 3 {
|
||
|
t.Error("Set number")
|
||
|
}
|
||
|
// string
|
||
|
o.Set("string", "x")
|
||
|
v, _ = o.Get("string")
|
||
|
if v.(string) != "x" {
|
||
|
t.Error("Set string")
|
||
|
}
|
||
|
// string slice
|
||
|
o.Set("strings", []string{
|
||
|
"t",
|
||
|
"u",
|
||
|
})
|
||
|
v, _ = o.Get("strings")
|
||
|
if v.([]string)[0] != "t" {
|
||
|
t.Error("Set strings first index")
|
||
|
}
|
||
|
if v.([]string)[1] != "u" {
|
||
|
t.Error("Set strings second index")
|
||
|
}
|
||
|
// mixed slice
|
||
|
o.Set("mixed", []interface{}{
|
||
|
1,
|
||
|
"1",
|
||
|
})
|
||
|
v, _ = o.Get("mixed")
|
||
|
if v.([]interface{})[0].(int) != 1 {
|
||
|
t.Error("Set mixed int")
|
||
|
}
|
||
|
if v.([]interface{})[1].(string) != "1" {
|
||
|
t.Error("Set mixed string")
|
||
|
}
|
||
|
// overriding existing key
|
||
|
o.Set("number", 4)
|
||
|
v, _ = o.Get("number")
|
||
|
if v.(int) != 4 {
|
||
|
t.Error("Override existing key")
|
||
|
}
|
||
|
// Keys method
|
||
|
keys := o.Keys()
|
||
|
expectedKeys := []string{
|
||
|
"number",
|
||
|
"string",
|
||
|
"strings",
|
||
|
"mixed",
|
||
|
}
|
||
|
for i := range keys {
|
||
|
if keys[i] != expectedKeys[i] {
|
||
|
t.Error("Keys method", keys[i], "!=", expectedKeys[i])
|
||
|
}
|
||
|
}
|
||
|
for i := range expectedKeys {
|
||
|
if keys[i] != expectedKeys[i] {
|
||
|
t.Error("Keys method", keys[i], "!=", expectedKeys[i])
|
||
|
}
|
||
|
}
|
||
|
// delete
|
||
|
o.Delete("strings")
|
||
|
o.Delete("not a key being used")
|
||
|
if len(o.Keys()) != 3 {
|
||
|
t.Error("Delete method")
|
||
|
}
|
||
|
_, ok := o.Get("strings")
|
||
|
if ok {
|
||
|
t.Error("Delete did not remove 'strings' key")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestBlankMarshalJSON(t *testing.T) {
|
||
|
o := New()
|
||
|
// blank map
|
||
|
b, err := json.Marshal(o)
|
||
|
if err != nil {
|
||
|
t.Error("Marshalling blank map to json", err)
|
||
|
}
|
||
|
s := string(b)
|
||
|
// check json is correctly ordered
|
||
|
if s != `{}` {
|
||
|
t.Error("JSON Marshaling blank map value is incorrect", s)
|
||
|
}
|
||
|
// convert to indented json
|
||
|
bi, err := json.MarshalIndent(o, "", " ")
|
||
|
if err != nil {
|
||
|
t.Error("Marshalling indented json for blank map", err)
|
||
|
}
|
||
|
si := string(bi)
|
||
|
ei := `{}`
|
||
|
if si != ei {
|
||
|
fmt.Println(ei)
|
||
|
fmt.Println(si)
|
||
|
t.Error("JSON MarshalIndent blank map value is incorrect", si)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestMarshalJSON(t *testing.T) {
|
||
|
o := New()
|
||
|
// number
|
||
|
o.Set("number", 3)
|
||
|
// string
|
||
|
o.Set("string", "x")
|
||
|
// new value keeps key in old position
|
||
|
o.Set("number", 4)
|
||
|
// keys not sorted alphabetically
|
||
|
o.Set("z", 1)
|
||
|
o.Set("a", 2)
|
||
|
o.Set("b", 3)
|
||
|
// slice
|
||
|
o.Set("slice", []interface{}{
|
||
|
"1",
|
||
|
1,
|
||
|
})
|
||
|
// orderedmap
|
||
|
v := New()
|
||
|
v.Set("e", 1)
|
||
|
v.Set("a", 2)
|
||
|
o.Set("orderedmap", v)
|
||
|
// double quote in key
|
||
|
o.Set(`test"ing`, 9)
|
||
|
// convert to json
|
||
|
b, err := json.Marshal(o)
|
||
|
if err != nil {
|
||
|
t.Error("Marshalling json", err)
|
||
|
}
|
||
|
s := string(b)
|
||
|
// check json is correctly ordered
|
||
|
if s != `{"number":4,"string":"x","z":1,"a":2,"b":3,"slice":["1",1],"orderedmap":{"e":1,"a":2},"test\"ing":9}` {
|
||
|
t.Error("JSON Marshal value is incorrect", s)
|
||
|
}
|
||
|
// convert to indented json
|
||
|
bi, err := json.MarshalIndent(o, "", " ")
|
||
|
if err != nil {
|
||
|
t.Error("Marshalling indented json", err)
|
||
|
}
|
||
|
si := string(bi)
|
||
|
ei := `{
|
||
|
"number": 4,
|
||
|
"string": "x",
|
||
|
"z": 1,
|
||
|
"a": 2,
|
||
|
"b": 3,
|
||
|
"slice": [
|
||
|
"1",
|
||
|
1
|
||
|
],
|
||
|
"orderedmap": {
|
||
|
"e": 1,
|
||
|
"a": 2
|
||
|
},
|
||
|
"test\"ing": 9
|
||
|
}`
|
||
|
if si != ei {
|
||
|
fmt.Println(ei)
|
||
|
fmt.Println(si)
|
||
|
t.Error("JSON MarshalIndent value is incorrect", si)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestUnmarshalJSON(t *testing.T) {
|
||
|
s := `{
|
||
|
"number": 4,
|
||
|
"string": "x",
|
||
|
"z": 1,
|
||
|
"a": "should not break with unclosed { character in value",
|
||
|
"b": 3,
|
||
|
"slice": [
|
||
|
"1",
|
||
|
1
|
||
|
],
|
||
|
"orderedmap": {
|
||
|
"e": 1,
|
||
|
"a { nested key with brace": "with a }}}} }} {{{ brace value",
|
||
|
"after": {
|
||
|
"link": "test {{{ with even deeper nested braces }"
|
||
|
}
|
||
|
},
|
||
|
"test\"ing": 9,
|
||
|
"after": 1,
|
||
|
"multitype_array": [
|
||
|
"test",
|
||
|
1,
|
||
|
{ "map": "obj", "it" : 5, ":colon in key": "colon: in value" }
|
||
|
],
|
||
|
"should not break with { character in key": 1
|
||
|
}`
|
||
|
o := New()
|
||
|
err := json.Unmarshal([]byte(s), &o)
|
||
|
if err != nil {
|
||
|
t.Error("JSON Unmarshal error", err)
|
||
|
}
|
||
|
// Check the root keys
|
||
|
expectedKeys := []string{
|
||
|
"number",
|
||
|
"string",
|
||
|
"z",
|
||
|
"a",
|
||
|
"b",
|
||
|
"slice",
|
||
|
"orderedmap",
|
||
|
"test\"ing",
|
||
|
"after",
|
||
|
"multitype_array",
|
||
|
"should not break with { character in key",
|
||
|
}
|
||
|
k := o.Keys()
|
||
|
for i := range k {
|
||
|
if k[i] != expectedKeys[i] {
|
||
|
t.Error("Unmarshal root key order", i, k[i], "!=", expectedKeys[i])
|
||
|
}
|
||
|
}
|
||
|
// Check nested maps are converted to orderedmaps
|
||
|
// nested 1 level deep
|
||
|
expectedKeys = []string{
|
||
|
"e",
|
||
|
"a { nested key with brace",
|
||
|
"after",
|
||
|
}
|
||
|
vi, ok := o.Get("orderedmap")
|
||
|
if !ok {
|
||
|
t.Error("Missing key for nested map 1 deep")
|
||
|
}
|
||
|
v := vi.(*Map)
|
||
|
k = v.Keys()
|
||
|
for i := range k {
|
||
|
if k[i] != expectedKeys[i] {
|
||
|
t.Error("Key order for nested map 1 deep ", i, k[i], "!=", expectedKeys[i])
|
||
|
}
|
||
|
}
|
||
|
// nested 2 levels deep
|
||
|
expectedKeys = []string{
|
||
|
"link",
|
||
|
}
|
||
|
vi, ok = v.Get("after")
|
||
|
if !ok {
|
||
|
t.Error("Missing key for nested map 2 deep")
|
||
|
}
|
||
|
v = vi.(*Map)
|
||
|
k = v.Keys()
|
||
|
for i := range k {
|
||
|
if k[i] != expectedKeys[i] {
|
||
|
t.Error("Key order for nested map 2 deep", i, k[i], "!=", expectedKeys[i])
|
||
|
}
|
||
|
}
|
||
|
// multitype array
|
||
|
expectedKeys = []string{
|
||
|
"map",
|
||
|
"it",
|
||
|
":colon in key",
|
||
|
}
|
||
|
vislice, ok := o.Get("multitype_array")
|
||
|
if !ok {
|
||
|
t.Error("Missing key for multitype array")
|
||
|
}
|
||
|
vslice := vislice.([]interface{})
|
||
|
vmap := vslice[2].(*Map)
|
||
|
k = vmap.Keys()
|
||
|
for i := range k {
|
||
|
if k[i] != expectedKeys[i] {
|
||
|
t.Error("Key order for nested map 2 deep", i, k[i], "!=", expectedKeys[i])
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestUnmarshalJSONSpecialChars(t *testing.T) {
|
||
|
s := `{ " \\\\\\\\\\\\ " : { "\\\\\\" : "\\\\\"\\" }, "\\": " \\\\ test " }`
|
||
|
o := New()
|
||
|
err := json.Unmarshal([]byte(s), &o)
|
||
|
if err != nil {
|
||
|
t.Error("JSON Unmarshal error with special chars", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestUnmarshalJSONArrayOfMaps(t *testing.T) {
|
||
|
s := `
|
||
|
{
|
||
|
"name": "test",
|
||
|
"percent": 6,
|
||
|
"breakdown": [
|
||
|
{
|
||
|
"name": "a",
|
||
|
"percent": 0.9
|
||
|
},
|
||
|
{
|
||
|
"name": "b",
|
||
|
"percent": 0.9
|
||
|
},
|
||
|
{
|
||
|
"name": "d",
|
||
|
"percent": 0.4
|
||
|
},
|
||
|
{
|
||
|
"name": "e",
|
||
|
"percent": 2.7
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
`
|
||
|
o := New()
|
||
|
err := json.Unmarshal([]byte(s), &o)
|
||
|
if err != nil {
|
||
|
t.Error("JSON Unmarshal error", err)
|
||
|
}
|
||
|
// Check the root keys
|
||
|
expectedKeys := []string{
|
||
|
"name",
|
||
|
"percent",
|
||
|
"breakdown",
|
||
|
}
|
||
|
k := o.Keys()
|
||
|
for i := range k {
|
||
|
if k[i] != expectedKeys[i] {
|
||
|
t.Error("Unmarshal root key order", i, k[i], "!=", expectedKeys[i])
|
||
|
}
|
||
|
}
|
||
|
// Check nested maps are converted to orderedmaps
|
||
|
// nested 1 level deep
|
||
|
expectedKeys = []string{
|
||
|
"name",
|
||
|
"percent",
|
||
|
}
|
||
|
vi, ok := o.Get("breakdown")
|
||
|
if !ok {
|
||
|
t.Error("Missing key for nested map 1 deep")
|
||
|
}
|
||
|
vs := vi.([]interface{})
|
||
|
for _, vInterface := range vs {
|
||
|
v := vInterface.(*Map)
|
||
|
k = v.Keys()
|
||
|
for i := range k {
|
||
|
if k[i] != expectedKeys[i] {
|
||
|
t.Error("Key order for nested map 1 deep ", i, k[i], "!=", expectedKeys[i])
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestInsertAt(t *testing.T) {
|
||
|
om := New()
|
||
|
om.Set("zero", 0)
|
||
|
om.Set("one", 1)
|
||
|
om.Set("two", 2)
|
||
|
|
||
|
err := om.InsertAt("TEST", 10000, 4) //3 is this added one in size of map
|
||
|
if err == nil {
|
||
|
t.Error("expected insert at greater position than size of map to produce error")
|
||
|
}
|
||
|
|
||
|
err = om.InsertAt("A", 100, 2)
|
||
|
if err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
// Test it's at end
|
||
|
if om.values[om.keys[2]] != 100 {
|
||
|
t.Error("expected entry A to be at position 2", om.keys)
|
||
|
}
|
||
|
if om.values[om.keys[3]] != 2 {
|
||
|
t.Error("expected two to be in position 1", om.keys)
|
||
|
}
|
||
|
|
||
|
err = om.InsertAt("B", 200, 0)
|
||
|
if err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
|
||
|
if om.values[om.keys[0]] != 200 {
|
||
|
t.Error("expected B to be position 0", om.keys)
|
||
|
}
|
||
|
|
||
|
err = om.InsertAt("C", 300, -1)
|
||
|
if err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
|
||
|
// Should show up at the end
|
||
|
if om.values[om.keys[len(om.keys)-1]] != 300 {
|
||
|
t.Error(fmt.Sprintf("expected C to be in position %d", len(om.keys)-1), om.keys)
|
||
|
}
|
||
|
|
||
|
err = om.InsertAt("D", 400, 1)
|
||
|
if err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
|
||
|
if om.values[om.keys[1]] != 400 {
|
||
|
t.Error("expceted D to be position 1", om.keys)
|
||
|
}
|
||
|
|
||
|
err = om.InsertAt("F", 600, -8)
|
||
|
if err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
if om.values[om.keys[0]] != 600 {
|
||
|
t.Error("expected F to be in position 0", om.keys)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestConcurrency(t *testing.T) {
|
||
|
wg := sync.WaitGroup{}
|
||
|
type concurrency struct {
|
||
|
a string
|
||
|
b int
|
||
|
c time.Time
|
||
|
d bool
|
||
|
}
|
||
|
|
||
|
//Starting Map
|
||
|
m := New()
|
||
|
m.Set("A", concurrency{"string", 10, time.Now(), true})
|
||
|
m.Set("B", concurrency{"string", 10, time.Now(), true})
|
||
|
m.Set("C", concurrency{"string", 10, time.Now(), true})
|
||
|
m.Set("D", concurrency{"string", 10, time.Now(), true})
|
||
|
m.Set("E", concurrency{"string", 10, time.Now(), true})
|
||
|
m.Set("F", concurrency{"string", 10, time.Now(), true})
|
||
|
m.Set("G", concurrency{"string", 10, time.Now(), true})
|
||
|
m.Set("H", concurrency{"string", 10, time.Now(), true})
|
||
|
//Inserts
|
||
|
wg.Add(1)
|
||
|
go func() {
|
||
|
defer wg.Done()
|
||
|
for i := 0; i < 50; i++ {
|
||
|
wg.Add(1)
|
||
|
go func(index int) {
|
||
|
defer wg.Done()
|
||
|
m.Set("New"+strconv.Itoa(index), concurrency{"string", index, time.Now(), cast.ToBool(index % 2)})
|
||
|
}(i)
|
||
|
}
|
||
|
}()
|
||
|
//Reads
|
||
|
wg.Add(1)
|
||
|
go func() {
|
||
|
defer wg.Done()
|
||
|
for i := 0; i < 50; i++ {
|
||
|
wg.Add(1)
|
||
|
go func(index int) {
|
||
|
defer wg.Done()
|
||
|
_, _ = m.Get("New" + strconv.Itoa(rand.Intn(99)))
|
||
|
}(i)
|
||
|
}
|
||
|
}()
|
||
|
//Marshalling like endpoint
|
||
|
wg.Add(1)
|
||
|
go func() {
|
||
|
defer wg.Done()
|
||
|
|
||
|
for i := 0; i < 50; i++ {
|
||
|
wg.Add(1)
|
||
|
go func(index int) {
|
||
|
defer wg.Done()
|
||
|
_, err := m.MarshalJSON()
|
||
|
if err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
}(i)
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
wg.Wait()
|
||
|
|
||
|
}
|