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