1// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
2// Use of this source code is governed by a MIT license found in the LICENSE file.
3
4package codec
5
6import (
7	"bufio"
8	"bytes"
9	"encoding/hex"
10	"math"
11	"os"
12	"regexp"
13	"strings"
14	"testing"
15)
16
17func TestCborIndefiniteLength(t *testing.T) {
18	oldMapType := testCborH.MapType
19	defer func() {
20		testCborH.MapType = oldMapType
21	}()
22	testCborH.MapType = testMapStrIntfTyp
23	// var (
24	// 	M1 map[string][]byte
25	// 	M2 map[uint64]bool
26	// 	L1 []interface{}
27	// 	S1 []string
28	// 	B1 []byte
29	// )
30	var v, vv interface{}
31	// define it (v), encode it using indefinite lengths, decode it (vv), compare v to vv
32	v = map[string]interface{}{
33		"one-byte-key":   []byte{1, 2, 3, 4, 5, 6},
34		"two-string-key": "two-value",
35		"three-list-key": []interface{}{true, false, uint64(1), int64(-1)},
36	}
37	var buf bytes.Buffer
38	// buf.Reset()
39	e := NewEncoder(&buf, testCborH)
40	buf.WriteByte(cborBdIndefiniteMap)
41	//----
42	buf.WriteByte(cborBdIndefiniteString)
43	e.MustEncode("one-")
44	e.MustEncode("byte-")
45	e.MustEncode("key")
46	buf.WriteByte(cborBdBreak)
47
48	buf.WriteByte(cborBdIndefiniteBytes)
49	e.MustEncode([]byte{1, 2, 3})
50	e.MustEncode([]byte{4, 5, 6})
51	buf.WriteByte(cborBdBreak)
52
53	//----
54	buf.WriteByte(cborBdIndefiniteString)
55	e.MustEncode("two-")
56	e.MustEncode("string-")
57	e.MustEncode("key")
58	buf.WriteByte(cborBdBreak)
59
60	buf.WriteByte(cborBdIndefiniteString)
61	e.MustEncode([]byte("two-")) // encode as bytes, to check robustness of code
62	e.MustEncode([]byte("value"))
63	buf.WriteByte(cborBdBreak)
64
65	//----
66	buf.WriteByte(cborBdIndefiniteString)
67	e.MustEncode("three-")
68	e.MustEncode("list-")
69	e.MustEncode("key")
70	buf.WriteByte(cborBdBreak)
71
72	buf.WriteByte(cborBdIndefiniteArray)
73	e.MustEncode(true)
74	e.MustEncode(false)
75	e.MustEncode(uint64(1))
76	e.MustEncode(int64(-1))
77	buf.WriteByte(cborBdBreak)
78
79	buf.WriteByte(cborBdBreak) // close map
80
81	NewDecoderBytes(buf.Bytes(), testCborH).MustDecode(&vv)
82	if err := deepEqual(v, vv); err != nil {
83		logT(t, "-------- Before and After marshal do not match: Error: %v", err)
84		logT(t, "    ....... GOLDEN:  (%T) %#v", v, v)
85		logT(t, "    ....... DECODED: (%T) %#v", vv, vv)
86		failT(t)
87	}
88}
89
90type testCborGolden struct {
91	Base64     string      `codec:"cbor"`
92	Hex        string      `codec:"hex"`
93	Roundtrip  bool        `codec:"roundtrip"`
94	Decoded    interface{} `codec:"decoded"`
95	Diagnostic string      `codec:"diagnostic"`
96	Skip       bool        `codec:"skip"`
97}
98
99// Some tests are skipped because they include numbers outside the range of int64/uint64
100func doTestCborGoldens(t *testing.T) {
101	oldMapType := testCborH.MapType
102	defer func() {
103		testCborH.MapType = oldMapType
104	}()
105	testCborH.MapType = testMapStrIntfTyp
106	// decode test-cbor-goldens.json into a list of []*testCborGolden
107	// for each one,
108	// - decode hex into []byte bs
109	// - decode bs into interface{} v
110	// - compare both using deepequal
111	// - for any miss, record it
112	var gs []*testCborGolden
113	f, err := os.Open("test-cbor-goldens.json")
114	if err != nil {
115		logT(t, "error opening test-cbor-goldens.json: %v", err)
116		failT(t)
117	}
118	defer f.Close()
119	jh := new(JsonHandle)
120	jh.MapType = testMapStrIntfTyp
121	// d := NewDecoder(f, jh)
122	d := NewDecoder(bufio.NewReader(f), jh)
123	// err = d.Decode(&gs)
124	d.MustDecode(&gs)
125	if err != nil {
126		logT(t, "error json decoding test-cbor-goldens.json: %v", err)
127		failT(t)
128	}
129
130	tagregex := regexp.MustCompile(`[\d]+\(.+?\)`)
131	hexregex := regexp.MustCompile(`h'([0-9a-fA-F]*)'`)
132	for i, g := range gs {
133		// fmt.Printf("%v, skip: %v, isTag: %v, %s\n", i, g.Skip, tagregex.MatchString(g.Diagnostic), g.Diagnostic)
134		// skip tags or simple or those with prefix, as we can't verify them.
135		if g.Skip || strings.HasPrefix(g.Diagnostic, "simple(") || tagregex.MatchString(g.Diagnostic) {
136			// fmt.Printf("%v: skipped\n", i)
137			logT(t, "[%v] skipping because skip=true OR unsupported simple value or Tag Value", i)
138			continue
139		}
140		// println("++++++++++++", i, "g.Diagnostic", g.Diagnostic)
141		if hexregex.MatchString(g.Diagnostic) {
142			// println(i, "g.Diagnostic matched hex")
143			if s2 := g.Diagnostic[2 : len(g.Diagnostic)-1]; s2 == "" {
144				g.Decoded = zeroByteSlice
145			} else if bs2, err2 := hex.DecodeString(s2); err2 == nil {
146				g.Decoded = bs2
147			}
148			// fmt.Printf("%v: hex: %v\n", i, g.Decoded)
149		}
150		bs, err := hex.DecodeString(g.Hex)
151		if err != nil {
152			logT(t, "[%v] error hex decoding %s [%v]: %v", i, g.Hex, err)
153			failT(t)
154		}
155		var v interface{}
156		NewDecoderBytes(bs, testCborH).MustDecode(&v)
157		if _, ok := v.(RawExt); ok {
158			continue
159		}
160		// check the diagnostics to compare
161		switch g.Diagnostic {
162		case "Infinity":
163			b := math.IsInf(v.(float64), 1)
164			testCborError(t, i, math.Inf(1), v, nil, &b)
165		case "-Infinity":
166			b := math.IsInf(v.(float64), -1)
167			testCborError(t, i, math.Inf(-1), v, nil, &b)
168		case "NaN":
169			// println(i, "checking NaN")
170			b := math.IsNaN(v.(float64))
171			testCborError(t, i, math.NaN(), v, nil, &b)
172		case "undefined":
173			b := v == nil
174			testCborError(t, i, nil, v, nil, &b)
175		default:
176			v0 := g.Decoded
177			// testCborCoerceJsonNumber(reflect.ValueOf(&v0))
178			testCborError(t, i, v0, v, deepEqual(v0, v), nil)
179		}
180	}
181}
182
183func testCborError(t *testing.T, i int, v0, v1 interface{}, err error, equal *bool) {
184	if err == nil && equal == nil {
185		// fmt.Printf("%v testCborError passed (err and equal nil)\n", i)
186		return
187	}
188	if err != nil {
189		logT(t, "[%v] deepEqual error: %v", i, err)
190		logT(t, "    ....... GOLDEN:  (%T) %#v", v0, v0)
191		logT(t, "    ....... DECODED: (%T) %#v", v1, v1)
192		failT(t)
193	}
194	if equal != nil && !*equal {
195		logT(t, "[%v] values not equal", i)
196		logT(t, "    ....... GOLDEN:  (%T) %#v", v0, v0)
197		logT(t, "    ....... DECODED: (%T) %#v", v1, v1)
198		failT(t)
199	}
200	// fmt.Printf("%v testCborError passed (checks passed)\n", i)
201}
202
203func TestCborGoldens(t *testing.T) {
204	doTestCborGoldens(t)
205}
206