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/xml
Viewing File: /opt/golang/1.22.0/src/encoding/xml/xml_test.go
// Copyright 2009 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 xml import ( "bytes" "fmt" "io" "reflect" "strings" "testing" "unicode/utf8" ) type toks struct { earlyEOF bool t []Token } func (t *toks) Token() (Token, error) { if len(t.t) == 0 { return nil, io.EOF } var tok Token tok, t.t = t.t[0], t.t[1:] if t.earlyEOF && len(t.t) == 0 { return tok, io.EOF } return tok, nil } func TestDecodeEOF(t *testing.T) { start := StartElement{Name: Name{Local: "test"}} tests := []struct { name string tokens []Token ok bool }{ { name: "OK", tokens: []Token{ start, start.End(), }, ok: true, }, { name: "Malformed", tokens: []Token{ start, StartElement{Name: Name{Local: "bad"}}, start.End(), }, ok: false, }, } for _, tc := range tests { for _, eof := range []bool{true, false} { name := fmt.Sprintf("%s/earlyEOF=%v", tc.name, eof) t.Run(name, func(t *testing.T) { d := NewTokenDecoder(&toks{ earlyEOF: eof, t: tc.tokens, }) err := d.Decode(&struct { XMLName Name `xml:"test"` }{}) if tc.ok && err != nil { t.Fatalf("d.Decode: expected nil error, got %v", err) } if _, ok := err.(*SyntaxError); !tc.ok && !ok { t.Errorf("d.Decode: expected syntax error, got %v", err) } }) } } } type toksNil struct { returnEOF bool t []Token } func (t *toksNil) Token() (Token, error) { if len(t.t) == 0 { if !t.returnEOF { // Return nil, nil before returning an EOF. It's legal, but // discouraged. t.returnEOF = true return nil, nil } return nil, io.EOF } var tok Token tok, t.t = t.t[0], t.t[1:] return tok, nil } func TestDecodeNilToken(t *testing.T) { for _, strict := range []bool{true, false} { name := fmt.Sprintf("Strict=%v", strict) t.Run(name, func(t *testing.T) { start := StartElement{Name: Name{Local: "test"}} bad := StartElement{Name: Name{Local: "bad"}} d := NewTokenDecoder(&toksNil{ // Malformed t: []Token{start, bad, start.End()}, }) d.Strict = strict err := d.Decode(&struct { XMLName Name `xml:"test"` }{}) if _, ok := err.(*SyntaxError); !ok { t.Errorf("d.Decode: expected syntax error, got %v", err) } }) } } const testInput = ` <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <body xmlns:foo="ns1" xmlns="ns2" xmlns:tag="ns3" ` + "\r\n\t" + ` > <hello lang="en">World &lt;&gt;&apos;&quot; &#x767d;&#40300;翔</hello> <query>&何; &is-it;</query> <goodbye /> <outer foo:attr="value" xmlns:tag="ns4"> <inner/> </outer> <tag:name> <![CDATA[Some text here.]]> </tag:name> </body><!-- missing final newline -->` var testEntity = map[string]string{"何": "What", "is-it": "is it?"} var rawTokens = []Token{ CharData("\n"), ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)}, CharData("\n"), Directive(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`), CharData("\n"), StartElement{Name{"", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}}, CharData("\n "), StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}}, CharData("World <>'\" 白鵬翔"), EndElement{Name{"", "hello"}}, CharData("\n "), StartElement{Name{"", "query"}, []Attr{}}, CharData("What is it?"), EndElement{Name{"", "query"}}, CharData("\n "), StartElement{Name{"", "goodbye"}, []Attr{}}, EndElement{Name{"", "goodbye"}}, CharData("\n "), StartElement{Name{"", "outer"}, []Attr{{Name{"foo", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}}, CharData("\n "), StartElement{Name{"", "inner"}, []Attr{}}, EndElement{Name{"", "inner"}}, CharData("\n "), EndElement{Name{"", "outer"}}, CharData("\n "), StartElement{Name{"tag", "name"}, []Attr{}}, CharData("\n "), CharData("Some text here."), CharData("\n "), EndElement{Name{"tag", "name"}}, CharData("\n"), EndElement{Name{"", "body"}}, Comment(" missing final newline "), } var cookedTokens = []Token{ CharData("\n"), ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)}, CharData("\n"), Directive(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`), CharData("\n"), StartElement{Name{"ns2", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}}, CharData("\n "), StartElement{Name{"ns2", "hello"}, []Attr{{Name{"", "lang"}, "en"}}}, CharData("World <>'\" 白鵬翔"), EndElement{Name{"ns2", "hello"}}, CharData("\n "), StartElement{Name{"ns2", "query"}, []Attr{}}, CharData("What is it?"), EndElement{Name{"ns2", "query"}}, CharData("\n "), StartElement{Name{"ns2", "goodbye"}, []Attr{}}, EndElement{Name{"ns2", "goodbye"}}, CharData("\n "), StartElement{Name{"ns2", "outer"}, []Attr{{Name{"ns1", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}}, CharData("\n "), StartElement{Name{"ns2", "inner"}, []Attr{}}, EndElement{Name{"ns2", "inner"}}, CharData("\n "), EndElement{Name{"ns2", "outer"}}, CharData("\n "), StartElement{Name{"ns3", "name"}, []Attr{}}, CharData("\n "), CharData("Some text here."), CharData("\n "), EndElement{Name{"ns3", "name"}}, CharData("\n"), EndElement{Name{"ns2", "body"}}, Comment(" missing final newline "), } const testInputAltEncoding = ` <?xml version="1.0" encoding="x-testing-uppercase"?> <TAG>VALUE</TAG>` var rawTokensAltEncoding = []Token{ CharData("\n"), ProcInst{"xml", []byte(`version="1.0" encoding="x-testing-uppercase"`)}, CharData("\n"), StartElement{Name{"", "tag"}, []Attr{}}, CharData("value"), EndElement{Name{"", "tag"}}, } var xmlInput = []string{ // unexpected EOF cases "<", "<t", "<t ", "<t/", "<!", "<!-", "<!--", "<!--c-", "<!--c--", "<!d", "<t></", "<t></t", "<?", "<?p", "<t a", "<t a=", "<t a='", "<t a=''", "<t/><![", "<t/><![C", "<t/><![CDATA[d", "<t/><![CDATA[d]", "<t/><![CDATA[d]]", // other Syntax errors "<>", "<t/a", "<0 />", "<?0 >", // "<!0 >", // let the Token() caller handle "</0>", "<t 0=''>", "<t a='&'>", "<t a='<'>", "<t>&nbspc;</t>", "<t a>", "<t a=>", "<t a=v>", // "<![CDATA[d]]>", // let the Token() caller handle "<t></e>", "<t></>", "<t></t!", "<t>cdata]]></t>", } func TestRawToken(t *testing.T) { d := NewDecoder(strings.NewReader(testInput)) d.Entity = testEntity testRawToken(t, d, testInput, rawTokens) } const nonStrictInput = ` <tag>non&entity</tag> <tag>&unknown;entity</tag> <tag>&#123</tag> <tag>&#zzz;</tag> <tag>&なまえ3;</tag> <tag>&lt-gt;</tag> <tag>&;</tag> <tag>&0a;</tag> ` var nonStrictTokens = []Token{ CharData("\n"), StartElement{Name{"", "tag"}, []Attr{}}, CharData("non&entity"), EndElement{Name{"", "tag"}}, CharData("\n"), StartElement{Name{"", "tag"}, []Attr{}}, CharData("&unknown;entity"), EndElement{Name{"", "tag"}}, CharData("\n"), StartElement{Name{"", "tag"}, []Attr{}}, CharData("&#123"), EndElement{Name{"", "tag"}}, CharData("\n"), StartElement{Name{"", "tag"}, []Attr{}}, CharData("&#zzz;"), EndElement{Name{"", "tag"}}, CharData("\n"), StartElement{Name{"", "tag"}, []Attr{}}, CharData("&なまえ3;"), EndElement{Name{"", "tag"}}, CharData("\n"), StartElement{Name{"", "tag"}, []Attr{}}, CharData("&lt-gt;"), EndElement{Name{"", "tag"}}, CharData("\n"), StartElement{Name{"", "tag"}, []Attr{}}, CharData("&;"), EndElement{Name{"", "tag"}}, CharData("\n"), StartElement{Name{"", "tag"}, []Attr{}}, CharData("&0a;"), EndElement{Name{"", "tag"}}, CharData("\n"), } func TestNonStrictRawToken(t *testing.T) { d := NewDecoder(strings.NewReader(nonStrictInput)) d.Strict = false testRawToken(t, d, nonStrictInput, nonStrictTokens) } type downCaser struct { t *testing.T r io.ByteReader } func (d *downCaser) ReadByte() (c byte, err error) { c, err = d.r.ReadByte() if c >= 'A' && c <= 'Z' { c += 'a' - 'A' } return } func (d *downCaser) Read(p []byte) (int, error) { d.t.Fatalf("unexpected Read call on downCaser reader") panic("unreachable") } func TestRawTokenAltEncoding(t *testing.T) { d := NewDecoder(strings.NewReader(testInputAltEncoding)) d.CharsetReader = func(charset string, input io.Reader) (io.Reader, error) { if charset != "x-testing-uppercase" { t.Fatalf("unexpected charset %q", charset) } return &downCaser{t, input.(io.ByteReader)}, nil } testRawToken(t, d, testInputAltEncoding, rawTokensAltEncoding) } func TestRawTokenAltEncodingNoConverter(t *testing.T) { d := NewDecoder(strings.NewReader(testInputAltEncoding)) token, err := d.RawToken() if token == nil { t.Fatalf("expected a token on first RawToken call") } if err != nil { t.Fatal(err) } token, err = d.RawToken() if token != nil { t.Errorf("expected a nil token; got %#v", token) } if err == nil { t.Fatalf("expected an error on second RawToken call") } const encoding = "x-testing-uppercase" if !strings.Contains(err.Error(), encoding) { t.Errorf("expected error to contain %q; got error: %v", encoding, err) } } func testRawToken(t *testing.T, d *Decoder, raw string, rawTokens []Token) { lastEnd := int64(0) for i, want := range rawTokens { start := d.InputOffset() have, err := d.RawToken() end := d.InputOffset() if err != nil { t.Fatalf("token %d: unexpected error: %s", i, err) } if !reflect.DeepEqual(have, want) { var shave, swant string if _, ok := have.(CharData); ok { shave = fmt.Sprintf("CharData(%q)", have) } else { shave = fmt.Sprintf("%#v", have) } if _, ok := want.(CharData); ok { swant = fmt.Sprintf("CharData(%q)", want) } else { swant = fmt.Sprintf("%#v", want) } t.Errorf("token %d = %s, want %s", i, shave, swant) } // Check that InputOffset returned actual token. switch { case start < lastEnd: t.Errorf("token %d: position [%d,%d) for %T is before previous token", i, start, end, have) case start >= end: // Special case: EndElement can be synthesized. if start == end && end == lastEnd { break } t.Errorf("token %d: position [%d,%d) for %T is empty", i, start, end, have) case end > int64(len(raw)): t.Errorf("token %d: position [%d,%d) for %T extends beyond input", i, start, end, have) default: text := raw[start:end] if strings.ContainsAny(text, "<>") && (!strings.HasPrefix(text, "<") || !strings.HasSuffix(text, ">")) { t.Errorf("token %d: misaligned raw token %#q for %T", i, text, have) } } lastEnd = end } } // Ensure that directives (specifically !DOCTYPE) include the complete // text of any nested directives, noting that < and > do not change // nesting depth if they are in single or double quotes. var nestedDirectivesInput = ` <!DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]> <!DOCTYPE [<!ENTITY xlt ">">]> <!DOCTYPE [<!ENTITY xlt "<">]> <!DOCTYPE [<!ENTITY xlt '>'>]> <!DOCTYPE [<!ENTITY xlt '<'>]> <!DOCTYPE [<!ENTITY xlt '">'>]> <!DOCTYPE [<!ENTITY xlt "'<">]> ` var nestedDirectivesTokens = []Token{ CharData("\n"), Directive(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`), CharData("\n"), Directive(`DOCTYPE [<!ENTITY xlt ">">]`), CharData("\n"), Directive(`DOCTYPE [<!ENTITY xlt "<">]`), CharData("\n"), Directive(`DOCTYPE [<!ENTITY xlt '>'>]`), CharData("\n"), Directive(`DOCTYPE [<!ENTITY xlt '<'>]`), CharData("\n"), Directive(`DOCTYPE [<!ENTITY xlt '">'>]`), CharData("\n"), Directive(`DOCTYPE [<!ENTITY xlt "'<">]`), CharData("\n"), } func TestNestedDirectives(t *testing.T) { d := NewDecoder(strings.NewReader(nestedDirectivesInput)) for i, want := range nestedDirectivesTokens { have, err := d.Token() if err != nil { t.Fatalf("token %d: unexpected error: %s", i, err) } if !reflect.DeepEqual(have, want) { t.Errorf("token %d = %#v want %#v", i, have, want) } } } func TestToken(t *testing.T) { d := NewDecoder(strings.NewReader(testInput)) d.Entity = testEntity for i, want := range cookedTokens { have, err := d.Token() if err != nil { t.Fatalf("token %d: unexpected error: %s", i, err) } if !reflect.DeepEqual(have, want) { t.Errorf("token %d = %#v want %#v", i, have, want) } } } func TestSyntax(t *testing.T) { for i := range xmlInput { d := NewDecoder(strings.NewReader(xmlInput[i])) var err error for _, err = d.Token(); err == nil; _, err = d.Token() { } if _, ok := err.(*SyntaxError); !ok { t.Fatalf(`xmlInput "%s": expected SyntaxError not received`, xmlInput[i]) } } } func TestInputLinePos(t *testing.T) { testInput := `<root> <?pi ?> <elt att = "val"> <![CDATA[ ]]><!-- --></elt> </root>` linePos := [][]int{ {1, 7}, {2, 1}, {3, 4}, {3, 6}, {6, 7}, {7, 1}, {8, 4}, {10, 4}, {10, 10}, {11, 1}, {11, 8}, } dec := NewDecoder(strings.NewReader(testInput)) for _, want := range linePos { if _, err := dec.Token(); err != nil { t.Errorf("Unexpected error: %v", err) continue } gotLine, gotCol := dec.InputPos() if gotLine != want[0] || gotCol != want[1] { t.Errorf("dec.InputPos() = %d,%d, want %d,%d", gotLine, gotCol, want[0], want[1]) } } } type allScalars struct { True1 bool True2 bool False1 bool False2 bool Int int Int8 int8 Int16 int16 Int32 int32 Int64 int64 Uint int Uint8 uint8 Uint16 uint16 Uint32 uint32 Uint64 uint64 Uintptr uintptr Float32 float32 Float64 float64 String string PtrString *string } var all = allScalars{ True1: true, True2: true, False1: false, False2: false, Int: 1, Int8: -2, Int16: 3, Int32: -4, Int64: 5, Uint: 6, Uint8: 7, Uint16: 8, Uint32: 9, Uint64: 10, Uintptr: 11, Float32: 13.0, Float64: 14.0, String: "15", PtrString: &sixteen, } var sixteen = "16" const testScalarsInput = `<allscalars> <True1>true</True1> <True2>1</True2> <False1>false</False1> <False2>0</False2> <Int>1</Int> <Int8>-2</Int8> <Int16>3</Int16> <Int32>-4</Int32> <Int64>5</Int64> <Uint>6</Uint> <Uint8>7</Uint8> <Uint16>8</Uint16> <Uint32>9</Uint32> <Uint64>10</Uint64> <Uintptr>11</Uintptr> <Float>12.0</Float> <Float32>13.0</Float32> <Float64>14.0</Float64> <String>15</String> <PtrString>16</PtrString> </allscalars>` func TestAllScalars(t *testing.T) { var a allScalars err := Unmarshal([]byte(testScalarsInput), &a) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(a, all) { t.Errorf("have %+v want %+v", a, all) } } type item struct { FieldA string } func TestIssue569(t *testing.T) { data := `<item><FieldA>abcd</FieldA></item>` var i item err := Unmarshal([]byte(data), &i) if err != nil || i.FieldA != "abcd" { t.Fatal("Expecting abcd") } } func TestUnquotedAttrs(t *testing.T) { data := "<tag attr=azAZ09:-_\t>" d := NewDecoder(strings.NewReader(data)) d.Strict = false token, err := d.Token() if _, ok := err.(*SyntaxError); ok { t.Errorf("Unexpected error: %v", err) } if token.(StartElement).Name.Local != "tag" { t.Errorf("Unexpected tag name: %v", token.(StartElement).Name.Local) } attr := token.(StartElement).Attr[0] if attr.Value != "azAZ09:-_" { t.Errorf("Unexpected attribute value: %v", attr.Value) } if attr.Name.Local != "attr" { t.Errorf("Unexpected attribute name: %v", attr.Name.Local) } } func TestValuelessAttrs(t *testing.T) { tests := [][3]string{ {"<p nowrap>", "p", "nowrap"}, {"<p nowrap >", "p", "nowrap"}, {"<input checked/>", "input", "checked"}, {"<input checked />", "input", "checked"}, } for _, test := range tests { d := NewDecoder(strings.NewReader(test[0])) d.Strict = false token, err := d.Token() if _, ok := err.(*SyntaxError); ok { t.Errorf("Unexpected error: %v", err) } if token.(StartElement).Name.Local != test[1] { t.Errorf("Unexpected tag name: %v", token.(StartElement).Name.Local) } attr := token.(StartElement).Attr[0] if attr.Value != test[2] { t.Errorf("Unexpected attribute value: %v", attr.Value) } if attr.Name.Local != test[2] { t.Errorf("Unexpected attribute name: %v", attr.Name.Local) } } } func TestCopyTokenCharData(t *testing.T) { data := []byte("same data") var tok1 Token = CharData(data) tok2 := CopyToken(tok1) if !reflect.DeepEqual(tok1, tok2) { t.Error("CopyToken(CharData) != CharData") } data[1] = 'o' if reflect.DeepEqual(tok1, tok2) { t.Error("CopyToken(CharData) uses same buffer.") } } func TestCopyTokenStartElement(t *testing.T) { elt := StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}} var tok1 Token = elt tok2 := CopyToken(tok1) if tok1.(StartElement).Attr[0].Value != "en" { t.Error("CopyToken overwrote Attr[0]") } if !reflect.DeepEqual(tok1, tok2) { t.Error("CopyToken(StartElement) != StartElement") } tok1.(StartElement).Attr[0] = Attr{Name{"", "lang"}, "de"} if reflect.DeepEqual(tok1, tok2) { t.Error("CopyToken(CharData) uses same buffer.") } } func TestCopyTokenComment(t *testing.T) { data := []byte("<!-- some comment -->") var tok1 Token = Comment(data) tok2 := CopyToken(tok1) if !reflect.DeepEqual(tok1, tok2) { t.Error("CopyToken(Comment) != Comment") } data[1] = 'o' if reflect.DeepEqual(tok1, tok2) { t.Error("CopyToken(Comment) uses same buffer.") } } func TestSyntaxErrorLineNum(t *testing.T) { testInput := "<P>Foo<P>\n\n<P>Bar</>\n" d := NewDecoder(strings.NewReader(testInput)) var err error for _, err = d.Token(); err == nil; _, err = d.Token() { } synerr, ok := err.(*SyntaxError) if !ok { t.Error("Expected SyntaxError.") } if synerr.Line != 3 { t.Error("SyntaxError didn't have correct line number.") } } func TestTrailingRawToken(t *testing.T) { input := `<FOO></FOO> ` d := NewDecoder(strings.NewReader(input)) var err error for _, err = d.RawToken(); err == nil; _, err = d.RawToken() { } if err != io.EOF { t.Fatalf("d.RawToken() = _, %v, want _, io.EOF", err) } } func TestTrailingToken(t *testing.T) { input := `<FOO></FOO> ` d := NewDecoder(strings.NewReader(input)) var err error for _, err = d.Token(); err == nil; _, err = d.Token() { } if err != io.EOF { t.Fatalf("d.Token() = _, %v, want _, io.EOF", err) } } func TestEntityInsideCDATA(t *testing.T) { input := `<test><![CDATA[ &val=foo ]]></test>` d := NewDecoder(strings.NewReader(input)) var err error for _, err = d.Token(); err == nil; _, err = d.Token() { } if err != io.EOF { t.Fatalf("d.Token() = _, %v, want _, io.EOF", err) } } var characterTests = []struct { in string err string }{ {"\x12<doc/>", "illegal character code U+0012"}, {"<?xml version=\"1.0\"?>\x0b<doc/>", "illegal character code U+000B"}, {"\xef\xbf\xbe<doc/>", "illegal character code U+FFFE"}, {"<?xml version=\"1.0\"?><doc>\r\n<hiya/>\x07<toots/></doc>", "illegal character code U+0007"}, {"<?xml version=\"1.0\"?><doc \x12='value'>what's up</doc>", "expected attribute name in element"}, {"<doc>&abc\x01;</doc>", "invalid character entity &abc (no semicolon)"}, {"<doc>&\x01;</doc>", "invalid character entity & (no semicolon)"}, {"<doc>&\xef\xbf\xbe;</doc>", "invalid character entity &\uFFFE;"}, {"<doc>&hello;</doc>", "invalid character entity &hello;"}, } func TestDisallowedCharacters(t *testing.T) { for i, tt := range characterTests { d := NewDecoder(strings.NewReader(tt.in)) var err error for err == nil { _, err = d.Token() } synerr, ok := err.(*SyntaxError) if !ok { t.Fatalf("input %d d.Token() = _, %v, want _, *SyntaxError", i, err) } if synerr.Msg != tt.err { t.Fatalf("input %d synerr.Msg wrong: want %q, got %q", i, tt.err, synerr.Msg) } } } func TestIsInCharacterRange(t *testing.T) { invalid := []rune{ utf8.MaxRune + 1, 0xD800, // surrogate min 0xDFFF, // surrogate max -1, } for _, r := range invalid { if isInCharacterRange(r) { t.Errorf("rune %U considered valid", r) } } } var procInstTests = []struct { input string expect [2]string }{ {`version="1.0" encoding="utf-8"`, [2]string{"1.0", "utf-8"}}, {`version="1.0" encoding='utf-8'`, [2]string{"1.0", "utf-8"}}, {`version="1.0" encoding='utf-8' `, [2]string{"1.0", "utf-8"}}, {`version="1.0" encoding=utf-8`, [2]string{"1.0", ""}}, {`encoding="FOO" `, [2]string{"", "FOO"}}, } func TestProcInstEncoding(t *testing.T) { for _, test := range procInstTests { if got := procInst("version", test.input); got != test.expect[0] { t.Errorf("procInst(version, %q) = %q; want %q", test.input, got, test.expect[0]) } if got := procInst("encoding", test.input); got != test.expect[1] { t.Errorf("procInst(encoding, %q) = %q; want %q", test.input, got, test.expect[1]) } } } // Ensure that directives with comments include the complete // text of any nested directives. var directivesWithCommentsInput = ` <!DOCTYPE [<!-- a comment --><!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]> <!DOCTYPE [<!ENTITY go "Golang"><!-- a comment-->]> <!DOCTYPE <!-> <!> <!----> <!-->--> <!--->--> [<!ENTITY go "Golang"><!-- a comment-->]> ` var directivesWithCommentsTokens = []Token{ CharData("\n"), Directive(`DOCTYPE [ <!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`), CharData("\n"), Directive(`DOCTYPE [<!ENTITY go "Golang"> ]`), CharData("\n"), Directive(`DOCTYPE <!-> <!> [<!ENTITY go "Golang"> ]`), CharData("\n"), } func TestDirectivesWithComments(t *testing.T) { d := NewDecoder(strings.NewReader(directivesWithCommentsInput)) for i, want := range directivesWithCommentsTokens { have, err := d.Token() if err != nil { t.Fatalf("token %d: unexpected error: %s", i, err) } if !reflect.DeepEqual(have, want) { t.Errorf("token %d = %#v want %#v", i, have, want) } } } // Writer whose Write method always returns an error. type errWriter struct{} func (errWriter) Write(p []byte) (n int, err error) { return 0, fmt.Errorf("unwritable") } func TestEscapeTextIOErrors(t *testing.T) { expectErr := "unwritable" err := EscapeText(errWriter{}, []byte{'A'}) if err == nil || err.Error() != expectErr { t.Errorf("have %v, want %v", err, expectErr) } } func TestEscapeTextInvalidChar(t *testing.T) { input := []byte("A \x00 terminated string.") expected := "A \uFFFD terminated string." buff := new(strings.Builder) if err := EscapeText(buff, input); err != nil { t.Fatalf("have %v, want nil", err) } text := buff.String() if text != expected { t.Errorf("have %v, want %v", text, expected) } } func TestIssue5880(t *testing.T) { type T []byte data, err := Marshal(T{192, 168, 0, 1}) if err != nil { t.Errorf("Marshal error: %v", err) } if !utf8.Valid(data) { t.Errorf("Marshal generated invalid UTF-8: %x", data) } } func TestIssue8535(t *testing.T) { type ExampleConflict struct { XMLName Name `xml:"example"` Link string `xml:"link"` AtomLink string `xml:"http://www.w3.org/2005/Atom link"` // Same name in a different name space } testCase := `<example> <title>Example</title> <link>http://example.com/default</link> <!-- not assigned --> <link>http://example.com/home</link> <!-- not assigned --> <ns:link xmlns:ns="http://www.w3.org/2005/Atom">http://example.com/ns</ns:link> </example>` var dest ExampleConflict d := NewDecoder(strings.NewReader(testCase)) if err := d.Decode(&dest); err != nil { t.Fatal(err) } } func TestEncodeXMLNS(t *testing.T) { testCases := []struct { f func() ([]byte, error) want string ok bool }{ {encodeXMLNS1, `<Test xmlns="http://example.com/ns"><Body>hello world</Body></Test>`, true}, {encodeXMLNS2, `<Test><body xmlns="http://example.com/ns">hello world</body></Test>`, true}, {encodeXMLNS3, `<Test xmlns="http://example.com/ns"><Body>hello world</Body></Test>`, true}, {encodeXMLNS4, `<Test xmlns="http://example.com/ns"><Body>hello world</Body></Test>`, false}, } for i, tc := range testCases { if b, err := tc.f(); err == nil { if got, want := string(b), tc.want; got != want { t.Errorf("%d: got %s, want %s \n", i, got, want) } } else { t.Errorf("%d: marshal failed with %s", i, err) } } } func encodeXMLNS1() ([]byte, error) { type T struct { XMLName Name `xml:"Test"` Ns string `xml:"xmlns,attr"` Body string } s := &T{Ns: "http://example.com/ns", Body: "hello world"} return Marshal(s) } func encodeXMLNS2() ([]byte, error) { type Test struct { Body string `xml:"http://example.com/ns body"` } s := &Test{Body: "hello world"} return Marshal(s) } func encodeXMLNS3() ([]byte, error) { type Test struct { XMLName Name `xml:"http://example.com/ns Test"` Body string } //s := &Test{XMLName: Name{"http://example.com/ns",""}, Body: "hello world"} is unusable as the "-" is missing // as documentation states s := &Test{Body: "hello world"} return Marshal(s) } func encodeXMLNS4() ([]byte, error) { type Test struct { Ns string `xml:"xmlns,attr"` Body string } s := &Test{Ns: "http://example.com/ns", Body: "hello world"} return Marshal(s) } func TestIssue11405(t *testing.T) { testCases := []string{ "<root>", "<root><foo>", "<root><foo></foo>", } for _, tc := range testCases { d := NewDecoder(strings.NewReader(tc)) var err error for { _, err = d.Token() if err != nil { break } } if _, ok := err.(*SyntaxError); !ok { t.Errorf("%s: Token: Got error %v, want SyntaxError", tc, err) } } } func TestIssue12417(t *testing.T) { testCases := []struct { s string ok bool }{ {`<?xml encoding="UtF-8" version="1.0"?><root/>`, true}, {`<?xml encoding="UTF-8" version="1.0"?><root/>`, true}, {`<?xml encoding="utf-8" version="1.0"?><root/>`, true}, {`<?xml encoding="uuu-9" version="1.0"?><root/>`, false}, } for _, tc := range testCases { d := NewDecoder(strings.NewReader(tc.s)) var err error for { _, err = d.Token() if err != nil { if err == io.EOF { err = nil } break } } if err != nil && tc.ok { t.Errorf("%q: Encoding charset: expected no error, got %s", tc.s, err) continue } if err == nil && !tc.ok { t.Errorf("%q: Encoding charset: expected error, got nil", tc.s) } } } func TestIssue7113(t *testing.T) { type C struct { XMLName Name `xml:""` // Sets empty namespace } type D struct { XMLName Name `xml:"d"` } type A struct { XMLName Name `xml:""` C C `xml:""` D D } var a A structSpace := "b" xmlTest := `<A xmlns="` + structSpace + `"><C xmlns=""></C><d></d></A>` t.Log(xmlTest) err := Unmarshal([]byte(xmlTest), &a) if err != nil { t.Fatal(err) } if a.XMLName.Space != structSpace { t.Errorf("overidding with empty namespace: unmarshalling, got %s, want %s\n", a.XMLName.Space, structSpace) } if len(a.C.XMLName.Space) != 0 { t.Fatalf("overidding with empty namespace: unmarshalling, got %s, want empty\n", a.C.XMLName.Space) } var b []byte b, err = Marshal(&a) if err != nil { t.Fatal(err) } if len(a.C.XMLName.Space) != 0 { t.Errorf("overidding with empty namespace: marshaling, got %s in C tag which should be empty\n", a.C.XMLName.Space) } if string(b) != xmlTest { t.Fatalf("overidding with empty namespace: marshalling, got %s, want %s\n", b, xmlTest) } var c A err = Unmarshal(b, &c) if err != nil { t.Fatalf("second Unmarshal failed: %s", err) } if c.XMLName.Space != "b" { t.Errorf("overidding with empty namespace: after marshaling & unmarshaling, XML name space: got %s, want %s\n", a.XMLName.Space, structSpace) } if len(c.C.XMLName.Space) != 0 { t.Errorf("overidding with empty namespace: after marshaling & unmarshaling, got %s, want empty\n", a.C.XMLName.Space) } } func TestIssue20396(t *testing.T) { var attrError = UnmarshalError("XML syntax error on line 1: expected attribute name in element") testCases := []struct { s string wantErr error }{ {`<a:te:st xmlns:a="abcd"/>`, // Issue 20396 UnmarshalError("XML syntax error on line 1: expected element name after <")}, {`<a:te=st xmlns:a="abcd"/>`, attrError}, {`<a:te&st xmlns:a="abcd"/>`, attrError}, {`<a:test xmlns:a="abcd"/>`, nil}, {`<a:te:st xmlns:a="abcd">1</a:te:st>`, UnmarshalError("XML syntax error on line 1: expected element name after <")}, {`<a:te=st xmlns:a="abcd">1</a:te=st>`, attrError}, {`<a:te&st xmlns:a="abcd">1</a:te&st>`, attrError}, {`<a:test xmlns:a="abcd">1</a:test>`, nil}, } var dest string for _, tc := range testCases { if got, want := Unmarshal([]byte(tc.s), &dest), tc.wantErr; got != want { if got == nil { t.Errorf("%s: Unexpected success, want %v", tc.s, want) } else if want == nil { t.Errorf("%s: Unexpected error, got %v", tc.s, got) } else if got.Error() != want.Error() { t.Errorf("%s: got %v, want %v", tc.s, got, want) } } } } func TestIssue20685(t *testing.T) { testCases := []struct { s string ok bool }{ {`<x:book xmlns:x="abcd" xmlns:y="abcd"><unclosetag>one</x:book>`, false}, {`<x:book xmlns:x="abcd" xmlns:y="abcd">one</x:book>`, true}, {`<x:book xmlns:x="abcd" xmlns:y="abcd">one</y:book>`, false}, {`<x:book xmlns:y="abcd" xmlns:x="abcd">one</y:book>`, false}, {`<x:book xmlns:x="abcd">one</y:book>`, false}, {`<x:book>one</y:book>`, false}, {`<xbook>one</ybook>`, false}, } for _, tc := range testCases { d := NewDecoder(strings.NewReader(tc.s)) var err error for { _, err = d.Token() if err != nil { if err == io.EOF { err = nil } break } } if err != nil && tc.ok { t.Errorf("%q: Closing tag with namespace : expected no error, got %s", tc.s, err) continue } if err == nil && !tc.ok { t.Errorf("%q: Closing tag with namespace : expected error, got nil", tc.s) } } } func tokenMap(mapping func(t Token) Token) func(TokenReader) TokenReader { return func(src TokenReader) TokenReader { return mapper{ t: src, f: mapping, } } } type mapper struct { t TokenReader f func(Token) Token } func (m mapper) Token() (Token, error) { tok, err := m.t.Token() if err != nil { return nil, err } return m.f(tok), nil } func TestNewTokenDecoderIdempotent(t *testing.T) { d := NewDecoder(strings.NewReader(`<br>`)) d2 := NewTokenDecoder(d) if d != d2 { t.Error("NewTokenDecoder did not detect underlying Decoder") } } func TestWrapDecoder(t *testing.T) { d := NewDecoder(strings.NewReader(`<quote>[Re-enter Clown with a letter, and FABIAN]</quote>`)) m := tokenMap(func(t Token) Token { switch tok := t.(type) { case StartElement: if tok.Name.Local == "quote" { tok.Name.Local = "blocking" return tok } case EndElement: if tok.Name.Local == "quote" { tok.Name.Local = "blocking" return tok } } return t }) d = NewTokenDecoder(m(d)) o := struct { XMLName Name `xml:"blocking"` Chardata string `xml:",chardata"` }{} if err := d.Decode(&o); err != nil { t.Fatal("Got unexpected error while decoding:", err) } if o.Chardata != "[Re-enter Clown with a letter, and FABIAN]" { t.Fatalf("Got unexpected chardata: `%s`\n", o.Chardata) } } type tokReader struct{} func (tokReader) Token() (Token, error) { return StartElement{}, nil } type Failure struct{} func (Failure) UnmarshalXML(*Decoder, StartElement) error { return nil } func TestTokenUnmarshaler(t *testing.T) { defer func() { if r := recover(); r != nil { t.Error("Unexpected panic using custom token unmarshaler") } }() d := NewTokenDecoder(tokReader{}) d.Decode(&Failure{}) } func testRoundTrip(t *testing.T, input string) { d := NewDecoder(strings.NewReader(input)) var tokens []Token var buf bytes.Buffer e := NewEncoder(&buf) for { tok, err := d.Token() if err == io.EOF { break } if err != nil { t.Fatalf("invalid input: %v", err) } if err := e.EncodeToken(tok); err != nil { t.Fatalf("failed to re-encode input: %v", err) } tokens = append(tokens, CopyToken(tok)) } if err := e.Flush(); err != nil { t.Fatal(err) } d = NewDecoder(&buf) for { tok, err := d.Token() if err == io.EOF { break } if err != nil { t.Fatalf("failed to decode output: %v", err) } if len(tokens) == 0 { t.Fatalf("unexpected token: %#v", tok) } a, b := tokens[0], tok if !reflect.DeepEqual(a, b) { t.Fatalf("token mismatch: %#v vs %#v", a, b) } tokens = tokens[1:] } if len(tokens) > 0 { t.Fatalf("lost tokens: %#v", tokens) } } func TestRoundTrip(t *testing.T) { tests := map[string]string{ "trailing colon": `<foo abc:="x"></foo>`, "comments in directives": `<!ENTITY x<!<!-- c1 [ " -->--x --> > <e></e> <!DOCTYPE xxx [ x<!-- c2 " -->--x ]>`, } for name, input := range tests { t.Run(name, func(t *testing.T) { testRoundTrip(t, input) }) } } func TestParseErrors(t *testing.T) { withDefaultHeader := func(s string) string { return `<?xml version="1.0" encoding="UTF-8"?>` + s } tests := []struct { src string err string }{ {withDefaultHeader(`</foo>`), `unexpected end element </foo>`}, {withDefaultHeader(`<x:foo></y:foo>`), `element <foo> in space x closed by </foo> in space y`}, {withDefaultHeader(`<? not ok ?>`), `expected target name after <?`}, {withDefaultHeader(`<!- not ok -->`), `invalid sequence <!- not part of <!--`}, {withDefaultHeader(`<!-? not ok -->`), `invalid sequence <!- not part of <!--`}, {withDefaultHeader(`<![not ok]>`), `invalid <![ sequence`}, {withDefaultHeader("\xf1"), `invalid UTF-8`}, // Header-related errors. {`<?xml version="1.1" encoding="UTF-8"?>`, `unsupported version "1.1"; only version 1.0 is supported`}, // Cases below are for "no errors". {withDefaultHeader(`<?ok?>`), ``}, {withDefaultHeader(`<?ok version="ok"?>`), ``}, } for _, test := range tests { d := NewDecoder(strings.NewReader(test.src)) var err error for { _, err = d.Token() if err != nil { break } } if test.err == "" { if err != io.EOF { t.Errorf("parse %s: have %q error, expected none", test.src, err) } continue } // Inv: err != nil if err == io.EOF { t.Errorf("parse %s: unexpected EOF", test.src) continue } if !strings.Contains(err.Error(), test.err) { t.Errorf("parse %s: can't find %q error sudbstring\nerror: %q", test.src, test.err, err) continue } } } const testInputHTMLAutoClose = `<?xml version="1.0" encoding="UTF-8"?> <br> <br/><br/> <br><br> <br></br> <BR> <BR/><BR/> <Br></Br> <BR><span id="test">abc</span><br/><br/>` func BenchmarkHTMLAutoClose(b *testing.B) { b.RunParallel(func(p *testing.PB) { for p.Next() { d := NewDecoder(strings.NewReader(testInputHTMLAutoClose)) d.Strict = false d.AutoClose = HTMLAutoClose d.Entity = HTMLEntity for { _, err := d.Token() if err != nil { if err == io.EOF { break } b.Fatalf("unexpected error: %v", err) } } } }) } func TestHTMLAutoClose(t *testing.T) { wantTokens := []Token{ ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)}, CharData("\n"), StartElement{Name{"", "br"}, []Attr{}}, EndElement{Name{"", "br"}}, CharData("\n"), StartElement{Name{"", "br"}, []Attr{}}, EndElement{Name{"", "br"}}, StartElement{Name{"", "br"}, []Attr{}}, EndElement{Name{"", "br"}}, CharData("\n"), StartElement{Name{"", "br"}, []Attr{}}, EndElement{Name{"", "br"}}, StartElement{Name{"", "br"}, []Attr{}}, EndElement{Name{"", "br"}}, CharData("\n"), StartElement{Name{"", "br"}, []Attr{}}, EndElement{Name{"", "br"}}, CharData("\n"), StartElement{Name{"", "BR"}, []Attr{}}, EndElement{Name{"", "BR"}}, CharData("\n"), StartElement{Name{"", "BR"}, []Attr{}}, EndElement{Name{"", "BR"}}, StartElement{Name{"", "BR"}, []Attr{}}, EndElement{Name{"", "BR"}}, CharData("\n"), StartElement{Name{"", "Br"}, []Attr{}}, EndElement{Name{"", "Br"}}, CharData("\n"), StartElement{Name{"", "BR"}, []Attr{}}, EndElement{Name{"", "BR"}}, StartElement{Name{"", "span"}, []Attr{{Name: Name{"", "id"}, Value: "test"}}}, CharData("abc"), EndElement{Name{"", "span"}}, StartElement{Name{"", "br"}, []Attr{}}, EndElement{Name{"", "br"}}, StartElement{Name{"", "br"}, []Attr{}}, EndElement{Name{"", "br"}}, } d := NewDecoder(strings.NewReader(testInputHTMLAutoClose)) d.Strict = false d.AutoClose = HTMLAutoClose d.Entity = HTMLEntity var haveTokens []Token for { tok, err := d.Token() if err != nil { if err == io.EOF { break } t.Fatalf("unexpected error: %v", err) } haveTokens = append(haveTokens, CopyToken(tok)) } if len(haveTokens) != len(wantTokens) { t.Errorf("tokens count mismatch: have %d, want %d", len(haveTokens), len(wantTokens)) } for i, want := range wantTokens { if i >= len(haveTokens) { t.Errorf("token[%d] expected %#v, have no token", i, want) } else { have := haveTokens[i] if !reflect.DeepEqual(have, want) { t.Errorf("token[%d] mismatch:\nhave: %#v\nwant: %#v", i, have, want) } } } }