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