PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /opt/golang/1.22.0/src/encoding/json
Viewing File: /opt/golang/1.22.0/src/encoding/json/stream_test.go
// Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package json import ( "bytes" "fmt" "io" "log" "net" "net/http" "net/http/httptest" "path" "reflect" "runtime" "runtime/debug" "strings" "testing" ) // TODO(https://go.dev/issue/52751): Replace with native testing support. // CaseName is a case name annotated with a file and line. type CaseName struct { Name string Where CasePos } // Name annotates a case name with the file and line of the caller. func Name(s string) (c CaseName) { c.Name = s runtime.Callers(2, c.Where.pc[:]) return c } // CasePos represents a file and line number. type CasePos struct{ pc [1]uintptr } func (pos CasePos) String() string { frames := runtime.CallersFrames(pos.pc[:]) frame, _ := frames.Next() return fmt.Sprintf("%s:%d", path.Base(frame.File), frame.Line) } // Test values for the stream test. // One of each JSON kind. var streamTest = []any{ 0.1, "hello", nil, true, false, []any{"a", "b", "c"}, map[string]any{"K": "Kelvin", "ß": "long s"}, 3.14, // another value to make sure something can follow map } var streamEncoded = `0.1 "hello" null true false ["a","b","c"] {"ß":"long s","K":"Kelvin"} 3.14 ` func TestEncoder(t *testing.T) { for i := 0; i <= len(streamTest); i++ { var buf strings.Builder enc := NewEncoder(&buf) // Check that enc.SetIndent("", "") turns off indentation. enc.SetIndent(">", ".") enc.SetIndent("", "") for j, v := range streamTest[0:i] { if err := enc.Encode(v); err != nil { t.Fatalf("#%d.%d Encode error: %v", i, j, err) } } if have, want := buf.String(), nlines(streamEncoded, i); have != want { t.Errorf("encoding %d items: mismatch:", i) diff(t, []byte(have), []byte(want)) break } } } func TestEncoderErrorAndReuseEncodeState(t *testing.T) { // Disable the GC temporarily to prevent encodeState's in Pool being cleaned away during the test. percent := debug.SetGCPercent(-1) defer debug.SetGCPercent(percent) // Trigger an error in Marshal with cyclic data. type Dummy struct { Name string Next *Dummy } dummy := Dummy{Name: "Dummy"} dummy.Next = &dummy var buf bytes.Buffer enc := NewEncoder(&buf) if err := enc.Encode(dummy); err == nil { t.Errorf("Encode(dummy) error: got nil, want non-nil") } type Data struct { A string I int } want := Data{A: "a", I: 1} if err := enc.Encode(want); err != nil { t.Errorf("Marshal error: %v", err) } var got Data if err := Unmarshal(buf.Bytes(), &got); err != nil { t.Errorf("Unmarshal error: %v", err) } if got != want { t.Errorf("Marshal/Unmarshal roundtrip:\n\tgot: %v\n\twant: %v", got, want) } } var streamEncodedIndent = `0.1 "hello" null true false [ >."a", >."b", >."c" >] { >."ß": "long s", >."K": "Kelvin" >} 3.14 ` func TestEncoderIndent(t *testing.T) { var buf strings.Builder enc := NewEncoder(&buf) enc.SetIndent(">", ".") for _, v := range streamTest { enc.Encode(v) } if have, want := buf.String(), streamEncodedIndent; have != want { t.Error("Encode mismatch:") diff(t, []byte(have), []byte(want)) } } type strMarshaler string func (s strMarshaler) MarshalJSON() ([]byte, error) { return []byte(s), nil } type strPtrMarshaler string func (s *strPtrMarshaler) MarshalJSON() ([]byte, error) { return []byte(*s), nil } func TestEncoderSetEscapeHTML(t *testing.T) { var c C var ct CText var tagStruct struct { Valid int `json:"<>&#! "` Invalid int `json:"\\"` } // This case is particularly interesting, as we force the encoder to // take the address of the Ptr field to use its MarshalJSON method. This // is why the '&' is important. marshalerStruct := &struct { NonPtr strMarshaler Ptr strPtrMarshaler }{`"<str>"`, `"<str>"`} // https://golang.org/issue/34154 stringOption := struct { Bar string `json:"bar,string"` }{`<html>foobar</html>`} tests := []struct { CaseName v any wantEscape string want string }{ {Name("c"), c, `"\u003c\u0026\u003e"`, `"<&>"`}, {Name("ct"), ct, `"\"\u003c\u0026\u003e\""`, `"\"<&>\""`}, {Name(`"<&>"`), "<&>", `"\u003c\u0026\u003e"`, `"<&>"`}, { Name("tagStruct"), tagStruct, `{"\u003c\u003e\u0026#! ":0,"Invalid":0}`, `{"<>&#! ":0,"Invalid":0}`, }, { Name(`"<str>"`), marshalerStruct, `{"NonPtr":"\u003cstr\u003e","Ptr":"\u003cstr\u003e"}`, `{"NonPtr":"<str>","Ptr":"<str>"}`, }, { Name("stringOption"), stringOption, `{"bar":"\"\\u003chtml\\u003efoobar\\u003c/html\\u003e\""}`, `{"bar":"\"<html>foobar</html>\""}`, }, } for _, tt := range tests { t.Run(tt.Name, func(t *testing.T) { var buf strings.Builder enc := NewEncoder(&buf) if err := enc.Encode(tt.v); err != nil { t.Fatalf("%s: Encode(%s) error: %s", tt.Where, tt.Name, err) } if got := strings.TrimSpace(buf.String()); got != tt.wantEscape { t.Errorf("%s: Encode(%s):\n\tgot: %s\n\twant: %s", tt.Where, tt.Name, got, tt.wantEscape) } buf.Reset() enc.SetEscapeHTML(false) if err := enc.Encode(tt.v); err != nil { t.Fatalf("%s: SetEscapeHTML(false) Encode(%s) error: %s", tt.Where, tt.Name, err) } if got := strings.TrimSpace(buf.String()); got != tt.want { t.Errorf("%s: SetEscapeHTML(false) Encode(%s):\n\tgot: %s\n\twant: %s", tt.Where, tt.Name, got, tt.want) } }) } } func TestDecoder(t *testing.T) { for i := 0; i <= len(streamTest); i++ { // Use stream without newlines as input, // just to stress the decoder even more. // Our test input does not include back-to-back numbers. // Otherwise stripping the newlines would // merge two adjacent JSON values. var buf bytes.Buffer for _, c := range nlines(streamEncoded, i) { if c != '\n' { buf.WriteRune(c) } } out := make([]any, i) dec := NewDecoder(&buf) for j := range out { if err := dec.Decode(&out[j]); err != nil { t.Fatalf("decode #%d/%d error: %v", j, i, err) } } if !reflect.DeepEqual(out, streamTest[0:i]) { t.Errorf("decoding %d items: mismatch:", i) for j := range out { if !reflect.DeepEqual(out[j], streamTest[j]) { t.Errorf("#%d:\n\tgot: %v\n\twant: %v", j, out[j], streamTest[j]) } } break } } } func TestDecoderBuffered(t *testing.T) { r := strings.NewReader(`{"Name": "Gopher"} extra `) var m struct { Name string } d := NewDecoder(r) err := d.Decode(&m) if err != nil { t.Fatal(err) } if m.Name != "Gopher" { t.Errorf("Name = %s, want Gopher", m.Name) } rest, err := io.ReadAll(d.Buffered()) if err != nil { t.Fatal(err) } if got, want := string(rest), " extra "; got != want { t.Errorf("Remaining = %s, want %s", got, want) } } func nlines(s string, n int) string { if n <= 0 { return "" } for i, c := range s { if c == '\n' { if n--; n == 0 { return s[0 : i+1] } } } return s } func TestRawMessage(t *testing.T) { var data struct { X float64 Id RawMessage Y float32 } const raw = `["\u0056",null]` const want = `{"X":0.1,"Id":["\u0056",null],"Y":0.2}` err := Unmarshal([]byte(want), &data) if err != nil { t.Fatalf("Unmarshal error: %v", err) } if string([]byte(data.Id)) != raw { t.Fatalf("Unmarshal:\n\tgot: %s\n\twant: %s", []byte(data.Id), raw) } got, err := Marshal(&data) if err != nil { t.Fatalf("Marshal error: %v", err) } if string(got) != want { t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want) } } func TestNullRawMessage(t *testing.T) { var data struct { X float64 Id RawMessage IdPtr *RawMessage Y float32 } const want = `{"X":0.1,"Id":null,"IdPtr":null,"Y":0.2}` err := Unmarshal([]byte(want), &data) if err != nil { t.Fatalf("Unmarshal error: %v", err) } if want, got := "null", string(data.Id); want != got { t.Fatalf("Unmarshal:\n\tgot: %s\n\twant: %s", got, want) } if data.IdPtr != nil { t.Fatalf("pointer mismatch: got non-nil, want nil") } got, err := Marshal(&data) if err != nil { t.Fatalf("Marshal error: %v", err) } if string(got) != want { t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want) } } func TestBlocking(t *testing.T) { tests := []struct { CaseName in string }{ {Name(""), `{"x": 1}`}, {Name(""), `[1, 2, 3]`}, } for _, tt := range tests { t.Run(tt.Name, func(t *testing.T) { r, w := net.Pipe() go w.Write([]byte(tt.in)) var val any // If Decode reads beyond what w.Write writes above, // it will block, and the test will deadlock. if err := NewDecoder(r).Decode(&val); err != nil { t.Errorf("%s: NewDecoder(%s).Decode error: %v", tt.Where, tt.in, err) } r.Close() w.Close() }) } } type decodeThis struct { v any } func TestDecodeInStream(t *testing.T) { tests := []struct { CaseName json string expTokens []any }{ // streaming token cases {CaseName: Name(""), json: `10`, expTokens: []any{float64(10)}}, {CaseName: Name(""), json: ` [10] `, expTokens: []any{ Delim('['), float64(10), Delim(']')}}, {CaseName: Name(""), json: ` [false,10,"b"] `, expTokens: []any{ Delim('['), false, float64(10), "b", Delim(']')}}, {CaseName: Name(""), json: `{ "a": 1 }`, expTokens: []any{ Delim('{'), "a", float64(1), Delim('}')}}, {CaseName: Name(""), json: `{"a": 1, "b":"3"}`, expTokens: []any{ Delim('{'), "a", float64(1), "b", "3", Delim('}')}}, {CaseName: Name(""), json: ` [{"a": 1},{"a": 2}] `, expTokens: []any{ Delim('['), Delim('{'), "a", float64(1), Delim('}'), Delim('{'), "a", float64(2), Delim('}'), Delim(']')}}, {CaseName: Name(""), json: `{"obj": {"a": 1}}`, expTokens: []any{ Delim('{'), "obj", Delim('{'), "a", float64(1), Delim('}'), Delim('}')}}, {CaseName: Name(""), json: `{"obj": [{"a": 1}]}`, expTokens: []any{ Delim('{'), "obj", Delim('['), Delim('{'), "a", float64(1), Delim('}'), Delim(']'), Delim('}')}}, // streaming tokens with intermittent Decode() {CaseName: Name(""), json: `{ "a": 1 }`, expTokens: []any{ Delim('{'), "a", decodeThis{float64(1)}, Delim('}')}}, {CaseName: Name(""), json: ` [ { "a" : 1 } ] `, expTokens: []any{ Delim('['), decodeThis{map[string]any{"a": float64(1)}}, Delim(']')}}, {CaseName: Name(""), json: ` [{"a": 1},{"a": 2}] `, expTokens: []any{ Delim('['), decodeThis{map[string]any{"a": float64(1)}}, decodeThis{map[string]any{"a": float64(2)}}, Delim(']')}}, {CaseName: Name(""), json: `{ "obj" : [ { "a" : 1 } ] }`, expTokens: []any{ Delim('{'), "obj", Delim('['), decodeThis{map[string]any{"a": float64(1)}}, Delim(']'), Delim('}')}}, {CaseName: Name(""), json: `{"obj": {"a": 1}}`, expTokens: []any{ Delim('{'), "obj", decodeThis{map[string]any{"a": float64(1)}}, Delim('}')}}, {CaseName: Name(""), json: `{"obj": [{"a": 1}]}`, expTokens: []any{ Delim('{'), "obj", decodeThis{[]any{ map[string]any{"a": float64(1)}, }}, Delim('}')}}, {CaseName: Name(""), json: ` [{"a": 1} {"a": 2}] `, expTokens: []any{ Delim('['), decodeThis{map[string]any{"a": float64(1)}}, decodeThis{&SyntaxError{"expected comma after array element", 11}}, }}, {CaseName: Name(""), json: `{ "` + strings.Repeat("a", 513) + `" 1 }`, expTokens: []any{ Delim('{'), strings.Repeat("a", 513), decodeThis{&SyntaxError{"expected colon after object key", 518}}, }}, {CaseName: Name(""), json: `{ "\a" }`, expTokens: []any{ Delim('{'), &SyntaxError{"invalid character 'a' in string escape code", 3}, }}, {CaseName: Name(""), json: ` \a`, expTokens: []any{ &SyntaxError{"invalid character '\\\\' looking for beginning of value", 1}, }}, } for _, tt := range tests { t.Run(tt.Name, func(t *testing.T) { dec := NewDecoder(strings.NewReader(tt.json)) for i, want := range tt.expTokens { var got any var err error if dt, ok := want.(decodeThis); ok { want = dt.v err = dec.Decode(&got) } else { got, err = dec.Token() } if errWant, ok := want.(error); ok { if err == nil || !reflect.DeepEqual(err, errWant) { t.Fatalf("%s:\n\tinput: %s\n\tgot error: %v\n\twant error: %v", tt.Where, tt.json, err, errWant) } break } else if err != nil { t.Fatalf("%s:\n\tinput: %s\n\tgot error: %v\n\twant error: nil", tt.Where, tt.json, err) } if !reflect.DeepEqual(got, want) { t.Fatalf("%s: token %d:\n\tinput: %s\n\tgot: %T(%v)\n\twant: %T(%v)", tt.Where, i, tt.json, got, got, want, want) } } }) } } // Test from golang.org/issue/11893 func TestHTTPDecoding(t *testing.T) { const raw = `{ "foo": "bar" }` ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(raw)) })) defer ts.Close() res, err := http.Get(ts.URL) if err != nil { log.Fatalf("http.Get error: %v", err) } defer res.Body.Close() foo := struct { Foo string }{} d := NewDecoder(res.Body) err = d.Decode(&foo) if err != nil { t.Fatalf("Decode error: %v", err) } if foo.Foo != "bar" { t.Errorf(`Decode: got %q, want "bar"`, foo.Foo) } // make sure we get the EOF the second time err = d.Decode(&foo) if err != io.EOF { t.Errorf("Decode error:\n\tgot: %v\n\twant: io.EOF", err) } }