github.com/urso/go-structform@v0.0.2/gotype/unfold_test.go (about)

     1  package gotype
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/urso/go-structform/json"
     9  )
    10  
    11  var unfoldSamples = []struct {
    12  	json  string
    13  	input interface{}
    14  	value interface{}
    15  }{
    16  
    17  	// primitives
    18  	{`null`, nil, new(interface{})},
    19  	{`""`, nil, new(string)},
    20  	{`true`, true, new(bool)},
    21  	{`true`, true, new(interface{})},
    22  	{`false`, false, new(bool)},
    23  	{`false`, false, new(interface{})},
    24  	{`null`, nil, new(*int)},
    25  	{`10`, int8(10), new(int8)},
    26  	{`10`, int8(10), new(int)},
    27  	{`10`, int8(10), new(*int8)},
    28  	{`10`, int8(10), new(*int)},
    29  	{`10`, int8(10), new(interface{})},
    30  	{`10`, int32(10), new(int64)},
    31  	{`10`, int32(10), new(int16)},
    32  	{`10`, int32(10), new(interface{})},
    33  	{`10`, int(10), new(int)},
    34  	{`10`, int(10), new(uint)},
    35  	{`10`, uint(10), new(uint)},
    36  	{`10`, uint(10), new(uint16)},
    37  	{`10`, uint(10), new(interface{})},
    38  	{`10`, uint8(10), new(int)},
    39  	{`10`, uint16(10), new(uint64)},
    40  	{`10`, uint32(10), new(uint8)},
    41  	{`12340`, uint16(12340), new(uint16)},
    42  	{`12340`, uint16(12340), new(interface{})},
    43  	{`1234567`, uint32(1234567), new(uint32)},
    44  	{`1234567`, uint32(1234567), new(int64)},
    45  	{`12345678190`, uint64(12345678190), new(uint64)},
    46  	{`12345678190`, uint64(12345678190), new(int64)},
    47  	{`-10`, int8(-10), new(int)},
    48  	{`-10`, int8(-10), new(int8)},
    49  	{`-10`, int8(-10), new(int32)},
    50  	{`-10`, int8(-10), new(int64)},
    51  	{`-10`, int8(-10), new(interface{})},
    52  	{`3.14`, float32(3.14), new(float32)},
    53  	{`3.14`, float32(3.14), new(interface{})},
    54  	{`3.14`, float64(3.14), new(float32)},
    55  	{`3.14`, float64(3.14), new(float64)},
    56  	{`3.14`, float64(3.14), new(interface{})},
    57  	{`"test"`, "test", new(string)},
    58  	{`"test"`, "test", new(interface{})},
    59  	{`"test with \" being escaped"`, "test with \" being escaped", new(string)},
    60  	{`"test with \" being escaped"`, "test with \" being escaped", new(interface{})},
    61  
    62  	// arrays
    63  	{`[]`, []uint8{}, new(interface{})},
    64  	{`[]`, []uint8{}, new([]uint8)},
    65  	{`[]`, []interface{}{}, new([]interface{})},
    66  	// {`[]`, []interface{}{}, &[]struct{ A string }{}},
    67  	{`[1,2,3]`, []uint8{1, 2, 3}, new(interface{})},
    68  	{`[1,2,3]`, []uint8{1, 2, 3}, &[]uint8{0, 0, 0}},
    69  	{`[1,2,3]`, []uint8{1, 2, 3}, &[]uint8{0, 0, 0, 4}},
    70  	{`[1,2,3]`, []uint8{1, 2, 3}, &[]uint8{}},
    71  	{`[1,2,3]`, []interface{}{1, 2, 3}, &[]uint8{}},
    72  	{`[1,2,3]`, []interface{}{1, 2, 3}, new([]uint8)},
    73  	{`[1,2,3]`, []int{1, 2, 3}, &[]interface{}{}},
    74  	{`[1,2,3]`, []int{1, 2, 3}, &[]interface{}{nil, nil, nil, 4}},
    75  	{`[1]`, []uint8{1}, &[]uint8{0, 2, 3}},
    76  	{`[1,2]`, []uint8{1, 2}, &[]uint8{0, 0, 3}},
    77  	{`[1,2]`, []interface{}{1, 2}, &[]uint{0, 0, 3}},
    78  	{`[1,2]`, []interface{}{1, 2}, &[]interface{}{0, 0, 3}},
    79  	{`["a","b"]`, []string{"a", "b"}, &[]interface{}{}},
    80  	{`["a","b"]`, []string{"a", "b"}, &[]interface{}{nil, nil}},
    81  	{`["a","b"]`, []string{"a", "b"}, &[]string{}},
    82  	{`["a","b"]`, []string{"a", "b"}, new([]string)},
    83  	{`[null,true,false,123,3.14,"test"]`,
    84  		[]interface{}{nil, true, false, 123, 3.14, "test"},
    85  		new(interface{})},
    86  	{`[null,true,false,123,3.14,"test"]`,
    87  		[]interface{}{nil, true, false, 123, 3.14, "test"},
    88  		&[]interface{}{}},
    89  	{`[1,2,3]`, []int{1, 2, 3}, &[]*int{}},
    90  	{`[1,null,3]`, []interface{}{1, nil, 3}, &[]*int{}},
    91  
    92  	// nested arrays
    93  	{`[[]]`, []interface{}{[]uint{}}, new(interface{})},
    94  	{`[]`, []interface{}{}, &[]interface{}{[]interface{}{1}}},
    95  	{`[]`, []interface{}{}, &[][]int{}},
    96  	{`[]`, []interface{}{}, &[][]int{{1}}},
    97  	{`[[1]]`, []interface{}{[]interface{}{1}}, &[][]int{}},
    98  	{`[[1,2,3],[4,5,6]]`,
    99  		[]interface{}{[]interface{}{1, 2, 3}, []interface{}{4, 5, 6}},
   100  		new(interface{})},
   101  	{`[[1],[4]]`,
   102  		[]interface{}{[]interface{}{1}, []interface{}{4}},
   103  		&[]interface{}{[]interface{}{0, 2, 3}}},
   104  	{`[[1],[4],[6]]`,
   105  		[]interface{}{[]interface{}{1}, []interface{}{4}, []interface{}{6}},
   106  		&[]interface{}{
   107  			[]interface{}{0, 2, 3},
   108  			[]interface{}{0, 5},
   109  		}},
   110  	{`[[1],[4],[6]]`,
   111  		[]interface{}{[]interface{}{1}, []interface{}{4}, []interface{}{6}},
   112  		&[][]int{
   113  			{0, 2, 3},
   114  			{0, 5},
   115  		},
   116  	},
   117  
   118  	// maps
   119  	{`{}`, map[string]interface{}{}, new(interface{})},
   120  	{`{}`, map[string]interface{}{}, &map[string]interface{}{}},
   121  	{`{"a":1}`, map[string]int{"a": 1}, new(interface{})},
   122  	{`{"a":1}`, map[string]int{"a": 1}, &map[string]interface{}{}},
   123  	{`{"a":1}`, map[string]int{"a": 1}, &map[string]interface{}{"a": 2}},
   124  	{`{"a":1}`, map[string]int{"a": 1}, &map[string]int{}},
   125  	{`{"a":1}`, map[string]int{"a": 1}, &map[string]int{"a": 2}},
   126  	{`{"a":1}`, struct{ A int }{1}, &map[string]interface{}{}},
   127  	{`{"a":1}`, struct{ A int }{1}, &map[string]int{}},
   128  	{`{"a":1,"b":"b","c":true}`,
   129  		map[string]interface{}{"a": 1, "b": "b", "c": true},
   130  		new(interface{}),
   131  	},
   132  
   133  	// nested maps
   134  	{`{"a":{}}`, map[string]interface{}{"a": map[string]interface{}{}}, new(interface{})},
   135  	{`{"a":{}}`,
   136  		map[string]interface{}{"a": map[string]interface{}{}},
   137  		&map[string]interface{}{}},
   138  	{`{"a":{}}`,
   139  		map[string]interface{}{"a": map[string]interface{}{}},
   140  		&map[string]interface{}{"a": nil}},
   141  	{`{"a":{}}`,
   142  		map[string]interface{}{"a": map[string]interface{}{}},
   143  		&map[string]map[string]string{"a": nil}},
   144  	{`{"0":{"a":1,"b":2,"c":3},"1":{"e":5,"f":6}}`,
   145  		map[string]map[string]int{
   146  			"0": {"a": 1, "b": 2, "c": 3},
   147  			"1": {"e": 5, "f": 6},
   148  		},
   149  		new(interface{})},
   150  	{`{"0":{"a":1,"b":2,"c":3},"1":{"e":5,"f":6}}`,
   151  		map[string]map[string]int{
   152  			"0": {"a": 1, "b": 2, "c": 3},
   153  			"1": {"e": 5, "f": 6},
   154  		},
   155  		&map[string]interface{}{}},
   156  	{`{"0":{"a":1,"b":2,"c":3},"1":{"e":5,"f":6}}`,
   157  		map[string]map[string]int{
   158  			"0": {"a": 1, "b": 2, "c": 3},
   159  			"1": {"e": 5, "f": 6},
   160  		},
   161  		&map[string]map[string]int64{}},
   162  	{`{"0":{"a":1}}`,
   163  		map[string]map[string]int{
   164  			"0": {"a": 1},
   165  		},
   166  		&map[string]interface{}{
   167  			"0": map[string]int{"b": 2, "c": 3},
   168  		}},
   169  	{`{"0":{"a":1},"1":{"e":5}}`,
   170  		map[string]map[string]int{
   171  			"0": {"a": 1},
   172  			"1": {"e": 5},
   173  		},
   174  		&map[string]interface{}{
   175  			"0": map[string]int{"b": 2, "c": 3},
   176  			"1": map[string]interface{}{"f": 6, "e": 0},
   177  		}},
   178  	{`{"0":{"a":1},"1":{"e":5}}`,
   179  		map[string]map[string]int{
   180  			"0": {"a": 1},
   181  			"1": {"e": 5},
   182  		},
   183  		&map[string]map[string]uint{
   184  			"0": {"b": 2, "c": 3},
   185  			"1": {"f": 6, "e": 0},
   186  		}},
   187  
   188  	// map in array
   189  	{`[{}]`, []interface{}{map[string]interface{}{}}, new(interface{})},
   190  	{`[{}]`, []interface{}{map[string]interface{}{}}, &[]map[string]int{}},
   191  	{`[{"a":1}]`, []map[string]int{{"a": 1}}, new(interface{})},
   192  	{`[{"a":1}]`, []map[string]int{{"a": 1}}, &[]interface{}{}},
   193  	{`[{"a":1}]`, []map[string]int{{"a": 1}}, &[]map[string]interface{}{}},
   194  	{`[{"a":1}]`, []map[string]int{{"a": 1}}, &[]map[string]interface{}{{"a": 2}}},
   195  	{`[{"a":1,"b":2}]`, []map[string]int{{"a": 1}}, &[]map[string]int{{"b": 2}}},
   196  	{`[{"a":1},{"b":2}]`, []map[string]int{{"a": 1}, {"b": 2}}, &[]map[string]int{}},
   197  	{`[{"a":1},{"b":"b"},{"c":true}]`,
   198  		[]map[string]interface{}{{"a": 1}, {"b": "b"}, {"c": true}},
   199  		new(interface{})},
   200  
   201  	// array in map
   202  	{`{"a": []}`, map[string]interface{}{"a": []int{}}, new(interface{})},
   203  	{`{"a":[1,2],"b":[3]}`, map[string][]int{"a": {1, 2}, "b": {3}}, new(interface{})},
   204  	{`{"a":[1,2],"b":[3]}`, map[string][]int{"a": {1, 2}, "b": {3}},
   205  		&map[string][]int{}},
   206  	{`{"a":[1,2],"b":[3,4,5]}`, map[string][]int{"a": {1, 2}, "b": {3, 4, 5}},
   207  		&map[string][]int{"a": {0, 2}, "b": {0}}},
   208  
   209  	// struct
   210  	{`{"a":1}`, map[string]int{"a": 1}, &struct{ A int }{}},
   211  	{`{"a":1}`, map[string]int{"a": 1}, &struct{ A *int }{}},
   212  	{`{"a": 1, "c": 2}`, map[string]int{"a": 1}, &struct{ A, b, C int }{b: 1, C: 2}},
   213  	{`{"a": {"c": 2}, "b": 1}`,
   214  		map[string]interface{}{"a": map[string]int{"c": 2}},
   215  		&struct {
   216  			A struct{ C int }
   217  			B int
   218  		}{B: 1},
   219  	},
   220  	{`{"a": 1}`,
   221  		map[string]interface{}{"a": 1},
   222  		&struct {
   223  			S struct {
   224  				A int
   225  			} `struct:",inline"`
   226  		}{},
   227  	},
   228  	{`{"a":{"b":{"c":1}}}`,
   229  		map[string]interface{}{
   230  			"a": map[string]interface{}{
   231  				"b": map[string]int{
   232  					"c": 1,
   233  				},
   234  			},
   235  		},
   236  		&struct{ A struct{ B struct{ C int } } }{},
   237  	},
   238  }
   239  
   240  func TestFoldUnfoldConsistent(t *testing.T) {
   241  	tests := unfoldSamples
   242  	for i, test := range tests {
   243  		t.Logf("run test (%v): %v (%T -> %T)", i, test.json, test.input, test.value)
   244  
   245  		u, err := NewUnfolder(test.value)
   246  		if err != nil {
   247  			t.Errorf("NewUnfolder failed with: %v", err)
   248  			continue
   249  		}
   250  
   251  		if err := Fold(test.input, u); err != nil {
   252  			t.Errorf("Fold-Unfold failed with: %v", err)
   253  			continue
   254  		}
   255  
   256  		if st := &u.unfolder; len(st.stack) > 0 {
   257  			t.Errorf("Unfolder state stack not empty: %v, %v", st.stack, st.current)
   258  			continue
   259  		}
   260  
   261  		// serialize to json
   262  		var buf bytes.Buffer
   263  		if err := Fold(test.value, json.NewVisitor(&buf)); err != nil {
   264  			t.Errorf("serialize to json failed with: %v", err)
   265  			continue
   266  		}
   267  
   268  		// compare conversions did preserve type
   269  		assertJSON(t, test.json, buf.String())
   270  	}
   271  }
   272  
   273  func TestUnfoldJsonInto(t *testing.T) {
   274  	tests := unfoldSamples
   275  	for i, test := range tests {
   276  		t.Logf("run test (%v): %v (%T -> %T)", i, test.json, test.input, test.value)
   277  
   278  		un, err := NewUnfolder(test.value)
   279  		if err != nil {
   280  			t.Fatal(err)
   281  		}
   282  
   283  		dec := json.NewParser(un)
   284  		input := test.json
   285  
   286  		err = dec.ParseString(input)
   287  		if err != nil {
   288  			t.Error(err)
   289  			continue
   290  		}
   291  
   292  		// check state valid by processing a second time
   293  		if err = un.SetTarget(test.value); err != nil {
   294  			t.Error(err)
   295  			continue
   296  		}
   297  
   298  		err = dec.ParseString(input)
   299  		if err != nil {
   300  			t.Error(err)
   301  			continue
   302  		}
   303  	}
   304  }
   305  
   306  func BenchmarkUnfoldJsonInto(b *testing.B) {
   307  	tests := unfoldSamples
   308  	for i, test := range tests {
   309  		name := fmt.Sprintf("%v (%T->%T)", test.json, test.input, test.value)
   310  		b.Logf("run test (%v): %v", i, name)
   311  
   312  		un, err := NewUnfolder(test.value)
   313  		if err != nil {
   314  			b.Fatal(err)
   315  		}
   316  
   317  		dec := json.NewParser(un)
   318  		input := test.json
   319  		// parse once to reset state for use of 'setTarget' in benchmark
   320  		if err := dec.ParseString(input); err != nil {
   321  			b.Error(err)
   322  			continue
   323  		}
   324  
   325  		b.Run(name, func(b *testing.B) {
   326  			for i := 0; i < b.N; i++ {
   327  				un.SetTarget(test.value)
   328  				err := dec.ParseString(input)
   329  				if err != nil {
   330  					b.Error(err)
   331  				}
   332  
   333  			}
   334  		})
   335  	}
   336  }