decode_test.gno

17.85 Kb ยท 554 lines
  1package json
  2
  3import (
  4	"bytes"
  5	"testing"
  6)
  7
  8type testNode struct {
  9	name  string
 10	input []byte
 11	value []byte
 12	_type ValueType
 13}
 14
 15func simpleValid(test *testNode, t *testing.T) {
 16	root, err := Unmarshal(test.input)
 17	if err != nil {
 18		t.Errorf("Error on Unmarshal(%s): %s", test.input, err.Error())
 19	} else if root == nil {
 20		t.Errorf("Error on Unmarshal(%s): root is nil", test.name)
 21	} else if root.nodeType != test._type {
 22		t.Errorf("Error on Unmarshal(%s): wrong type", test.name)
 23	} else if !bytes.Equal(root.source(), test.value) {
 24		t.Errorf("Error on Unmarshal(%s): %s != %s", test.name, root.source(), test.value)
 25	}
 26}
 27
 28func simpleInvalid(test *testNode, t *testing.T) {
 29	root, err := Unmarshal(test.input)
 30	if err == nil {
 31		t.Errorf("Error on Unmarshal(%s): error expected, got '%s'", test.name, root.source())
 32	} else if root != nil {
 33		t.Errorf("Error on Unmarshal(%s): root is not nil", test.name)
 34	}
 35}
 36
 37func simpleCorrupted(name string) *testNode {
 38	return &testNode{name: name, input: []byte(name)}
 39}
 40
 41func TestUnmarshal_StringSimpleSuccess(t *testing.T) {
 42	tests := []*testNode{
 43		{name: "blank", input: []byte("\"\""), _type: String, value: []byte("\"\"")},
 44		{name: "char", input: []byte("\"c\""), _type: String, value: []byte("\"c\"")},
 45		{name: "word", input: []byte("\"cat\""), _type: String, value: []byte("\"cat\"")},
 46		{name: "spaces", input: []byte("  \"good cat or dog\"\r\n "), _type: String, value: []byte("\"good cat or dog\"")},
 47		{name: "backslash", input: []byte("\"good \\\"cat\\\"\""), _type: String, value: []byte("\"good \\\"cat\\\"\"")},
 48		{name: "backslash 2", input: []byte("\"good \\\\\\\"cat\\\"\""), _type: String, value: []byte("\"good \\\\\\\"cat\\\"\"")},
 49	}
 50	for _, test := range tests {
 51		t.Run(test.name, func(t *testing.T) {
 52			simpleValid(test, t)
 53		})
 54	}
 55}
 56
 57func TestUnmarshal_NumericSimpleSuccess(t *testing.T) {
 58	tests := []*testNode{
 59		{name: "1", input: []byte("1"), _type: Number, value: []byte("1")},
 60		{name: "-1", input: []byte("-1"), _type: Number, value: []byte("-1")},
 61
 62		{name: "1234567890", input: []byte("1234567890"), _type: Number, value: []byte("1234567890")},
 63		{name: "-123", input: []byte("-123"), _type: Number, value: []byte("-123")},
 64
 65		{name: "123.456", input: []byte("123.456"), _type: Number, value: []byte("123.456")},
 66		{name: "-123.456", input: []byte("-123.456"), _type: Number, value: []byte("-123.456")},
 67
 68		{name: "1e3", input: []byte("1e3"), _type: Number, value: []byte("1e3")},
 69		{name: "1e+3", input: []byte("1e+3"), _type: Number, value: []byte("1e+3")},
 70		{name: "1e-3", input: []byte("1e-3"), _type: Number, value: []byte("1e-3")},
 71		{name: "-1e3", input: []byte("-1e3"), _type: Number, value: []byte("-1e3")},
 72		{name: "-1e-3", input: []byte("-1e-3"), _type: Number, value: []byte("-1e-3")},
 73
 74		{name: "1.123e3456", input: []byte("1.123e3456"), _type: Number, value: []byte("1.123e3456")},
 75		{name: "1.123e-3456", input: []byte("1.123e-3456"), _type: Number, value: []byte("1.123e-3456")},
 76		{name: "-1.123e3456", input: []byte("-1.123e3456"), _type: Number, value: []byte("-1.123e3456")},
 77		{name: "-1.123e-3456", input: []byte("-1.123e-3456"), _type: Number, value: []byte("-1.123e-3456")},
 78
 79		{name: "1E3", input: []byte("1E3"), _type: Number, value: []byte("1E3")},
 80		{name: "1E-3", input: []byte("1E-3"), _type: Number, value: []byte("1E-3")},
 81		{name: "-1E3", input: []byte("-1E3"), _type: Number, value: []byte("-1E3")},
 82		{name: "-1E-3", input: []byte("-1E-3"), _type: Number, value: []byte("-1E-3")},
 83
 84		{name: "1.123E3456", input: []byte("1.123E3456"), _type: Number, value: []byte("1.123E3456")},
 85		{name: "1.123E-3456", input: []byte("1.123E-3456"), _type: Number, value: []byte("1.123E-3456")},
 86		{name: "-1.123E3456", input: []byte("-1.123E3456"), _type: Number, value: []byte("-1.123E3456")},
 87		{name: "-1.123E-3456", input: []byte("-1.123E-3456"), _type: Number, value: []byte("-1.123E-3456")},
 88
 89		{name: "-1.123E-3456 with spaces", input: []byte(" \r -1.123E-3456 \t\n"), _type: Number, value: []byte("-1.123E-3456")},
 90	}
 91	for _, test := range tests {
 92		t.Run(test.name, func(t *testing.T) {
 93			root, err := Unmarshal(test.input)
 94			if err != nil {
 95				t.Errorf("Error on Unmarshal(%s): %s", test.name, err.Error())
 96			} else if root == nil {
 97				t.Errorf("Error on Unmarshal(%s): root is nil", test.name)
 98			} else if root.nodeType != test._type {
 99				t.Errorf("Error on Unmarshal(%s): wrong type", test.name)
100			} else if !bytes.Equal(root.source(), test.value) {
101				t.Errorf("Error on Unmarshal(%s): %s != %s", test.name, root.source(), test.value)
102			}
103		})
104	}
105}
106
107func TestUnmarshal_StringSimpleCorrupted(t *testing.T) {
108	tests := []*testNode{
109		{name: "white NL", input: []byte("\"foo\nbar\"")},
110		{name: "white R", input: []byte("\"foo\rbar\"")},
111		{name: "white Tab", input: []byte("\"foo\tbar\"")},
112		{name: "wrong quotes", input: []byte("'cat'")},
113		{name: "double string", input: []byte("\"Hello\" \"World\"")},
114		{name: "quotes in quotes", input: []byte("\"good \"cat\"\"")},
115	}
116	for _, test := range tests {
117		t.Run(test.name, func(t *testing.T) {
118			simpleInvalid(test, t)
119		})
120	}
121}
122
123func TestUnmarshal_ObjectSimpleSuccess(t *testing.T) {
124	tests := []*testNode{
125		{name: "{}", input: []byte("{}"), _type: Object, value: []byte("{}")},
126		{name: `{ \r\n }`, input: []byte("{ \r\n }"), _type: Object, value: []byte("{ \r\n }")},
127		{name: `{"key":1}`, input: []byte(`{"key":1}`), _type: Object, value: []byte(`{"key":1}`)},
128		{name: `{"key":true}`, input: []byte(`{"key":true}`), _type: Object, value: []byte(`{"key":true}`)},
129		{name: `{"key":"value"}`, input: []byte(`{"key":"value"}`), _type: Object, value: []byte(`{"key":"value"}`)},
130		{name: `{"foo":"bar","baz":"foo"}`, input: []byte(`{"foo":"bar", "baz":"foo"}`), _type: Object, value: []byte(`{"foo":"bar", "baz":"foo"}`)},
131		{name: "spaces", input: []byte(`  {  "foo"  :  "bar"  , "baz"   :   "foo"   }    `), _type: Object, value: []byte(`{  "foo"  :  "bar"  , "baz"   :   "foo"   }`)},
132		{name: "nested", input: []byte(`{"foo":{"bar":{"baz":{}}}}`), _type: Object, value: []byte(`{"foo":{"bar":{"baz":{}}}}`)},
133		{name: "array", input: []byte(`{"array":[{},{},{"foo":[{"bar":["baz"]}]}]}`), _type: Object, value: []byte(`{"array":[{},{},{"foo":[{"bar":["baz"]}]}]}`)},
134	}
135
136	for _, test := range tests {
137		t.Run(test.name, func(t *testing.T) {
138			simpleValid(test, t)
139		})
140	}
141}
142
143func TestUnmarshal_ObjectSimpleCorrupted(t *testing.T) {
144	tests := []*testNode{
145		simpleCorrupted("{{{\"key\": \"foo\"{{{{"),
146		simpleCorrupted("}"),
147		simpleCorrupted("{ }}}}}}}"),
148		simpleCorrupted(" }"),
149		simpleCorrupted("{,}"),
150		simpleCorrupted("{:}"),
151		simpleCorrupted("{100000}"),
152		simpleCorrupted("{1:1}"),
153		simpleCorrupted("{'1:2,3:4'}"),
154		simpleCorrupted(`{"d"}`),
155		simpleCorrupted(`{"foo"}`),
156		simpleCorrupted(`{"foo":}`),
157		simpleCorrupted(`{:"foo"}`),
158		simpleCorrupted(`{"foo":bar}`),
159		simpleCorrupted(`{"foo":"bar",}`),
160		simpleCorrupted(`{}{}`),
161		simpleCorrupted(`{},{}`),
162		simpleCorrupted(`{[},{]}`),
163		simpleCorrupted(`{[,]}`),
164		simpleCorrupted(`{[]}`),
165		simpleCorrupted(`{}1`),
166		simpleCorrupted(`1{}`),
167		simpleCorrupted(`{"x"::1}`),
168		simpleCorrupted(`{null:null}`),
169		simpleCorrupted(`{"foo:"bar"}`),
170	}
171
172	for _, test := range tests {
173		t.Run(test.name, func(t *testing.T) {
174			simpleInvalid(test, t)
175		})
176	}
177}
178
179func TestUnmarshal_NullSimpleCorrupted(t *testing.T) {
180	tests := []*testNode{
181		{name: "nul", input: []byte("nul")},
182		{name: "nil", input: []byte("nil")},
183		{name: "nill", input: []byte("nill")},
184		{name: "NILL", input: []byte("NILL")},
185		{name: "Null", input: []byte("Null")},
186		{name: "NULL", input: []byte("NULL")},
187		{name: "spaces", input: []byte("Nu ll")},
188		{name: "null1", input: []byte("null1")},
189		{name: "double", input: []byte("null null")},
190	}
191
192	for _, test := range tests {
193		t.Run(test.name, func(t *testing.T) {
194			simpleInvalid(test, t)
195		})
196	}
197}
198
199func TestUnmarshal_BoolSimpleSuccess(t *testing.T) {
200	tests := []*testNode{
201		{name: "lower true", input: []byte("true"), _type: Boolean, value: []byte("true")},
202		{name: "lower false", input: []byte("false"), _type: Boolean, value: []byte("false")},
203		{name: "spaces true", input: []byte("  true\r\n "), _type: Boolean, value: []byte("true")},
204		{name: "spaces false", input: []byte("  false\r\n "), _type: Boolean, value: []byte("false")},
205	}
206
207	for _, test := range tests {
208		t.Run(test.name, func(t *testing.T) {
209			simpleValid(test, t)
210		})
211	}
212}
213
214func TestUnmarshal_BoolSimpleCorrupted(t *testing.T) {
215	tests := []*testNode{
216		simpleCorrupted("tru"),
217		simpleCorrupted("fals"),
218		simpleCorrupted("tre"),
219		simpleCorrupted("fal se"),
220		simpleCorrupted("true false"),
221		simpleCorrupted("True"),
222		simpleCorrupted("TRUE"),
223		simpleCorrupted("False"),
224		simpleCorrupted("FALSE"),
225	}
226
227	for _, test := range tests {
228		t.Run(test.name, func(t *testing.T) {
229			simpleInvalid(test, t)
230		})
231	}
232}
233
234func TestUnmarshal_ArraySimpleSuccess(t *testing.T) {
235	tests := []*testNode{
236		{name: "[]", input: []byte("[]"), _type: Array, value: []byte("[]")},
237		{name: "[1]", input: []byte("[1]"), _type: Array, value: []byte("[1]")},
238		{name: "[1,2,3]", input: []byte("[1,2,3]"), _type: Array, value: []byte("[1,2,3]")},
239		{name: "[1, 2, 3]", input: []byte("[1, 2, 3]"), _type: Array, value: []byte("[1, 2, 3]")},
240		{name: "[1,[2],3]", input: []byte("[1,[2],3]"), _type: Array, value: []byte("[1,[2],3]")},
241		{name: "[[],[],[]]", input: []byte("[[],[],[]]"), _type: Array, value: []byte("[[],[],[]]")},
242		{name: "[[[[[]]]]]", input: []byte("[[[[[]]]]]"), _type: Array, value: []byte("[[[[[]]]]]")},
243		{name: "[true,null,1,\"foo\",[]]", input: []byte("[true,null,1,\"foo\",[]]"), _type: Array, value: []byte("[true,null,1,\"foo\",[]]")},
244		{name: "spaces", input: []byte("\n\r [\n1\n ]\r\n"), _type: Array, value: []byte("[\n1\n ]")},
245	}
246
247	for _, test := range tests {
248		t.Run(test.name, func(t *testing.T) {
249			simpleValid(test, t)
250		})
251	}
252}
253
254func TestUnmarshal_ArraySimpleCorrupted(t *testing.T) {
255	tests := []*testNode{
256		simpleCorrupted("[,]"),
257		simpleCorrupted("[]\\"),
258		simpleCorrupted("[1,]"),
259		simpleCorrupted("[[]"),
260		simpleCorrupted("[]]"),
261		simpleCorrupted("1[]"),
262		simpleCorrupted("[]1"),
263		simpleCorrupted("[[]1]"),
264	}
265
266	for _, test := range tests {
267		t.Run(test.name, func(t *testing.T) {
268			simpleInvalid(test, t)
269		})
270	}
271}
272
273// Examples from https://json.org/example.html
274func TestUnmarshal(t *testing.T) {
275	tests := []struct {
276		name  string
277		value string
278	}{
279		{
280			name: "glossary",
281			value: `{
282				"glossary": {
283					"title": "example glossary",
284					"GlossDiv": {
285						"title": "S",
286						"GlossList": {
287							"GlossEntry": {
288								"ID": "SGML",
289								"SortAs": "SGML",
290								"GlossTerm": "Standard Generalized Markup Language",
291								"Acronym": "SGML",
292								"Abbrev": "ISO 8879:1986",
293								"GlossDef": {
294									"para": "A meta-markup language, used to create markup languages such as DocBook.",
295									"GlossSeeAlso": ["GML", "XML"]
296								},
297								"GlossSee": "markup"
298							}
299						}
300					}
301				}
302			}`,
303		},
304		{
305			name: "menu",
306			value: `{"menu": {
307				"id": "file",
308				"value": "File",
309				"popup": {
310				  "menuitem": [
311					{"value": "New", "onclick": "CreateNewDoc()"},
312					{"value": "Open", "onclick": "OpenDoc()"},
313					{"value": "Close", "onclick": "CloseDoc()"}
314				  ]
315				}
316			}}`,
317		},
318		{
319			name: "widget",
320			value: `{"widget": {
321				"debug": "on",
322				"window": {
323					"title": "Sample Konfabulator Widget",
324					"name": "main_window",
325					"width": 500,
326					"height": 500
327				},
328				"image": { 
329					"src": "Images/Sun.png",
330					"name": "sun1",
331					"hOffset": 250,
332					"vOffset": 250,
333					"alignment": "center"
334				},
335				"text": {
336					"data": "Click Here",
337					"size": 36,
338					"style": "bold",
339					"name": "text1",
340					"hOffset": 250,
341					"vOffset": 100,
342					"alignment": "center",
343					"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
344				}
345			}}    `,
346		},
347		{
348			name: "web-app",
349			value: `{"web-app": {
350				"servlet": [   
351				  {
352					"servlet-name": "cofaxCDS",
353					"servlet-class": "org.cofax.cds.CDSServlet",
354					"init-param": {
355					  "configGlossary:installationAt": "Philadelphia, PA",
356					  "configGlossary:adminEmail": "ksm@pobox.com",
357					  "configGlossary:poweredBy": "Cofax",
358					  "configGlossary:poweredByIcon": "/images/cofax.gif",
359					  "configGlossary:staticPath": "/content/static",
360					  "templateProcessorClass": "org.cofax.WysiwygTemplate",
361					  "templateLoaderClass": "org.cofax.FilesTemplateLoader",
362					  "templatePath": "templates",
363					  "templateOverridePath": "",
364					  "defaultListTemplate": "listTemplate.htm",
365					  "defaultFileTemplate": "articleTemplate.htm",
366					  "useJSP": false,
367					  "jspListTemplate": "listTemplate.jsp",
368					  "jspFileTemplate": "articleTemplate.jsp",
369					  "cachePackageTagsTrack": 200,
370					  "cachePackageTagsStore": 200,
371					  "cachePackageTagsRefresh": 60,
372					  "cacheTemplatesTrack": 100,
373					  "cacheTemplatesStore": 50,
374					  "cacheTemplatesRefresh": 15,
375					  "cachePagesTrack": 200,
376					  "cachePagesStore": 100,
377					  "cachePagesRefresh": 10,
378					  "cachePagesDirtyRead": 10,
379					  "searchEngineListTemplate": "forSearchEnginesList.htm",
380					  "searchEngineFileTemplate": "forSearchEngines.htm",
381					  "searchEngineRobotsDb": "WEB-INF/robots.db",
382					  "useDataStore": true,
383					  "dataStoreClass": "org.cofax.SqlDataStore",
384					  "redirectionClass": "org.cofax.SqlRedirection",
385					  "dataStoreName": "cofax",
386					  "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
387					  "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
388					  "dataStoreUser": "sa",
389					  "dataStorePassword": "dataStoreTestQuery",
390					  "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
391					  "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
392					  "dataStoreInitConns": 10,
393					  "dataStoreMaxConns": 100,
394					  "dataStoreConnUsageLimit": 100,
395					  "dataStoreLogLevel": "debug",
396					  "maxUrlLength": 500}},
397				  {
398					"servlet-name": "cofaxEmail",
399					"servlet-class": "org.cofax.cds.EmailServlet",
400					"init-param": {
401					"mailHost": "mail1",
402					"mailHostOverride": "mail2"}},
403				  {
404					"servlet-name": "cofaxAdmin",
405					"servlet-class": "org.cofax.cds.AdminServlet"},
406			   
407				  {
408					"servlet-name": "fileServlet",
409					"servlet-class": "org.cofax.cds.FileServlet"},
410				  {
411					"servlet-name": "cofaxTools",
412					"servlet-class": "org.cofax.cms.CofaxToolsServlet",
413					"init-param": {
414					  "templatePath": "toolstemplates/",
415					  "log": 1,
416					  "logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
417					  "logMaxSize": "",
418					  "dataLog": 1,
419					  "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
420					  "dataLogMaxSize": "",
421					  "removePageCache": "/content/admin/remove?cache=pages&id=",
422					  "removeTemplateCache": "/content/admin/remove?cache=templates&id=",
423					  "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
424					  "lookInContext": 1,
425					  "adminGroupID": 4,
426					  "betaServer": true}}],
427				"servlet-mapping": {
428				  "cofaxCDS": "/",
429				  "cofaxEmail": "/cofaxutil/aemail/*",
430				  "cofaxAdmin": "/admin/*",
431				  "fileServlet": "/static/*",
432				  "cofaxTools": "/tools/*"},
433			   
434				"taglib": {
435				  "taglib-uri": "cofax.tld",
436				  "taglib-location": "/WEB-INF/tlds/cofax.tld"}}}`,
437		},
438		{
439			name: "SVG Viewer",
440			value: `{"menu": {
441				"header": "SVG Viewer",
442				"items": [
443					{"id": "Open"},
444					{"id": "OpenNew", "label": "Open New"},
445					null,
446					{"id": "ZoomIn", "label": "Zoom In"},
447					{"id": "ZoomOut", "label": "Zoom Out"},
448					{"id": "OriginalView", "label": "Original View"},
449					null,
450					{"id": "Quality"},
451					{"id": "Pause"},
452					{"id": "Mute"},
453					null,
454					{"id": "Find", "label": "Find..."},
455					{"id": "FindAgain", "label": "Find Again"},
456					{"id": "Copy"},
457					{"id": "CopyAgain", "label": "Copy Again"},
458					{"id": "CopySVG", "label": "Copy SVG"},
459					{"id": "ViewSVG", "label": "View SVG"},
460					{"id": "ViewSource", "label": "View Source"},
461					{"id": "SaveAs", "label": "Save As"},
462					null,
463					{"id": "Help"},
464					{"id": "About", "label": "About Adobe CVG Viewer..."}
465				]
466			}}`,
467		},
468	}
469	for _, test := range tests {
470		t.Run(test.name, func(t *testing.T) {
471			_, err := Unmarshal([]byte(test.value))
472			if err != nil {
473				t.Errorf("Error on Unmarshal: %s", err.Error())
474			}
475		})
476	}
477}
478
479func TestUnmarshalSafe(t *testing.T) {
480	json := []byte(`{ "store": {
481		"book": [ 
482		  { "category": "reference",
483			"author": "Nigel Rees",
484			"title": "Sayings of the Century",
485			"price": 8.95
486		  },
487		  { "category": "fiction",
488			"author": "Evelyn Waugh",
489			"title": "Sword of Honour",
490			"price": 12.99
491		  },
492		  { "category": "fiction",
493			"author": "Herman Melville",
494			"title": "Moby Dick",
495			"isbn": "0-553-21311-3",
496			"price": 8.99
497		  },
498		  { "category": "fiction",
499			"author": "J. R. R. Tolkien",
500			"title": "The Lord of the Rings",
501			"isbn": "0-395-19395-8",
502			"price": 22.99
503		  }
504		],
505		"bicycle": {
506		  "color": "red",
507		  "price": 19.95
508		}
509	  }
510	}`)
511	safe, err := UnmarshalSafe(json)
512	if err != nil {
513		t.Errorf("Error on Unmarshal: %s", err.Error())
514	} else if safe == nil {
515		t.Errorf("Error on Unmarshal: safe is nil")
516	} else {
517		root, err := Unmarshal(json)
518		if err != nil {
519			t.Errorf("Error on Unmarshal: %s", err.Error())
520		} else if root == nil {
521			t.Errorf("Error on Unmarshal: root is nil")
522		} else if !bytes.Equal(root.source(), safe.source()) {
523			t.Errorf("Error on UnmarshalSafe: values not same")
524		}
525	}
526}
527
528// BenchmarkGoStdUnmarshal-8   	   61698	     19350 ns/op	     288 B/op	       6 allocs/op
529// BenchmarkUnmarshal-8        	   45620	     26165 ns/op	   21889 B/op	     367 allocs/op
530//
531// type bench struct {
532// 	Name  string `json:"name"`
533// 	Value int    `json:"value"`
534// }
535
536// func BenchmarkGoStdUnmarshal(b *testing.B) {
537// 	data := []byte(webApp)
538// 	for i := 0; i < b.N; i++ {
539// 		err := json.Unmarshal(data, &bench{})
540// 		if err != nil {
541// 			b.Fatal(err)
542// 		}
543// 	}
544// }
545
546// func BenchmarkUnmarshal(b *testing.B) {
547// 	data := []byte(webApp)
548// 	for i := 0; i < b.N; i++ {
549// 		_, err := Unmarshal(data)
550// 		if err != nil {
551// 			b.Fatal(err)
552// 		}
553// 	}
554// }