1 ## oils_failures_allowed: 2
2 ## tags: dev-minimal
3
4 #### usage errors
5
6 json read zz
7 echo status=$?
8
9 json write
10
11 ## status: 3
12 ## STDOUT:
13 status=2
14 ## END
15
16 #### json write STRING
17 shopt --set parse_proc
18
19 json write ('foo')
20 var s = 'foo'
21 json write (s)
22 ## STDOUT:
23 "foo"
24 "foo"
25 ## END
26
27 #### json write ARRAY
28 json write (:|foo.cc foo.h|)
29 json write --indent 0 (['foo.cc', 'foo.h'])
30 ## STDOUT:
31 [
32 "foo.cc",
33 "foo.h"
34 ]
35 [
36 "foo.cc",
37 "foo.h"
38 ]
39 ## END
40
41 #### json write Dict
42 json write ({k: 'v', k2: [4, 5]})
43
44 json write ([{k: 'v', k2: 'v2'}, {}])
45
46 ## STDOUT:
47 {
48 "k": "v",
49 "k2": [
50 4,
51 5
52 ]
53 }
54 [
55 {
56 "k": "v",
57 "k2": "v2"
58 },
59 {
60
61 }
62 ]
63 ## END
64
65 #### json write compact format
66 shopt --set parse_proc
67
68 # TODO: ORDER of keys should be PRESERVED
69 var mydict = {name: "bob", age: 30}
70
71 json write --pretty=0 (mydict)
72 # ignored
73 json write --pretty=F --indent 4 (mydict)
74 ## STDOUT:
75 {"name":"bob","age":30}
76 {"name":"bob","age":30}
77 ## END
78
79 #### json write in command sub
80 shopt -s oil:all # for echo
81 var mydict = {name: "bob", age: 30}
82 json write (mydict)
83 var x = $(json write (mydict))
84 echo $x
85 ## STDOUT:
86 {
87 "name": "bob",
88 "age": 30
89 }
90 {
91 "name": "bob",
92 "age": 30
93 }
94 ## END
95
96 #### json read passed invalid args
97
98 # EOF
99 json read
100 echo status=$?
101
102 json read 'z z'
103 echo status=$?
104
105 json read a b c
106 echo status=$?
107
108 ## STDOUT:
109 status=1
110 status=2
111 status=2
112 ## END
113
114 #### json read uses $_reply var
115
116 echo '{"age": 42}' | json read
117 json write (_reply)
118
119 ## STDOUT:
120 {
121 "age": 42
122 }
123 ## END
124
125 #### json read with redirect
126 echo '{"age": 42}' > $TMP/foo.txt
127 json read (&x) < $TMP/foo.txt
128 pp cell :x
129 ## STDOUT:
130 x = (Cell exported:F readonly:F nameref:F val:(value.Dict d:[Dict age (value.Int i:42)]))
131 ## END
132
133 #### json read at end of pipeline (relies on lastpipe)
134 echo '{"age": 43}' | json read (&y)
135 pp cell y
136 ## STDOUT:
137 y = (Cell exported:F readonly:F nameref:F val:(value.Dict d:[Dict age (value.Int i:43)]))
138 ## END
139
140 #### invalid JSON
141 echo '{' | json read (&y)
142 echo pipeline status = $?
143 pp cell y
144 ## status: 1
145 ## STDOUT:
146 pipeline status = 1
147 ## END
148
149 #### json write expression
150 json write --pretty=0 ([1,2,3])
151 echo status=$?
152
153 json write (5, 6) # to many args
154 echo status=$?
155
156 ## status: 3
157 ## STDOUT:
158 [1,2,3]
159 status=0
160 ## END
161
162 #### json write evaluation error
163
164 #var block = ^(echo hi)
165 #json write (block)
166 #echo status=$?
167
168 # undefined var
169 json write (a)
170 echo 'should have failed'
171
172 ## status: 1
173 ## STDOUT:
174 ## END
175
176 #### json write of List in cycle
177
178 var L = [1, 2, 3]
179 setvar L[0] = L
180
181 shopt -s ysh:upgrade
182 fopen >tmp.txt {
183 pp line (L)
184 }
185 fgrep -n -o '[ ...' tmp.txt
186
187 json write (L)
188 echo 'should have failed'
189
190 ## status: 1
191 ## STDOUT:
192 1:[ ...
193 ## END
194
195 #### json write of Dict in cycle
196
197 var d = {}
198 setvar d.k = d
199
200 shopt -s ysh:upgrade
201 fopen >tmp.txt {
202 pp line (d)
203 }
204 fgrep -n -o '{ ...' tmp.txt
205
206 json write (d)
207 echo 'should have failed'
208
209 ## status: 1
210 ## STDOUT:
211 1:{ ...
212 ## END
213
214 #### json read doesn't accept u'' or b'' strings
215
216 json read <<EOF
217 {"key": u'val'}
218 EOF
219 echo status=$?
220
221 #pp line (_reply)
222
223 json read <<EOF
224 {"key": b'val'}
225 EOF
226 echo status=$?
227
228 ## STDOUT:
229 status=1
230 status=1
231 ## END
232
233 #### json write emits Unicode replacement char for binary data \yff
234
235 json write ([3, "foo", $'-\xff\xfe---\xfd=']) > tmp.txt
236
237 # Round trip it for good measure
238 json read < tmp.txt
239
240 json write (_reply)
241
242 ## STDOUT:
243 [
244 3,
245 "foo",
246 "-��---�="
247 ]
248 ## END
249
250 #### json8 write emits b'' strings for binary data \yff
251
252 json8 write ([3, "foo", $'-\xff\xfe-\xfd='])
253
254 ## STDOUT:
255 [
256 3,
257 "foo",
258 b'-\yff\yfe-\yfd='
259 ]
260 ## END
261
262
263 #### json8 write bytes vs unicode string
264
265 u=$'mu \u03bc \x01 \" \\ \b\f\n\r\t'
266 u2=$'\x01\x1f' # this is a valid unicode string
267
268 b=$'\xff' # this isn't valid unicode
269
270 json8 write (u)
271 json8 write (u2)
272
273 json8 write (b)
274
275 ## STDOUT:
276 "mu μ \u0001 \" \\ \b\f\n\r\t"
277 "\u0001\u001f"
278 b'\yff'
279 ## END
280
281 #### JSON \/ escapes supported
282
283 msg='"\/"'
284
285 echo "$msg" | python3 -c 'import json, sys; print(json.load(sys.stdin))'
286
287 echo "$msg" | json read
288 echo reply=$_reply
289
290 j8="b'\\/'"
291 echo "$msg" | json read
292 echo reply=$_reply
293
294
295 ## STDOUT:
296 /
297 reply=/
298 reply=/
299 ## END
300
301 #### J8 supports superfluous \" escapes, but JSON doesn't support \' escapes
302
303 json8 read <<'EOF'
304 b'\"'
305 EOF
306 echo reply=$_reply
307
308 json8 read <<'EOF'
309 b'\'\'\b\f\n\r\t\"\\'
310 EOF
311 pp line (_reply)
312
313 # Suppress traceback
314 python3 -c 'import json, sys; print(json.load(sys.stdin))' 2>/dev/null <<'EOF'
315 "\'"
316 EOF
317 echo python3=$?
318
319 json read <<'EOF'
320 "\'"
321 EOF
322 echo json=$?
323
324 ## STDOUT:
325 reply="
326 (Str) "''\b\f\n\r\t\"\\"
327 python3=1
328 json=1
329 ## END
330
331 #### Escaping uses \u0001 in "", but \u{1} in b''
332
333 s1=$'\x01'
334 s2=$'\x01\xff\x1f' # byte string
335
336 json8 write (s1)
337 json8 write (s2)
338
339 ## STDOUT:
340 "\u0001"
341 b'\u{1}\yff\u{1f}'
342 ## END
343
344
345 #### json8 read
346
347 # Avoid conflict on stdin from spec test framework?
348
349 $SH $REPO_ROOT/spec/testdata/j8-read.sh
350
351 ## STDOUT:
352 (Dict) {}
353 (List) []
354 (List) [42]
355 (List) [true,false]
356 (Dict) {"k":"v"}
357 (Dict) {"k":null}
358 (Dict) {"k":1,"k2":2}
359 (Dict) {"k":{"k2":null}}
360 (Dict) {"k":{"k2":"v2"},"k3":"backslash \\ \" \n line 2 μ "}
361 (Dict) {"k":{"k2":"v2"},"k3":"backslash \\ \" \n line 2 μ "}
362 ## END
363
364 #### json8 round trip
365
366 var obj = [42, 1.5, null, true, "hi", b'\yff\yfe\b\n""']
367
368 json8 write --pretty=F (obj) > j
369
370 cat j
371
372 json8 read < j
373
374 json8 write (_reply)
375
376 ## STDOUT:
377 [42,1.5,null,true,"hi",b'\yff\yfe\b\n""']
378 [
379 42,
380 1.5,
381 null,
382 true,
383 "hi",
384 b'\yff\yfe\b\n""'
385 ]
386 ## END
387
388 #### json round trip (regression)
389
390 var d = {
391 short: '-v', long: '--verbose', type: null, default: '', help: 'Enable verbose logging'
392 }
393
394 json write (d) | json read
395
396 pp line (_reply)
397
398 ## STDOUT:
399 (Dict) {"short":"-v","long":"--verbose","type":null,"default":"","help":"Enable verbose logging"}
400 ## END
401
402 #### round trip: decode surrogate pair and encode
403
404 var j = r'"\ud83e\udd26"'
405 echo $j | json read (&c1)
406
407 json write (c1)
408
409 var j = r'"\uD83E\uDD26"'
410 echo $j | json read (&c2)
411
412 json write (c2)
413
414 # Not a surrogate pair
415 var j = r'"\u0001\u0002"'
416 echo $j | json read (&c3)
417
418 json write (c3)
419
420 var j = r'"\u0100\u0101\u0102"'
421 echo $j | json read (&c4)
422
423 json write (c4)
424
425 ## STDOUT:
426 "🤦"
427 "🤦"
428 "\u0001\u0002"
429 "ĀāĂ"
430 ## END
431
432 #### round trip: decode surrogate half and encode
433
434 # TODO: Weird Python allows this to be decoded, but I think the Bjoern state
435 # machine will not!
436
437 shopt -s ysh:upgrade
438
439 for j in '"\ud83e"' '"\udd26"' {
440 var s = fromJson(j)
441 pp line (s)
442
443 # TODO: modify DFA to return the code point in the surrogate range, and
444 # print it in JSON mode
445 # j8 mode could possibly use byte strings
446 json write (s)
447 }
448
449 ## STDOUT:
450 (Str) b'\ya0\ybe'
451 "\ud83e"
452 (Str) b'\yb4\ya6'
453 "\udd26"
454 ## END
455
456 #### toJson() toJ8() - TODO: test difference
457
458 var obj = [42, 1.5, null, true, "hi"]
459
460 echo $[toJson(obj)]
461 echo $[toJ8(obj)]
462
463 ## STDOUT:
464 [42,1.5,null,true,"hi"]
465 [42,1.5,null,true,"hi"]
466 ## END
467
468 #### fromJson() fromJ8() - TODO: test difference
469
470 var message ='[42,1.5,null,true,"hi"]'
471
472 pp line (fromJson(message))
473 pp line (fromJ8(message))
474
475 ## STDOUT:
476 (List) [42,1.5,null,true,"hi"]
477 (List) [42,1.5,null,true,"hi"]
478 ## END
479
480 #### User can handle errors - toJson() toJ8()
481 shopt -s ysh:upgrade
482
483 var obj = []
484 call obj->append(obj)
485
486 try {
487 echo $[toJson(obj)]
488 }
489 echo status=$_status
490 echo "encode error $[_error.message]" | sed 's/0x[a-f0-9]\+/(object id)/'
491
492 try { # use different style
493 echo $[toJ8( /d+/ )]
494 }
495 echo status=$_status
496 echo "encode error $[_error.message]"
497
498 # This makes the interpreter fail with a message
499 echo $[toJson(obj)]
500
501 ## status: 4
502 ## STDOUT:
503 status=4
504 encode error Can't encode List (object id) in object cycle
505 status=4
506 encode error Can't serialize object of type Eggex
507 ## END
508
509 #### User can handle errors - fromJson() fromJ8()
510 shopt -s ysh:upgrade
511
512 var message ='[42,1.5,null,true,"hi"'
513
514 try {
515 var obj = fromJson(message)
516 }
517 echo status=$_status
518 echo "decode error $[_error.message]" | egrep -o '.*Expected.*RBracket'
519
520 try {
521 var obj = fromJ8(message)
522 }
523 echo status=$_status
524 echo "decode error $[_error.message]" | egrep -o '.*Expected.*RBracket'
525
526 try {
527 var obj = fromJson('[+]')
528 }
529 echo "positions $[_error.start_pos] - $[_error.end_pos]"
530
531 # This makes the interpreter fail with a message
532 var obj = fromJson(message)
533
534 ## status: 4
535 ## STDOUT:
536 status=4
537 decode error Expected Id.J8_RBracket
538 status=4
539 decode error Expected Id.J8_RBracket
540 positions 1 - 2
541 ## END
542
543
544 #### ASCII control chars can't appear literally in messages
545 shopt -s ysh:upgrade
546
547 var message=$'"\x01"'
548 #echo $message | od -c
549
550 try {
551 var obj = fromJson(message)
552 }
553 echo status=$_status
554 echo "$[_error.message]" | egrep -o 'ASCII control chars'
555
556 ## STDOUT:
557 status=4
558 ASCII control chars
559 ## END
560
561
562 #### JSON string can have unescaped ' and J8 string can have unescaped "
563
564 json read <<EOF
565 "'"
566 EOF
567
568 pp line (_reply)
569
570
571
572 json8 read <<EOF
573 u'"'
574 EOF
575
576 pp line (_reply)
577
578 ## STDOUT:
579 (Str) "'"
580 (Str) "\""
581 ## END
582
583 #### \yff can't appear in u'' code strings (command)
584
585 shopt -s ysh:upgrade
586
587 echo -n b'\yfd' | od -A n -t x1
588 echo -n u'\yfd' | od -A n -t x1
589
590 ## status: 2
591 ## STDOUT:
592 fd
593 ## END
594
595 #### \yff can't appear in u'' code strings (expr)
596
597 var x = b'\yfe'
598 write -n -- $x | od -A n -t x1
599
600 var x = u'\yfe'
601 write -n -- $x | od -A n -t x1
602
603 ## status: 2
604 ## STDOUT:
605 fe
606 ## END
607
608 #### \yff can't appear in u'' multiline code strings
609
610 shopt -s ysh:upgrade
611
612 echo -n b'''\yfc''' | od -A n -t x1
613 echo -n u'''\yfd''' | od -A n -t x1
614
615 ## status: 2
616 ## STDOUT:
617 fc
618 ## END
619
620 #### \yff can't appear in u'' data strings
621
622 #shopt -s ysh:upgrade
623
624 json8 read (&b) <<'EOF'
625 b'\yfe'
626 EOF
627 pp line (b)
628
629 json8 read (&u) <<'EOF'
630 u'\yfe'
631 EOF
632 pp line (u) # undefined
633
634 ## status: 1
635 ## STDOUT:
636 (Str) b'\yfe'
637 ## END
638
639 #### \u{dc00} can't be in surrogate range in code (command)
640
641 shopt -s ysh:upgrade
642
643 echo -n u'\u{dc00}' | od -A n -t x1
644
645 ## status: 2
646 ## STDOUT:
647 ## END
648
649 #### \u{dc00} can't be in surrogate range in code (expr)
650
651 shopt -s ysh:upgrade
652
653 var x = u'\u{dc00}'
654 echo $x | od -A n -t x1
655
656 ## status: 2
657 ## STDOUT:
658 ## END
659
660 #### \u{dc00} can't be in surrogate range in data
661
662 json8 read <<'EOF'
663 ["long string", u'hello \u{d7ff}', "other"]
664 EOF
665 echo status=$?
666
667 json8 read <<'EOF'
668 ["long string", u'hello \u{d800}', "other"]
669 EOF
670 echo status=$?
671
672 json8 read <<'EOF'
673 ["long string", u'hello \u{dfff}', "other"]
674 EOF
675 echo status=$?
676
677 json8 read <<'EOF'
678 ["long string", u'hello \u{e000}', "other"]
679 EOF
680 echo status=$?
681
682
683 ## STDOUT:
684 status=0
685 status=1
686 status=1
687 status=0
688 ## END
689
690
691
692 #### Inf and NaN can't be encoded or decoded
693
694 # This works in Python, should probably support it
695
696 var n = float("NaN")
697 var i = float("inf")
698
699 pp line (n)
700 pp line (i)
701
702 json dump (n)
703 json dump (i)
704
705 ## status: 2
706 ## STDOUT:
707 ## END
708
709 #### Invalid UTF-8 in JSON is rejected
710
711 echo $'"\xff"' | json read
712 echo status=$?
713
714 echo $'"\xff"' | json8 read
715 echo status=$?
716
717 echo $'\xff' | json read
718 echo status=$?
719
720 echo $'\xff' | json8 read
721 echo status=$?
722
723 ## STDOUT:
724 status=1
725 status=1
726 status=1
727 status=1
728 ## END
729
730 #### Invalid JSON in J8 is rejected
731
732 json8 read <<EOF
733 b'$(echo -e -n '\xff')'
734 EOF
735 echo status=$?
736
737 json8 read <<EOF
738 u'$(echo -e -n '\xff')'
739 EOF
740 echo status=$?
741
742 ## STDOUT:
743 status=1
744 status=1
745 ## END
746
747 #### '' means the same thing as u''
748
749 echo "''" | json8 read
750 pp line (_reply)
751
752 echo "'\u{3bc}'" | json8 read
753 pp line (_reply)
754
755 echo "'\yff'" | json8 read
756 echo status=$?
757
758 ## STDOUT:
759 (Str) ""
760 (Str) "μ"
761 status=1
762 ## END
763
764 #### decode deeply nested structure (stack overflow)
765
766 shopt -s ysh:upgrade
767
768 proc pairs(n) {
769 var m = int(n) # TODO: 1 .. n should auto-convert?
770
771 for i in (1 .. m) {
772 write -n -- '['
773 }
774 for i in (1 .. m) {
775 write -n -- ']'
776 }
777 }
778
779 # This is all Python can handle; C++ can handle more
780 msg=$(pairs 50)
781
782 #echo $msg
783
784 echo "$msg" | json read
785 pp line (_reply)
786 echo len=$[len(_reply)]
787
788 ## STDOUT:
789 (List) [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
790 len=1
791 ## END